diff options
Diffstat (limited to 'Examples')
62 files changed, 31805 insertions, 0 deletions
diff --git a/Examples/MicrophoneCS/CaptureThread.cs b/Examples/MicrophoneCS/CaptureThread.cs new file mode 100644 index 0000000..b76a929 --- /dev/null +++ b/Examples/MicrophoneCS/CaptureThread.cs @@ -0,0 +1,61 @@ +using System.Runtime.ExceptionServices; +using Whisper; + +namespace MicrophoneCS +{ + sealed class CaptureThread: CaptureCallbacks + { + public CaptureThread( CommandLineArgs args, Context context, iAudioCapture source ) + { + callbacks = new TranscribeCallbacks( args ); + this.context = context; + this.source = source; + + thread = new Thread( threadMain ) { Name = "Capture Thread" }; + Console.WriteLine( "Press any key to quit" ); + thread.Start(); + } + + static void readKeyCallback( object? state ) + { + CaptureThread ct = ( state as CaptureThread ) ?? throw new ApplicationException(); + Console.ReadKey(); + ct.shouldQuit = true; + } + + public void join() + { + ThreadPool.QueueUserWorkItem( readKeyCallback, this ); + thread.Join(); + edi?.Throw(); + } + + volatile bool shouldQuit = false; + + protected override bool shouldCancel( Context sender ) => + shouldQuit; + + protected override void captureStatusChanged( Context sender, eCaptureStatus status ) + { + Console.WriteLine( $"CaptureStatusChanged: {status}" ); + } + + readonly TranscribeCallbacks callbacks; + readonly Thread thread; + readonly Context context; + readonly iAudioCapture source; + ExceptionDispatchInfo? edi = null; + + void threadMain() + { + try + { + context.runCapture( source, callbacks, this ); + } + catch( Exception ex ) + { + edi = ExceptionDispatchInfo.Capture( ex ); + } + } + } +}
\ No newline at end of file diff --git a/Examples/MicrophoneCS/CommandLineArgs.cs b/Examples/MicrophoneCS/CommandLineArgs.cs new file mode 100644 index 0000000..be5fbe9 --- /dev/null +++ b/Examples/MicrophoneCS/CommandLineArgs.cs @@ -0,0 +1,145 @@ +using System.Globalization; +using System.Reflection; +using Whisper; + +namespace MicrophoneCS +{ + sealed record class CommandLineArgs + { + public int n_threads = Environment.ProcessorCount; + public int offset_t_ms = 0; + public int offset_n = 0; + public int duration_ms = 0; + public int max_context = -1; + public int max_len = 0; + + public float word_thold = 0.01f; + + public bool speed_up = false; + public bool translate = false; + public bool diarize = false; + public bool output_txt = false; + public bool print_special = false; + public bool print_progress = false; + public bool print_colors = true; + public bool no_timestamps = false; + public int[]? prompt = null; + public int captureDeviceIndex = 0; + + public eLanguage language = eLanguage.English; + public string model = string.Empty; + + const bool output_wts = false; + public bool listDevices = false; + + public void apply( ref Parameters p ) + { + p.setFlag( eFullParamsFlags.PrintRealtime, false ); + p.setFlag( eFullParamsFlags.PrintProgress, print_progress ); + p.setFlag( eFullParamsFlags.PrintTimestamps, !no_timestamps ); + p.setFlag( eFullParamsFlags.PrintSpecial, print_special ); + p.setFlag( eFullParamsFlags.Translate, translate ); + p.language = language; + p.cpuThreads = n_threads; + if( max_context >= 0 ) + p.n_max_text_ctx = max_context; + p.offset_ms = offset_t_ms; + p.duration_ms = duration_ms; + p.setFlag( eFullParamsFlags.TokenTimestamps, output_wts || max_len > 0 ); + p.thold_pt = word_thold; + p.max_len = output_wts && max_len == 0 ? 60 : max_len; + p.setFlag( eFullParamsFlags.SpeedupAudio, speed_up ); + } + + public eResultFlags resultFlags() + { + eResultFlags flags = eResultFlags.None; + bool wts = output_wts || max_len > 0; + if( !no_timestamps || wts ) + flags |= eResultFlags.Timestamps; + if( wts || print_colors ) + flags |= eResultFlags.Tokens; + return flags; + } + + static eLanguage parseLanguage( string lang ) => + Library.languageFromCode( lang ) ?? throw new ArgumentException( $"Unknown language code \"{lang}\"" ); + + public CommandLineArgs( string[] argv ) + { + for( int i = 0; i < argv.Length; i++ ) + { + string arg = argv[ i ]; + if( arg == "-h" || arg == "--help" ) + { + printUsage(); + throw new OperationCanceledException(); + } + else if( arg == "-c" || arg == "--capture" ) captureDeviceIndex = int.Parse( argv[ ++i ] ); + else if( arg == "-ld" || arg == "--list-devices" ) listDevices = true; + else if( arg == "-t" || arg == "--threads" ) n_threads = int.Parse( argv[ ++i ] ); + else if( arg == "-ot" || arg == "--offset-t" ) offset_t_ms = int.Parse( argv[ ++i ] ); + else if( arg == "-on" || arg == "--offset-n" ) offset_n = int.Parse( argv[ ++i ] ); + else if( arg == "-d" || arg == "--duration" ) duration_ms = int.Parse( argv[ ++i ] ); + else if( arg == "-mc" || arg == "--max-context" ) max_context = int.Parse( argv[ ++i ] ); + else if( arg == "-ml" || arg == "--max-len" ) max_len = int.Parse( argv[ ++i ] ); + else if( arg == "-wt" || arg == "--word-thold" ) word_thold = float.Parse( argv[ ++i ], CultureInfo.InvariantCulture ); + else if( arg == "-su" || arg == "--speed-up" ) speed_up = true; + else if( arg == "-tr" || arg == "--translate" ) translate = true; + else if( arg == "-di" || arg == "--diarize" ) diarize = true; + else if( arg == "-otxt" || arg == "--output-txt" ) output_txt = true; + else if( arg == "-ps" || arg == "--print-special" ) print_special = true; + else if( arg == "-nc" || arg == "--no-colors" ) print_colors = false; + else if( arg == "-pp" || arg == "--print-progress" ) print_progress = true; + else if( arg == "-nt" || arg == "--no-timestamps" ) no_timestamps = true; + else if( arg == "-l" || arg == "--language" ) language = parseLanguage( argv[ ++i ] ); + else if( arg == "--prompt" ) prompt = parsePrompt( argv[ ++i ] ); + else if( arg == "-m" || arg == "--model" ) model = argv[ ++i ]; + else + throw new ArgumentException( $"Unknown argument: \"{arg}\"" ); + } + if( string.IsNullOrWhiteSpace( model ) ) + throw new ArgumentException( "The model file is not provided in the arguments" ); + if( !File.Exists( model ) ) + throw new FileNotFoundException( "Model not found", model ); + } + + static string cstr( bool b ) => b.ToString(); + + static int[]? parsePrompt( string str ) + { + if( string.IsNullOrWhiteSpace( str ) ) + return null; + // TODO: expose whisper_tokenize function, as a method of iModel COM interface + throw new NotImplementedException(); + } + + void printUsage() + { + Console.WriteLine(); + + Console.WriteLine( "usage: {0} [options] file0.mp3 file1.wma ...", Path.GetFileName( Assembly.GetExecutingAssembly().Location ) ); + Console.WriteLine(); + Console.WriteLine( "options:" ); + Console.WriteLine( " -h, --help [default] show this help message and exit" ); + Console.WriteLine( " -t N, --threads N [{0,-7:D}] number of threads to use during computation", n_threads ); + Console.WriteLine( " -ot N, --offset-t N [{0,-7:D}] time offset in milliseconds", offset_t_ms ); + Console.WriteLine( " -on N, --offset-n N [{0,-7:D}] segment index offset", offset_n ); + Console.WriteLine( " -d N, --duration N [{0,-7:D}] duration of audio to process in milliseconds", duration_ms ); + Console.WriteLine( " -mc N, --max-context N [{0,-7:D}] maximum number of text context tokens to store", max_context ); + Console.WriteLine( " -ml N, --max-len N [{0,-7:D}] maximum segment length in characters", max_len ); + Console.WriteLine( " -wt N, --word-thold N [{0,-7:F2}] word timestamp probability threshold", word_thold ); + Console.WriteLine( " -su, --speed-up [{0,-7}] speed up audio by x2 (reduced accuracy)", cstr( speed_up ) ); + Console.WriteLine( " -tr, --translate [{0,-7}] translate from source language to english", cstr( translate ) ); + Console.WriteLine( " -di, --diarize [{0,-7}] stereo audio diarization", cstr( diarize ) ); + Console.WriteLine( " -otxt, --output-txt [{0,-7}] output result in a text file", cstr( output_txt ) ); + Console.WriteLine( " -ps, --print-special [{0,-7}] print special tokens", cstr( print_special ) ); + Console.WriteLine( " -nc, --no-colors [{0,-7}] do not print colors", cstr( !print_colors ) ); + Console.WriteLine( " -nt, --no-timestamps [{0,-7}] do not print timestamps", cstr( no_timestamps ) ); + Console.WriteLine( " -l LANG, --language LANG [{0,-7}] spoken language", language.getCode() ); + Console.WriteLine( " --prompt PROMPT [ ] initial prompt" ); + Console.WriteLine( " -m FNAME, --model FNAME [{0,-7}] model path", model ); + Console.WriteLine( " -f FNAME, --file FNAME [{0,-7}] path of the input audio file", "" ); + } + } +}
\ No newline at end of file diff --git a/Examples/MicrophoneCS/MicrophoneCS.cs b/Examples/MicrophoneCS/MicrophoneCS.cs new file mode 100644 index 0000000..c095ee1 --- /dev/null +++ b/Examples/MicrophoneCS/MicrophoneCS.cs @@ -0,0 +1,56 @@ +using Whisper; + +namespace MicrophoneCS +{ + static class Program + { + static int Main( string[] args ) + { + try + { + CommandLineArgs cla; + try + { + cla = new CommandLineArgs( args ); + } + catch( OperationCanceledException ) + { + return 1; + } + const eLoggerFlags loggerFlags = eLoggerFlags.UseStandardError | eLoggerFlags.SkipFormatMessage; + Library.setLogSink( eLogLevel.Debug, loggerFlags ); + + using iMediaFoundation mf = Library.initMediaFoundation(); + CaptureDeviceId[] devices = mf.listCaptureDevices() ?? + throw new ApplicationException( "This computer has no audio capture devices" ); + + if( cla.listDevices ) + { + for( int i = 0; i < devices.Length; i++ ) + Console.WriteLine( "#{0}: {1}", i, devices[ i ].displayName ); + return 0; + } + if( cla.captureDeviceIndex < 0 || cla.captureDeviceIndex >= devices.Length ) + throw new ApplicationException( $"Capture device index is out of range; the valid range is [ 0 .. {devices.Length - 1} ]" ); + + using iAudioCapture captureDev = mf.openCaptureDevice( devices[ cla.captureDeviceIndex ] ); + + using iModel model = Library.loadModel( cla.model ); + using Context context = model.createContext(); + cla.apply( ref context.parameters ); + + CaptureThread thread = new CaptureThread( cla, context, captureDev ); + thread.join(); + + context.timingsPrint(); + return 0; + } + catch( Exception ex ) + { + // Console.WriteLine( ex.Message ); + Console.WriteLine( ex.ToString() ); + return ex.HResult; + } + } + } +}
\ No newline at end of file diff --git a/Examples/MicrophoneCS/MicrophoneCS.csproj b/Examples/MicrophoneCS/MicrophoneCS.csproj new file mode 100644 index 0000000..f417d20 --- /dev/null +++ b/Examples/MicrophoneCS/MicrophoneCS.csproj @@ -0,0 +1,27 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net6.0-windows</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> + <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> + <Platforms>x64</Platforms> + </PropertyGroup> + + <ItemGroup> + <Compile Include="..\TranscribeCS\AnsiCodes.cs" Link="AnsiCodes.cs" /> + </ItemGroup> + + <ItemGroup> + <Content Include="..\..\x64\$(Configuration)\Whisper.dll" Link="Whisper.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\WhisperNet\WhisperNet.csproj" /> + </ItemGroup> + +</Project>
\ No newline at end of file diff --git a/Examples/MicrophoneCS/TranscribeCallbacks.cs b/Examples/MicrophoneCS/TranscribeCallbacks.cs new file mode 100644 index 0000000..e4d14f4 --- /dev/null +++ b/Examples/MicrophoneCS/TranscribeCallbacks.cs @@ -0,0 +1,114 @@ +using System.Globalization; +using Whisper; + +namespace MicrophoneCS +{ + /// <summary>Implementation of Callbacks abstract class, to print these segments as soon as they’re produced by the library.</summary> + sealed class TranscribeCallbacks: Callbacks + { + readonly CommandLineArgs args; + readonly eResultFlags resultFlags; + + public TranscribeCallbacks( CommandLineArgs args ) + { + this.args = args; + resultFlags = args.resultFlags(); + Console.OutputEncoding = System.Text.Encoding.UTF8; + } + + // Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9] + // Lowest is red, middle is yellow, highest is green. + readonly string[] k_colors = new string[] + { + "\x1B[38;5;196m", "\x1B[38;5;202m", "\x1B[38;5;208m", "\x1B[38;5;214m", "\x1B[38;5;220m", + "\x1B[38;5;226m", "\x1B[38;5;190m", "\x1B[38;5;154m", "\x1B[38;5;118m", "\x1B[38;5;82m" + }; + + int colorIndex( in sToken tok ) + { + float p = tok.probability; + float p3 = p * p * p; + int col = (int)( p3 * k_colors.Length ); + col = Math.Clamp( col, 0, k_colors.Length - 1 ); + return col; + } + + public static string printTime( TimeSpan ts ) => + ts.ToString( "hh':'mm':'ss'.'fff", CultureInfo.InvariantCulture ); + public static string printTimeWithComma( TimeSpan ts ) => + ts.ToString( "hh':'mm':'ss','fff", CultureInfo.InvariantCulture ); + + protected override void onNewSegment( Context sender, int countNew ) + { + TranscribeResult res = sender.results( resultFlags ); + ReadOnlySpan<sToken> tokens = res.tokens; + + int s0 = res.segments.Length - countNew; + if( s0 == 0 ) + Console.WriteLine(); + + for( int i = s0; i < res.segments.Length; i++ ) + { + sSegment seg = res.segments[ i ]; + + if( args.no_timestamps ) + { + if( args.print_colors && AnsiCodes.enabled ) + { + foreach( sToken tok in res.getTokens( seg ) ) + { + if( !args.print_special && tok.hasFlag( eTokenFlags.Special ) ) + continue; + Console.Write( "{0}{1}{2}", k_colors[ colorIndex( tok ) ], tok.text, "\x1B[0m" ); + } + } + else + Console.Write( seg.text ); + Console.Out.Flush(); + continue; + } + + string speaker = ""; +#if false + if( args.diarize && pcmf32s.size() == 2 ) + { + const size_t n_samples = pcmf32s[ 0 ].size(); + const int64_t is0 = SourceAudio::sampleFromTimestamp( seg.time.begin, n_samples ); + const int64_t is1 = SourceAudio::sampleFromTimestamp( seg.time.end, n_samples ); + + double energy0 = 0.0f; + double energy1 = 0.0f; + + for( int64_t j = is0; j < is1; j++ ) + { + energy0 += fabs( pcmf32s[ 0 ][ j ] ); + energy1 += fabs( pcmf32s[ 1 ][ j ] ); + } + + if( energy0 > 1.1 * energy1 ) + speaker = "(speaker 0)"; + else if( energy1 > 1.1 * energy0 ) + speaker = "(speaker 1)"; + else + speaker = "(speaker ?)"; + + //printf("is0 = %lld, is1 = %lld, energy0 = %f, energy1 = %f, %s\n", is0, is1, energy0, energy1, speaker.c_str()); + } +#endif + if( args.print_colors && AnsiCodes.enabled ) + { + Console.Write( "[{0} --> {1}] ", printTime( seg.time.begin ), printTime( seg.time.end ) ); + foreach( sToken tok in res.getTokens( seg ) ) + { + if( !args.print_special && tok.hasFlag( eTokenFlags.Special ) ) + continue; + Console.Write( "{0}{1}{2}{3}", speaker, k_colors[ colorIndex( tok ) ], tok.text, "\x1B[0m" ); + } + Console.WriteLine(); + } + else + Console.WriteLine( "[{0} --> {1}] {2}{3}", printTime( seg.time.begin ), printTime( seg.time.end ), speaker, seg.text ); + } + } + } +}
\ No newline at end of file diff --git a/Examples/OldMain/OldMain.vcxproj b/Examples/OldMain/OldMain.vcxproj new file mode 100644 index 0000000..26f2c71 --- /dev/null +++ b/Examples/OldMain/OldMain.vcxproj @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{596f9770-9aeb-49d3-86ca-4200197df12b}</ProjectGuid> + <RootNamespace>OldMain</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <IncludePath>$(SolutionDir)Whisper\Source\;$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <IncludePath>$(SolutionDir)Whisper\Source\;$(IncludePath)</IncludePath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet> + <LanguageStandard>stdcpp20</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet> + <LanguageStandard>stdcpp20</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\Whisper\source\ggml.c"> + <ExcludedFromBuild>true</ExcludedFromBuild> + </ClCompile> + <ClCompile Include="..\..\Whisper\source\ggmlMsvc.c" /> + <ClCompile Include="..\..\Whisper\source\whisper.cpp" /> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\Whisper\source\ggml.h" /> + <ClInclude Include="..\..\Whisper\source\whisper.h" /> + <ClInclude Include="dr_wav.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Examples/OldMain/OldMain.vcxproj.filters b/Examples/OldMain/OldMain.vcxproj.filters new file mode 100644 index 0000000..78f29f0 --- /dev/null +++ b/Examples/OldMain/OldMain.vcxproj.filters @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="..\..\Whisper\source\ggmlMsvc.c" /> + <ClCompile Include="..\..\Whisper\source\whisper.cpp" /> + <ClCompile Include="main.cpp" /> + <ClCompile Include="..\..\Whisper\source\ggml.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\Whisper\source\ggml.h" /> + <ClInclude Include="..\..\Whisper\source\whisper.h" /> + <ClInclude Include="dr_wav.h" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Examples/OldMain/dr_wav.h b/Examples/OldMain/dr_wav.h new file mode 100644 index 0000000..fd3e95b --- /dev/null +++ b/Examples/OldMain/dr_wav.h @@ -0,0 +1,6434 @@ +/* +WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. +dr_wav - v0.12.16 - 2020-12-02 + +David Reid - mackron@gmail.com + +GitHub: https://github.com/mackron/dr_libs +*/ + +/* +RELEASE NOTES - VERSION 0.12 +============================ +Version 0.12 includes breaking changes to custom chunk handling. + + +Changes to Chunk Callback +------------------------- +dr_wav supports the ability to fire a callback when a chunk is encounted (except for WAVE and FMT chunks). The callback has been updated to include both the +container (RIFF or Wave64) and the FMT chunk which contains information about the format of the data in the wave file. + +Previously, there was no direct way to determine the container, and therefore no way to discriminate against the different IDs in the chunk header (RIFF and +Wave64 containers encode chunk ID's differently). The `container` parameter can be used to know which ID to use. + +Sometimes it can be useful to know the data format at the time the chunk callback is fired. A pointer to a `drwav_fmt` object is now passed into the chunk +callback which will give you information about the data format. To determine the sample format, use `drwav_fmt_get_format()`. This will return one of the +`DR_WAVE_FORMAT_*` tokens. +*/ + +/* +Introduction +============ +This is a single file library. To use it, do something like the following in one .c file. + + ```c + #define DR_WAV_IMPLEMENTATION + #include "dr_wav.h" + ``` + +You can then #include this file in other parts of the program as you would with any other header file. Do something like the following to read audio data: + + ```c + drwav wav; + if (!drwav_init_file(&wav, "my_song.wav", NULL)) { + // Error opening WAV file. + } + + drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32)); + size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames); + + ... + + drwav_uninit(&wav); + ``` + +If you just want to quickly open and read the audio data in a single operation you can do something like this: + + ```c + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalPCMFrameCount; + float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount, NULL); + if (pSampleData == NULL) { + // Error opening and reading WAV file. + } + + ... + + drwav_free(pSampleData); + ``` + +The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in this case), but you can still output the +audio data in its internal format (see notes below for supported formats): + + ```c + size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames); + ``` + +You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for a particular data format: + + ```c + size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer); + ``` + +dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at `drwav_init_write()`, +`drwav_init_file_write()`, etc. Use `drwav_write_pcm_frames()` to write samples, or `drwav_write_raw()` to write raw data in the "data" chunk. + + ```c + drwav_data_format format; + format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64. + format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes. + format.channels = 2; + format.sampleRate = 44100; + format.bitsPerSample = 16; + drwav_init_file_write(&wav, "data/recording.wav", &format, NULL); + + ... + + drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples); + ``` + +dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work without any manual intervention. + + +Build Options +============= +#define these options before including this file. + +#define DR_WAV_NO_CONVERSION_API + Disables conversion APIs such as `drwav_read_pcm_frames_f32()` and `drwav_s16_to_f32()`. + +#define DR_WAV_NO_STDIO + Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc. + + + +Notes +===== +- Samples are always interleaved. +- The default read function does not do any data conversion. Use `drwav_read_pcm_frames_f32()`, `drwav_read_pcm_frames_s32()` and `drwav_read_pcm_frames_s16()` + to read and convert audio data to 32-bit floating point, signed 32-bit integer and signed 16-bit integer samples respectively. Tested and supported internal + formats include the following: + - Unsigned 8-bit PCM + - Signed 12-bit PCM + - Signed 16-bit PCM + - Signed 24-bit PCM + - Signed 32-bit PCM + - IEEE 32-bit floating point + - IEEE 64-bit floating point + - A-law and u-law + - Microsoft ADPCM + - IMA ADPCM (DVI, format code 0x11) +- dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format. +*/ + +#ifndef dr_wav_h +#define dr_wav_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRWAV_STRINGIFY(x) #x +#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) + +#define DRWAV_VERSION_MAJOR 0 +#define DRWAV_VERSION_MINOR 12 +#define DRWAV_VERSION_REVISION 16 +#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) + +#include <stddef.h> /* For size_t. */ + +/* Sized types. */ +typedef signed char drwav_int8; +typedef unsigned char drwav_uint8; +typedef signed short drwav_int16; +typedef unsigned short drwav_uint16; +typedef signed int drwav_int32; +typedef unsigned int drwav_uint32; +#if defined(_MSC_VER) + typedef signed __int64 drwav_int64; + typedef unsigned __int64 drwav_uint64; +#else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long drwav_int64; + typedef unsigned long long drwav_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + typedef drwav_uint64 drwav_uintptr; +#else + typedef drwav_uint32 drwav_uintptr; +#endif +typedef drwav_uint8 drwav_bool8; +typedef drwav_uint32 drwav_bool32; +#define DRWAV_TRUE 1 +#define DRWAV_FALSE 0 + +#if !defined(DRWAV_API) + #if defined(DRWAV_DLL) + #if defined(_WIN32) + #define DRWAV_DLL_IMPORT __declspec(dllimport) + #define DRWAV_DLL_EXPORT __declspec(dllexport) + #define DRWAV_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRWAV_DLL_IMPORT __attribute__((visibility("default"))) + #define DRWAV_DLL_EXPORT __attribute__((visibility("default"))) + #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRWAV_DLL_IMPORT + #define DRWAV_DLL_EXPORT + #define DRWAV_DLL_PRIVATE static + #endif + #endif + + #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) + #define DRWAV_API DRWAV_DLL_EXPORT + #else + #define DRWAV_API DRWAV_DLL_IMPORT + #endif + #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE + #else + #define DRWAV_API extern + #define DRWAV_PRIVATE static + #endif +#endif + +typedef drwav_int32 drwav_result; +#define DRWAV_SUCCESS 0 +#define DRWAV_ERROR -1 /* A generic error. */ +#define DRWAV_INVALID_ARGS -2 +#define DRWAV_INVALID_OPERATION -3 +#define DRWAV_OUT_OF_MEMORY -4 +#define DRWAV_OUT_OF_RANGE -5 +#define DRWAV_ACCESS_DENIED -6 +#define DRWAV_DOES_NOT_EXIST -7 +#define DRWAV_ALREADY_EXISTS -8 +#define DRWAV_TOO_MANY_OPEN_FILES -9 +#define DRWAV_INVALID_FILE -10 +#define DRWAV_TOO_BIG -11 +#define DRWAV_PATH_TOO_LONG -12 +#define DRWAV_NAME_TOO_LONG -13 +#define DRWAV_NOT_DIRECTORY -14 +#define DRWAV_IS_DIRECTORY -15 +#define DRWAV_DIRECTORY_NOT_EMPTY -16 +#define DRWAV_END_OF_FILE -17 +#define DRWAV_NO_SPACE -18 +#define DRWAV_BUSY -19 +#define DRWAV_IO_ERROR -20 +#define DRWAV_INTERRUPT -21 +#define DRWAV_UNAVAILABLE -22 +#define DRWAV_ALREADY_IN_USE -23 +#define DRWAV_BAD_ADDRESS -24 +#define DRWAV_BAD_SEEK -25 +#define DRWAV_BAD_PIPE -26 +#define DRWAV_DEADLOCK -27 +#define DRWAV_TOO_MANY_LINKS -28 +#define DRWAV_NOT_IMPLEMENTED -29 +#define DRWAV_NO_MESSAGE -30 +#define DRWAV_BAD_MESSAGE -31 +#define DRWAV_NO_DATA_AVAILABLE -32 +#define DRWAV_INVALID_DATA -33 +#define DRWAV_TIMEOUT -34 +#define DRWAV_NO_NETWORK -35 +#define DRWAV_NOT_UNIQUE -36 +#define DRWAV_NOT_SOCKET -37 +#define DRWAV_NO_ADDRESS -38 +#define DRWAV_BAD_PROTOCOL -39 +#define DRWAV_PROTOCOL_UNAVAILABLE -40 +#define DRWAV_PROTOCOL_NOT_SUPPORTED -41 +#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRWAV_SOCKET_NOT_SUPPORTED -44 +#define DRWAV_CONNECTION_RESET -45 +#define DRWAV_ALREADY_CONNECTED -46 +#define DRWAV_NOT_CONNECTED -47 +#define DRWAV_CONNECTION_REFUSED -48 +#define DRWAV_NO_HOST -49 +#define DRWAV_IN_PROGRESS -50 +#define DRWAV_CANCELLED -51 +#define DRWAV_MEMORY_ALREADY_MAPPED -52 +#define DRWAV_AT_END -53 + +/* Common data formats. */ +#define DR_WAVE_FORMAT_PCM 0x1 +#define DR_WAVE_FORMAT_ADPCM 0x2 +#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 +#define DR_WAVE_FORMAT_ALAW 0x6 +#define DR_WAVE_FORMAT_MULAW 0x7 +#define DR_WAVE_FORMAT_DVI_ADPCM 0x11 +#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE + +/* Constants. */ +#ifndef DRWAV_MAX_SMPL_LOOPS +#define DRWAV_MAX_SMPL_LOOPS 1 +#endif + +/* Flags to pass into drwav_init_ex(), etc. */ +#define DRWAV_SEQUENTIAL 0x00000001 + +DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); +DRWAV_API const char* drwav_version_string(void); + +typedef enum +{ + drwav_seek_origin_start, + drwav_seek_origin_current +} drwav_seek_origin; + +typedef enum +{ + drwav_container_riff, + drwav_container_w64, + drwav_container_rf64 +} drwav_container; + +typedef struct +{ + union + { + drwav_uint8 fourcc[4]; + drwav_uint8 guid[16]; + } id; + + /* The size in bytes of the chunk. */ + drwav_uint64 sizeInBytes; + + /* + RIFF = 2 byte alignment. + W64 = 8 byte alignment. + */ + unsigned int paddingSize; +} drwav_chunk_header; + +typedef struct +{ + /* + The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications + that require support for data formats not natively supported by dr_wav. + */ + drwav_uint16 formatTag; + + /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */ + drwav_uint16 channels; + + /* The sample rate. Usually set to something like 44100. */ + drwav_uint32 sampleRate; + + /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */ + drwav_uint32 avgBytesPerSec; + + /* Block align. This is equal to the number of channels * bytes per sample. */ + drwav_uint16 blockAlign; + + /* Bits per sample. */ + drwav_uint16 bitsPerSample; + + /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */ + drwav_uint16 extendedSize; + + /* + The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample> + is always rounded up to the nearest multiple of 8. This variable contains information about exactly how + many bits are valid per sample. Mainly used for informational purposes. + */ + drwav_uint16 validBitsPerSample; + + /* The channel mask. Not used at the moment. */ + drwav_uint32 channelMask; + + /* The sub-format, exactly as specified by the wave file. */ + drwav_uint8 subFormat[16]; +} drwav_fmt; + +DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT); + + +/* +Callback for when data is read. Return value is the number of bytes actually read. + +pUserData [in] The user data that was passed to drwav_init() and family. +pBufferOut [out] The output buffer. +bytesToRead [in] The number of bytes to read. + +Returns the number of bytes actually read. + +A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until +either the entire bytesToRead is filled or you have reached the end of the stream. +*/ +typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +/* +Callback for when data is written. Returns value is the number of bytes actually written. + +pUserData [in] The user data that was passed to drwav_init_write() and family. +pData [out] A pointer to the data to write. +bytesToWrite [in] The number of bytes to write. + +Returns the number of bytes actually written. + +If the return value differs from bytesToWrite, it indicates an error. +*/ +typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); + +/* +Callback for when data needs to be seeked. + +pUserData [in] The user data that was passed to drwav_init() and family. +offset [in] The number of bytes to move, relative to the origin. Will never be negative. +origin [in] The origin of the seek - the current position or the start of the stream. + +Returns whether or not the seek was successful. + +Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drwav_seek_origin_start or +drwav_seek_origin_current. +*/ +typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); + +/* +Callback for when drwav_init_ex() finds a chunk. + +pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family. +onRead [in] A pointer to the function to call when reading. +onSeek [in] A pointer to the function to call when seeking. +pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family. +pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk. +container [in] Whether or not the WAV file is a RIFF or Wave64 container. If you're unsure of the difference, assume RIFF. +pFMT [in] A pointer to the object containing the contents of the "fmt" chunk. + +Returns the number of bytes read + seeked. + +To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same for seeking with onSeek(). The return value must +be the total number of bytes you have read _plus_ seeked. + +Use the `container` argument to discriminate the fields in `pChunkHeader->id`. If the container is `drwav_container_riff` or `drwav_container_rf64` you should +use `id.fourcc`, otherwise you should use `id.guid`. + +The `pFMT` parameter can be used to determine the data format of the wave file. Use `drwav_fmt_get_format()` to get the sample format, which will be one of the +`DR_WAVE_FORMAT_*` identifiers. + +The read pointer will be sitting on the first byte after the chunk's header. You must not attempt to read beyond the boundary of the chunk. +*/ +typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT); + +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drwav_allocation_callbacks; + +/* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */ +typedef struct +{ + const drwav_uint8* data; + size_t dataSize; + size_t currentReadPos; +} drwav__memory_stream; + +/* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */ +typedef struct +{ + void** ppData; + size_t* pDataSize; + size_t dataSize; + size_t dataCapacity; + size_t currentWritePos; +} drwav__memory_stream_write; + +typedef struct +{ + drwav_container container; /* RIFF, W64. */ + drwav_uint32 format; /* DR_WAVE_FORMAT_* */ + drwav_uint32 channels; + drwav_uint32 sampleRate; + drwav_uint32 bitsPerSample; +} drwav_data_format; + + +/* See the following for details on the 'smpl' chunk: https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl */ +typedef struct +{ + drwav_uint32 cuePointId; + drwav_uint32 type; + drwav_uint32 start; + drwav_uint32 end; + drwav_uint32 fraction; + drwav_uint32 playCount; +} drwav_smpl_loop; + + typedef struct +{ + drwav_uint32 manufacturer; + drwav_uint32 product; + drwav_uint32 samplePeriod; + drwav_uint32 midiUnityNotes; + drwav_uint32 midiPitchFraction; + drwav_uint32 smpteFormat; + drwav_uint32 smpteOffset; + drwav_uint32 numSampleLoops; + drwav_uint32 samplerData; + drwav_smpl_loop loops[DRWAV_MAX_SMPL_LOOPS]; +} drwav_smpl; + +typedef struct +{ + /* A pointer to the function to call when more data is needed. */ + drwav_read_proc onRead; + + /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */ + drwav_write_proc onWrite; + + /* A pointer to the function to call when the wav file needs to be seeked. */ + drwav_seek_proc onSeek; + + /* The user data to pass to callbacks. */ + void* pUserData; + + /* Allocation callbacks. */ + drwav_allocation_callbacks allocationCallbacks; + + + /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */ + drwav_container container; + + + /* Structure containing format information exactly as specified by the wav file. */ + drwav_fmt fmt; + + /* The sample rate. Will be set to something like 44100. */ + drwav_uint32 sampleRate; + + /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */ + drwav_uint16 channels; + + /* The bits per sample. Will be set to something like 16, 24, etc. */ + drwav_uint16 bitsPerSample; + + /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */ + drwav_uint16 translatedFormatTag; + + /* The total number of PCM frames making up the audio data. */ + drwav_uint64 totalPCMFrameCount; + + + /* The size in bytes of the data chunk. */ + drwav_uint64 dataChunkDataSize; + + /* The position in the stream of the first byte of the data chunk. This is used for seeking. */ + drwav_uint64 dataChunkDataPos; + + /* The number of bytes remaining in the data chunk. */ + drwav_uint64 bytesRemaining; + + + /* + Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always + set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation. + */ + drwav_uint64 dataChunkDataSizeTargetWrite; + + /* Keeps track of whether or not the wav writer was initialized in sequential mode. */ + drwav_bool32 isSequentialWrite; + + + /* smpl chunk. */ + drwav_smpl smpl; + + + /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */ + drwav__memory_stream memoryStream; + drwav__memory_stream_write memoryStreamWrite; + + /* Generic data for compressed formats. This data is shared across all block-compressed formats. */ + struct + { + drwav_uint64 iCurrentPCMFrame; /* The index of the next PCM frame that will be read by drwav_read_*(). This is used with "totalPCMFrameCount" to ensure we don't read excess samples at the end of the last block. */ + } compressed; + + /* Microsoft ADPCM specific data. */ + struct + { + drwav_uint32 bytesRemainingInBlock; + drwav_uint16 predictor[2]; + drwav_int32 delta[2]; + drwav_int32 cachedFrames[4]; /* Samples are stored in this cache during decoding. */ + drwav_uint32 cachedFrameCount; + drwav_int32 prevFrames[2][2]; /* The previous 2 samples for each channel (2 channels at most). */ + } msadpcm; + + /* IMA ADPCM specific data. */ + struct + { + drwav_uint32 bytesRemainingInBlock; + drwav_int32 predictor[2]; + drwav_int32 stepIndex[2]; + drwav_int32 cachedFrames[16]; /* Samples are stored in this cache during decoding. */ + drwav_uint32 cachedFrameCount; + } ima; +} drwav; + + +/* +Initializes a pre-allocated drwav object for reading. + +pWav [out] A pointer to the drwav object being initialized. +onRead [in] The function to call when data needs to be read from the client. +onSeek [in] The function to call when the read position of the client data needs to move. +onChunk [in, optional] The function to call when a chunk is enumerated at initialized time. +pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk. +flags [in, optional] A set of flags for controlling how things are loaded. + +Returns true if successful; false otherwise. + +Close the loader with drwav_uninit(). + +This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory() +to open the stream from a file or from a block of memory respectively. + +Possible values for flags: + DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function + to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored. + +drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);". + +The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt +after the function returns. + +See also: drwav_init_file(), drwav_init_memory(), drwav_uninit() +*/ +DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); + +/* +Initializes a pre-allocated drwav object for writing. + +onWrite [in] The function to call when data needs to be written. +onSeek [in] The function to call when the write position needs to move. +pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek. + +Returns true if successful; false otherwise. + +Close the writer with drwav_uninit(). + +This is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write() +to open the stream from a file or from a block of memory respectively. + +If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform +a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek. + +See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit() +*/ +DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); + +/* +Utility function to determine the target size of the entire data to be written (including all headers and chunks). + +Returns the target size in bytes. + +Useful if the application needs to know the size to allocate. + +Only writing to the RIFF chunk and one data chunk is currently supported. + +See also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write() +*/ +DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); + +/* +Uninitializes the given drwav object. + +Use this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()). +*/ +DRWAV_API drwav_result drwav_uninit(drwav* pWav); + + +/* +Reads raw audio data. + +This is the lowest level function for reading audio data. It simply reads the given number of +bytes of the raw internal sample data. + +Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for +reading sample data in a consistent format. + +pBufferOut can be NULL in which case a seek will be performed. + +Returns the number of bytes actually read. +*/ +DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); + +/* +Reads up to the specified number of PCM frames from the WAV file. + +The output data will be in the file's internal format, converted to native-endian byte order. Use +drwav_read_pcm_frames_s16/f32/s32() to read data in a specific format. + +If the return value is less than <framesToRead> it means the end of the file has been reached or +you have requested more PCM frames than can possibly fit in the output buffer. + +This function will only work when sample data is of a fixed size and uncompressed. If you are +using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32(). + +pBufferOut can be NULL in which case a seek will be performed. +*/ +DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); + +/* +Seeks to the given PCM frame. + +Returns true if successful; false otherwise. +*/ +DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); + + +/* +Writes raw audio data. + +Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error. +*/ +DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); + +/* +Writes PCM frames. + +Returns the number of PCM frames written. + +Input samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to +little-endian. Use drwav_write_raw() to write raw audio data without performing any conversion. +*/ +DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); +DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); +DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); + + +/* Conversion Utilities */ +#ifndef DR_WAV_NO_CONVERSION_API + +/* +Reads a chunk of audio data and converts it to signed 16-bit PCM samples. + +pBufferOut can be NULL in which case a seek will be performed. + +Returns the number of PCM frames actually read. + +If the return value is less than <framesToRead> it means the end of the file has been reached. +*/ +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); + +/* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); + +/* Low-level function for converting A-law samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting u-law samples to signed 16-bit PCM samples. */ +DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + + +/* +Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples. + +pBufferOut can be NULL in which case a seek will be performed. + +Returns the number of PCM frames actually read. + +If the return value is less than <framesToRead> it means the end of the file has been reached. +*/ +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); + +/* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); + +/* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); + +/* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */ +DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + + +/* +Reads a chunk of audio data and converts it to signed 32-bit PCM samples. + +pBufferOut can be NULL in which case a seek will be performed. + +Returns the number of PCM frames actually read. + +If the return value is less than <framesToRead> it means the end of the file has been reached. +*/ +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); + +/* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); + +/* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); + +/* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); + +/* Low-level function for converting A-law samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +/* Low-level function for converting u-law samples to signed 32-bit PCM samples. */ +DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +#endif /* DR_WAV_NO_CONVERSION_API */ + + +/* High-Level Convenience Helpers */ + +#ifndef DR_WAV_NO_STDIO +/* +Helper for initializing a wave file for reading using stdio. + +This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav +objects because the operating system may restrict the number of file handles an application can have open at +any given time. +*/ +DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); + +/* +Helper for initializing a wave file for writing using stdio. + +This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav +objects because the operating system may restrict the number of file handles an application can have open at +any given time. +*/ +DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif /* DR_WAV_NO_STDIO */ + +/* +Helper for initializing a loader from a pre-allocated memory buffer. + +This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +the lifetime of the drwav object. + +The buffer should contain the contents of the entire wave file, not just the sample data. +*/ +DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); + +/* +Helper for initializing a writer which outputs data to a memory buffer. + +dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free(). + +The buffer will remain allocated even after drwav_uninit() is called. The buffer should not be considered valid +until after drwav_uninit() has been called. +*/ +DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); + + +#ifndef DR_WAV_NO_CONVERSION_API +/* +Opens and reads an entire wav file in a single operation. + +The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. +*/ +DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_WAV_NO_STDIO +/* +Opens and decodes an entire wav file in a single operation. + +The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. +*/ +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif +/* +Opens and decodes an entire wav file from a block of memory in a single operation. + +The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. +*/ +DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif + +/* Frees data that was allocated internally by dr_wav. */ +DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); + +/* Converts bytes from a wav stream to a sized type of native endian. */ +DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data); +DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data); +DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data); +DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data); +DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data); +DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data); + +/* Compares a GUID for the purpose of checking the type of a Wave64 chunk. */ +DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]); + +/* Compares a four-character-code for the purpose of checking the type of a RIFF chunk. */ +DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); + +#ifdef __cplusplus +} +#endif +#endif /* dr_wav_h */ + + +/************************************************************************************************************************************************************ + ************************************************************************************************************************************************************ + + IMPLEMENTATION + + ************************************************************************************************************************************************************ + ************************************************************************************************************************************************************/ +#if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) +#ifndef dr_wav_c +#define dr_wav_c + +#include <stdlib.h> +#include <string.h> /* For memcpy(), memset() */ +#include <limits.h> /* For INT_MAX */ + +#ifndef DR_WAV_NO_STDIO +#include <stdio.h> +#include <wchar.h> +#endif + +/* Standard library stuff. */ +#ifndef DRWAV_ASSERT +#include <assert.h> +#define DRWAV_ASSERT(expression) assert(expression) +#endif +#ifndef DRWAV_MALLOC +#define DRWAV_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRWAV_REALLOC +#define DRWAV_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRWAV_FREE +#define DRWAV_FREE(p) free((p)) +#endif +#ifndef DRWAV_COPY_MEMORY +#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRWAV_ZERO_MEMORY +#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef DRWAV_ZERO_OBJECT +#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p)) +#endif + +#define drwav_countof(x) (sizeof(x) / sizeof(x[0])) +#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) +#define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) +#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) + +#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ + +/* CPU architecture. */ +#if defined(__x86_64__) || defined(_M_X64) + #define DRWAV_X64 +#elif defined(__i386) || defined(_M_IX86) + #define DRWAV_X86 +#elif defined(__arm__) || defined(_M_ARM) + #define DRWAV_ARM +#endif + +#ifdef _MSC_VER + #define DRWAV_INLINE __forceinline +#elif defined(__GNUC__) + /* + I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when + the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some + case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the + command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue + I am using "__inline__" only when we're compiling in strict ANSI mode. + */ + #if defined(__STRICT_ANSI__) + #define DRWAV_INLINE __inline__ __attribute__((always_inline)) + #else + #define DRWAV_INLINE inline __attribute__((always_inline)) + #endif +#elif defined(__WATCOMC__) + #define DRWAV_INLINE __inline +#else + #define DRWAV_INLINE +#endif + +#if defined(SIZE_MAX) + #define DRWAV_SIZE_MAX SIZE_MAX +#else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRWAV_SIZE_MAX 0xFFFFFFFF + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #define DRWAV_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_bswap16) + #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap32) + #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap64) + #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #endif + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #endif +#endif + +DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision) +{ + if (pMajor) { + *pMajor = DRWAV_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = DRWAV_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = DRWAV_VERSION_REVISION; + } +} + +DRWAV_API const char* drwav_version_string(void) +{ + return DRWAV_VERSION_STRING; +} + +/* +These limits are used for basic validation when initializing the decoder. If you exceed these limits, first of all: what on Earth are +you doing?! (Let me know, I'd be curious!) Second, you can adjust these by #define-ing them before the dr_wav implementation. +*/ +#ifndef DRWAV_MAX_SAMPLE_RATE +#define DRWAV_MAX_SAMPLE_RATE 384000 +#endif +#ifndef DRWAV_MAX_CHANNELS +#define DRWAV_MAX_CHANNELS 256 +#endif +#ifndef DRWAV_MAX_BITS_PER_SAMPLE +#define DRWAV_MAX_BITS_PER_SAMPLE 64 +#endif + +static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; /* 66666972-912E-11CF-A5D6-28DB04C10000 */ +static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */ +/*static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};*/ /* 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A */ +static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */ +static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */ +static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */ +static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A */ + +static DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) +{ + int i; + for (i = 0; i < 16; i += 1) { + if (a[i] != b[i]) { + return DRWAV_FALSE; + } + } + + return DRWAV_TRUE; +} + +static DRWAV_INLINE drwav_bool32 drwav__fourcc_equal(const drwav_uint8* a, const char* b) +{ + return + a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] && + a[3] == b[3]; +} + + + +static DRWAV_INLINE int drwav__is_little_endian(void) +{ +#if defined(DRWAV_X86) || defined(DRWAV_X64) + return DRWAV_TRUE; +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN + return DRWAV_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static DRWAV_INLINE drwav_uint16 drwav__bytes_to_u16(const drwav_uint8* data) +{ + return (data[0] << 0) | (data[1] << 8); +} + +static DRWAV_INLINE drwav_int16 drwav__bytes_to_s16(const drwav_uint8* data) +{ + return (short)drwav__bytes_to_u16(data); +} + +static DRWAV_INLINE drwav_uint32 drwav__bytes_to_u32(const drwav_uint8* data) +{ + return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +} + +static DRWAV_INLINE drwav_int32 drwav__bytes_to_s32(const drwav_uint8* data) +{ + return (drwav_int32)drwav__bytes_to_u32(data); +} + +static DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const drwav_uint8* data) +{ + return + ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | + ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); +} + +static DRWAV_INLINE drwav_int64 drwav__bytes_to_s64(const drwav_uint8* data) +{ + return (drwav_int64)drwav__bytes_to_u64(data); +} + +static DRWAV_INLINE void drwav__bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid) +{ + int i; + for (i = 0; i < 16; ++i) { + guid[i] = data[i]; + } +} + + +static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) +{ +#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} + +static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) +{ +#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ + /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ + drwav_uint32 r; + __asm__ __volatile__ ( + #if defined(DRWAV_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + +static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) +{ +#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ + return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) | + ((n & ((drwav_uint64)0xFF000000 )) << 8) | + ((n & ((drwav_uint64)0x00FF0000 )) << 24) | + ((n & ((drwav_uint64)0x0000FF00 )) << 40) | + ((n & ((drwav_uint64)0x000000FF )) << 56); +#endif +} + + +static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) +{ + return (drwav_int16)drwav__bswap16((drwav_uint16)n); +} + +static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); + } +} + + +static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) +{ + drwav_uint8 t; + t = p[0]; + p[0] = p[2]; + p[2] = t; +} + +static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + drwav_uint8* pSample = pSamples + (iSample*3); + drwav__bswap_s24(pSample); + } +} + + +static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) +{ + return (drwav_int32)drwav__bswap32((drwav_uint32)n); +} + +static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); + } +} + + +static DRWAV_INLINE float drwav__bswap_f32(float n) +{ + union { + drwav_uint32 i; + float f; + } x; + x.f = n; + x.i = drwav__bswap32(x.i); + + return x.f; +} + +static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); + } +} + + +static DRWAV_INLINE double drwav__bswap_f64(double n) +{ + union { + drwav_uint64 i; + double f; + } x; + x.f = n; + x.i = drwav__bswap64(x.i); + + return x.f; +} + +static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount) +{ + drwav_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]); + } +} + + +static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) +{ + /* Assumes integer PCM. Floating point PCM is done in drwav__bswap_samples_ieee(). */ + switch (bytesPerSample) + { + case 2: /* s16, s12 (loosely packed) */ + { + drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); + } break; + case 3: /* s24 */ + { + drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); + } break; + case 4: /* s32 */ + { + drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); + } break; + default: + { + /* Unsupported format. */ + DRWAV_ASSERT(DRWAV_FALSE); + } break; + } +} + +static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) +{ + switch (bytesPerSample) + { + #if 0 /* Contributions welcome for f16 support. */ + case 2: /* f16 */ + { + drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount); + } break; + #endif + case 4: /* f32 */ + { + drwav__bswap_samples_f32((float*)pSamples, sampleCount); + } break; + case 8: /* f64 */ + { + drwav__bswap_samples_f64((double*)pSamples, sampleCount); + } break; + default: + { + /* Unsupported format. */ + DRWAV_ASSERT(DRWAV_FALSE); + } break; + } +} + +static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format) +{ + switch (format) + { + case DR_WAVE_FORMAT_PCM: + { + drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample); + } break; + + case DR_WAVE_FORMAT_IEEE_FLOAT: + { + drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample); + } break; + + case DR_WAVE_FORMAT_ALAW: + case DR_WAVE_FORMAT_MULAW: + { + drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); + } break; + + case DR_WAVE_FORMAT_ADPCM: + case DR_WAVE_FORMAT_DVI_ADPCM: + default: + { + /* Unsupported format. */ + DRWAV_ASSERT(DRWAV_FALSE); + } break; + } +} + + +static void* drwav__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return DRWAV_MALLOC(sz); +} + +static void* drwav__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return DRWAV_REALLOC(p, sz); +} + +static void drwav__free_default(void* p, void* pUserData) +{ + (void)pUserData; + DRWAV_FREE(p); +} + + +static void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + + /* Try using realloc(). */ + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + + return NULL; +} + +static void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + + /* Try emulating realloc() in terms of malloc()/free(). */ + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + + if (p != NULL) { + DRWAV_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + + return p2; + } + + return NULL; +} + +static void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} + + +static drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + /* Copy. */ + return *pAllocationCallbacks; + } else { + /* Defaults. */ + drwav_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = drwav__malloc_default; + allocationCallbacks.onRealloc = drwav__realloc_default; + allocationCallbacks.onFree = drwav__free_default; + return allocationCallbacks; + } +} + + +static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) +{ + return + formatTag == DR_WAVE_FORMAT_ADPCM || + formatTag == DR_WAVE_FORMAT_DVI_ADPCM; +} + +static unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) +{ + return (unsigned int)(chunkSize % 2); +} + +static unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) +{ + return (unsigned int)(chunkSize % 8); +} + +static drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +static drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +static drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); + +static drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) +{ + if (container == drwav_container_riff || container == drwav_container_rf64) { + drwav_uint8 sizeInBytes[4]; + + if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { + return DRWAV_AT_END; + } + + if (onRead(pUserData, sizeInBytes, 4) != 4) { + return DRWAV_INVALID_FILE; + } + + pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes); + pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); + *pRunningBytesReadOut += 8; + } else { + drwav_uint8 sizeInBytes[8]; + + if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { + return DRWAV_AT_END; + } + + if (onRead(pUserData, sizeInBytes, 8) != 8) { + return DRWAV_INVALID_FILE; + } + + pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */ + pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); + *pRunningBytesReadOut += 24; + } + + return DRWAV_SUCCESS; +} + +static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +{ + drwav_uint64 bytesRemainingToSeek = offset; + while (bytesRemainingToSeek > 0) { + if (bytesRemainingToSeek > 0x7FFFFFFF) { + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + bytesRemainingToSeek -= 0x7FFFFFFF; + } else { + if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + bytesRemainingToSeek = 0; + } + } + + return DRWAV_TRUE; +} + +static drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +{ + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, drwav_seek_origin_start); + } + + /* Larger than 32-bit seek. */ + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { + return DRWAV_FALSE; + } + offset -= 0x7FFFFFFF; + + for (;;) { + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, drwav_seek_origin_current); + } + + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + offset -= 0x7FFFFFFF; + } + + /* Should never get here. */ + /*return DRWAV_TRUE; */ +} + + +static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) +{ + drwav_chunk_header header; + drwav_uint8 fmt[16]; + + if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + + /* Skip non-fmt chunks. */ + while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav__fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) { + if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; + + /* Try the next header. */ + if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + } + + + /* Validation. */ + if (container == drwav_container_riff || container == drwav_container_rf64) { + if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) { + return DRWAV_FALSE; + } + } else { + if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) { + return DRWAV_FALSE; + } + } + + + if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += sizeof(fmt); + + fmtOut->formatTag = drwav__bytes_to_u16(fmt + 0); + fmtOut->channels = drwav__bytes_to_u16(fmt + 2); + fmtOut->sampleRate = drwav__bytes_to_u32(fmt + 4); + fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8); + fmtOut->blockAlign = drwav__bytes_to_u16(fmt + 12); + fmtOut->bitsPerSample = drwav__bytes_to_u16(fmt + 14); + + fmtOut->extendedSize = 0; + fmtOut->validBitsPerSample = 0; + fmtOut->channelMask = 0; + memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat)); + + if (header.sizeInBytes > 16) { + drwav_uint8 fmt_cbSize[2]; + int bytesReadSoFar = 0; + + if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { + return DRWAV_FALSE; /* Expecting more data. */ + } + *pRunningBytesReadOut += sizeof(fmt_cbSize); + + bytesReadSoFar = 18; + + fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize); + if (fmtOut->extendedSize > 0) { + /* Simple validation. */ + if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + if (fmtOut->extendedSize != 22) { + return DRWAV_FALSE; + } + } + + if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + drwav_uint8 fmtext[22]; + if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { + return DRWAV_FALSE; /* Expecting more data. */ + } + + fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0); + fmtOut->channelMask = drwav__bytes_to_u32(fmtext + 2); + drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat); + } else { + if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + } + *pRunningBytesReadOut += fmtOut->extendedSize; + + bytesReadSoFar += fmtOut->extendedSize; + } + + /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */ + if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); + } + + if (header.paddingSize > 0) { + if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += header.paddingSize; + } + + return DRWAV_TRUE; +} + + +static size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) +{ + size_t bytesRead; + + DRWAV_ASSERT(onRead != NULL); + DRWAV_ASSERT(pCursor != NULL); + + bytesRead = onRead(pUserData, pBufferOut, bytesToRead); + *pCursor += bytesRead; + return bytesRead; +} + +#if 0 +static drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) +{ + DRWAV_ASSERT(onSeek != NULL); + DRWAV_ASSERT(pCursor != NULL); + + if (!onSeek(pUserData, offset, origin)) { + return DRWAV_FALSE; + } + + if (origin == drwav_seek_origin_start) { + *pCursor = offset; + } else { + *pCursor += offset; + } + + return DRWAV_TRUE; +} +#endif + + + +static drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) +{ + /* + The bytes per frame is a bit ambiguous. It can be either be based on the bits per sample, or the block align. The way I'm doing it here + is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align. + */ + if ((pWav->bitsPerSample & 0x7) == 0) { + /* Bits per sample is a multiple of 8. */ + return (pWav->bitsPerSample * pWav->fmt.channels) >> 3; + } else { + return pWav->fmt.blockAlign; + } +} + +DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) +{ + if (pFMT == NULL) { + return 0; + } + + if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) { + return pFMT->formatTag; + } else { + return drwav__bytes_to_u16(pFMT->subFormat); /* Only the first two bytes are required. */ + } +} + +static drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL || onRead == NULL || onSeek == NULL) { + return DRWAV_FALSE; + } + + DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + pWav->onRead = onRead; + pWav->onSeek = onSeek; + pWav->pUserData = pReadSeekUserData; + pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + + if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { + return DRWAV_FALSE; /* Invalid allocation callbacks. */ + } + + return DRWAV_TRUE; +} + +static drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) +{ + /* This function assumes drwav_preinit() has been called beforehand. */ + + drwav_uint64 cursor; /* <-- Keeps track of the byte position so we can seek to specific locations. */ + drwav_bool32 sequential; + drwav_uint8 riff[4]; + drwav_fmt fmt; + unsigned short translatedFormatTag; + drwav_bool32 foundDataChunk; + drwav_uint64 dataChunkSize = 0; /* <-- Important! Don't explicitly set this to 0 anywhere else. Calculation of the size of the data chunk is performed in different paths depending on the container. */ + drwav_uint64 sampleCountFromFactChunk = 0; /* Same as dataChunkSize - make sure this is the only place this is initialized to 0. */ + drwav_uint64 chunkSize; + + cursor = 0; + sequential = (flags & DRWAV_SEQUENTIAL) != 0; + + /* The first 4 bytes should be the RIFF identifier. */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { + return DRWAV_FALSE; + } + + /* + The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for + w64 it will start with "riff". + */ + if (drwav__fourcc_equal(riff, "RIFF")) { + pWav->container = drwav_container_riff; + } else if (drwav__fourcc_equal(riff, "riff")) { + int i; + drwav_uint8 riff2[12]; + + pWav->container = drwav_container_w64; + + /* Check the rest of the GUID for validity. */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { + return DRWAV_FALSE; + } + + for (i = 0; i < 12; ++i) { + if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { + return DRWAV_FALSE; + } + } + } else if (drwav__fourcc_equal(riff, "RF64")) { + pWav->container = drwav_container_rf64; + } else { + return DRWAV_FALSE; /* Unknown or unsupported container. */ + } + + + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + drwav_uint8 chunkSizeBytes[4]; + drwav_uint8 wave[4]; + + /* RIFF/WAVE */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return DRWAV_FALSE; + } + + if (pWav->container == drwav_container_riff) { + if (drwav__bytes_to_u32(chunkSizeBytes) < 36) { + return DRWAV_FALSE; /* Chunk size should always be at least 36 bytes. */ + } + } else { + if (drwav__bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) { + return DRWAV_FALSE; /* Chunk size should always be set to -1/0xFFFFFFFF for RF64. The actual size is retrieved later. */ + } + } + + if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return DRWAV_FALSE; + } + + if (!drwav__fourcc_equal(wave, "WAVE")) { + return DRWAV_FALSE; /* Expecting "WAVE". */ + } + } else { + drwav_uint8 chunkSizeBytes[8]; + drwav_uint8 wave[16]; + + /* W64 */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return DRWAV_FALSE; + } + + if (drwav__bytes_to_u64(chunkSizeBytes) < 80) { + return DRWAV_FALSE; + } + + if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return DRWAV_FALSE; + } + + if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) { + return DRWAV_FALSE; + } + } + + + /* For RF64, the "ds64" chunk must come next, before the "fmt " chunk. */ + if (pWav->container == drwav_container_rf64) { + drwav_uint8 sizeBytes[8]; + drwav_uint64 bytesRemainingInChunk; + drwav_chunk_header header; + drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + if (!drwav__fourcc_equal(header.id.fourcc, "ds64")) { + return DRWAV_FALSE; /* Expecting "ds64". */ + } + + bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; + + /* We don't care about the size of the RIFF chunk - skip it. */ + if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { + return DRWAV_FALSE; + } + bytesRemainingInChunk -= 8; + cursor += 8; + + + /* Next 8 bytes is the size of the "data" chunk. */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return DRWAV_FALSE; + } + bytesRemainingInChunk -= 8; + dataChunkSize = drwav__bytes_to_u64(sizeBytes); + + + /* Next 8 bytes is the same count which we would usually derived from the FACT chunk if it was available. */ + if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return DRWAV_FALSE; + } + bytesRemainingInChunk -= 8; + sampleCountFromFactChunk = drwav__bytes_to_u64(sizeBytes); + + + /* Skip over everything else. */ + if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { + return DRWAV_FALSE; + } + cursor += bytesRemainingInChunk; + } + + + /* The next bytes should be the "fmt " chunk. */ + if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) { + return DRWAV_FALSE; /* Failed to read the "fmt " chunk. */ + } + + /* Basic validation. */ + if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) || + (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) || + (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || + fmt.blockAlign == 0) { + return DRWAV_FALSE; /* Probably an invalid WAV file. */ + } + + + /* Translate the internal format. */ + translatedFormatTag = fmt.formatTag; + if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0); + } + + + /* + We need to enumerate over each chunk for two reasons: + 1) The "data" chunk may not be the next one + 2) We may want to report each chunk back to the client + + In order to correctly report each chunk back to the client we will need to keep looping until the end of the file. + */ + foundDataChunk = DRWAV_FALSE; + + /* The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop. */ + for (;;) + { + drwav_chunk_header header; + drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != DRWAV_SUCCESS) { + if (!foundDataChunk) { + return DRWAV_FALSE; + } else { + break; /* Probably at the end of the file. Get out of the loop. */ + } + } + + /* Tell the client about this chunk. */ + if (!sequential && onChunk != NULL) { + drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); + + /* + dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before + we called the callback. + */ + if (callbackBytesRead > 0) { + if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { + return DRWAV_FALSE; + } + } + } + + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + + chunkSize = header.sizeInBytes; + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + if (drwav__fourcc_equal(header.id.fourcc, "data")) { + foundDataChunk = DRWAV_TRUE; + if (pWav->container != drwav_container_rf64) { /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */ + dataChunkSize = chunkSize; + } + } + } else { + if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) { + foundDataChunk = DRWAV_TRUE; + dataChunkSize = chunkSize; + } + } + + /* + If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for + this is that we would otherwise require a backwards seek which sequential mode forbids. + */ + if (foundDataChunk && sequential) { + break; + } + + /* Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats. */ + if (pWav->container == drwav_container_riff) { + if (drwav__fourcc_equal(header.id.fourcc, "fact")) { + drwav_uint32 sampleCount; + if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { + return DRWAV_FALSE; + } + chunkSize -= 4; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + + /* + The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this + for Microsoft ADPCM formats. + */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + sampleCountFromFactChunk = sampleCount; + } else { + sampleCountFromFactChunk = 0; + } + } + } else if (pWav->container == drwav_container_w64) { + if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) { + if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { + return DRWAV_FALSE; + } + chunkSize -= 8; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + } + } else if (pWav->container == drwav_container_rf64) { + /* We retrieved the sample count from the ds64 chunk earlier so no need to do that here. */ + } + + /* "smpl" chunk. */ + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + if (drwav__fourcc_equal(header.id.fourcc, "smpl")) { + drwav_uint8 smplHeaderData[36]; /* 36 = size of the smpl header section, not including the loop data. */ + if (chunkSize >= sizeof(smplHeaderData)) { + drwav_uint64 bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplHeaderData, sizeof(smplHeaderData), &cursor); + chunkSize -= bytesJustRead; + + if (bytesJustRead == sizeof(smplHeaderData)) { + drwav_uint32 iLoop; + + pWav->smpl.manufacturer = drwav__bytes_to_u32(smplHeaderData+0); + pWav->smpl.product = drwav__bytes_to_u32(smplHeaderData+4); + pWav->smpl.samplePeriod = drwav__bytes_to_u32(smplHeaderData+8); + pWav->smpl.midiUnityNotes = drwav__bytes_to_u32(smplHeaderData+12); + pWav->smpl.midiPitchFraction = drwav__bytes_to_u32(smplHeaderData+16); + pWav->smpl.smpteFormat = drwav__bytes_to_u32(smplHeaderData+20); + pWav->smpl.smpteOffset = drwav__bytes_to_u32(smplHeaderData+24); + pWav->smpl.numSampleLoops = drwav__bytes_to_u32(smplHeaderData+28); + pWav->smpl.samplerData = drwav__bytes_to_u32(smplHeaderData+32); + + for (iLoop = 0; iLoop < pWav->smpl.numSampleLoops && iLoop < drwav_countof(pWav->smpl.loops); ++iLoop) { + drwav_uint8 smplLoopData[24]; /* 24 = size of a loop section in the smpl chunk. */ + bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplLoopData, sizeof(smplLoopData), &cursor); + chunkSize -= bytesJustRead; + + if (bytesJustRead == sizeof(smplLoopData)) { + pWav->smpl.loops[iLoop].cuePointId = drwav__bytes_to_u32(smplLoopData+0); + pWav->smpl.loops[iLoop].type = drwav__bytes_to_u32(smplLoopData+4); + pWav->smpl.loops[iLoop].start = drwav__bytes_to_u32(smplLoopData+8); + pWav->smpl.loops[iLoop].end = drwav__bytes_to_u32(smplLoopData+12); + pWav->smpl.loops[iLoop].fraction = drwav__bytes_to_u32(smplLoopData+16); + pWav->smpl.loops[iLoop].playCount = drwav__bytes_to_u32(smplLoopData+20); + } else { + break; /* Break from the smpl loop for loop. */ + } + } + } + } else { + /* Looks like invalid data. Ignore the chunk. */ + } + } + } else { + if (drwav__guid_equal(header.id.guid, drwavGUID_W64_SMPL)) { + /* + This path will be hit when a W64 WAV file contains a smpl chunk. I don't have a sample file to test this path, so a contribution + is welcome to add support for this. + */ + } + } + + /* Make sure we seek past the padding. */ + chunkSize += header.paddingSize; + if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) { + break; + } + cursor += chunkSize; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + } + + /* If we haven't found a data chunk, return an error. */ + if (!foundDataChunk) { + return DRWAV_FALSE; + } + + /* We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. */ + if (!sequential) { + if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { + return DRWAV_FALSE; + } + cursor = pWav->dataChunkDataPos; + } + + + /* At this point we should be sitting on the first byte of the raw audio data. */ + + pWav->fmt = fmt; + pWav->sampleRate = fmt.sampleRate; + pWav->channels = fmt.channels; + pWav->bitsPerSample = fmt.bitsPerSample; + pWav->bytesRemaining = dataChunkSize; + pWav->translatedFormatTag = translatedFormatTag; + pWav->dataChunkDataSize = dataChunkSize; + + if (sampleCountFromFactChunk != 0) { + pWav->totalPCMFrameCount = sampleCountFromFactChunk; + } else { + pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav); + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 totalBlockHeaderSizeInBytes; + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + + /* Make sure any trailing partial block is accounted for. */ + if ((blockCount * fmt.blockAlign) < dataChunkSize) { + blockCount += 1; + } + + /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */ + totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); + pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 totalBlockHeaderSizeInBytes; + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + + /* Make sure any trailing partial block is accounted for. */ + if ((blockCount * fmt.blockAlign) < dataChunkSize) { + blockCount += 1; + } + + /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */ + totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); + pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; + + /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */ + pWav->totalPCMFrameCount += blockCount; + } + } + + /* Some formats only support a certain number of channels. */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + if (pWav->channels > 2) { + return DRWAV_FALSE; + } + } + +#ifdef DR_WAV_LIBSNDFILE_COMPAT + /* + I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website), + it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count + from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct + way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should + always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my + correctness tests against libsndfile, and is disabled by default. + */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */ + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; + } +#endif + + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); +} + + +static drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize) +{ + drwav_uint64 chunkSize = 4 + 24 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 24 = "fmt " chunk. */ + if (chunkSize > 0xFFFFFFFFUL) { + chunkSize = 0xFFFFFFFFUL; + } + + return (drwav_uint32)chunkSize; /* Safe cast due to the clamp above. */ +} + +static drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) +{ + if (dataChunkSize <= 0xFFFFFFFFUL) { + return (drwav_uint32)dataChunkSize; + } else { + return 0xFFFFFFFFUL; + } +} + +static drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) +{ + drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); + + return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; /* +24 because W64 includes the size of the GUID and size fields. */ +} + +static drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) +{ + return 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ +} + +static drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize) +{ + drwav_uint64 chunkSize = 4 + 36 + 24 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 36 = "ds64" chunk. 24 = "fmt " chunk. */ + if (chunkSize > 0xFFFFFFFFUL) { + chunkSize = 0xFFFFFFFFUL; + } + + return chunkSize; +} + +static drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize) +{ + return dataChunkSize; +} + + +static size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + /* Generic write. Assumes no byte reordering required. */ + return pWav->onWrite(pWav->pUserData, pData, dataSize); +} + +static size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + if (!drwav__is_little_endian()) { + value = drwav__bswap16(value); + } + + return drwav__write(pWav, &value, 2); +} + +static size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + if (!drwav__is_little_endian()) { + value = drwav__bswap32(value); + } + + return drwav__write(pWav, &value, 4); +} + +static size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + + if (!drwav__is_little_endian()) { + value = drwav__bswap64(value); + } + + return drwav__write(pWav, &value, 8); +} + + +static drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL || onWrite == NULL) { + return DRWAV_FALSE; + } + + if (!isSequential && onSeek == NULL) { + return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */ + } + + /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */ + if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { + return DRWAV_FALSE; + } + if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { + return DRWAV_FALSE; + } + + DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + pWav->onWrite = onWrite; + pWav->onSeek = onSeek; + pWav->pUserData = pUserData; + pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + + if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { + return DRWAV_FALSE; /* Invalid allocation callbacks. */ + } + + pWav->fmt.formatTag = (drwav_uint16)pFormat->format; + pWav->fmt.channels = (drwav_uint16)pFormat->channels; + pWav->fmt.sampleRate = pFormat->sampleRate; + pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); + pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); + pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->fmt.extendedSize = 0; + pWav->isSequentialWrite = isSequential; + + return DRWAV_TRUE; +} + +static drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +{ + /* The function assumes drwav_preinit_write() was called beforehand. */ + + size_t runningPos = 0; + drwav_uint64 initialDataChunkSize = 0; + drwav_uint64 chunkSizeFMT; + + /* + The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In + sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non- + sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek. + */ + if (pWav->isSequentialWrite) { + initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; + + /* + The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64 + so for the sake of simplicity I'm not doing any validation for that. + */ + if (pFormat->container == drwav_container_riff) { + if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { + return DRWAV_FALSE; /* Not enough room to store every sample. */ + } + } + } + + pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; + + + /* "RIFF" chunk. */ + if (pFormat->container == drwav_container_riff) { + drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; /* +28 = "WAVE" + [sizeof "fmt " chunk] */ + runningPos += drwav__write(pWav, "RIFF", 4); + runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); + runningPos += drwav__write(pWav, "WAVE", 4); + } else if (pFormat->container == drwav_container_w64) { + drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ + runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); + runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); + } else if (pFormat->container == drwav_container_rf64) { + runningPos += drwav__write(pWav, "RF64", 4); + runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always 0xFFFFFFFF for RF64. Set to a proper value in the "ds64" chunk. */ + runningPos += drwav__write(pWav, "WAVE", 4); + } + + + /* "ds64" chunk (RF64 only). */ + if (pFormat->container == drwav_container_rf64) { + drwav_uint32 initialds64ChunkSize = 28; /* 28 = [Size of RIFF (8 bytes)] + [Size of DATA (8 bytes)] + [Sample Count (8 bytes)] + [Table Length (4 bytes)]. Table length always set to 0. */ + drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; /* +8 for the ds64 header. */ + + runningPos += drwav__write(pWav, "ds64", 4); + runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); /* Size of ds64. */ + runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); /* Size of RIFF. Set to true value at the end. */ + runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); /* Size of DATA. Set to true value at the end. */ + runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); /* Sample count. */ + runningPos += drwav__write_u32ne_to_le(pWav, 0); /* Table length. Always set to zero in our case since we're not doing any other chunks than "DATA". */ + } + + + /* "fmt " chunk. */ + if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) { + chunkSizeFMT = 16; + runningPos += drwav__write(pWav, "fmt ", 4); + runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); + } else if (pFormat->container == drwav_container_w64) { + chunkSizeFMT = 40; + runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); + } + + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); + runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); + runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); + + pWav->dataChunkDataPos = runningPos; + + /* "data" chunk. */ + if (pFormat->container == drwav_container_riff) { + drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; + runningPos += drwav__write(pWav, "data", 4); + runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == drwav_container_w64) { + drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ + runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == drwav_container_rf64) { + runningPos += drwav__write(pWav, "data", 4); + runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always set to 0xFFFFFFFF for RF64. The true size of the data chunk is specified in the ds64 chunk. */ + } + + /* + The runningPos variable is incremented in the section above but is left unused which is causing some static analysis tools to detect it + as a dead store. I'm leaving this as-is for safety just in case I want to expand this function later to include other tags and want to + keep track of the running position for whatever reason. The line below should silence the static analysis tools. + */ + (void)runningPos; + + /* Set some properties for the client's convenience. */ + pWav->container = pFormat->container; + pWav->channels = (drwav_uint16)pFormat->channels; + pWav->sampleRate = pFormat->sampleRate; + pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->translatedFormatTag = (drwav_uint16)pFormat->format; + + return DRWAV_TRUE; +} + + +DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + return drwav_init_write__internal(pWav, pFormat, 0); /* DRWAV_FALSE = Not Sequential */ +} + +DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + return drwav_init_write__internal(pWav, pFormat, totalSampleCount); /* DRWAV_TRUE = Sequential */ +} + +DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); +} + +DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +{ + /* Casting totalSampleCount to drwav_int64 for VC6 compatibility. No issues in practice because nobody is going to exhaust the whole 63 bits. */ + drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalSampleCount * pFormat->channels * pFormat->bitsPerSample/8.0); + drwav_uint64 riffChunkSizeBytes; + drwav_uint64 fileSizeBytes = 0; + + if (pFormat->container == drwav_container_riff) { + riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes); + fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */ + } else if (pFormat->container == drwav_container_w64) { + riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); + fileSizeBytes = riffChunkSizeBytes; + } else if (pFormat->container == drwav_container_rf64) { + riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes); + fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */ + } + + return fileSizeBytes; +} + + +#ifndef DR_WAV_NO_STDIO + +/* drwav_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ +#include <errno.h> +static drwav_result drwav_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRWAV_SUCCESS; + #ifdef EPERM + case EPERM: return DRWAV_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRWAV_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRWAV_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRWAV_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRWAV_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRWAV_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRWAV_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRWAV_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRWAV_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRWAV_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRWAV_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRWAV_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRWAV_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRWAV_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRWAV_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRWAV_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRWAV_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRWAV_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRWAV_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRWAV_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRWAV_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRWAV_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRWAV_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRWAV_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRWAV_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRWAV_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRWAV_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRWAV_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRWAV_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRWAV_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRWAV_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRWAV_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRWAV_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRWAV_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRWAV_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRWAV_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRWAV_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRWAV_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRWAV_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRWAV_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRWAV_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRWAV_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRWAV_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRWAV_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRWAV_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRWAV_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRWAV_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRWAV_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRWAV_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRWAV_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRWAV_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRWAV_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRWAV_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRWAV_ERROR; + #endif + #ifdef EADV + case EADV: return DRWAV_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRWAV_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRWAV_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRWAV_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRWAV_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRWAV_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRWAV_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRWAV_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRWAV_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRWAV_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRWAV_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRWAV_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRWAV_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRWAV_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRWAV_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRWAV_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRWAV_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRWAV_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRWAV_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRWAV_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRWAV_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRWAV_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRWAV_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRWAV_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRWAV_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRWAV_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRWAV_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRWAV_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRWAV_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRWAV_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRWAV_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRWAV_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRWAV_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRWAV_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRWAV_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRWAV_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRWAV_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRWAV_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRWAV_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRWAV_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRWAV_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRWAV_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRWAV_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRWAV_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRWAV_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRWAV_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRWAV_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRWAV_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRWAV_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRWAV_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRWAV_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRWAV_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRWAV_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRWAV_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRWAV_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRWAV_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRWAV_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRWAV_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRWAV_ERROR; + #endif + default: return DRWAV_ERROR; + } +} + +static drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if _MSC_VER && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRWAV_INVALID_ARGS; + } + +#if _MSC_VER && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drwav_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drwav_result result = drwav_result_from_errno(errno); + if (result == DRWAV_SUCCESS) { + result = DRWAV_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return DRWAV_SUCCESS; +} + +/* +_wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + +This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() +fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. +*/ +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRWAV_HAS_WFOPEN + #endif +#endif + +static drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRWAV_INVALID_ARGS; + } + +#if defined(DRWAV_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drwav_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drwav_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can + think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + */ + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + DRWAV_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drwav_result_from_errno(errno); + } + + pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRWAV_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + DRWAV_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + + if (*ppFile == NULL) { + return DRWAV_ERROR; + } +#endif + + return DRWAV_SUCCESS; +} + + +static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) +{ + return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); +} + +static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); +} + + +static drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav_bool32 result; + + result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRWAV_TRUE) { + fclose(pFile); + return result; + } + + result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); + if (result != DRWAV_TRUE) { + fclose(pFile); + return result; + } + + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); +} + + +static drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav_bool32 result; + + result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRWAV_TRUE) { + fclose(pFile); + return result; + } + + result = drwav_init_write__internal(pWav, pFormat, totalSampleCount); + if (result != DRWAV_TRUE) { + fclose(pFile); + return result; + } + + return DRWAV_TRUE; +} + +static drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); +} + +static drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + + /* This takes ownership of the FILE* object. */ + return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} +#endif /* DR_WAV_NO_STDIO */ + + +static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + drwav* pWav = (drwav*)pUserData; + size_t bytesRemaining; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); + + bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); + pWav->memoryStream.currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) +{ + drwav* pWav = (drwav*)pUserData; + DRWAV_ASSERT(pWav != NULL); + + if (origin == drwav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { + return DRWAV_FALSE; /* Trying to seek too far forward. */ + } + } else { + if (pWav->memoryStream.currentReadPos < (size_t)-offset) { + return DRWAV_FALSE; /* Trying to seek too far backwards. */ + } + } + + /* This will never underflow thanks to the clamps above. */ + pWav->memoryStream.currentReadPos += offset; + } else { + if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { + pWav->memoryStream.currentReadPos = offset; + } else { + return DRWAV_FALSE; /* Trying to seek too far forward. */ + } + } + + return DRWAV_TRUE; +} + +static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) +{ + drwav* pWav = (drwav*)pUserData; + size_t bytesRemaining; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); + + bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; + if (bytesRemaining < bytesToWrite) { + /* Need to reallocate. */ + void* pNewData; + size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; + + /* If doubling wasn't enough, just make it the minimum required size to write the data. */ + if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { + newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; + } + + pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); + if (pNewData == NULL) { + return 0; + } + + *pWav->memoryStreamWrite.ppData = pNewData; + pWav->memoryStreamWrite.dataCapacity = newDataCapacity; + } + + DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); + + pWav->memoryStreamWrite.currentWritePos += bytesToWrite; + if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { + pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; + } + + *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; + + return bytesToWrite; +} + +static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) +{ + drwav* pWav = (drwav*)pUserData; + DRWAV_ASSERT(pWav != NULL); + + if (origin == drwav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { + offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); /* Trying to seek too far forward. */ + } + } else { + if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { + offset = -(int)pWav->memoryStreamWrite.currentWritePos; /* Trying to seek too far backwards. */ + } + } + + /* This will never underflow thanks to the clamps above. */ + pWav->memoryStreamWrite.currentWritePos += offset; + } else { + if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { + pWav->memoryStreamWrite.currentWritePos = offset; + } else { + pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */ + } + } + + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (data == NULL || dataSize == 0) { + return DRWAV_FALSE; + } + + if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + pWav->memoryStream.data = (const drwav_uint8*)data; + pWav->memoryStream.dataSize = dataSize; + pWav->memoryStream.currentReadPos = 0; + + return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); +} + + +static drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (ppData == NULL || pDataSize == NULL) { + return DRWAV_FALSE; + } + + *ppData = NULL; /* Important because we're using realloc()! */ + *pDataSize = 0; + + if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { + return DRWAV_FALSE; + } + + pWav->memoryStreamWrite.ppData = ppData; + pWav->memoryStreamWrite.pDataSize = pDataSize; + pWav->memoryStreamWrite.dataSize = 0; + pWav->memoryStreamWrite.dataCapacity = 0; + pWav->memoryStreamWrite.currentWritePos = 0; + + return drwav_init_write__internal(pWav, pFormat, totalSampleCount); +} + +DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); +} + +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} + + + +DRWAV_API drwav_result drwav_uninit(drwav* pWav) +{ + drwav_result result = DRWAV_SUCCESS; + + if (pWav == NULL) { + return DRWAV_INVALID_ARGS; + } + + /* + If the drwav object was opened in write mode we'll need to finalize a few things: + - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers. + - Set the size of the "data" chunk. + */ + if (pWav->onWrite != NULL) { + drwav_uint32 paddingSize = 0; + + /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */ + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); + } else { + paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); + } + + if (paddingSize > 0) { + drwav_uint64 paddingData = 0; + drwav__write(pWav, &paddingData, paddingSize); /* Byte order does not matter for this. */ + } + + /* + Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need + to do this when using non-sequential mode. + */ + if (pWav->onSeek && !pWav->isSequentialWrite) { + if (pWav->container == drwav_container_riff) { + /* The "RIFF" chunk size. */ + if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { + drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize); + drwav__write_u32ne_to_le(pWav, riffChunkSize); + } + + /* the "data" chunk size. */ + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) { + drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); + drwav__write_u32ne_to_le(pWav, dataChunkSize); + } + } else if (pWav->container == drwav_container_w64) { + /* The "RIFF" chunk size. */ + if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { + drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, riffChunkSize); + } + + /* The "data" chunk size. */ + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) { + drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, dataChunkSize); + } + } else if (pWav->container == drwav_container_rf64) { + /* We only need to update the ds64 chunk. The "RIFF" and "data" chunks always have their sizes set to 0xFFFFFFFF for RF64. */ + int ds64BodyPos = 12 + 8; + + /* The "RIFF" chunk size. */ + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { + drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, riffChunkSize); + } + + /* The "data" chunk size. */ + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { + drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, dataChunkSize); + } + } + } + + /* Validation for sequential mode. */ + if (pWav->isSequentialWrite) { + if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { + result = DRWAV_INVALID_FILE; + } + } + } + +#ifndef DR_WAV_NO_STDIO + /* + If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file() + was used by looking at the onRead and onSeek callbacks. + */ + if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { + fclose((FILE*)pWav->pUserData); + } +#endif + + return result; +} + + + +DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) +{ + size_t bytesRead; + + if (pWav == NULL || bytesToRead == 0) { + return 0; + } + + if (bytesToRead > pWav->bytesRemaining) { + bytesToRead = (size_t)pWav->bytesRemaining; + } + + if (pBufferOut != NULL) { + bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); + } else { + /* We need to seek. If we fail, we need to read-and-discard to make sure we get a good byte count. */ + bytesRead = 0; + while (bytesRead < bytesToRead) { + size_t bytesToSeek = (bytesToRead - bytesRead); + if (bytesToSeek > 0x7FFFFFFF) { + bytesToSeek = 0x7FFFFFFF; + } + + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { + break; + } + + bytesRead += bytesToSeek; + } + + /* When we get here we may need to read-and-discard some data. */ + while (bytesRead < bytesToRead) { + drwav_uint8 buffer[4096]; + size_t bytesSeeked; + size_t bytesToSeek = (bytesToRead - bytesRead); + if (bytesToSeek > sizeof(buffer)) { + bytesToSeek = sizeof(buffer); + } + + bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); + bytesRead += bytesSeeked; + + if (bytesSeeked < bytesToSeek) { + break; /* Reached the end. */ + } + } + } + + pWav->bytesRemaining -= bytesRead; + return bytesRead; +} + + + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +{ + drwav_uint32 bytesPerFrame; + drwav_uint64 bytesToRead; /* Intentionally uint64 instead of size_t so we can do a check that we're not reading too much on 32-bit builds. */ + + if (pWav == NULL || framesToRead == 0) { + return 0; + } + + /* Cannot use this function for compressed formats. */ + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + return 0; + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + /* Don't try to read more samples than can potentially fit in the output buffer. */ + bytesToRead = framesToRead * bytesPerFrame; + if (bytesToRead > DRWAV_SIZE_MAX) { + bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; /* Round the number of bytes to read to a clean frame boundary. */ + } + + /* + Doing an explicit check here just to make it clear that we don't want to be attempt to read anything if there's no bytes to read. There + *could* be a time where it evaluates to 0 due to overflowing. + */ + if (bytesToRead == 0) { + return 0; + } + + return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + + if (pBufferOut != NULL) { + drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, drwav_get_bytes_per_pcm_frame(pWav)/pWav->channels, pWav->translatedFormatTag); + } + + return framesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +{ + if (drwav__is_little_endian()) { + return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } else { + return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } +} + + + +DRWAV_API drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) +{ + if (pWav->onWrite != NULL) { + return DRWAV_FALSE; /* No seeking in write mode. */ + } + + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { + return DRWAV_FALSE; + } + + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + pWav->compressed.iCurrentPCMFrame = 0; + + /* Cached data needs to be cleared for compressed formats. */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + DRWAV_ZERO_OBJECT(&pWav->msadpcm); + } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + DRWAV_ZERO_OBJECT(&pWav->ima); + } else { + DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */ + } + } + + pWav->bytesRemaining = pWav->dataChunkDataSize; + return DRWAV_TRUE; +} + +DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) +{ + /* Seeking should be compatible with wave files > 2GB. */ + + if (pWav == NULL || pWav->onSeek == NULL) { + return DRWAV_FALSE; + } + + /* No seeking in write mode. */ + if (pWav->onWrite != NULL) { + return DRWAV_FALSE; + } + + /* If there are no samples, just return DRWAV_TRUE without doing anything. */ + if (pWav->totalPCMFrameCount == 0) { + return DRWAV_TRUE; + } + + /* Make sure the sample is clamped. */ + if (targetFrameIndex >= pWav->totalPCMFrameCount) { + targetFrameIndex = pWav->totalPCMFrameCount - 1; + } + + /* + For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need + to seek back to the start. + */ + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + /* TODO: This can be optimized. */ + + /* + If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards, + we first need to seek back to the start and then just do the same thing as a forward seek. + */ + if (targetFrameIndex < pWav->compressed.iCurrentPCMFrame) { + if (!drwav_seek_to_first_pcm_frame(pWav)) { + return DRWAV_FALSE; + } + } + + if (targetFrameIndex > pWav->compressed.iCurrentPCMFrame) { + drwav_uint64 offsetInFrames = targetFrameIndex - pWav->compressed.iCurrentPCMFrame; + + drwav_int16 devnull[2048]; + while (offsetInFrames > 0) { + drwav_uint64 framesRead = 0; + drwav_uint64 framesToRead = offsetInFrames; + if (framesToRead > drwav_countof(devnull)/pWav->channels) { + framesToRead = drwav_countof(devnull)/pWav->channels; + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); + } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); + } else { + DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */ + } + + if (framesRead != framesToRead) { + return DRWAV_FALSE; + } + + offsetInFrames -= framesRead; + } + } + } else { + drwav_uint64 totalSizeInBytes; + drwav_uint64 currentBytePos; + drwav_uint64 targetBytePos; + drwav_uint64 offset; + + totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav); + DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); + + currentBytePos = totalSizeInBytes - pWav->bytesRemaining; + targetBytePos = targetFrameIndex * drwav_get_bytes_per_pcm_frame(pWav); + + if (currentBytePos < targetBytePos) { + /* Offset forwards. */ + offset = (targetBytePos - currentBytePos); + } else { + /* Offset backwards. */ + if (!drwav_seek_to_first_pcm_frame(pWav)) { + return DRWAV_FALSE; + } + offset = targetBytePos; + } + + while (offset > 0) { + int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); + if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + + pWav->bytesRemaining -= offset32; + offset -= offset32; + } + } + + return DRWAV_TRUE; +} + + +DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) +{ + size_t bytesWritten; + + if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { + return 0; + } + + bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); + pWav->dataChunkDataSize += bytesWritten; + + return bytesWritten; +} + + +DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +{ + drwav_uint64 bytesToWrite; + drwav_uint64 bytesWritten; + const drwav_uint8* pRunningData; + + if (pWav == NULL || framesToWrite == 0 || pData == NULL) { + return 0; + } + + bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); + if (bytesToWrite > DRWAV_SIZE_MAX) { + return 0; + } + + bytesWritten = 0; + pRunningData = (const drwav_uint8*)pData; + + while (bytesToWrite > 0) { + size_t bytesJustWritten; + drwav_uint64 bytesToWriteThisIteration; + + bytesToWriteThisIteration = bytesToWrite; + DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */ + + bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); + if (bytesJustWritten == 0) { + break; + } + + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; + } + + return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; +} + +DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +{ + drwav_uint64 bytesToWrite; + drwav_uint64 bytesWritten; + drwav_uint32 bytesPerSample; + const drwav_uint8* pRunningData; + + if (pWav == NULL || framesToWrite == 0 || pData == NULL) { + return 0; + } + + bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); + if (bytesToWrite > DRWAV_SIZE_MAX) { + return 0; + } + + bytesWritten = 0; + pRunningData = (const drwav_uint8*)pData; + + bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; + + while (bytesToWrite > 0) { + drwav_uint8 temp[4096]; + drwav_uint32 sampleCount; + size_t bytesJustWritten; + drwav_uint64 bytesToWriteThisIteration; + + bytesToWriteThisIteration = bytesToWrite; + DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */ + + /* + WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need + to use an intermediary buffer for the conversion. + */ + sampleCount = sizeof(temp)/bytesPerSample; + + if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { + bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; + } + + DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); + drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag); + + bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); + if (bytesJustWritten == 0) { + break; + } + + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; + } + + return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; +} + +DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +{ + if (drwav__is_little_endian()) { + return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); + } else { + return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); + } +} + + +static drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead = 0; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(framesToRead > 0); + + /* TODO: Lots of room for optimization here. */ + + while (framesToRead > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) { + /* If there are no cached frames we need to load a new block. */ + if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + /* Mono. */ + drwav_uint8 header[7]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 1); + pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 3); + pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 5); + pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; + pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.cachedFrameCount = 2; + } else { + /* Stereo. */ + drwav_uint8 header[14]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.predictor[1] = header[1]; + pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 2); + pWav->msadpcm.delta[1] = drwav__bytes_to_s16(header + 4); + pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 6); + pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav__bytes_to_s16(header + 8); + pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 10); + pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav__bytes_to_s16(header + 12); + + pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; + pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; + pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; + pWav->msadpcm.cachedFrameCount = 2; + } + } + + /* Output anything that's cached. */ + while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) { + if (pBufferOut != NULL) { + drwav_uint32 iSample = 0; + for (iSample = 0; iSample < pWav->channels; iSample += 1) { + pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; + } + + pBufferOut += pWav->channels; + } + + framesToRead -= 1; + totalFramesRead += 1; + pWav->compressed.iCurrentPCMFrame += 1; + pWav->msadpcm.cachedFrameCount -= 1; + } + + if (framesToRead == 0) { + return totalFramesRead; + } + + + /* + If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next + loop iteration which will trigger the loading of a new block. + */ + if (pWav->msadpcm.cachedFrameCount == 0) { + if (pWav->msadpcm.bytesRemainingInBlock == 0) { + continue; + } else { + static drwav_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + + drwav_uint8 nibbles; + drwav_int32 nibble0; + drwav_int32 nibble1; + + if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock -= 1; + + /* TODO: Optimize away these if statements. */ + nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } + nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } + + if (pWav->channels == 1) { + /* Mono. */ + drwav_int32 newSample0; + drwav_int32 newSample1; + + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = drwav_clamp(newSample0, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample0; + + + newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[0]; + newSample1 = drwav_clamp(newSample1, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample1; + + + pWav->msadpcm.cachedFrames[2] = newSample0; + pWav->msadpcm.cachedFrames[3] = newSample1; + pWav->msadpcm.cachedFrameCount = 2; + } else { + /* Stereo. */ + drwav_int32 newSample0; + drwav_int32 newSample1; + + /* Left. */ + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = drwav_clamp(newSample0, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample0; + + + /* Right. */ + newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[1]; + newSample1 = drwav_clamp(newSample1, -32768, 32767); + + pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; + if (pWav->msadpcm.delta[1] < 16) { + pWav->msadpcm.delta[1] = 16; + } + + pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; + pWav->msadpcm.prevFrames[1][1] = newSample1; + + pWav->msadpcm.cachedFrames[2] = newSample0; + pWav->msadpcm.cachedFrames[3] = newSample1; + pWav->msadpcm.cachedFrameCount = 1; + } + } + } + } + + return totalFramesRead; +} + + +static drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead = 0; + drwav_uint32 iChannel; + + static drwav_int32 indexTable[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + + static drwav_int32 stepTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(framesToRead > 0); + + /* TODO: Lots of room for optimization here. */ + + while (framesToRead > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) { + /* If there are no cached samples we need to load a new block. */ + if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + /* Mono. */ + drwav_uint8 header[4]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + if (header[2] >= drwav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); + pWav->ima.bytesRemainingInBlock = 0; + return totalFramesRead; /* Invalid data. */ + } + + pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = header[2]; + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; + pWav->ima.cachedFrameCount = 1; + } else { + /* Stereo. */ + drwav_uint8 header[8]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); + pWav->ima.bytesRemainingInBlock = 0; + return totalFramesRead; /* Invalid data. */ + } + + pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = header[2]; + pWav->ima.predictor[1] = drwav__bytes_to_s16(header + 4); + pWav->ima.stepIndex[1] = header[6]; + + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; + pWav->ima.cachedFrameCount = 1; + } + } + + /* Output anything that's cached. */ + while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) { + if (pBufferOut != NULL) { + drwav_uint32 iSample; + for (iSample = 0; iSample < pWav->channels; iSample += 1) { + pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; + } + pBufferOut += pWav->channels; + } + + framesToRead -= 1; + totalFramesRead += 1; + pWav->compressed.iCurrentPCMFrame += 1; + pWav->ima.cachedFrameCount -= 1; + } + + if (framesToRead == 0) { + return totalFramesRead; + } + + /* + If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next + loop iteration which will trigger the loading of a new block. + */ + if (pWav->ima.cachedFrameCount == 0) { + if (pWav->ima.bytesRemainingInBlock == 0) { + continue; + } else { + /* + From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the + left channel, 4 bytes for the right channel. + */ + pWav->ima.cachedFrameCount = 8; + for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { + drwav_uint32 iByte; + drwav_uint8 nibbles[4]; + if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { + pWav->ima.cachedFrameCount = 0; + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock -= 4; + + for (iByte = 0; iByte < 4; ++iByte) { + drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); + drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); + + drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; + drwav_int32 predictor = pWav->ima.predictor[iChannel]; + + drwav_int32 diff = step >> 3; + if (nibble0 & 1) diff += step >> 2; + if (nibble0 & 2) diff += step >> 1; + if (nibble0 & 4) diff += step; + if (nibble0 & 8) diff = -diff; + + predictor = drwav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; + + + step = stepTable[pWav->ima.stepIndex[iChannel]]; + predictor = pWav->ima.predictor[iChannel]; + + diff = step >> 3; + if (nibble1 & 1) diff += step >> 2; + if (nibble1 & 2) diff += step >> 1; + if (nibble1 & 4) diff += step; + if (nibble1 & 8) diff = -diff; + + predictor = drwav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; + } + } + } + } + } + + return totalFramesRead; +} + + +#ifndef DR_WAV_NO_CONVERSION_API +static unsigned short g_drwavAlawTable[256] = { + 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, + 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, + 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, + 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, + 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, + 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, + 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, + 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, + 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, + 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, + 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, + 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, + 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, + 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, + 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, + 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 +}; + +static unsigned short g_drwavMulawTable[256] = { + 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, + 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, + 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, + 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, + 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, + 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, + 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, + 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, + 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, + 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, + 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, + 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, + 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, + 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 +}; + +static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) +{ + return (short)g_drwavAlawTable[sampleIn]; +} + +static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) +{ + return (short)g_drwavMulawTable[sampleIn]; +} + + + +static void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + + /* Special case for 8-bit sample data because it's treated as unsigned. */ + if (bytesPerSample == 1) { + drwav_u8_to_s16(pOut, pIn, totalSampleCount); + return; + } + + + /* Slightly more optimal implementation for common formats. */ + if (bytesPerSample == 2) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const drwav_int16*)pIn)[i]; + } + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_s16(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); + return; + } + + + /* Anything more than 64 bits per sample is not supported. */ + if (bytesPerSample > 8) { + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + + + /* Generic, slow converter. */ + for (i = 0; i < totalSampleCount; ++i) { + drwav_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; + shift += 8; + } + + pIn += j; + *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); + } +} + +static void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} + +static drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint32 bytesPerFrame; + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + + /* Fast path. */ + if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + /* Don't try to read more samples than can potentially fit in the output buffer. */ + if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); + } + + return 0; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + + +DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x << 8; + r = r - 32768; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8; + r = x >> 8; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x >> 16; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + float c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5f); + r = r - 32768; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + double x = pIn[i]; + double c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5); + r = r - 32768; + pOut[i] = (short)r; + } +} + +DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + for (i = 0; i < sampleCount; ++i) { + pOut[i] = drwav__alaw_to_s16(pIn[i]); + } +} + +DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + for (i = 0; i < sampleCount; ++i) { + pOut[i] = drwav__mulaw_to_s16(pIn[i]); + } +} + + + +static void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + + /* Special case for 8-bit sample data because it's treated as unsigned. */ + if (bytesPerSample == 1) { + drwav_u8_to_f32(pOut, pIn, sampleCount); + return; + } + + /* Slightly more optimal implementation for common formats. */ + if (bytesPerSample == 2) { + drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_f32(pOut, pIn, sampleCount); + return; + } + if (bytesPerSample == 4) { + drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); + return; + } + + + /* Anything more than 64 bits per sample is not supported. */ + if (bytesPerSample > 8) { + DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + return; + } + + + /* Generic, slow converter. */ + for (i = 0; i < sampleCount; ++i) { + drwav_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; + shift += 8; + } + + pIn += j; + *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); + } +} + +static void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + unsigned int i; + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((const float*)pIn)[i]; + } + return; + } else if (bytesPerSample == 8) { + drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount); + return; + } else { + /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ + DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + return; + } +} + + +static drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)framesRead*pWav->channels, bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_f32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + /* + We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't + want to duplicate that code. + */ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + if (framesRead == 0) { + break; + } + + drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_f32__ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + /* + We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't + want to duplicate that code. + */ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + if (framesRead == 0) { + break; + } + + drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + /* Fast path. */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + /* Don't try to read more samples than can potentially fit in the output buffer. */ + if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels; + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_pcm_frames_f32__msadpcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_f32__ima(pWav, framesToRead, pBufferOut); + } + + return 0; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + + +DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + +#ifdef DR_WAV_LIBSNDFILE_COMPAT + /* + It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears + libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note + the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated + correctness testing. This is disabled by default. + */ + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (pIn[i] / 256.0f) * 2 - 1; + } +#else + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ + x = x - 1; /* 0..2 to -1..1 */ + + *pOut++ = x; + } +#endif +} + +DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] * 0.000030517578125f; + } +} + +DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + double x; + drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); + drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); + drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); + + x = (double)((drwav_int32)(a | b | c) >> 8); + *pOut++ = (float)(x * 0.00000011920928955078125); + } +} + +DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)(pIn[i] / 2147483648.0); + } +} + +DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)pIn[i]; + } +} + +DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; + } +} + +DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; + } +} + + + +static void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + + /* Special case for 8-bit sample data because it's treated as unsigned. */ + if (bytesPerSample == 1) { + drwav_u8_to_s32(pOut, pIn, totalSampleCount); + return; + } + + /* Slightly more optimal implementation for common formats. */ + if (bytesPerSample == 2) { + drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const drwav_int32*)pIn)[i]; + } + return; + } + + + /* Anything more than 64 bits per sample is not supported. */ + if (bytesPerSample > 8) { + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + + + /* Generic, slow converter. */ + for (i = 0; i < totalSampleCount; ++i) { + drwav_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; + shift += 8; + } + + pIn += j; + *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); + } +} + +static void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} + + +static drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + drwav_uint32 bytesPerFrame; + + /* Fast path. */ + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_s32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + /* + We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't + want to duplicate that code. + */ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + if (framesRead == 0) { + break; + } + + drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_s32__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + /* + We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't + want to duplicate that code. + */ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + if (framesRead == 0) { + break; + } + + drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */ + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +static drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096]; + + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + totalFramesRead = 0; + + while (framesToRead > 0) { + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + if (framesRead == 0) { + break; + } + + drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); + + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + + /* Don't try to read more samples than can potentially fit in the output buffer. */ + if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels; + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_pcm_frames_s32__msadpcm(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_s32__ima(pWav, framesToRead, pBufferOut); + } + + return 0; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + } + + return framesRead; +} + + +DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((int)pIn[i] - 128) << 24; + } +} + +DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] << 16; + } +} + +DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + unsigned int s0 = pIn[i*3 + 0]; + unsigned int s1 = pIn[i*3 + 1]; + unsigned int s2 = pIn[i*3 + 2]; + + drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); + *pOut++ = sample32; + } +} + +DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + } +} + +DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + } +} + +DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; + } +} + +DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + + if (pOut == NULL || pIn == NULL) { + return; + } + + for (i= 0; i < sampleCount; ++i) { + *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; + } +} + + + +static drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +{ + drwav_uint64 sampleDataSize; + drwav_int16* pSampleData; + drwav_uint64 framesRead; + + DRWAV_ASSERT(pWav != NULL); + + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); + return NULL; /* File's too big. */ + } + + pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; /* Failed to allocate memory. */ + } + + framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); + return NULL; /* There was an error reading the samples. */ + } + + drwav_uninit(pWav); + + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + + return pSampleData; +} + +static float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +{ + drwav_uint64 sampleDataSize; + float* pSampleData; + drwav_uint64 framesRead; + + DRWAV_ASSERT(pWav != NULL); + + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); + return NULL; /* File's too big. */ + } + + pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; /* Failed to allocate memory. */ + } + + framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); + return NULL; /* There was an error reading the samples. */ + } + + drwav_uninit(pWav); + + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + + return pSampleData; +} + +static drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +{ + drwav_uint64 sampleDataSize; + drwav_int32* pSampleData; + drwav_uint64 framesRead; + + DRWAV_ASSERT(pWav != NULL); + + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); + return NULL; /* File's too big. */ + } + + pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */ + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; /* Failed to allocate memory. */ + } + + framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); + return NULL; /* There was an error reading the samples. */ + } + + drwav_uninit(pWav); + + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + + return pSampleData; +} + + + +DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +#ifndef DR_WAV_NO_STDIO +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + + +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#endif + +DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} + +DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + drwav wav; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#endif /* DR_WAV_NO_CONVERSION_API */ + + +DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + drwav__free_from_callbacks(p, pAllocationCallbacks); + } else { + drwav__free_default(p, NULL); + } +} + +DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data) +{ + return drwav__bytes_to_u16(data); +} + +DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data) +{ + return drwav__bytes_to_s16(data); +} + +DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data) +{ + return drwav__bytes_to_u32(data); +} + +DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data) +{ + return drwav__bytes_to_s32(data); +} + +DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data) +{ + return drwav__bytes_to_u64(data); +} + +DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data) +{ + return drwav__bytes_to_s64(data); +} + + +DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) +{ + return drwav__guid_equal(a, b); +} + +DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) +{ + return drwav__fourcc_equal(a, b); +} + +#endif /* dr_wav_c */ +#endif /* DR_WAV_IMPLEMENTATION */ + +/* +RELEASE NOTES - v0.11.0 +======================= +Version 0.11.0 has breaking API changes. + +Improved Client-Defined Memory Allocation +----------------------------------------- +The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The +existing system of DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE are still in place and will be used by default when no custom +allocation callbacks are specified. + +To use the new system, you pass in a pointer to a drwav_allocation_callbacks object to drwav_init() and family, like this: + + void* my_malloc(size_t sz, void* pUserData) + { + return malloc(sz); + } + void* my_realloc(void* p, size_t sz, void* pUserData) + { + return realloc(p, sz); + } + void my_free(void* p, void* pUserData) + { + free(p); + } + + ... + + drwav_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = &myData; + allocationCallbacks.onMalloc = my_malloc; + allocationCallbacks.onRealloc = my_realloc; + allocationCallbacks.onFree = my_free; + drwav_init_file(&wav, "my_file.wav", &allocationCallbacks); + +The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. + +Passing in null for the allocation callbacks object will cause dr_wav to use defaults which is the same as DRWAV_MALLOC, +DRWAV_REALLOC and DRWAV_FREE and the equivalent of how it worked in previous versions. + +Every API that opens a drwav object now takes this extra parameter. These include the following: + + drwav_init() + drwav_init_ex() + drwav_init_file() + drwav_init_file_ex() + drwav_init_file_w() + drwav_init_file_w_ex() + drwav_init_memory() + drwav_init_memory_ex() + drwav_init_write() + drwav_init_write_sequential() + drwav_init_write_sequential_pcm_frames() + drwav_init_file_write() + drwav_init_file_write_sequential() + drwav_init_file_write_sequential_pcm_frames() + drwav_init_file_write_w() + drwav_init_file_write_sequential_w() + drwav_init_file_write_sequential_pcm_frames_w() + drwav_init_memory_write() + drwav_init_memory_write_sequential() + drwav_init_memory_write_sequential_pcm_frames() + drwav_open_and_read_pcm_frames_s16() + drwav_open_and_read_pcm_frames_f32() + drwav_open_and_read_pcm_frames_s32() + drwav_open_file_and_read_pcm_frames_s16() + drwav_open_file_and_read_pcm_frames_f32() + drwav_open_file_and_read_pcm_frames_s32() + drwav_open_file_and_read_pcm_frames_s16_w() + drwav_open_file_and_read_pcm_frames_f32_w() + drwav_open_file_and_read_pcm_frames_s32_w() + drwav_open_memory_and_read_pcm_frames_s16() + drwav_open_memory_and_read_pcm_frames_f32() + drwav_open_memory_and_read_pcm_frames_s32() + +Endian Improvements +------------------- +Previously, the following APIs returned little-endian audio data. These now return native-endian data. This improves compatibility +on big-endian architectures. + + drwav_read_pcm_frames() + drwav_read_pcm_frames_s16() + drwav_read_pcm_frames_s32() + drwav_read_pcm_frames_f32() + drwav_open_and_read_pcm_frames_s16() + drwav_open_and_read_pcm_frames_s32() + drwav_open_and_read_pcm_frames_f32() + drwav_open_file_and_read_pcm_frames_s16() + drwav_open_file_and_read_pcm_frames_s32() + drwav_open_file_and_read_pcm_frames_f32() + drwav_open_file_and_read_pcm_frames_s16_w() + drwav_open_file_and_read_pcm_frames_s32_w() + drwav_open_file_and_read_pcm_frames_f32_w() + drwav_open_memory_and_read_pcm_frames_s16() + drwav_open_memory_and_read_pcm_frames_s32() + drwav_open_memory_and_read_pcm_frames_f32() + +APIs have been added to give you explicit control over whether or not audio data is read or written in big- or little-endian byte +order: + + drwav_read_pcm_frames_le() + drwav_read_pcm_frames_be() + drwav_read_pcm_frames_s16le() + drwav_read_pcm_frames_s16be() + drwav_read_pcm_frames_f32le() + drwav_read_pcm_frames_f32be() + drwav_read_pcm_frames_s32le() + drwav_read_pcm_frames_s32be() + drwav_write_pcm_frames_le() + drwav_write_pcm_frames_be() + +Removed APIs +------------ +The following APIs were deprecated in version 0.10.0 and have now been removed: + + drwav_open() + drwav_open_ex() + drwav_open_write() + drwav_open_write_sequential() + drwav_open_file() + drwav_open_file_ex() + drwav_open_file_write() + drwav_open_file_write_sequential() + drwav_open_memory() + drwav_open_memory_ex() + drwav_open_memory_write() + drwav_open_memory_write_sequential() + drwav_close() + + + +RELEASE NOTES - v0.10.0 +======================= +Version 0.10.0 has breaking API changes. There are no significant bug fixes in this release, so if you are affected you do +not need to upgrade. + +Removed APIs +------------ +The following APIs were deprecated in version 0.9.0 and have been completely removed in version 0.10.0: + + drwav_read() + drwav_read_s16() + drwav_read_f32() + drwav_read_s32() + drwav_seek_to_sample() + drwav_write() + drwav_open_and_read_s16() + drwav_open_and_read_f32() + drwav_open_and_read_s32() + drwav_open_file_and_read_s16() + drwav_open_file_and_read_f32() + drwav_open_file_and_read_s32() + drwav_open_memory_and_read_s16() + drwav_open_memory_and_read_f32() + drwav_open_memory_and_read_s32() + drwav::totalSampleCount + +See release notes for version 0.9.0 at the bottom of this file for replacement APIs. + +Deprecated APIs +--------------- +The following APIs have been deprecated. There is a confusing and completely arbitrary difference between drwav_init*() and +drwav_open*(), where drwav_init*() initializes a pre-allocated drwav object, whereas drwav_open*() will first allocated a +drwav object on the heap and then initialize it. drwav_open*() has been deprecated which means you must now use a pre- +allocated drwav object with drwav_init*(). If you need the previous functionality, you can just do a malloc() followed by +a called to one of the drwav_init*() APIs. + + drwav_open() + drwav_open_ex() + drwav_open_write() + drwav_open_write_sequential() + drwav_open_file() + drwav_open_file_ex() + drwav_open_file_write() + drwav_open_file_write_sequential() + drwav_open_memory() + drwav_open_memory_ex() + drwav_open_memory_write() + drwav_open_memory_write_sequential() + drwav_close() + +These APIs will be removed completely in a future version. The rationale for this change is to remove confusion between the +two different ways to initialize a drwav object. +*/ + +/* +REVISION HISTORY +================ +v0.12.16 - 2020-12-02 + - Fix a bug when trying to read more bytes than can fit in a size_t. + +v0.12.15 - 2020-11-21 + - Fix compilation with OpenWatcom. + +v0.12.14 - 2020-11-13 + - Minor code clean up. + +v0.12.13 - 2020-11-01 + - Improve compiler support for older versions of GCC. + +v0.12.12 - 2020-09-28 + - Add support for RF64. + - Fix a bug in writing mode where the size of the RIFF chunk incorrectly includes the header section. + +v0.12.11 - 2020-09-08 + - Fix a compilation error on older compilers. + +v0.12.10 - 2020-08-24 + - Fix a bug when seeking with ADPCM formats. + +v0.12.9 - 2020-08-02 + - Simplify sized types. + +v0.12.8 - 2020-07-25 + - Fix a compilation warning. + +v0.12.7 - 2020-07-15 + - Fix some bugs on big-endian architectures. + - Fix an error in s24 to f32 conversion. + +v0.12.6 - 2020-06-23 + - Change drwav_read_*() to allow NULL to be passed in as the output buffer which is equivalent to a forward seek. + - Fix a buffer overflow when trying to decode invalid IMA-ADPCM files. + - Add include guard for the implementation section. + +v0.12.5 - 2020-05-27 + - Minor documentation fix. + +v0.12.4 - 2020-05-16 + - Replace assert() with DRWAV_ASSERT(). + - Add compile-time and run-time version querying. + - DRWAV_VERSION_MINOR + - DRWAV_VERSION_MAJOR + - DRWAV_VERSION_REVISION + - DRWAV_VERSION_STRING + - drwav_version() + - drwav_version_string() + +v0.12.3 - 2020-04-30 + - Fix compilation errors with VC6. + +v0.12.2 - 2020-04-21 + - Fix a bug where drwav_init_file() does not close the file handle after attempting to load an erroneous file. + +v0.12.1 - 2020-04-13 + - Fix some pedantic warnings. + +v0.12.0 - 2020-04-04 + - API CHANGE: Add container and format parameters to the chunk callback. + - Minor documentation updates. + +v0.11.5 - 2020-03-07 + - Fix compilation error with Visual Studio .NET 2003. + +v0.11.4 - 2020-01-29 + - Fix some static analysis warnings. + - Fix a bug when reading f32 samples from an A-law encoded stream. + +v0.11.3 - 2020-01-12 + - Minor changes to some f32 format conversion routines. + - Minor bug fix for ADPCM conversion when end of file is reached. + +v0.11.2 - 2019-12-02 + - Fix a possible crash when using custom memory allocators without a custom realloc() implementation. + - Fix an integer overflow bug. + - Fix a null pointer dereference bug. + - Add limits to sample rate, channels and bits per sample to tighten up some validation. + +v0.11.1 - 2019-10-07 + - Internal code clean up. + +v0.11.0 - 2019-10-06 + - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation + routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: + - drwav_init() + - drwav_init_ex() + - drwav_init_file() + - drwav_init_file_ex() + - drwav_init_file_w() + - drwav_init_file_w_ex() + - drwav_init_memory() + - drwav_init_memory_ex() + - drwav_init_write() + - drwav_init_write_sequential() + - drwav_init_write_sequential_pcm_frames() + - drwav_init_file_write() + - drwav_init_file_write_sequential() + - drwav_init_file_write_sequential_pcm_frames() + - drwav_init_file_write_w() + - drwav_init_file_write_sequential_w() + - drwav_init_file_write_sequential_pcm_frames_w() + - drwav_init_memory_write() + - drwav_init_memory_write_sequential() + - drwav_init_memory_write_sequential_pcm_frames() + - drwav_open_and_read_pcm_frames_s16() + - drwav_open_and_read_pcm_frames_f32() + - drwav_open_and_read_pcm_frames_s32() + - drwav_open_file_and_read_pcm_frames_s16() + - drwav_open_file_and_read_pcm_frames_f32() + - drwav_open_file_and_read_pcm_frames_s32() + - drwav_open_file_and_read_pcm_frames_s16_w() + - drwav_open_file_and_read_pcm_frames_f32_w() + - drwav_open_file_and_read_pcm_frames_s32_w() + - drwav_open_memory_and_read_pcm_frames_s16() + - drwav_open_memory_and_read_pcm_frames_f32() + - drwav_open_memory_and_read_pcm_frames_s32() + Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use + DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE. + - Add support for reading and writing PCM frames in an explicit endianness. New APIs: + - drwav_read_pcm_frames_le() + - drwav_read_pcm_frames_be() + - drwav_read_pcm_frames_s16le() + - drwav_read_pcm_frames_s16be() + - drwav_read_pcm_frames_f32le() + - drwav_read_pcm_frames_f32be() + - drwav_read_pcm_frames_s32le() + - drwav_read_pcm_frames_s32be() + - drwav_write_pcm_frames_le() + - drwav_write_pcm_frames_be() + - Remove deprecated APIs. + - API CHANGE: The following APIs now return native-endian data. Previously they returned little-endian data. + - drwav_read_pcm_frames() + - drwav_read_pcm_frames_s16() + - drwav_read_pcm_frames_s32() + - drwav_read_pcm_frames_f32() + - drwav_open_and_read_pcm_frames_s16() + - drwav_open_and_read_pcm_frames_s32() + - drwav_open_and_read_pcm_frames_f32() + - drwav_open_file_and_read_pcm_frames_s16() + - drwav_open_file_and_read_pcm_frames_s32() + - drwav_open_file_and_read_pcm_frames_f32() + - drwav_open_file_and_read_pcm_frames_s16_w() + - drwav_open_file_and_read_pcm_frames_s32_w() + - drwav_open_file_and_read_pcm_frames_f32_w() + - drwav_open_memory_and_read_pcm_frames_s16() + - drwav_open_memory_and_read_pcm_frames_s32() + - drwav_open_memory_and_read_pcm_frames_f32() + +v0.10.1 - 2019-08-31 + - Correctly handle partial trailing ADPCM blocks. + +v0.10.0 - 2019-08-04 + - Remove deprecated APIs. + - Add wchar_t variants for file loading APIs: + drwav_init_file_w() + drwav_init_file_ex_w() + drwav_init_file_write_w() + drwav_init_file_write_sequential_w() + - Add drwav_target_write_size_bytes() which calculates the total size in bytes of a WAV file given a format and sample count. + - Add APIs for specifying the PCM frame count instead of the sample count when opening in sequential write mode: + drwav_init_write_sequential_pcm_frames() + drwav_init_file_write_sequential_pcm_frames() + drwav_init_file_write_sequential_pcm_frames_w() + drwav_init_memory_write_sequential_pcm_frames() + - Deprecate drwav_open*() and drwav_close(): + drwav_open() + drwav_open_ex() + drwav_open_write() + drwav_open_write_sequential() + drwav_open_file() + drwav_open_file_ex() + drwav_open_file_write() + drwav_open_file_write_sequential() + drwav_open_memory() + drwav_open_memory_ex() + drwav_open_memory_write() + drwav_open_memory_write_sequential() + drwav_close() + - Minor documentation updates. + +v0.9.2 - 2019-05-21 + - Fix warnings. + +v0.9.1 - 2019-05-05 + - Add support for C89. + - Change license to choice of public domain or MIT-0. + +v0.9.0 - 2018-12-16 + - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and + will be removed in v0.10.0. Deprecated APIs and their replacements: + drwav_read() -> drwav_read_pcm_frames() + drwav_read_s16() -> drwav_read_pcm_frames_s16() + drwav_read_f32() -> drwav_read_pcm_frames_f32() + drwav_read_s32() -> drwav_read_pcm_frames_s32() + drwav_seek_to_sample() -> drwav_seek_to_pcm_frame() + drwav_write() -> drwav_write_pcm_frames() + drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16() + drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32() + drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32() + drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16() + drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32() + drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32() + drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16() + drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32() + drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32() + drwav::totalSampleCount -> drwav::totalPCMFrameCount + - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*(). + - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*(). + - Add built-in support for smpl chunks. + - Add support for firing a callback for each chunk in the file at initialization time. + - This is enabled through the drwav_init_ex(), etc. family of APIs. + - Handle invalid FMT chunks more robustly. + +v0.8.5 - 2018-09-11 + - Const correctness. + - Fix a potential stack overflow. + +v0.8.4 - 2018-08-07 + - Improve 64-bit detection. + +v0.8.3 - 2018-08-05 + - Fix C++ build on older versions of GCC. + +v0.8.2 - 2018-08-02 + - Fix some big-endian bugs. + +v0.8.1 - 2018-06-29 + - Add support for sequential writing APIs. + - Disable seeking in write mode. + - Fix bugs with Wave64. + - Fix typos. + +v0.8 - 2018-04-27 + - Bug fix. + - Start using major.minor.revision versioning. + +v0.7f - 2018-02-05 + - Restrict ADPCM formats to a maximum of 2 channels. + +v0.7e - 2018-02-02 + - Fix a crash. + +v0.7d - 2018-02-01 + - Fix a crash. + +v0.7c - 2018-02-01 + - Set drwav.bytesPerSample to 0 for all compressed formats. + - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for + all format conversion reading APIs (*_s16, *_s32, *_f32 APIs). + - Fix some divide-by-zero errors. + +v0.7b - 2018-01-22 + - Fix errors with seeking of compressed formats. + - Fix compilation error when DR_WAV_NO_CONVERSION_API + +v0.7a - 2017-11-17 + - Fix some GCC warnings. + +v0.7 - 2017-11-04 + - Add writing APIs. + +v0.6 - 2017-08-16 + - API CHANGE: Rename dr_* types to drwav_*. + - Add support for custom implementations of malloc(), realloc(), etc. + - Add support for Microsoft ADPCM. + - Add support for IMA ADPCM (DVI, format code 0x11). + - Optimizations to drwav_read_s16(). + - Bug fixes. + +v0.5g - 2017-07-16 + - Change underlying type for booleans to unsigned. + +v0.5f - 2017-04-04 + - Fix a minor bug with drwav_open_and_read_s16() and family. + +v0.5e - 2016-12-29 + - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this. + - Minor fixes to documentation. + +v0.5d - 2016-12-28 + - Use drwav_int* and drwav_uint* sized types to improve compiler support. + +v0.5c - 2016-11-11 + - Properly handle JUNK chunks that come before the FMT chunk. + +v0.5b - 2016-10-23 + - A minor change to drwav_bool8 and drwav_bool32 types. + +v0.5a - 2016-10-11 + - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering. + - Improve A-law and mu-law efficiency. + +v0.5 - 2016-09-29 + - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to + keep it consistent with dr_audio and dr_flac. + +v0.4b - 2016-09-18 + - Fixed a typo in documentation. + +v0.4a - 2016-09-18 + - Fixed a typo. + - Change date format to ISO 8601 (YYYY-MM-DD) + +v0.4 - 2016-07-13 + - API CHANGE. Make onSeek consistent with dr_flac. + - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac. + - Added support for Sony Wave64. + +v0.3a - 2016-05-28 + - API CHANGE. Return drwav_bool32 instead of int in onSeek callback. + - Fixed a memory leak. + +v0.3 - 2016-05-22 + - Lots of API changes for consistency. + +v0.2a - 2016-05-16 + - Fixed Linux/GCC build. + +v0.2 - 2016-05-11 + - Added support for reading data as signed 32-bit PCM for consistency with dr_flac. + +v0.1a - 2016-05-07 + - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize. + +v0.1 - 2016-05-04 + - Initial versioned release. +*/ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <http://unlicense.org/> + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2020 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/Examples/OldMain/main.cpp b/Examples/OldMain/main.cpp new file mode 100644 index 0000000..6e991b7 --- /dev/null +++ b/Examples/OldMain/main.cpp @@ -0,0 +1,684 @@ +#include "whisper.h" + +// third-party utilities +// use your favorite implementations +#define DR_WAV_IMPLEMENTATION +#include "dr_wav.h" + +#include <cmath> +#include <fstream> +#include <cstdio> +#include <string> +#include <thread> +#include <vector> + +// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9] +// Lowest is red, middle is yellow, highest is green. +const std::vector<std::string> k_colors = { + "\033[38;5;196m", "\033[38;5;202m", "\033[38;5;208m", "\033[38;5;214m", "\033[38;5;220m", + "\033[38;5;226m", "\033[38;5;190m", "\033[38;5;154m", "\033[38;5;118m", "\033[38;5;82m", +}; + +// 500 -> 00:05.000 +// 6000 -> 01:00.000 +std::string to_timestamp(int64_t t, bool comma = false) { + int64_t msec = t * 10; + int64_t hr = msec / (1000 * 60 * 60); + msec = msec - hr * (1000 * 60 * 60); + int64_t min = msec / (1000 * 60); + msec = msec - min * (1000 * 60); + int64_t sec = msec / 1000; + msec = msec - sec * 1000; + + char buf[32]; + snprintf(buf, sizeof(buf), "%02d:%02d:%02d%s%03d", (int) hr, (int) min, (int) sec, comma ? "," : ".", (int) msec); + + return std::string(buf); +} + +int timestamp_to_sample(int64_t t, int n_samples) { + return std::max(0, std::min((int) n_samples - 1, (int) ((t*WHISPER_SAMPLE_RATE)/100))); +} + +// helper function to replace substrings +void replace_all(std::string & s, const std::string & search, const std::string & replace) { + for (size_t pos = 0; ; pos += replace.length()) { + pos = s.find(search, pos); + if (pos == std::string::npos) break; + s.erase(pos, search.length()); + s.insert(pos, replace); + } +} + +// command-line parameters +struct whisper_params { + int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency()); + int32_t n_processors = 1; + int32_t offset_t_ms = 0; + int32_t offset_n = 0; + int32_t duration_ms = 0; + int32_t max_context = -1; + int32_t max_len = 0; + + float word_thold = 0.01f; + + bool speed_up = false; + bool translate = false; + bool diarize = false; + bool output_txt = false; + bool output_vtt = false; + bool output_srt = false; + bool output_wts = false; + bool print_special = false; + bool print_colors = false; + bool print_progress = false; + bool no_timestamps = false; + + std::string language = "en"; + std::string prompt; + std::string model = "models/ggml-base.en.bin"; + + std::vector<std::string> fname_inp = {}; +}; + +void whisper_print_usage(int argc, char ** argv, const whisper_params & params); + +bool whisper_params_parse(int argc, char ** argv, whisper_params & params) { + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + if (arg[0] != '-') { + params.fname_inp.push_back(arg); + continue; + } + + if (arg == "-h" || arg == "--help") { + whisper_print_usage(argc, argv, params); + exit(0); + } + else if (arg == "-t" || arg == "--threads") { params.n_threads = std::stoi(argv[++i]); } + else if (arg == "-p" || arg == "--processors") { params.n_processors = std::stoi(argv[++i]); } + else if (arg == "-ot" || arg == "--offset-t") { params.offset_t_ms = std::stoi(argv[++i]); } + else if (arg == "-on" || arg == "--offset-n") { params.offset_n = std::stoi(argv[++i]); } + else if (arg == "-d" || arg == "--duration") { params.duration_ms = std::stoi(argv[++i]); } + else if (arg == "-mc" || arg == "--max-context") { params.max_context = std::stoi(argv[++i]); } + else if (arg == "-ml" || arg == "--max-len") { params.max_len = std::stoi(argv[++i]); } + else if (arg == "-wt" || arg == "--word-thold") { params.word_thold = std::stof(argv[++i]); } + else if (arg == "-su" || arg == "--speed-up") { params.speed_up = true; } + else if (arg == "-tr" || arg == "--translate") { params.translate = true; } + else if (arg == "-di" || arg == "--diarize") { params.diarize = true; } + else if (arg == "-otxt" || arg == "--output-txt") { params.output_txt = true; } + else if (arg == "-ovtt" || arg == "--output-vtt") { params.output_vtt = true; } + else if (arg == "-osrt" || arg == "--output-srt") { params.output_srt = true; } + else if (arg == "-owts" || arg == "--output-words") { params.output_wts = true; } + else if (arg == "-ps" || arg == "--print-special") { params.print_special = true; } + else if (arg == "-pc" || arg == "--print-colors") { params.print_colors = true; } + else if (arg == "-pp" || arg == "--print-progress") { params.print_progress = true; } + else if (arg == "-nt" || arg == "--no-timestamps") { params.no_timestamps = true; } + else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; } + else if ( arg == "--prompt") { params.prompt = argv[++i]; } + else if (arg == "-m" || arg == "--model") { params.model = argv[++i]; } + else if (arg == "-f" || arg == "--file") { params.fname_inp.emplace_back(argv[++i]); } + else { + fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); + whisper_print_usage(argc, argv, params); + exit(0); + } + } + + return true; +} + +void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & params) { + fprintf(stderr, "\n"); + fprintf(stderr, "usage: %s [options] file0.wav file1.wav ...\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -h, --help [default] show this help message and exit\n"); + fprintf(stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads); + fprintf(stderr, " -p N, --processors N [%-7d] number of processors to use during computation\n", params.n_processors); + fprintf(stderr, " -ot N, --offset-t N [%-7d] time offset in milliseconds\n", params.offset_t_ms); + fprintf(stderr, " -on N, --offset-n N [%-7d] segment index offset\n", params.offset_n); + fprintf(stderr, " -d N, --duration N [%-7d] duration of audio to process in milliseconds\n", params.duration_ms); + fprintf(stderr, " -mc N, --max-context N [%-7d] maximum number of text context tokens to store\n", params.max_context); + fprintf(stderr, " -ml N, --max-len N [%-7d] maximum segment length in characters\n", params.max_len); + fprintf(stderr, " -wt N, --word-thold N [%-7.2f] word timestamp probability threshold\n", params.word_thold); + fprintf(stderr, " -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n", params.speed_up ? "true" : "false"); + fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false"); + fprintf(stderr, " -di, --diarize [%-7s] stereo audio diarization\n", params.diarize ? "true" : "false"); + fprintf(stderr, " -otxt, --output-txt [%-7s] output result in a text file\n", params.output_txt ? "true" : "false"); + fprintf(stderr, " -ovtt, --output-vtt [%-7s] output result in a vtt file\n", params.output_vtt ? "true" : "false"); + fprintf(stderr, " -osrt, --output-srt [%-7s] output result in a srt file\n", params.output_srt ? "true" : "false"); + fprintf(stderr, " -owts, --output-words [%-7s] output script for generating karaoke video\n", params.output_wts ? "true" : "false"); + fprintf(stderr, " -ps, --print-special [%-7s] print special tokens\n", params.print_special ? "true" : "false"); + fprintf(stderr, " -pc, --print-colors [%-7s] print colors\n", params.print_colors ? "true" : "false"); + fprintf(stderr, " -pp, --print-progress [%-7s] print progress\n", params.print_progress ? "true" : "false"); + fprintf(stderr, " -nt, --no-timestamps [%-7s] do not print timestamps\n", params.no_timestamps ? "false" : "true"); + fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language ('auto' for auto-detect)\n", params.language.c_str()); + fprintf(stderr, " --prompt PROMPT [%-7s] initial prompt\n", params.prompt.c_str()); + fprintf(stderr, " -m FNAME, --model FNAME [%-7s] model path\n", params.model.c_str()); + fprintf(stderr, " -f FNAME, --file FNAME [%-7s] input WAV file path\n", ""); + fprintf(stderr, "\n"); +} + +struct whisper_print_user_data { + const whisper_params * params; + + const std::vector<std::vector<float>> * pcmf32s; +}; + +void whisper_print_segment_callback(struct whisper_context * ctx, int n_new, void * user_data) { + const auto & params = *((whisper_print_user_data *) user_data)->params; + const auto & pcmf32s = *((whisper_print_user_data *) user_data)->pcmf32s; + + const int n_segments = whisper_full_n_segments(ctx); + + // print the last n_new segments + const int s0 = n_segments - n_new; + if (s0 == 0) { + printf("\n"); + } + + for (int i = s0; i < n_segments; i++) { + if (params.no_timestamps) { + if (params.print_colors) { + for (int j = 0; j < whisper_full_n_tokens(ctx, i); ++j) { + if (params.print_special == false) { + const whisper_token id = whisper_full_get_token_id(ctx, i, j); + if (id >= whisper_token_eot(ctx)) { + continue; + } + } + + const char * text = whisper_full_get_token_text(ctx, i, j); + const float p = whisper_full_get_token_p (ctx, i, j); + + const int col = std::max(0, std::min((int) k_colors.size(), (int) (std::pow(p, 3)*float(k_colors.size())))); + + printf("%s%s%s", k_colors[col].c_str(), text, "\033[0m"); + } + } else { + const char * text = whisper_full_get_segment_text(ctx, i); + printf("%s", text); + } + fflush(stdout); + } else { + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + + std::string speaker; + + if (params.diarize && pcmf32s.size() == 2) { + const int64_t n_samples = pcmf32s[0].size(); + + const int64_t is0 = timestamp_to_sample(t0, n_samples); + const int64_t is1 = timestamp_to_sample(t1, n_samples); + + double energy0 = 0.0f; + double energy1 = 0.0f; + + for (int64_t j = is0; j < is1; j++) { + energy0 += fabs(pcmf32s[0][j]); + energy1 += fabs(pcmf32s[1][j]); + } + + if (energy0 > 1.1*energy1) { + speaker = "(speaker 0)"; + } else if (energy1 > 1.1*energy0) { + speaker = "(speaker 1)"; + } else { + speaker = "(speaker ?)"; + } + + //printf("is0 = %lld, is1 = %lld, energy0 = %f, energy1 = %f, %s\n", is0, is1, energy0, energy1, speaker.c_str()); + } + + if (params.print_colors) { + printf("[%s --> %s] ", to_timestamp(t0).c_str(), to_timestamp(t1).c_str()); + for (int j = 0; j < whisper_full_n_tokens(ctx, i); ++j) { + if (params.print_special == false) { + const whisper_token id = whisper_full_get_token_id(ctx, i, j); + if (id >= whisper_token_eot(ctx)) { + continue; + } + } + + const char * text = whisper_full_get_token_text(ctx, i, j); + const float p = whisper_full_get_token_p (ctx, i, j); + + const int col = std::max(0, std::min((int) k_colors.size(), (int) (std::pow(p, 3)*float(k_colors.size())))); + + printf("%s%s%s%s", speaker.c_str(), k_colors[col].c_str(), text, "\033[0m"); + } + printf("\n"); + } else { + const char * text = whisper_full_get_segment_text(ctx, i); + + printf("[%s --> %s] %s%s\n", to_timestamp(t0).c_str(), to_timestamp(t1).c_str(), speaker.c_str(), text); + } + } + } +} + +bool output_txt(struct whisper_context * ctx, const char * fname) { + std::ofstream fout(fname); + if (!fout.is_open()) { + fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname); + return false; + } + + fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname); + + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + fout << text << "\n"; + } + + return true; +} + +bool output_vtt(struct whisper_context * ctx, const char * fname) { + std::ofstream fout(fname); + if (!fout.is_open()) { + fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname); + return false; + } + + fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname); + + fout << "WEBVTT\n\n"; + + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + + fout << to_timestamp(t0) << " --> " << to_timestamp(t1) << "\n"; + fout << text << "\n\n"; + } + + return true; +} + +bool output_srt(struct whisper_context * ctx, const char * fname, const whisper_params & params) { + std::ofstream fout(fname); + if (!fout.is_open()) { + fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname); + return false; + } + + fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname); + + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + + fout << i + 1 + params.offset_n << "\n"; + fout << to_timestamp(t0, true) << " --> " << to_timestamp(t1, true) << "\n"; + fout << text << "\n\n"; + } + + return true; +} + +// karaoke video generation +// outputs a bash script that uses ffmpeg to generate a video with the subtitles +// TODO: font parameter adjustments +bool output_wts(struct whisper_context * ctx, const char * fname, const char * fname_inp, const whisper_params & /*params*/, float t_sec) { + std::ofstream fout(fname); + + fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname); + + // TODO: become parameter + static const char * font = "/System/Library/Fonts/Supplemental/Courier New Bold.ttf"; + + fout << "#!/bin/bash" << "\n"; + fout << "\n"; + + fout << "ffmpeg -i " << fname_inp << " -f lavfi -i color=size=1200x120:duration=" << t_sec << ":rate=25:color=black -vf \""; + + for (int i = 0; i < whisper_full_n_segments(ctx); i++) { + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + + const int n = whisper_full_n_tokens(ctx, i); + + std::vector<whisper_token_data> tokens(n); + for (int j = 0; j < n; ++j) { + tokens[j] = whisper_full_get_token_data(ctx, i, j); + } + + if (i > 0) { + fout << ","; + } + + // background text + fout << "drawtext=fontfile='" << font << "':fontsize=24:fontcolor=gray:x=(w-text_w)/2:y=h/2:text='':enable='between(t," << t0/100.0 << "," << t0/100.0 << ")'"; + + bool is_first = true; + + for (int j = 0; j < n; ++j) { + const auto & token = tokens[j]; + + if (tokens[j].id >= whisper_token_eot(ctx)) { + continue; + } + + std::string txt_bg; + std::string txt_fg; // highlight token + std::string txt_ul; // underline + + txt_bg = "> "; + txt_fg = "> "; + txt_ul = "\\ \\ "; + + { + for (int k = 0; k < n; ++k) { + const auto & token2 = tokens[k]; + + if (tokens[k].id >= whisper_token_eot(ctx)) { + continue; + } + + const std::string txt = whisper_token_to_str(ctx, token2.id); + + txt_bg += txt; + + if (k == j) { + for (int l = 0; l < (int) txt.size(); ++l) { + txt_fg += txt[l]; + txt_ul += "_"; + } + txt_fg += "|"; + } else { + for (int l = 0; l < (int) txt.size(); ++l) { + txt_fg += "\\ "; + txt_ul += "\\ "; + } + } + } + + ::replace_all(txt_bg, "'", "\u2019"); + ::replace_all(txt_bg, "\"", "\\\""); + ::replace_all(txt_fg, "'", "\u2019"); + ::replace_all(txt_fg, "\"", "\\\""); + } + + if (is_first) { + // background text + fout << ",drawtext=fontfile='" << font << "':fontsize=24:fontcolor=gray:x=(w-text_w)/2:y=h/2:text='" << txt_bg << "':enable='between(t," << t0/100.0 << "," << t1/100.0 << ")'"; + is_first = false; + } + + // foreground text + fout << ",drawtext=fontfile='" << font << "':fontsize=24:fontcolor=lightgreen:x=(w-text_w)/2+8:y=h/2:text='" << txt_fg << "':enable='between(t," << token.t0/100.0 << "," << token.t1/100.0 << ")'"; + + // underline + fout << ",drawtext=fontfile='" << font << "':fontsize=24:fontcolor=lightgreen:x=(w-text_w)/2+8:y=h/2+16:text='" << txt_ul << "':enable='between(t," << token.t0/100.0 << "," << token.t1/100.0 << ")'"; + } + } + + fout << "\" -c:v libx264 -pix_fmt yuv420p -y " << fname_inp << ".mp4" << "\n"; + + fout << "\n\n"; + fout << "echo \"Your video has been saved to " << fname_inp << ".mp4\"" << "\n"; + fout << "\n"; + fout << "echo \" ffplay " << fname_inp << ".mp4\"\n"; + fout << "\n"; + + fout.close(); + + fprintf(stderr, "%s: run 'source %s' to generate karaoke video\n", __func__, fname); + + return true; +} + +int main(int argc, char ** argv) { + whisper_params params; + + if (whisper_params_parse(argc, argv, params) == false) { + return 1; + } + + if (params.fname_inp.empty()) { + fprintf(stderr, "error: no input files specified\n"); + whisper_print_usage(argc, argv, params); + return 2; + } + + if (params.language != "auto" && whisper_lang_id(params.language.c_str()) == -1) { + fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str()); + whisper_print_usage(argc, argv, params); + exit(0); + } + + // whisper init + + struct whisper_context * ctx = whisper_init(params.model.c_str()); + + if (ctx == nullptr) { + fprintf(stderr, "error: failed to initialize whisper context\n"); + return 3; + } + + // initial prompt + std::vector<whisper_token> prompt_tokens; + + if (!params.prompt.empty()) { + prompt_tokens.resize(1024); + prompt_tokens.resize(whisper_tokenize(ctx, params.prompt.c_str(), prompt_tokens.data(), prompt_tokens.size())); + + fprintf(stderr, "\n"); + fprintf(stderr, "initial prompt: '%s'\n", params.prompt.c_str()); + fprintf(stderr, "initial tokens: [ "); + for (int i = 0; i < (int) prompt_tokens.size(); ++i) { + fprintf(stderr, "%d ", prompt_tokens[i]); + } + fprintf(stderr, "]\n"); + } + + for (int f = 0; f < (int) params.fname_inp.size(); ++f) { + const auto fname_inp = params.fname_inp[f]; + + std::vector<float> pcmf32; // mono-channel F32 PCM + std::vector<std::vector<float>> pcmf32s; // stereo-channel F32 PCM + + // WAV input + { + drwav wav; + std::vector<uint8_t> wav_data; // used for pipe input from stdin + + if (fname_inp == "-") { + { + uint8_t buf[1024]; + while (true) + { + const size_t n = fread(buf, 1, sizeof(buf), stdin); + if (n == 0) { + break; + } + wav_data.insert(wav_data.end(), buf, buf + n); + } + } + + if (drwav_init_memory(&wav, wav_data.data(), wav_data.size(), nullptr) == false) { + fprintf(stderr, "error: failed to open WAV file from stdin\n"); + return 4; + } + + fprintf(stderr, "%s: read %zu bytes from stdin\n", __func__, wav_data.size()); + } + else if (drwav_init_file(&wav, fname_inp.c_str(), nullptr) == false) { + fprintf(stderr, "error: failed to open '%s' as WAV file\n", fname_inp.c_str()); + return 5; + } + + if (wav.channels != 1 && wav.channels != 2) { + fprintf(stderr, "%s: WAV file '%s' must be mono or stereo\n", argv[0], fname_inp.c_str()); + return 6; + } + + if (params.diarize && wav.channels != 2 && params.no_timestamps == false) { + fprintf(stderr, "%s: WAV file '%s' must be stereo for diarization and timestamps have to be enabled\n", argv[0], fname_inp.c_str()); + return 6; + } + + if (wav.sampleRate != WHISPER_SAMPLE_RATE) { + fprintf(stderr, "%s: WAV file '%s' must be 16 kHz\n", argv[0], fname_inp.c_str()); + return 8; + } + + if (wav.bitsPerSample != 16) { + fprintf(stderr, "%s: WAV file '%s' must be 16-bit\n", argv[0], fname_inp.c_str()); + return 9; + } + + const uint64_t n = wav_data.empty() ? wav.totalPCMFrameCount : wav_data.size()/(wav.channels*wav.bitsPerSample/8); + + std::vector<int16_t> pcm16; + pcm16.resize(n*wav.channels); + drwav_read_pcm_frames_s16(&wav, n, pcm16.data()); + drwav_uninit(&wav); + + // convert to mono, float + pcmf32.resize(n); + if (wav.channels == 1) { + for (uint64_t i = 0; i < n; i++) { + pcmf32[i] = float(pcm16[i])/32768.0f; + } + } else { + for (uint64_t i = 0; i < n; i++) { + pcmf32[i] = float(pcm16[2*i] + pcm16[2*i + 1])/65536.0f; + } + } + + if (params.diarize) { + // convert to stereo, float + pcmf32s.resize(2); + + pcmf32s[0].resize(n); + pcmf32s[1].resize(n); + for (uint64_t i = 0; i < n; i++) { + pcmf32s[0][i] = float(pcm16[2*i])/32768.0f; + pcmf32s[1][i] = float(pcm16[2*i + 1])/32768.0f; + } + } + } + + // print system information + { + fprintf(stderr, "\n"); + fprintf(stderr, "system_info: n_threads = %d / %d | %s\n", + params.n_threads*params.n_processors, std::thread::hardware_concurrency(), whisper_print_system_info()); + } + + // print some info about the processing + { + fprintf(stderr, "\n"); + if (!whisper_is_multilingual(ctx)) { + if (params.language != "en" || params.translate) { + params.language = "en"; + params.translate = false; + fprintf(stderr, "%s: WARNING: model is not multilingual, ignoring language and translation options\n", __func__); + } + } + fprintf(stderr, "%s: processing '%s' (%d samples, %.1f sec), %d threads, %d processors, lang = %s, task = %s, timestamps = %d ...\n", + __func__, fname_inp.c_str(), int(pcmf32.size()), float(pcmf32.size())/WHISPER_SAMPLE_RATE, + params.n_threads, params.n_processors, + params.language.c_str(), + params.translate ? "translate" : "transcribe", + params.no_timestamps ? 0 : 1); + + fprintf(stderr, "\n"); + } + + // run the inference + { + whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY); + + wparams.print_realtime = false; + wparams.print_progress = params.print_progress; + wparams.print_timestamps = !params.no_timestamps; + wparams.print_special = params.print_special; + wparams.translate = params.translate; + wparams.language = params.language.c_str(); + wparams.n_threads = params.n_threads; + wparams.n_max_text_ctx = params.max_context >= 0 ? params.max_context : wparams.n_max_text_ctx; + wparams.offset_ms = params.offset_t_ms; + wparams.duration_ms = params.duration_ms; + + wparams.token_timestamps = params.output_wts || params.max_len > 0; + wparams.thold_pt = params.word_thold; + wparams.max_len = params.output_wts && params.max_len == 0 ? 60 : params.max_len; + + wparams.speed_up = params.speed_up; + + wparams.prompt_tokens = prompt_tokens.empty() ? nullptr : prompt_tokens.data(); + wparams.prompt_n_tokens = prompt_tokens.empty() ? 0 : prompt_tokens.size(); + + whisper_print_user_data user_data = { ¶ms, &pcmf32s }; + + // this callback is called on each new segment + if (!wparams.print_realtime) { + wparams.new_segment_callback = whisper_print_segment_callback; + wparams.new_segment_callback_user_data = &user_data; + } + + // example for abort mechanism + // in this example, we do not abort the processing, but we could if the flag is set to true + // the callback is called before every encoder run - if it returns false, the processing is aborted + { + static bool is_aborted = false; // NOTE: this should be atomic to avoid data race + + wparams.encoder_begin_callback = [](struct whisper_context * /*ctx*/, void * user_data) { + bool is_aborted = *(bool*)user_data; + return !is_aborted; + }; + wparams.encoder_begin_callback_user_data = &is_aborted; + } + + if (whisper_full_parallel(ctx, wparams, pcmf32.data(), pcmf32.size(), params.n_processors) != 0) { + fprintf(stderr, "%s: failed to process audio\n", argv[0]); + return 10; + } + } + + // output stuff + { + printf("\n"); + + // output to text file + if (params.output_txt) { + const auto fname_txt = fname_inp + ".txt"; + output_txt(ctx, fname_txt.c_str()); + } + + // output to VTT file + if (params.output_vtt) { + const auto fname_vtt = fname_inp + ".vtt"; + output_vtt(ctx, fname_vtt.c_str()); + } + + // output to SRT file + if (params.output_srt) { + const auto fname_srt = fname_inp + ".srt"; + output_srt(ctx, fname_srt.c_str(), params); + } + + // output to WTS file + if (params.output_wts) { + const auto fname_wts = fname_inp + ".wts"; + output_wts(ctx, fname_wts.c_str(), fname_inp.c_str(), params, float(pcmf32.size() + 1000)/WHISPER_SAMPLE_RATE); + } + } + } + + whisper_print_timings(ctx); + whisper_free(ctx); + + return 0; +} diff --git a/Examples/TranscribeCS/AnsiCodes.cs b/Examples/TranscribeCS/AnsiCodes.cs new file mode 100644 index 0000000..be04ce3 --- /dev/null +++ b/Examples/TranscribeCS/AnsiCodes.cs @@ -0,0 +1,68 @@ +using System.Runtime.InteropServices; + +/// <summary>Utility class to setup console coloring with ANSI codes.</summary> +/// <remarks>The feature requires Windows 10 or newer</remarks> +static class AnsiCodes +{ + const string dll = "kernel32.dll"; + + [DllImport( dll, SetLastError = true )] + static extern IntPtr GetStdHandle( int nStdHandle ); + + const int STD_OUTPUT_HANDLE = -11; + + [Flags] + enum ConsoleModes: uint + { + // Input flags + ENABLE_PROCESSED_INPUT = 0x0001, + ENABLE_LINE_INPUT = 0x0002, + ENABLE_ECHO_INPUT = 0x0004, + ENABLE_WINDOW_INPUT = 0x0008, + ENABLE_MOUSE_INPUT = 0x0010, + ENABLE_INSERT_MODE = 0x0020, + ENABLE_QUICK_EDIT_MODE = 0x0040, + ENABLE_EXTENDED_FLAGS = 0x0080, + ENABLE_AUTO_POSITION = 0x0100, + ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200, + + // Output flags + ENABLE_PROCESSED_OUTPUT = 0x0001, + ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002, + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004, + DISABLE_NEWLINE_AUTO_RETURN = 0x0008, + ENABLE_LVB_GRID_WORLDWIDE = 0x0010 + } + + [DllImport( dll, SetLastError = true )] + static extern bool GetConsoleMode( IntPtr hConsoleHandle, out ConsoleModes mode ); + + [DllImport( dll, SetLastError = true )] + static extern bool SetConsoleMode( IntPtr hConsoleHandle, ConsoleModes mode ); + + static AnsiCodes() + { + IntPtr h = GetStdHandle( STD_OUTPUT_HANDLE ); + IntPtr INVALID_HANDLE_VALUE = (IntPtr)( -1 ); + if( h == INVALID_HANDLE_VALUE ) + return; + + if( !GetConsoleMode( h, out ConsoleModes mode ) ) + return; + + if( mode.HasFlag( ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING ) ) + { + enabled = true; + return; + } + + mode |= ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if( SetConsoleMode( h, mode ) ) + { + enabled = true; + return; + } + } + + public static readonly bool enabled = false; +}
\ No newline at end of file diff --git a/Examples/TranscribeCS/CommandLineArgs.cs b/Examples/TranscribeCS/CommandLineArgs.cs new file mode 100644 index 0000000..4f9fb74 --- /dev/null +++ b/Examples/TranscribeCS/CommandLineArgs.cs @@ -0,0 +1,155 @@ +using System.Globalization; +using System.Reflection; +using Whisper; + +namespace TranscribeCS +{ + sealed record class CommandLineArgs + { + public int n_threads = Environment.ProcessorCount; + public int offset_t_ms = 0; + public int offset_n = 0; + public int duration_ms = 0; + public int max_context = -1; + public int max_len = 0; + + public float word_thold = 0.01f; + + public bool speed_up = false; + public bool translate = false; + public bool diarize = false; + public bool output_txt = false; + public bool output_vtt = false; + public bool output_srt = false; + public bool print_special = false; + public bool print_progress = false; + public bool print_colors = true; + public bool no_timestamps = false; + public int[]? prompt = null; + + public eLanguage language = eLanguage.English; + public string model = string.Empty; + public readonly List<string> fileNames = new List<string>(); + + const bool output_wts = false; + public void apply( ref Parameters p ) + { + p.setFlag( eFullParamsFlags.PrintRealtime, false ); + p.setFlag( eFullParamsFlags.PrintProgress, print_progress ); + p.setFlag( eFullParamsFlags.PrintTimestamps, !no_timestamps ); + p.setFlag( eFullParamsFlags.PrintSpecial, print_special ); + p.setFlag( eFullParamsFlags.Translate, translate ); + p.language = language; + p.cpuThreads = n_threads; + if( max_context >= 0 ) + p.n_max_text_ctx = max_context; + p.offset_ms = offset_t_ms; + p.duration_ms = duration_ms; + p.setFlag( eFullParamsFlags.TokenTimestamps, output_wts || max_len > 0 ); + p.thold_pt = word_thold; + p.max_len = output_wts && max_len == 0 ? 60 : max_len; + p.setFlag( eFullParamsFlags.SpeedupAudio, speed_up ); + } + + public eResultFlags resultFlags() + { + eResultFlags flags = eResultFlags.None; + bool wts = output_wts || max_len > 0; + if( !no_timestamps || wts ) + flags |= eResultFlags.Timestamps; + if( wts || print_colors ) + flags |= eResultFlags.Tokens; + return flags; + } + + static eLanguage parseLanguage( string lang ) => + Library.languageFromCode( lang ) ?? throw new ArgumentException( $"Unknown language code \"{lang}\"" ); + + public CommandLineArgs( string[] argv ) + { + for( int i = 0; i < argv.Length; i++ ) + { + string arg = argv[ i ]; + if( arg[ 0 ] != '-' ) + { + fileNames.Add( arg ); + continue; + } + if( arg == "-h" || arg == "--help" ) + { + printUsage(); + throw new OperationCanceledException(); + } + else if( arg == "-t" || arg == "--threads" ) n_threads = int.Parse( argv[ ++i ] ); + else if( arg == "-ot" || arg == "--offset-t" ) offset_t_ms = int.Parse( argv[ ++i ] ); + else if( arg == "-on" || arg == "--offset-n" ) offset_n = int.Parse( argv[ ++i ] ); + else if( arg == "-d" || arg == "--duration" ) duration_ms = int.Parse( argv[ ++i ] ); + else if( arg == "-mc" || arg == "--max-context" ) max_context = int.Parse( argv[ ++i ] ); + else if( arg == "-ml" || arg == "--max-len" ) max_len = int.Parse( argv[ ++i ] ); + else if( arg == "-wt" || arg == "--word-thold" ) word_thold = float.Parse( argv[ ++i ], CultureInfo.InvariantCulture ); + else if( arg == "-su" || arg == "--speed-up" ) speed_up = true; + else if( arg == "-tr" || arg == "--translate" ) translate = true; + else if( arg == "-di" || arg == "--diarize" ) diarize = true; + else if( arg == "-otxt" || arg == "--output-txt" ) output_txt = true; + else if( arg == "-ovtt" || arg == "--output-vtt" ) output_vtt = true; + else if( arg == "-osrt" || arg == "--output-srt" ) output_srt = true; + else if( arg == "-ps" || arg == "--print-special" ) print_special = true; + else if( arg == "-nc" || arg == "--no-colors" ) print_colors = false; + else if( arg == "-pp" || arg == "--print-progress" ) print_progress = true; + else if( arg == "-nt" || arg == "--no-timestamps" ) no_timestamps = true; + else if( arg == "-l" || arg == "--language" ) language = parseLanguage( argv[ ++i ] ); + else if( arg == "--prompt" ) prompt = parsePrompt( argv[ ++i ] ); + else if( arg == "-m" || arg == "--model" ) model = argv[ ++i ]; + else if( arg == "-f" || arg == "--file" ) fileNames.Add( argv[ ++i ] ); + else + throw new ArgumentException( $"Unknown argument: \"{arg}\"" ); + } + if( string.IsNullOrWhiteSpace( model ) ) + throw new ArgumentException( "The model file is not provided in the arguments" ); + if( !File.Exists( model ) ) + throw new FileNotFoundException( "Model not found", model ); + if( fileNames.Count <= 0 ) + throw new ArgumentException( "Please supply at least 1 input audio file to process" ); + } + + static string cstr( bool b ) => b.ToString(); + + static int[]? parsePrompt( string str ) + { + if( string.IsNullOrWhiteSpace( str ) ) + return null; + // TODO: expose whisper_tokenize function, as a method of iModel COM interface + throw new NotImplementedException(); + } + + void printUsage() + { + Console.WriteLine(); + + Console.WriteLine( "usage: {0} [options] file0.mp3 file1.wma ...", Path.GetFileName( Assembly.GetExecutingAssembly().Location ) ); + Console.WriteLine(); + Console.WriteLine( "options:" ); + Console.WriteLine( " -h, --help [default] show this help message and exit" ); + Console.WriteLine( " -t N, --threads N [{0,-7:D}] number of threads to use during computation", n_threads ); + Console.WriteLine( " -ot N, --offset-t N [{0,-7:D}] time offset in milliseconds", offset_t_ms ); + Console.WriteLine( " -on N, --offset-n N [{0,-7:D}] segment index offset", offset_n ); + Console.WriteLine( " -d N, --duration N [{0,-7:D}] duration of audio to process in milliseconds", duration_ms ); + Console.WriteLine( " -mc N, --max-context N [{0,-7:D}] maximum number of text context tokens to store", max_context ); + Console.WriteLine( " -ml N, --max-len N [{0,-7:D}] maximum segment length in characters", max_len ); + Console.WriteLine( " -wt N, --word-thold N [{0,-7:F2}] word timestamp probability threshold", word_thold ); + Console.WriteLine( " -su, --speed-up [{0,-7}] speed up audio by x2 (reduced accuracy)", cstr( speed_up ) ); + Console.WriteLine( " -tr, --translate [{0,-7}] translate from source language to english", cstr( translate ) ); + Console.WriteLine( " -di, --diarize [{0,-7}] stereo audio diarization", cstr( diarize ) ); + Console.WriteLine( " -otxt, --output-txt [{0,-7}] output result in a text file", cstr( output_txt ) ); + Console.WriteLine( " -ovtt, --output-vtt [{0,-7}] output result in a vtt file", cstr( output_vtt ) ); + Console.WriteLine( " -osrt, --output-srt [{0,-7}] output result in a srt file", cstr( output_srt ) ); + Console.WriteLine( " -ps, --print-special [{0,-7}] print special tokens", cstr( print_special ) ); + Console.WriteLine( " -nc, --no-colors [{0,-7}] do not print colors", cstr( !print_colors ) ); + Console.WriteLine( " -nt, --no-timestamps [{0,-7}] do not print timestamps", cstr( no_timestamps ) ); + Console.WriteLine( " -l LANG, --language LANG [{0,-7}] spoken language", language.getCode() ); + Console.WriteLine( " --prompt PROMPT [ ] initial prompt" ); + Console.WriteLine( " -m FNAME, --model FNAME [{0,-7}] model path", model ); + Console.WriteLine( " -f FNAME, --file FNAME [{0,-7}] path of the input audio file", "" ); + } + } +}
\ No newline at end of file diff --git a/Examples/TranscribeCS/Transcribe.cs b/Examples/TranscribeCS/Transcribe.cs new file mode 100644 index 0000000..6a1e500 --- /dev/null +++ b/Examples/TranscribeCS/Transcribe.cs @@ -0,0 +1,114 @@ +using System.Globalization; +using Whisper; + +namespace TranscribeCS +{ + /// <summary>Implementation of Callbacks abstract class, to print these segments as soon as they’re produced by the library.</summary> + sealed class Transcribe: Callbacks + { + readonly CommandLineArgs args; + readonly eResultFlags resultFlags; + + public Transcribe( CommandLineArgs args ) + { + this.args = args; + resultFlags = args.resultFlags(); + Console.OutputEncoding = System.Text.Encoding.UTF8; + } + + // Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9] + // Lowest is red, middle is yellow, highest is green. + readonly string[] k_colors = new string[] + { + "\x1B[38;5;196m", "\x1B[38;5;202m", "\x1B[38;5;208m", "\x1B[38;5;214m", "\x1B[38;5;220m", + "\x1B[38;5;226m", "\x1B[38;5;190m", "\x1B[38;5;154m", "\x1B[38;5;118m", "\x1B[38;5;82m" + }; + + int colorIndex( in sToken tok ) + { + float p = tok.probability; + float p3 = p * p * p; + int col = (int)( p3 * k_colors.Length ); + col = Math.Clamp( col, 0, k_colors.Length - 1 ); + return col; + } + + public static string printTime( TimeSpan ts ) => + ts.ToString( "hh':'mm':'ss'.'fff", CultureInfo.InvariantCulture ); + public static string printTimeWithComma( TimeSpan ts ) => + ts.ToString( "hh':'mm':'ss','fff", CultureInfo.InvariantCulture ); + + protected override void onNewSegment( Context sender, int countNew ) + { + TranscribeResult res = sender.results( resultFlags ); + ReadOnlySpan<sToken> tokens = res.tokens; + + int s0 = res.segments.Length - countNew; + if( s0 == 0 ) + Console.WriteLine(); + + for( int i = s0; i < res.segments.Length; i++ ) + { + sSegment seg = res.segments[ i ]; + + if( args.no_timestamps ) + { + if( args.print_colors && AnsiCodes.enabled ) + { + foreach( sToken tok in res.getTokens( seg ) ) + { + if( !args.print_special && tok.hasFlag( eTokenFlags.Special ) ) + continue; + Console.Write( "{0}{1}{2}", k_colors[ colorIndex( tok ) ], tok.text, "\x1B[0m" ); + } + } + else + Console.Write( seg.text ); + Console.Out.Flush(); + continue; + } + + string speaker = ""; +#if false + if( args.diarize && pcmf32s.size() == 2 ) + { + const size_t n_samples = pcmf32s[ 0 ].size(); + const int64_t is0 = SourceAudio::sampleFromTimestamp( seg.time.begin, n_samples ); + const int64_t is1 = SourceAudio::sampleFromTimestamp( seg.time.end, n_samples ); + + double energy0 = 0.0f; + double energy1 = 0.0f; + + for( int64_t j = is0; j < is1; j++ ) + { + energy0 += fabs( pcmf32s[ 0 ][ j ] ); + energy1 += fabs( pcmf32s[ 1 ][ j ] ); + } + + if( energy0 > 1.1 * energy1 ) + speaker = "(speaker 0)"; + else if( energy1 > 1.1 * energy0 ) + speaker = "(speaker 1)"; + else + speaker = "(speaker ?)"; + + //printf("is0 = %lld, is1 = %lld, energy0 = %f, energy1 = %f, %s\n", is0, is1, energy0, energy1, speaker.c_str()); + } +#endif + if( args.print_colors && AnsiCodes.enabled ) + { + Console.Write( "[{0} --> {1}] ", printTime( seg.time.begin ), printTime( seg.time.end ) ); + foreach( sToken tok in res.getTokens( seg ) ) + { + if( !args.print_special && tok.hasFlag( eTokenFlags.Special ) ) + continue; + Console.Write( "{0}{1}{2}{3}", speaker, k_colors[ colorIndex( tok ) ], tok.text, "\x1B[0m" ); + } + Console.WriteLine(); + } + else + Console.WriteLine( "[{0} --> {1}] {2}{3}", printTime( seg.time.begin ), printTime( seg.time.end ), speaker, seg.text ); + } + } + } +}
\ No newline at end of file diff --git a/Examples/TranscribeCS/TranscribeCS.cs b/Examples/TranscribeCS/TranscribeCS.cs new file mode 100644 index 0000000..9b828e3 --- /dev/null +++ b/Examples/TranscribeCS/TranscribeCS.cs @@ -0,0 +1,102 @@ +using Whisper; + +namespace TranscribeCS +{ + static class Program + { + static readonly bool streamAudio = true; + + static int Main( string[] args ) + { + try + { + CommandLineArgs cla; + try + { + cla = new CommandLineArgs( args ); + } + catch( OperationCanceledException ) + { + return 1; + } + const eLoggerFlags loggerFlags = eLoggerFlags.UseStandardError | eLoggerFlags.SkipFormatMessage; + Library.setLogSink( eLogLevel.Debug, loggerFlags ); + + using iModel model = Library.loadModel( cla.model ); + using Context context = model.createContext(); + cla.apply( ref context.parameters ); + using iMediaFoundation mf = Library.initMediaFoundation(); + Transcribe transcribe = new Transcribe( cla ); + + foreach( string audioFile in cla.fileNames ) + { + if( streamAudio ) + { + using iAudioReader reader = mf.openAudioFile( audioFile ); + context.runFull( reader, transcribe, null, cla.prompt ); + } + else + { + using iAudioBuffer buffer = mf.loadAudioFile( audioFile ); + context.runFull( buffer, transcribe, cla.prompt ); + } + // When asked to, produce these text files + if( cla.output_txt ) + writeTextFile( context, audioFile ); + if( cla.output_srt ) + writeSubRip( context, audioFile, cla ); + if( cla.output_vtt ) + writeWebVTT( context, audioFile ); + } + + context.timingsPrint(); + return 0; + } + catch( Exception ex ) + { + Console.WriteLine( ex.Message ); + return ex.HResult; + } + } + + static void writeTextFile( Context context, string audioPath ) + { + using var stream = File.CreateText( Path.ChangeExtension( audioPath, ".txt" ) ); + foreach( sSegment seg in context.results().segments ) + stream.WriteLine( seg.text ); + } + + static void writeSubRip( Context context, string audioPath, CommandLineArgs cliArgs ) + { + using var stream = File.CreateText( Path.ChangeExtension( audioPath, ".srt" ) ); + var segments = context.results( eResultFlags.Timestamps ).segments; + + for( int i = 0; i < segments.Length; i++ ) + { + stream.WriteLine( i + 1 + cliArgs.offset_n ); + sSegment seg = segments[ i ]; + string begin = Transcribe.printTimeWithComma( seg.time.begin ); + string end = Transcribe.printTimeWithComma( seg.time.end ); + stream.WriteLine( "{0} --> {1}", begin, end ); + stream.WriteLine( seg.text ); + stream.WriteLine(); + } + } + + static void writeWebVTT( Context context, string audioPath ) + { + using var stream = File.CreateText( Path.ChangeExtension( audioPath, ".vtt" ) ); + stream.WriteLine( "WEBVTT" ); + stream.WriteLine(); + + foreach( sSegment seg in context.results( eResultFlags.Timestamps ).segments ) + { + string begin = Transcribe.printTime( seg.time.begin ); + string end = Transcribe.printTime( seg.time.end ); + stream.WriteLine( "{0} --> {1}", begin, end ); + stream.WriteLine( seg.text ); + stream.WriteLine(); + } + } + } +}
\ No newline at end of file diff --git a/Examples/TranscribeCS/TranscribeCS.csproj b/Examples/TranscribeCS/TranscribeCS.csproj new file mode 100644 index 0000000..e9b8d0f --- /dev/null +++ b/Examples/TranscribeCS/TranscribeCS.csproj @@ -0,0 +1,19 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net6.0-windows</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> + <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> + <Platforms>x64</Platforms> + </PropertyGroup> + <ItemGroup> + <Content Include="..\..\x64\$(Configuration)\Whisper.dll" Link="Whisper.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\WhisperNet\WhisperNet.csproj" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Examples/WhisperDesktop/AppState.cpp b/Examples/WhisperDesktop/AppState.cpp new file mode 100644 index 0000000..6697e6a --- /dev/null +++ b/Examples/WhisperDesktop/AppState.cpp @@ -0,0 +1,192 @@ +#include "stdafx.h" +#include "AppState.h" +#include "Utils/miscUtils.h" +#include <commctrl.h> +#pragma comment(lib, "Comctl32.lib") +// #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#include "CircleIndicator.h" + +namespace +{ + static const HKEY regKeyRoot = HKEY_CURRENT_USER; + const LPCTSTR regKey = LR"(SOFTWARE\const.me\WhisperDesktop)"; + const LPCTSTR regValPath = L"modelPath"; + const LPCTSTR regValImpl = L"modelImpl"; + const LPCTSTR regValLang = L"language"; + const LPCTSTR regValLastScreen = L"screen"; + + static HRESULT readString( CRegKey& k, LPCTSTR name, CString& rdi ) + { + ULONG nChars = 0; + LSTATUS lss = k.QueryStringValue( name, nullptr, &nChars ); + if( lss != ERROR_SUCCESS ) + return HRESULT_FROM_WIN32( lss ); + if( nChars == 0 ) + { + rdi = L""; + return S_FALSE; + } + + lss = k.QueryStringValue( name, rdi.GetBufferSetLength( nChars ), &nChars ); + rdi.ReleaseBuffer(); + if( lss != ERROR_SUCCESS ) + return HRESULT_FROM_WIN32( lss ); + + return S_OK; + } + + using Whisper::eModelImplementation; +} + +HRESULT AppState::startup() +{ + HRESULT hr = CoInitializeEx( nullptr, COINIT_MULTITHREADED ); + if( FAILED( hr ) ) + { + reportFatalError( "CoInitializeEx failed", hr ); + return hr; + } + coInit = true; + + LSTATUS lss = registryKey.Create( regKeyRoot, regKey ); + if( lss != ERROR_SUCCESS ) + { + hr = HRESULT_FROM_WIN32( lss ); + reportFatalError( "Unable to open the registry key", hr ); + return hr; + } + + INITCOMMONCONTROLSEX init; + init.dwSize = sizeof( init ); + init.dwICC = ICC_LINK_CLASS | ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_TAB_CLASSES; + const BOOL icc = InitCommonControlsEx( &init ); + if( !icc ) + { + reportFatalError( "InitCommonControlsEx failed", HRESULT_FROM_WIN32( GetLastError() ) ); + return E_FAIL; + } + + hr = initMediaFoundation( &mediaFoundation ); + if( FAILED( hr ) ) + { + reportFatalError( "Unable to initialize Media Foundation runtime", hr ); + return hr; + } + + hr = console.initialize(); + if( FAILED( hr ) ) + { + reportFatalError( "Unable to initialize logging", hr ); + return hr; + } + + hr = CircleIndicator::registerClass(); + if( FAILED( hr ) ) + { + reportFatalError( "Unable to register custom controls", hr ); + return hr; + } + appIcon.LoadIcon( IDI_WHISPERDESKTOP ); + return S_OK; +} + +AppState::~AppState() +{ + if( coInit ) + { + CoUninitialize(); + coInit = false; + } +} + +HRESULT AppState::findModelSource() +{ + CHECK( readString( registryKey, regValPath, source.path ) ); + + { + CAtlFile file; + CHECK( file.Create( source.path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING ) ); + ULONGLONG len; + CHECK( file.GetSize( len ) ); + source.sizeInBytes = len; + } + + CString impl; + CHECK( readString( registryKey, regValImpl, impl ) ); + CHECK( implParse( impl, source.impl ) ); + source.found = true; + return S_OK; +} + +HRESULT AppState::saveModelSource() +{ + LSTATUS lss = registryKey.SetStringValue( regValPath, source.path ); + if( lss != ERROR_SUCCESS ) + return HRESULT_FROM_WIN32( lss ); + + LPCTSTR impl = implString( source.impl ); + if( nullptr == impl ) + return E_INVALIDARG; + lss = registryKey.SetStringValue( regValImpl, impl ); + if( lss != ERROR_SUCCESS ) + return HRESULT_FROM_WIN32( lss ); + + return S_OK; +} + +uint32_t AppState::languageRead() +{ + DWORD dw; + LSTATUS lss = registryKey.QueryDWORDValue( regValLang, dw ); + if( lss == ERROR_SUCCESS ) + return dw; + return UINT_MAX; +} + +void AppState::languageWrite( uint32_t key ) +{ + registryKey.SetDWORDValue( regValLang, key ); +} + +CString AppState::stringLoad( LPCTSTR name ) +{ + CString res; + readString( registryKey, name, res ); + return res; +} +void AppState::stringStore( LPCTSTR name, LPCTSTR value ) +{ + registryKey.SetStringValue( name, value ); +} +uint32_t AppState::dwordLoad( LPCTSTR name, uint32_t fallback ) +{ + DWORD dw; + LSTATUS lss = registryKey.QueryDWORDValue( name, dw ); + if( lss == ERROR_SUCCESS ) + return dw; + return fallback; +} +void AppState::dwordStore( LPCTSTR name, uint32_t value ) +{ + registryKey.SetDWORDValue( name, value ); +} + +void AppState::lastScreenSave( HRESULT code ) +{ + dwordStore( regValLastScreen, (uint32_t)code ); +} + +HRESULT AppState::lastScreenLoad() +{ + return (HRESULT)dwordLoad( regValLastScreen, SCREEN_TRANSCRIBE ); +} + +void AppState::setupIcon( CWindow* wnd ) +{ + HICON ic = appIcon; + if( nullptr != ic ) + { + wnd->SendMessage( WM_SETICON, ICON_SMALL, (LPARAM)ic ); + wnd->SendMessage( WM_SETICON, ICON_BIG, (LPARAM)ic ); + } +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/AppState.h b/Examples/WhisperDesktop/AppState.h new file mode 100644 index 0000000..0a45a21 --- /dev/null +++ b/Examples/WhisperDesktop/AppState.h @@ -0,0 +1,51 @@ +#pragma once +#include "Utils/DebugConsole.h" + +class AppState +{ + bool coInit = false; + CRegKey registryKey; + CIcon appIcon; +public: + + struct ModelSource + { + CString path; + bool found = false; + Whisper::eModelImplementation impl = (Whisper::eModelImplementation)0; + uint64_t sizeInBytes = 0; + }; + ModelSource source; + + DebugConsole console; + CComPtr<Whisper::iMediaFoundation> mediaFoundation; + CComPtr<Whisper::iModel> model; + + ~AppState(); + + // Setup the initial things + HRESULT startup(); + + HRESULT findModelSource(); + + HRESULT saveModelSource(); + + uint32_t languageRead(); + void languageWrite( uint32_t key ); + + CString stringLoad( LPCTSTR name ); + void stringStore( LPCTSTR name, LPCTSTR value ); + uint32_t dwordLoad( LPCTSTR name, uint32_t fallback ); + void dwordStore( LPCTSTR name, uint32_t value ); + + bool automaticallyLoadModel = true; + + void lastScreenSave( HRESULT code ); + HRESULT lastScreenLoad(); + + void setupIcon( CWindow* wnd ); +}; + +constexpr HRESULT SCREEN_MODEL = 1; +constexpr HRESULT SCREEN_TRANSCRIBE = 2; +constexpr HRESULT SCREEN_CAPTURE = 3;
\ No newline at end of file diff --git a/Examples/WhisperDesktop/CaptureDlg.cpp b/Examples/WhisperDesktop/CaptureDlg.cpp new file mode 100644 index 0000000..f1030dd --- /dev/null +++ b/Examples/WhisperDesktop/CaptureDlg.cpp @@ -0,0 +1,505 @@ +#include "stdafx.h" +#include "CaptureDlg.h" + +HRESULT CaptureDlg::show() +{ + auto res = DoModal( nullptr ); + if( res == -1 ) + return HRESULT_FROM_WIN32( GetLastError() ); + switch( res ) + { + case IDC_BACK: + return SCREEN_MODEL; + case IDC_TRANSCRIBE: + return SCREEN_TRANSCRIBE; + } + return S_OK; +} + +static const LPCTSTR regValDevice = L"captureDevice"; +static const LPCTSTR regValOutPath = L"captureTextFile"; +static const LPCTSTR regValOutFormat = L"captureTextFlags"; + +enum struct CaptureDlg::eTextFlags : uint32_t +{ + Save = 1, + Append = 2, + Timestamps = 4, +}; + +LRESULT CaptureDlg::OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) +{ + // First DDX call, hooks up variables to controls. + DoDataExchange( false ); + + languageSelector.initialize( m_hWnd, IDC_LANGUAGE, appState ); + cbTranslate.initialize( m_hWnd, IDC_TRANSLATE, appState ); + cbConsole.initialize( m_hWnd, IDC_CONSOLE, appState ); + + pendingState.initialize( + // Controls to disable while pending, re-enable afterwards + { + languageSelector, + cbCaptureDevice, + checkSave, checkAppend, checkTimestamps, transcribeOutputPath, transcribeOutputBrowse, + GetDlgItem( IDC_DEV_REFRESH ), + GetDlgItem( IDC_BACK ), + GetDlgItem( IDC_TRANSCRIBE ), + GetDlgItem( IDCANCEL ), + }, + // Controls to show while pending, hide afterwards + { + voiceActivity, GetDlgItem( IDC_VOICE_ACTIVITY_LBL ), + transcribeActivity, GetDlgItem( IDC_TRANS_LBL ), + stalled, GetDlgItem( IDC_STALL_LBL ), + progressBar, + } ); + + stalled.setActiveColor( flipRgb( 0xffcc33 ) ); + + HRESULT hr = work.create( this ); + if( FAILED( hr ) ) + { + reportError( m_hWnd, L"CreateThreadpoolWork failed", nullptr, hr ); + EndDialog( IDCANCEL ); + } + + listDevices(); + selectDevice( appState.stringLoad( regValDevice ) ); + + constexpr uint32_t defaultFlags = (uint32_t)eTextFlags::Append; + uint32_t flags = appState.dwordLoad( regValOutFormat, defaultFlags ); + if( flags & (uint32_t)eTextFlags::Save ) + checkSave.SetCheck( BST_CHECKED ); + if( flags & (uint32_t)eTextFlags::Append ) + checkAppend.SetCheck( BST_CHECKED ); + if( flags & (uint32_t)eTextFlags::Timestamps ) + checkTimestamps.SetCheck( BST_CHECKED ); + + transcribeOutputPath.SetWindowText( appState.stringLoad( regValOutPath ) ); + onSaveTextCheckbox(); + + appState.lastScreenSave( SCREEN_CAPTURE ); + appState.setupIcon( this ); + ATLVERIFY( CenterWindow() ); + return 0; +} + +HRESULT __stdcall CaptureDlg::listDevicesCallback( int len, const Whisper::sCaptureDevice* buffer, void* pv ) noexcept +{ + std::vector<sCaptureDevice>& devices = *( std::vector<sCaptureDevice> * )pv; + devices.resize( len ); + for( int i = 0; i < len; i++ ) + { + devices[ i ].displayName = buffer[ i ].displayName; + devices[ i ].endpoint = buffer[ i ].endpoint; + } + return S_OK; +} + +bool CaptureDlg::listDevices() +{ + appState.mediaFoundation->listCaptureDevices( &listDevicesCallback, &devices ); + cbCaptureDevice.ResetContent(); + for( const auto& dev : devices ) + cbCaptureDevice.AddString( dev.displayName ); + return !devices.empty(); +} + +void CaptureDlg::onDeviceRefresh() +{ + // Save the current selection + const int curSel = cbCaptureDevice.GetCurSel(); + CString str; + if( curSel >= 0 && curSel < (int)devices.size() ) + str = std::move( devices[ curSel ].endpoint ); + + // Refresh + listDevices(); + + // Restore the selection + selectDevice( str ); + + const size_t len = devices.size(); + if( len == 0 ) + { + MessageBox( L"No capture devices found on this computer.\nIf you have a USB microphone, connect it to this PC,\nand press “refresh” button.", + L"Capture Devices", MB_OK | MB_ICONWARNING ); + } + else + { + const char* suffix = ( len != 1 ) ? "s" : ""; + str.Format( L"Detected %zu audio capture device%S.", len, suffix ); + MessageBox( str, L"Capture Devices", MB_OK | MB_ICONINFORMATION ); + } +} + +bool CaptureDlg::selectDevice( LPCTSTR endpoint ) +{ + if( nullptr != endpoint && 0 != *endpoint ) + { + for( size_t i = 0; i < devices.size(); i++ ) + { + if( devices[ i ].endpoint == endpoint ) + { + cbCaptureDevice.SetCurSel( (int)i ); + return true; + } + } + } + + if( !devices.empty() ) + cbCaptureDevice.SetCurSel( 0 ); + return false; +} + +void CaptureDlg::onSaveTextCheckbox() +{ + const BOOL enabled = ( checkSave.GetCheck() == BST_CHECKED ); + std::array<HWND, 4> controls = { checkAppend, checkTimestamps, transcribeOutputPath, transcribeOutputBrowse }; + for( HWND w : controls ) + ::EnableWindow( w, enabled ); +} + +void CaptureDlg::onBrowseResult() +{ + LPCTSTR title = L"Output Text File"; + LPCTSTR outputFilters = L"Text files (*.txt)\0*.txt\0\0"; + CString path; + transcribeOutputPath.GetWindowText( path ); + if( !getSaveFileName( m_hWnd, title, outputFilters, path ) ) + return; + + LPCTSTR ext = PathFindExtension( path ); + if( 0 == *ext ) + { + wchar_t* const buffer = path.GetBufferSetLength( path.GetLength() + 5 ); + PathRenameExtension( buffer, L".txt" ); + path.ReleaseBuffer(); + } + + transcribeOutputPath.SetWindowText( path ); +} + +CaptureDlg::eTextFlags CaptureDlg::getOutputFlags() +{ + uint32_t flags = 0; + if( checkSave.GetCheck() == BST_CHECKED ) + flags |= (uint32_t)eTextFlags::Save; + if( checkAppend.GetCheck() == BST_CHECKED ) + flags |= (uint32_t)eTextFlags::Append; + if( checkTimestamps.GetCheck() == BST_CHECKED ) + flags |= (uint32_t)eTextFlags::Timestamps; + return (eTextFlags)flags; +} + +void CaptureDlg::setPending( bool nowPending ) +{ + pendingState.setPending( nowPending ); + if( nowPending ) + { + progressBar.SetMarquee( TRUE, 0 ); + btnRunCapture.SetWindowText( L"Stop" ); + } + else + { + progressBar.SetMarquee( FALSE, 0 ); + btnRunCapture.SetWindowText( L"Capture" ); + btnRunCapture.EnableWindow( TRUE ); + captureRunning = false; + } +} + +void CaptureDlg::onRunCapture() +{ + if( captureRunning ) + { + threadState.stopRequested = true; + btnRunCapture.EnableWindow( FALSE ); + return; + } + + int dev = cbCaptureDevice.GetCurSel(); + if( dev < 0 || dev >= (int)devices.size() ) + { + showError( L"Please select a capture device", S_FALSE ); + return; + } + threadState.endpoint = devices[ dev ].endpoint; + threadState.language = languageSelector.selectedLanguage(); + threadState.translate = cbTranslate.checked(); + if( isInvalidTranslate( m_hWnd, threadState.language, threadState.translate ) ) + return; + + threadState.flags = getOutputFlags(); + if( (uint32_t)threadState.flags & (uint32_t)eTextFlags::Save ) + { + transcribeOutputPath.GetWindowText( threadState.textOutputPath ); + if( threadState.textOutputPath.GetLength() <= 0 ) + { + showError( L"Please specify the output text file", S_FALSE ); + return; + } + appState.stringStore( regValOutPath, threadState.textOutputPath ); + } + else + cbConsole.ensureChecked(); + + languageSelector.saveSelection( appState ); + cbTranslate.saveSelection( appState ); + appState.stringStore( regValDevice, threadState.endpoint ); + appState.dwordStore( regValOutFormat, (uint32_t)threadState.flags ); + + captureRunning = true; + threadState.errorMessage = L""; + threadState.stopRequested = false; + threadState.captureParams.minDuration = 7; + threadState.captureParams.maxDuration = 11; + setPending( true ); + work.post(); +} + +void __declspec( noinline ) CaptureDlg::getThreadError() +{ + getLastError( threadState.errorMessage ); +} + +#define CHECK_EX( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) { getThreadError(); return __hr; } } + +static HRESULT appendDate( CString& str, const SYSTEMTIME& time ) +{ + constexpr DWORD dateFlags = DATE_LONGDATE; + int cc = GetDateFormatEx( LOCALE_NAME_USER_DEFAULT, dateFlags, &time, nullptr, nullptr, 0, nullptr ); + if( 0 == cc ) + return getLastHr(); + + const int oldLength = str.GetLength(); + wchar_t* const buffer = str.GetBufferSetLength( oldLength + cc ); + cc = GetDateFormatEx( LOCALE_NAME_USER_DEFAULT, dateFlags, &time, nullptr, buffer + oldLength, cc, nullptr ); + if( 0 != cc ) + { + str.ReleaseBuffer(); + return S_OK; + } + HRESULT hr = getLastHr(); + str.ReleaseBuffer(); + return hr; +} + +static HRESULT appendTime( CString& str, const SYSTEMTIME& time ) +{ + constexpr DWORD timeFlags = 0; + int cc = GetTimeFormatEx( LOCALE_NAME_USER_DEFAULT, timeFlags, &time, nullptr, nullptr, 0 ); + if( 0 == cc ) + return getLastHr(); + + const int oldLength = str.GetLength(); + wchar_t* const buffer = str.GetBufferSetLength( oldLength + cc ); + cc = GetTimeFormatEx( LOCALE_NAME_USER_DEFAULT, timeFlags, &time, nullptr, buffer + oldLength, cc ); + if( 0 != cc ) + { + str.ReleaseBuffer(); + return S_OK; + } + HRESULT hr = getLastHr(); + str.ReleaseBuffer(); + return hr; +} + +static HRESULT printDateTime( CAtlFile& file ) +{ + SYSTEMTIME time; + GetLocalTime( &time ); + + CString str; + str = L"==== Captured on "; + CHECK( appendDate( str, time ) ); + str += L", "; + CHECK( appendTime( str, time ) ); + str += L" ====\r\n"; + + CStringA u8; + makeUtf8( u8, str ); + return file.Write( cstr( u8 ), (DWORD)u8.GetLength() ); +} + +inline HRESULT CaptureDlg::runCapture() +{ + clearLastError(); + using namespace Whisper; + CComPtr<iAudioCapture> capture; + CHECK_EX( appState.mediaFoundation->openCaptureDevice( threadState.endpoint, threadState.captureParams, &capture ) ); + + HRESULT hr; + CAtlFile file; + const uint32_t flags = (uint32_t)threadState.flags; + if( flags & (uint32_t)eTextFlags::Save ) + { + const bool append = 0 != ( flags & (uint32_t)eTextFlags::Append ); + const DWORD creation = append ? OPEN_ALWAYS : CREATE_ALWAYS; + hr = file.Create( threadState.textOutputPath, GENERIC_WRITE, FILE_SHARE_READ, creation ); + if( FAILED( hr ) ) + { + threadState.errorMessage = L"Unable to create the output text file"; + return hr; + } + if( append ) + { + ULONGLONG size; + CHECK( file.GetSize( size ) ); + if( size == 0 ) + CHECK( writeUtf8Bom( file ) ) + else + CHECK( file.Seek( 0, SEEK_END ) ); + } + else + { + CHECK( writeUtf8Bom( file ) ); + } + + if( flags & (uint32_t)eTextFlags::Timestamps ) + CHECK( printDateTime( file ) ); + + threadState.file = &file; + } + else + threadState.file = nullptr; + + CComPtr<iContext> context; + CHECK_EX( appState.model->createContext( &context ) ); + + sFullParams fullParams; + CHECK_EX( context->fullDefaultParams( eSamplingStrategy::Greedy, &fullParams ) ); + fullParams.language = threadState.language; + fullParams.setFlag( eFullParamsFlags::Translate, threadState.translate ); + fullParams.resetFlag( eFullParamsFlags::PrintRealtime ); + fullParams.new_segment_callback = &newSegmentCallback; + fullParams.new_segment_callback_user_data = this; + + sCaptureCallbacks callbacks; + callbacks.shouldCancel = &cbCancel; + callbacks.captureStatus = &cbStatus; + callbacks.pv = this; + + CHECK_EX( context->runCapture( fullParams, callbacks, capture ) ); + threadState.file = nullptr; + + context->timingsPrint(); + return S_OK; +} + +void __stdcall CaptureDlg::poolCallback() noexcept +{ + const HRESULT hr = runCapture(); + PostMessage( WM_CALLBACK_COMPLETION, hr ); +} + +void CaptureDlg::showError( LPCTSTR text, HRESULT hr ) +{ + reportError( m_hWnd, text, L"Capture failed", hr ); +} + +LRESULT CaptureDlg::onThreadQuit( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) +{ + setPending( false ); + + const HRESULT hr = (HRESULT)wParam; + if( FAILED( hr ) ) + { + LPCTSTR failMessage = L"Capture failed"; + + if( threadState.errorMessage.GetLength() > 0 ) + { + CString tmp = failMessage; + tmp += L"\n"; + tmp += threadState.errorMessage; + showError( tmp, hr ); + } + else + showError( failMessage, hr ); + + return 0; + } + else + { + if( (uint32_t)threadState.flags & (uint32_t)eTextFlags::Save ) + ShellExecute( NULL, L"open", threadState.textOutputPath, NULL, NULL, SW_SHOW ); + } + + return 0; +} + +LRESULT CaptureDlg::onThreadStatus( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) +{ + using namespace Whisper; + const uint8_t newStatus = (uint8_t)wParam; + // Update the GUI + voiceActivity.setActive( 0 != ( newStatus & (uint8_t)eCaptureStatus::Voice ) ); + transcribeActivity.setActive( 0 != ( newStatus & (uint8_t)eCaptureStatus::Transcribing ) ); + stalled.setActive( 0 != ( newStatus & (uint8_t)eCaptureStatus::Stalled ) ); + return 0; +} + +HRESULT __stdcall CaptureDlg::cbCancel( void* pv ) noexcept +{ + CaptureDlg& dialog = *(CaptureDlg*)pv; + return dialog.threadState.stopRequested ? S_OK : S_FALSE; +} + +HRESULT __stdcall CaptureDlg::cbStatus( void* pv, Whisper::eCaptureStatus status ) noexcept +{ + CaptureDlg& dialog = *(CaptureDlg*)pv; + if( dialog.PostMessage( WM_CALLBACK_STATUS, (uint8_t)status ) ) + return S_OK; + return getLastHr(); +} + +HRESULT __cdecl CaptureDlg::newSegmentCallback( Whisper::iContext* ctx, uint32_t n_new, void* user_data ) noexcept +{ + using namespace Whisper; + CComPtr<iTranscribeResult> result; + const eResultFlags flags = eResultFlags::Timestamps | eResultFlags::Tokens; + CHECK( ctx->getResults( flags, &result ) ); + CHECK( logNewSegments( result, n_new ) ); + + CaptureDlg& dialog = *(CaptureDlg*)user_data; + return dialog.appendTextFile( result, n_new ); +} + +HRESULT CaptureDlg::appendTextFile( Whisper::iTranscribeResult* results, uint32_t newSegments ) +{ + if( nullptr == threadState.file || 0 == newSegments ) + return S_OK; + + using namespace Whisper; + sTranscribeLength length; + CHECK( results->getSize( length ) ); + + const size_t len = length.countSegments; + size_t i = len - newSegments; + + const sSegment* const segments = results->getSegments(); + CStringA str; + for( ; i < len; i++ ) + { + const sSegment& seg = segments[ i ]; + if( 0 != ( (uint32_t)threadState.flags & (uint32_t)eTextFlags::Timestamps ) ) + { + str = "["; + printTimeStamp( str, seg.time.begin ); + str += " --> "; + printTimeStamp( str, seg.time.end ); + str += "] "; + } + else + str = ""; + + str += seg.text; + str += "\r\n"; + + CHECK( threadState.file->Write( cstr( str ), (DWORD)str.GetLength() ) ); + } + + CHECK( threadState.file->Flush() ); + return S_OK; +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/CaptureDlg.h b/Examples/WhisperDesktop/CaptureDlg.h new file mode 100644 index 0000000..c66b10d --- /dev/null +++ b/Examples/WhisperDesktop/CaptureDlg.h @@ -0,0 +1,143 @@ +#pragma once +#include "AppState.h" +#include "Utils/WTL/atlddx.h" +#include "Utils/miscUtils.h" +#include "Utils/LanguageDropdown.h" +#include "Utils/TranslateCheckbox.h" +#include "Utils/PendingState.h" +#include "CircleIndicator.h" + +class CaptureDlg : + public CDialogImpl<CaptureDlg>, + public CWinDataExchange<CaptureDlg>, + public iThreadPoolCallback +{ + AppState& appState; + +public: + static constexpr UINT IDD = IDD_CAPTURE_DIALOG; + static constexpr UINT WM_CALLBACK_COMPLETION = WM_APP + 1; + static constexpr UINT WM_CALLBACK_STATUS = WM_APP + 2; + + CaptureDlg( AppState& app ) : appState( app ) { } + + HRESULT show(); + + BEGIN_MSG_MAP( CaptureDlg ) + MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog ) + ON_BUTTON_CLICK( IDC_CONSOLE, cbConsole.click ) + ON_BUTTON_CLICK( IDC_DEV_REFRESH, onDeviceRefresh ); + ON_BUTTON_CLICK( IDC_BROWSE_RESULT, onBrowseResult ); + ON_BUTTON_CLICK( IDC_SAVE_TEXT, onSaveTextCheckbox ); + ON_BUTTON_CLICK( IDC_RUN_CAPTURE, onRunCapture ); + + ON_BUTTON_CLICK( IDCANCEL, onClose ) + ON_BUTTON_CLICK( IDC_BACK, onBack ) + ON_BUTTON_CLICK( IDC_TRANSCRIBE, onTranscribe ); + + MESSAGE_HANDLER( WM_CALLBACK_COMPLETION, onThreadQuit ); + MESSAGE_HANDLER( WM_CALLBACK_STATUS, onThreadStatus ); + END_MSG_MAP() + + BEGIN_DDX_MAP( CaptureDlg ) + DDX_CONTROL_HANDLE( IDC_DEVICE, cbCaptureDevice ) + DDX_CONTROL_HANDLE( IDC_RUN_CAPTURE, btnRunCapture ); + DDX_CONTROL_HANDLE( IDC_TRANSCRIBE_PROGRESS, progressBar ); + DDX_CONTROL_HANDLE( IDC_SAVE_TEXT, checkSave ) + DDX_CONTROL_HANDLE( IDC_SAVE_APPEND, checkAppend ) + DDX_CONTROL_HANDLE( IDC_SAVE_TIMESTAMPS, checkTimestamps ) + DDX_CONTROL_HANDLE( IDC_PATH_RESULT, transcribeOutputPath ) + DDX_CONTROL_HANDLE( IDC_BROWSE_RESULT, transcribeOutputBrowse ); + + DDX_CONTROL( IDC_VOICE_ACTIVITY, voiceActivity ); + DDX_CONTROL( IDC_TRANS_STATUS, transcribeActivity ); + DDX_CONTROL( IDC_STALL_STATUS, stalled ); + + END_DDX_MAP() + +private: + PendingState pendingState; + void setPending( bool nowPending ); + + LRESULT OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); + + void onClose() + { + ATLVERIFY( EndDialog( IDCANCEL ) ); + } + void onBack() + { + ATLVERIFY( EndDialog( IDC_BACK ) ); + } + void onTranscribe() + { + ATLVERIFY( EndDialog( IDC_TRANSCRIBE ) ); + } + + // List capture devices, and populate the combobox + bool listDevices(); + void onDeviceRefresh(); + bool selectDevice( LPCTSTR endpoint ); + + static HRESULT __stdcall listDevicesCallback( int len, const Whisper::sCaptureDevice* buffer, void* pv ) noexcept; + ConsoleCheckbox cbConsole; + LanguageDropdown languageSelector; + TranslateCheckbox cbTranslate; + CComboBox cbCaptureDevice; + + void onBrowseResult(); + + enum struct eTextFlags : uint32_t; + CButton checkSave, checkAppend, checkTimestamps; + CEdit transcribeOutputPath; + CButton transcribeOutputBrowse; + void onSaveTextCheckbox(); + eTextFlags getOutputFlags(); + + CButton btnRunCapture; + CProgressBarCtrl progressBar; + ThreadPoolWork work; + + struct sCaptureDevice + { + CString displayName; + CString endpoint; + }; + std::vector<sCaptureDevice> devices; + + void showError( LPCTSTR text, HRESULT hr ); + + CircleIndicator voiceActivity; + CircleIndicator transcribeActivity; + CircleIndicator stalled; + + struct sThreadState + { + volatile bool stopRequested; + bool translate; + eTextFlags flags; + CAtlFile* file; + uint32_t language; + Whisper::sCaptureParams captureParams; + CString endpoint; + CString textOutputPath; + CString errorMessage; + }; + sThreadState threadState; + bool captureRunning = false; + + void getThreadError(); + void onRunCapture(); + HRESULT runCapture(); + void __stdcall poolCallback() noexcept override final; + + LRESULT onThreadQuit( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); + LRESULT onThreadStatus( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); + + static HRESULT __stdcall cbCancel( void* pv ) noexcept; + static HRESULT __stdcall cbStatus( void* pv, Whisper::eCaptureStatus status ) noexcept; + + static HRESULT __cdecl newSegmentCallback( Whisper::iContext* ctx, uint32_t n_new, void* user_data ) noexcept; + + HRESULT appendTextFile( Whisper::iTranscribeResult* results, uint32_t newSegments ); +};
\ No newline at end of file diff --git a/Examples/WhisperDesktop/CircleIndicator.cpp b/Examples/WhisperDesktop/CircleIndicator.cpp new file mode 100644 index 0000000..593bd4a --- /dev/null +++ b/Examples/WhisperDesktop/CircleIndicator.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "CircleIndicator.h" +#include <atltypes.h> +#include "AppState.h" + +namespace +{ + static const LPCTSTR windowClassName = L"CircleIndicator"; + + // Font with these symbols, shipped with Windows since forever: + // https://learn.microsoft.com/en-us/typography/font-list/segoe-ui-symbol + static const LPCTSTR fontName = L"Segoe UI Symbol"; + + // Outlined circle + static const LPCTSTR circleOutline = L"⚪"; + // Filled circle + static const LPCTSTR circleFilled = L"⚫"; + + // Font size for that symbol font, in points + constexpr int fontSizePoints = 14; + + // Default active color for the indicator + constexpr uint32_t defaultActiveColor = 0x7FFF7F; +} + +CircleIndicator::CircleIndicator() : + activeColor( defaultActiveColor ) +{ } + +ATL::CWndClassInfo& CircleIndicator::GetWndClassInfo() +{ + // Use custom class style with CS_PARENTDC, and COLOR_3DFACE for the background + static ATL::CWndClassInfo wc = + { + { sizeof( WNDCLASSEX ), + CS_HREDRAW | CS_VREDRAW | CS_PARENTDC, + StartWindowProc, + 0, 0, NULL, NULL, NULL, (HBRUSH)( COLOR_3DFACE + 1 ), NULL, windowClassName, NULL }, + NULL, NULL, IDC_ARROW, TRUE, 0, _T( "" ) + }; + return wc; +} + +// Class registration +HRESULT CircleIndicator::registerClass() +{ + WNDPROC pUnusedWndSuperProc = nullptr; + ATOM a = GetWndClassInfo().Register( &pUnusedWndSuperProc ); + if( 0 != a ) + return S_OK; + return getLastHr(); +} + +HRESULT CircleIndicator::createFont( int height ) +{ + LOGFONT logFont; + memset( &logFont, 0, sizeof( logFont ) ); + logFont.lfHeight = height; + logFont.lfCharSet = ANSI_CHARSET; + logFont.lfOutPrecision = OUT_TT_PRECIS; + logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + wcsncpy_s( logFont.lfFaceName, fontName, _TRUNCATE ); + font.CreateFontIndirect( &logFont ); + if( font ) + return S_OK; + return E_FAIL; +} + +void CircleIndicator::onDestroy() +{ + if( font ) + font.DeleteObject(); +} + +void CircleIndicator::onPaint( CDCHandle dc ) +{ + CRect rectInt32; + GetClientRect( &rectInt32 ); + + CPaintDC pdc( m_hWnd ); + + const int logPixels = pdc.GetDeviceCaps( LOGPIXELSY ); + int fontSize = -MulDiv( fontSizePoints, logPixels, 72 ); + if( !font || fontHeight != fontSize ) + { + if( font ) + font.DeleteObject(); + HRESULT hr = createFont( fontSize ); + if( FAILED( hr ) ) + return; + fontHeight = fontSize; + } + + pdc.SetBkColor( GetSysColor( COLOR_3DFACE ) ); + pdc.SelectFont( font ); + pdc.SetBkMode( OPAQUE ); + constexpr UINT textFormat = DT_CENTER | DT_VCENTER; + + if( isActive ) + { + pdc.SetTextColor( activeColor ); + pdc.DrawText( circleFilled, 1, rectInt32, textFormat ); + pdc.SetBkMode( TRANSPARENT ); + } + + pdc.SetTextColor( 0 ); + pdc.DrawText( circleOutline, 1, rectInt32, textFormat ); +} + +void CircleIndicator::setActive( bool nowActive ) +{ + if( nowActive == isActive ) + return; + + // Repaint the control + isActive = nowActive; + InvalidateRect( nullptr ); +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/CircleIndicator.h b/Examples/WhisperDesktop/CircleIndicator.h new file mode 100644 index 0000000..492524d --- /dev/null +++ b/Examples/WhisperDesktop/CircleIndicator.h @@ -0,0 +1,36 @@ +#pragma once +#include "Utils/miscUtils.h" +#include "Utils/WTL/atlcrack.h" + +// This control renders a black circle, and in the active state, the circle is filled with a bright color. +class CircleIndicator: public CWindowImpl<CircleIndicator> +{ +public: + static ATL::CWndClassInfo& GetWndClassInfo(); + + BEGIN_MSG_MAP( CircleIndicator ) + MSG_WM_PAINT( onPaint ) + MSG_WM_DESTROY( onDestroy ) + END_MSG_MAP() + + // Class registration + static HRESULT registerClass(); + + void setActive( bool nowActive ); + + void setActiveColor( uint32_t col ) + { + activeColor = col; + } + CircleIndicator(); + +private: + bool isActive = false; + uint32_t activeColor; + int fontHeight = 0; + CFont font; + HRESULT createFont( int height ); + + void onDestroy(); + void onPaint( CDCHandle dc ); +};
\ No newline at end of file diff --git a/Examples/WhisperDesktop/LoadModelDlg.cpp b/Examples/WhisperDesktop/LoadModelDlg.cpp new file mode 100644 index 0000000..1b2bf03 --- /dev/null +++ b/Examples/WhisperDesktop/LoadModelDlg.cpp @@ -0,0 +1,206 @@ +#include "stdafx.h" +#include "LoadModelDlg.h" +#include "Utils/miscUtils.h" +#include "Utils/logger.h" + +constexpr int progressMaxInteger = 1024 * 8; + +HRESULT LoadModelDlg::show() +{ + auto res = DoModal( nullptr ); + if( res == -1 ) + return HRESULT_FROM_WIN32( GetLastError() ); + if( res == IDOK ) + { + HRESULT hr = appState.lastScreenLoad(); + switch( hr ) + { + case SCREEN_TRANSCRIBE: + case SCREEN_CAPTURE: + return hr; + default: + return SCREEN_TRANSCRIBE; + } + } + return S_OK; +} + +LRESULT LoadModelDlg::OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) +{ + // First DDX call, hooks up variables to controls. + DoDataExchange( false ); + + cbConsole.initialize( m_hWnd, IDC_CONSOLE, appState ); + implPopulateCombobox( cbModelType, appState.source.impl ); + modelPath.SetWindowTextW( appState.source.path ); + + HRESULT hr = work.create( this ); + if( FAILED( hr ) ) + { + CString text = L"CreateThreadpoolWork failed\n"; + text += formatErrorMessage( hr ); + ::MessageBox( m_hWnd, text, L"Unable to load the model", MB_OK | MB_ICONWARNING ); + return TRUE; + } + + editorsWindows.reserve( 5 ); + editorsWindows = { modelPath, cbModelType, GetDlgItem( IDC_BROWSE ), GetDlgItem( IDOK ), GetDlgItem( IDCANCEL ) }; + pendingWindows.reserve( 2 ); + pendingWindows = { GetDlgItem( IDC_PENDING_TEXT ), progressBar }; + + progressBar.SetRange32( 0, progressMaxInteger ); + progressBar.SetStep( 1 ); + + appState.setupIcon( this ); + ATLVERIFY( CenterWindow() ); + if( !appState.source.found || !appState.automaticallyLoadModel ) + return 0; + + // AppState.findModelSource() method has located model parameters in registry; + // Post a notification identical to the "OK" button click event. + PostMessage( WM_COMMAND, IDOK, (LPARAM)( GetDlgItem( IDOK ).m_hWnd ) ); + + return 0; +} + +LRESULT LoadModelDlg::OnBrowse( UINT, INT, HWND, BOOL& bHandled ) +{ + bHandled = TRUE; + + CString path; + modelPath.GetWindowText( path ); + if( !getOpenFileName( m_hWnd, L"Select a GGML Model File", L"Binary files (*.bin)\0*.bin\0\0", path ) ) + return 0; + + modelPath.SetWindowText( path ); + appState.source.path = path; + return 0; +} + +LRESULT LoadModelDlg::validationError( LPCTSTR message ) +{ + reportError( m_hWnd, message, L"Unable to load the model" ); + return 0; +} + +LRESULT LoadModelDlg::validationError( LPCTSTR message, HRESULT hr ) +{ + reportError( m_hWnd, message, L"Unable to load the model", hr ); + return 0; +} + +void LoadModelDlg::setPending( bool nowPending ) +{ + const BOOL enable = nowPending ? FALSE : TRUE; + for( HWND w : editorsWindows ) + ::EnableWindow( w, enable ); + + const int show = nowPending ? SW_NORMAL : SW_HIDE; + for( HWND w : pendingWindows ) + ::ShowWindow( w, show ); + + if( nowPending ) + progressBar.SetMarquee( TRUE, 0 ); + else + progressBar.SetMarquee( FALSE, 0 ); +} + +LRESULT LoadModelDlg::OnOk( UINT, INT, HWND, BOOL& bHandled ) +{ + modelPath.GetWindowText( path ); + if( path.GetLength() <= 0 ) + return validationError( L"Please select a model GGML file" ); + + { + CAtlFile file; + HRESULT hr = file.Create( path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING ); + if( FAILED( hr ) ) + return validationError( L"Unable to open the model file", hr ); + + ULONGLONG cb = 0; + file.GetSize( cb ); + appState.source.sizeInBytes = cb; + } + + impl = implGetValue( cbModelType ); + if( impl == (Whisper::eModelImplementation)0 ) + return validationError( L"Please select a model type" ); + + setPending( true ); + work.post(); + return 0; +} + +void __stdcall LoadModelDlg::poolCallback() noexcept +{ + CComPtr<Whisper::iModel> model; + clearLastError(); + loadError = L""; + Whisper::sLoadModelCallbacks lmcb; + lmcb.cancel = nullptr; + lmcb.progress = &LoadModelDlg::progressCallback; + lmcb.pv = this; + HRESULT hr = Whisper::loadModel( path, impl, &lmcb, &model ); + if( SUCCEEDED( hr ) ) + appState.model = model; + else + getLastError( loadError ); + + this->PostMessage( WM_CALLBACK_STATUS, (WPARAM)hr ); +} + +HRESULT __stdcall LoadModelDlg::progressCallback( double val, void* pv ) noexcept +{ + LoadModelDlg& dialog = *(LoadModelDlg*)pv; + constexpr double mul = progressMaxInteger; + int pos = lround( mul * val ); + dialog.progressBar.PostMessage( PBM_SETPOS, pos, 0 ); + return S_OK; +} + +LRESULT LoadModelDlg::OnCallbackStatus( UINT, WPARAM wParam, LPARAM, BOOL& bHandled ) +{ + setPending( false ); + + bHandled = TRUE; + const HRESULT hr = (HRESULT)wParam; + if( FAILED( hr ) ) + { + LPCTSTR failMessage = L"Error loading the model"; + if( loadError.GetLength() > 0 ) + { + CString tmp = failMessage; + tmp += L"\n"; + tmp += loadError; + return validationError( tmp, hr ); + } + else + return validationError( failMessage, hr ); + } + + appState.source.path = path; + appState.source.impl = impl; + appState.saveModelSource(); + + EndDialog( IDOK ); + return 0; +} + +LRESULT LoadModelDlg::OnHyperlink( int idCtrl, LPNMHDR pnmh, BOOL& bHandled ) +{ + const UINT code = pnmh->code; + switch( code ) + { + case NM_CLICK: + case NM_RETURN: + break; + default: + return 0; + } + + PNMLINK pNMLink = (PNMLINK)pnmh; + LPCTSTR url = pNMLink->item.szUrl; + ShellExecute( NULL, L"open", url, NULL, NULL, SW_SHOW ); + bHandled = TRUE; + return 0; +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/LoadModelDlg.h b/Examples/WhisperDesktop/LoadModelDlg.h new file mode 100644 index 0000000..a8d7aea --- /dev/null +++ b/Examples/WhisperDesktop/LoadModelDlg.h @@ -0,0 +1,69 @@ +#pragma once +#include "AppState.h" +#include "Utils/WTL/atlddx.h" +#include "Utils/miscUtils.h" + +class LoadModelDlg: + public CDialogImpl<LoadModelDlg>, + public CWinDataExchange<LoadModelDlg>, + public iThreadPoolCallback +{ + AppState& appState; +public: + static constexpr UINT IDD = IDD_OPEN_MODEL; + static constexpr UINT WM_CALLBACK_STATUS = WM_APP + 1; + + LoadModelDlg( AppState& app ) : appState( app ) { } + + HRESULT show(); + + BEGIN_MSG_MAP( LoadModelDlg ) + MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog ) + ON_BUTTON_CLICK( IDC_CONSOLE, cbConsole.click ) + COMMAND_ID_HANDLER( IDCANCEL, OnCommand ) + COMMAND_ID_HANDLER( IDOK, OnOk ) + COMMAND_ID_HANDLER( IDC_BROWSE, OnBrowse ) + MESSAGE_HANDLER( WM_CALLBACK_STATUS, OnCallbackStatus ) + NOTIFY_ID_HANDLER( IDC_HYPERLINKS, OnHyperlink ) + END_MSG_MAP() + + BEGIN_DDX_MAP( LoadModelDlg ) + DDX_CONTROL_HANDLE( IDC_PATH, modelPath ) + DDX_CONTROL_HANDLE( IDC_MODEL_TYPE, cbModelType ) + DDX_CONTROL_HANDLE( IDC_PROGRESS, progressBar ); + END_DDX_MAP() + +private: + std::vector<HWND> editorsWindows; + std::vector<HWND> pendingWindows; + void setPending( bool nowPending ); + + LRESULT OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); + LRESULT OnCallbackStatus( UINT, WPARAM wParam, LPARAM, BOOL& bHandled ); + + LRESULT OnCommand( UINT, INT nIdentifier, HWND, BOOL& bHandled ) + { + ATLVERIFY( EndDialog( nIdentifier ) ); + return 0; + } + LRESULT OnBrowse( UINT, INT, HWND, BOOL& bHandled ); + LRESULT OnOk( UINT, INT, HWND, BOOL& bHandled ); + + ConsoleCheckbox cbConsole; + CComboBox cbModelType; + CEdit modelPath; + CProgressBarCtrl progressBar; + + LRESULT validationError( LPCTSTR message ); + LRESULT validationError( LPCTSTR message, HRESULT hr ); + + ThreadPoolWork work; + CString path; + Whisper::eModelImplementation impl; + CString loadError; + void __stdcall poolCallback() noexcept override final; + + LRESULT OnHyperlink( int idCtrl, LPNMHDR pnmh, BOOL& bHandled ); + + static HRESULT __stdcall progressCallback( double val, void* pv ) noexcept; +};
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Resource.h b/Examples/WhisperDesktop/Resource.h new file mode 100644 index 0000000..f4a9af5 --- /dev/null +++ b/Examples/WhisperDesktop/Resource.h @@ -0,0 +1,61 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by WhisperDesktop.rc +// +#define IDC_MYICON 2 +#define IDD_WHISPERDESKTOP_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDI_WHISPERDESKTOP 107 +#define IDI_SMALL 108 +#define IDR_MAINFRAME 128 +#define IDD_OPEN_MODEL 129 +#define IDD_MAIN_DIALOG 130 +#define IDD_TRANSCRIBE_DIALOG 130 +#define IDD_CAPTURE_DIALOG 131 +#define IDC_PATH 1000 +#define IDC_BROWSE 1001 +#define IDC_MODEL_TYPE 1002 +#define IDC_PATH_RESULT 1002 +#define IDC_PROGRESS 1003 +#define IDC_BROWSE_RESULT 1003 +#define IDC_SYSLINK1 1004 +#define IDC_HYPERLINKS 1004 +#define IDC_TRANSCRIBE_PROGRESS 1004 +#define IDC_PENDING_TEXT 1005 +#define IDC_MODEL_DESC 1006 +#define IDC_LANGUAGE 1007 +#define IDC_OUTPUT_FORMAT 1008 +#define IDC_PATH_MEDIA 1009 +#define IDC_DEVICE 1009 +#define IDC_BROWSE_MEDIA 1010 +#define IDC_TRANSCRIBE 1011 +#define IDC_BACK 1012 +#define IDC_CHECK1 1013 +#define IDC_CONSOLE 1013 +#define IDC_CAPTURE 1014 +#define IDC_DEV_REFRESH 1015 +#define IDC_SAVE_TEXT 1016 +#define IDC_SAVE_APPEND 1017 +#define IDC_SAVE_TIMESTAMPS 1018 +#define IDC_RUN_CAPTURE 1019 +#define IDC_VOICE_ACTIVITY 1020 +#define IDC_VOICE_ACTIVITY_LBL 1021 +#define IDC_TRANS_STATUS 1022 +#define IDC_TRANS_LBL 1023 +#define IDC_STALL_STATUS 1024 +#define IDC_STALL_LBL 1025 +#define IDC_TRANSLATE 1026 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1027 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/Examples/WhisperDesktop/TranscribeDlg.cpp b/Examples/WhisperDesktop/TranscribeDlg.cpp new file mode 100644 index 0000000..14bec05 --- /dev/null +++ b/Examples/WhisperDesktop/TranscribeDlg.cpp @@ -0,0 +1,493 @@ +#include "stdafx.h" +#include "TranscribeDlg.h" +#include "Utils/logger.h" + +HRESULT TranscribeDlg::show() +{ + auto res = DoModal( nullptr ); + if( res == -1 ) + return HRESULT_FROM_WIN32( GetLastError() ); + switch( res ) + { + case IDC_BACK: + return SCREEN_MODEL; + case IDC_CAPTURE: + return SCREEN_CAPTURE; + } + return S_OK; +} + +constexpr int progressMaxInteger = 1024 * 8; + +static const LPCTSTR regValInput = L"sourceMedia"; +static const LPCTSTR regValOutFormat = L"resultFormat"; +static const LPCTSTR regValOutPath = L"resultPath"; + +LRESULT TranscribeDlg::OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) +{ + // First DDX call, hooks up variables to controls. + DoDataExchange( false ); + printModelDescription(); + languageSelector.initialize( m_hWnd, IDC_LANGUAGE, appState ); + cbConsole.initialize( m_hWnd, IDC_CONSOLE, appState ); + cbTranslate.initialize( m_hWnd, IDC_TRANSLATE, appState ); + populateOutputFormats(); + + pendingState.initialize( + { + languageSelector, + sourceMediaPath, GetDlgItem( IDC_BROWSE_MEDIA ), + transcribeOutFormat, + transcribeOutputPath, GetDlgItem( IDC_BROWSE_RESULT ), + GetDlgItem( IDC_TRANSCRIBE ), + GetDlgItem( IDCANCEL ), + GetDlgItem( IDC_BACK ), + GetDlgItem( IDC_CAPTURE ) + }, + { + progressBar, GetDlgItem( IDC_PENDING_TEXT ) + } ); + + HRESULT hr = work.create( this ); + if( FAILED( hr ) ) + { + reportError( m_hWnd, L"CreateThreadpoolWork failed", nullptr, hr ); + EndDialog( IDCANCEL ); + } + + progressBar.SetRange32( 0, progressMaxInteger ); + progressBar.SetStep( 1 ); + + sourceMediaPath.SetWindowText( appState.stringLoad( regValInput ) ); + transcribeOutFormat.SetCurSel( (int)appState.dwordLoad( regValOutFormat, 0 ) ); + transcribeOutputPath.SetWindowText( appState.stringLoad( regValOutPath ) ); + BOOL unused; + OnOutFormatChange( 0, 0, nullptr, unused ); + + appState.lastScreenSave( SCREEN_TRANSCRIBE ); + appState.setupIcon( this ); + ATLVERIFY( CenterWindow() ); + return 0; +} + +void TranscribeDlg::printModelDescription() +{ + CString text; + if( S_OK == appState.model->isMultilingual() ) + text = L"Multilingual"; + else + text = L"Single-language"; + text += L" model \""; + LPCTSTR path = appState.source.path; + path = ::PathFindFileName( path ); + text += path; + text += L"\", "; + const int64_t cb = appState.source.sizeInBytes; + if( cb < 1 << 30 ) + { + constexpr double mul = 1.0 / ( 1 << 20 ); + double mb = (double)cb * mul; + text.AppendFormat( L"%.1f MB", mb ); + } + else + { + constexpr double mul = 1.0 / ( 1 << 30 ); + double gb = (double)cb * mul; + text.AppendFormat( L"%.2f GB", gb ); + } + text += L" on disk, "; + text += implString( appState.source.impl ); + text += L" implementation"; + + modelDesc.SetWindowText( text ); +} + +void TranscribeDlg::populateOutputFormats() +{ + transcribeOutFormat.AddString( L"None" ); + transcribeOutFormat.AddString( L"Text File" ); + transcribeOutFormat.AddString( L"SubRip subtitles" ); + transcribeOutFormat.AddString( L"WebVTT subtitles" ); +} + +enum struct TranscribeDlg::eOutputFormat : uint8_t +{ + None = 0, + Text = 1, + SubRip = 2, + WebVTT = 3 +}; + +LRESULT TranscribeDlg::OnOutFormatChange( UINT, INT, HWND, BOOL& bHandled ) +{ + const BOOL enabled = transcribeOutFormat.GetCurSel() != 0; + transcribeOutputPath.EnableWindow( enabled ); + transcribeOutputBrowse.EnableWindow( enabled ); + return 0; +} + +void TranscribeDlg::onBrowseMedia() +{ + LPCTSTR title = L"Input audio file to transcribe"; + LPCTSTR filters = L"Multimedia Files\0*.wav;*.wave;*.mp3;*.wma;*.mp4;*.mpeg4;*.mkv\0\0"; + + CString path; + sourceMediaPath.GetWindowText( path ); + if( getOpenFileName( m_hWnd, title, filters, path ) ) + sourceMediaPath.SetWindowText( path ); +} + +static const LPCTSTR outputFilters = L"Text files (*.txt)\0*.txt\0SubRip subtitles (*.srt)\0*.srt\0WebVTT subtitles (*.vtt)\0*.vtt\0\0"; +static const std::array<LPCTSTR, 3> outputExtensions = +{ + L".txt", L".srt", L".vtt" +}; + +void TranscribeDlg::onBrowseOutput() +{ + const DWORD origFilterIndex = (DWORD)transcribeOutFormat.GetCurSel() - 1; + + LPCTSTR title = L"Output Text File"; + CString path; + transcribeOutputPath.GetWindowText( path ); + DWORD filterIndex = origFilterIndex; + if( !getSaveFileName( m_hWnd, title, outputFilters, path, &filterIndex ) ) + return; + + LPCTSTR ext = PathFindExtension( path ); + if( 0 == *ext && filterIndex < outputExtensions.size() ) + { + wchar_t* const buffer = path.GetBufferSetLength( path.GetLength() + 5 ); + PathRenameExtension( buffer, outputExtensions[ filterIndex ] ); + path.ReleaseBuffer(); + } + + transcribeOutputPath.SetWindowText( path ); + if( filterIndex != origFilterIndex ) + transcribeOutFormat.SetCurSel( filterIndex + 1 ); +} + +void TranscribeDlg::setPending( bool nowPending ) +{ + pendingState.setPending( nowPending ); +} + +void TranscribeDlg::transcribeError( LPCTSTR text, HRESULT hr ) +{ + reportError( m_hWnd, text, L"Unable to transcribe audio", hr ); +} + +void TranscribeDlg::onTranscribe() +{ + // Validate input + sourceMediaPath.GetWindowText( transcribeArgs.pathMedia ); + if( transcribeArgs.pathMedia.GetLength() <= 0 ) + { + transcribeError( L"Please select an input audio file" ); + return; + } + + if( !PathFileExists( transcribeArgs.pathMedia ) ) + { + transcribeError( L"Input audio file does not exist", HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ); + return; + } + + transcribeArgs.language = languageSelector.selectedLanguage(); + transcribeArgs.translate = cbTranslate.checked(); + if( isInvalidTranslate( m_hWnd, transcribeArgs.language, transcribeArgs.translate ) ) + return; + + transcribeArgs.format = (eOutputFormat)(uint8_t)transcribeOutFormat.GetCurSel(); + if( transcribeArgs.format != eOutputFormat::None ) + { + transcribeOutputPath.GetWindowText( transcribeArgs.pathOutput ); + if( transcribeArgs.pathOutput.GetLength() <= 0 ) + { + transcribeError( L"Please select an output text file" ); + return; + } + appState.stringStore( regValOutPath, transcribeArgs.pathOutput ); + } + else + cbConsole.ensureChecked(); + + appState.dwordStore( regValOutFormat, (uint32_t)(int)transcribeArgs.format ); + languageSelector.saveSelection( appState ); + cbTranslate.saveSelection( appState ); + appState.stringStore( regValInput, transcribeArgs.pathMedia ); + + setPending( true ); + + work.post(); +} + +void __stdcall TranscribeDlg::poolCallback() noexcept +{ + HRESULT hr = transcribe(); + PostMessage( WM_CALLBACK_STATUS, (WPARAM)hr ); +} + +static void printTime( CString& rdi, int64_t ticks ) +{ + const Whisper::sTimeSpan ts{ (uint64_t)ticks }; + const Whisper::sTimeSpanFields fields = ts; + + if( fields.days != 0 ) + { + rdi.AppendFormat( L"%i days, %i hours", fields.days, (int)fields.hours ); + return; + } + if( ( fields.hours | fields.minutes ) != 0 ) + { + rdi.AppendFormat( L"%02d:%02d:%02d", (int)fields.hours, (int)fields.minutes, (int)fields.seconds ); + return; + } + rdi.AppendFormat( L"%.3f seconds", (double)ticks / 1E7 ); +} + +LRESULT TranscribeDlg::onCallbackStatus( UINT, WPARAM wParam, LPARAM, BOOL& bHandled ) +{ + setPending( false ); + const HRESULT hr = (HRESULT)wParam; + if( FAILED( hr ) ) + { + LPCTSTR failMessage = L"Transcribe failed"; + + if( transcribeArgs.errorMessage.GetLength() > 0 ) + { + CString tmp = failMessage; + tmp += L"\n"; + tmp += transcribeArgs.errorMessage; + transcribeError( tmp, hr ); + } + else + transcribeError( failMessage, hr ); + + return 0; + } + + const int64_t elapsed = ( GetTickCount64() - transcribeArgs.startTime ) * 10'000; + const int64_t media = transcribeArgs.mediaDuration; + CString message = L"Transcribed the audio\nMedia duration: "; + printTime( message, media ); + message += L"\nProcessing time: "; + printTime( message, elapsed ); + message += L"\nRelative processing speed: "; + double mul = (double)media / (double)elapsed; + message.AppendFormat( L"%g", mul ); + + MessageBox( message, L"Transcribe Completed", MB_OK | MB_ICONINFORMATION ); + return 0; +} + +void TranscribeDlg::getThreadError() +{ + getLastError( transcribeArgs.errorMessage ); +} + +#define CHECK_EX( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) { getThreadError(); return __hr; } } + +HRESULT TranscribeDlg::transcribe() +{ + transcribeArgs.startTime = GetTickCount64(); + clearLastError(); + transcribeArgs.errorMessage = L""; + + using namespace Whisper; + CComPtr<iAudioReader> reader; + + CHECK_EX( appState.mediaFoundation->openAudioFile( transcribeArgs.pathMedia, false, &reader ) ); + CHECK_EX( reader->getDuration( transcribeArgs.mediaDuration ) ); + + const eOutputFormat format = transcribeArgs.format; + CAtlFile outputFile; + if( format != eOutputFormat::None ) + CHECK( outputFile.Create( transcribeArgs.pathOutput, GENERIC_WRITE, 0, CREATE_ALWAYS ) ); + + transcribeArgs.resultFlags = eResultFlags::Timestamps | eResultFlags::Tokens; + + CComPtr<iContext> context; + CHECK_EX( appState.model->createContext( &context ) ); + + sFullParams fullParams; + CHECK_EX( context->fullDefaultParams( eSamplingStrategy::Greedy, &fullParams ) ); + fullParams.language = transcribeArgs.language; + fullParams.setFlag( eFullParamsFlags::Translate, transcribeArgs.translate ); + fullParams.resetFlag( eFullParamsFlags::PrintRealtime ); + + fullParams.new_segment_callback_user_data = this; + fullParams.new_segment_callback = &newSegmentCallbackStatic; + + // Setup the progress indication sink + sProgressSink progressSink{ &progressCallbackStatic, this }; + // Run the transcribe + CHECK_EX( context->runStreamed( fullParams, progressSink, reader ) ); + + context->timingsPrint(); + + if( format == eOutputFormat::None ) + return S_OK; + + CComPtr<iTranscribeResult> result; + CHECK_EX( context->getResults( transcribeArgs.resultFlags, &result ) ); + + sTranscribeLength len; + CHECK_EX( result->getSize( len ) ); + const sSegment* const segments = result->getSegments(); + + switch( format ) + { + case eOutputFormat::Text: + return writeTextFile( segments, len.countSegments, outputFile ); + case eOutputFormat::SubRip: + return writeSubRip( segments, len.countSegments, outputFile ); + case eOutputFormat::WebVTT: + return writeWebVTT( segments, len.countSegments, outputFile ); + default: + return E_FAIL; + } +} + +#undef CHECK_EX + +inline HRESULT TranscribeDlg::progressCallback( double p ) noexcept +{ + constexpr double mul = progressMaxInteger; + int pos = lround( mul * p ); + progressBar.PostMessage( PBM_SETPOS, pos, 0 ); + return S_OK; +} + +HRESULT __cdecl TranscribeDlg::progressCallbackStatic( double p, Whisper::iContext* ctx, void* pv ) noexcept +{ + TranscribeDlg* dlg = (TranscribeDlg*)pv; + return dlg->progressCallback( p ); +} + +namespace +{ + HRESULT write( CAtlFile& file, const CStringA& line ) + { + if( line.GetLength() > 0 ) + CHECK( file.Write( cstr( line ), (DWORD)line.GetLength() ) ); + return S_OK; + } + + void printTime( CStringA& rdi, Whisper::sTimeSpan time, bool comma ) + { + Whisper::sTimeSpanFields fields = time; + const char separator = comma ? ',' : '.'; + rdi.AppendFormat( "%02d:%02d:%02d%c%03d", + (int)fields.hours, + (int)fields.minutes, + (int)fields.seconds, + separator, + fields.ticks / 10'000 ); + } + + const char* skipBlank( const char* rsi ) + { + while( true ) + { + const char c = *rsi; + if( c == ' ' || c == '\t' ) + { + rsi++; + continue; + } + return rsi; + } + } +} + +using Whisper::sSegment; + + +HRESULT TranscribeDlg::writeTextFile( const sSegment* const segments, const size_t length, CAtlFile& file ) +{ + using namespace Whisper; + CHECK( writeUtf8Bom( file ) ); + CStringA line; + for( size_t i = 0; i < length; i++ ) + { + line = skipBlank( segments[ i ].text ); + line += "\r\n"; + CHECK( write( file, line ) ); + } + return S_OK; +} + +HRESULT TranscribeDlg::writeSubRip( const sSegment* const segments, const size_t length, CAtlFile& file ) +{ + CHECK( writeUtf8Bom( file ) ); + CStringA line; + for( size_t i = 0; i < length; i++ ) + { + const sSegment& seg = segments[ i ]; + + line.Format( "%zu\r\n", i + 1 ); + printTime( line, seg.time.begin, true ); + line += " --> "; + printTime( line, seg.time.end, true ); + line += "\r\n"; + line += skipBlank( seg.text ); + line += "\r\n\r\n"; + CHECK( write( file, line ) ); + } + return S_OK; +} + +HRESULT TranscribeDlg::writeWebVTT( const sSegment* const segments, const size_t length, CAtlFile& file ) +{ + CHECK( writeUtf8Bom( file ) ); + CStringA line; + line = "WEBVTT\r\n\r\n"; + CHECK( write( file, line ) ); + + for( size_t i = 0; i < length; i++ ) + { + const sSegment& seg = segments[ i ]; + line = ""; + + printTime( line, seg.time.begin, false ); + line += " --> "; + printTime( line, seg.time.end, false ); + line += "\r\n"; + line += skipBlank( seg.text ); + line += "\r\n\r\n"; + CHECK( write( file, line ) ); + } + return S_OK; +} + +inline HRESULT TranscribeDlg::newSegmentCallback( Whisper::iContext* ctx, uint32_t n_new ) +{ + using namespace Whisper; + CComPtr<iTranscribeResult> result; + CHECK( ctx->getResults( transcribeArgs.resultFlags, &result ) ); + return logNewSegments( result, n_new ); +} + +HRESULT __cdecl TranscribeDlg::newSegmentCallbackStatic( Whisper::iContext* ctx, uint32_t n_new, void* user_data ) noexcept +{ + TranscribeDlg* dlg = (TranscribeDlg*)user_data; + return dlg->newSegmentCallback( ctx, n_new ); +} + +void TranscribeDlg::onWmClose() +{ + if( GetDlgItem( IDCANCEL ).IsWindowEnabled() ) + { + EndDialog( IDCANCEL ); + return; + } + + constexpr UINT flags = MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2; + const int res = this->MessageBox( L"Transcribe is in progress.\nDo you want to quit anyway?", L"Confirm exit", flags ); + if( res != IDYES ) + return; + + // TODO: instead of ExitProcess(), implement another callback in the DLL API, for proper cancellation of the background task + ExitProcess( 1 ); +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/TranscribeDlg.h b/Examples/WhisperDesktop/TranscribeDlg.h new file mode 100644 index 0000000..354e002 --- /dev/null +++ b/Examples/WhisperDesktop/TranscribeDlg.h @@ -0,0 +1,124 @@ +#pragma once +#include "AppState.h" +#include "Utils/WTL/atlddx.h" +#include "Utils/WTL/atlcrack.h" +#include "Utils/miscUtils.h" +#include "Utils/LanguageDropdown.h" +#include "Utils/TranslateCheckbox.h" +#include "Utils/PendingState.h" + +class TranscribeDlg : + public CDialogImpl<TranscribeDlg>, + public CWinDataExchange<TranscribeDlg>, + public iThreadPoolCallback +{ + AppState& appState; + +public: + static constexpr UINT IDD = IDD_TRANSCRIBE_DIALOG; + static constexpr UINT WM_CALLBACK_STATUS = WM_APP + 1; + + TranscribeDlg( AppState& app ) : appState( app ) { } + + // Show this dialog modally, without parent. + HRESULT show(); + + BEGIN_MSG_MAP( LoadModelDlg ) + MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog ) + ON_BUTTON_CLICK( IDC_CONSOLE, cbConsole.click ) + ON_BUTTON_CLICK( IDCANCEL, onClose ) + ON_BUTTON_CLICK( IDC_BACK, onBack ) + ON_BUTTON_CLICK( IDC_BROWSE_MEDIA, onBrowseMedia ) + ON_BUTTON_CLICK( IDC_BROWSE_RESULT, onBrowseOutput ) + ON_BUTTON_CLICK( IDC_TRANSCRIBE, onTranscribe ) + ON_BUTTON_CLICK( IDC_CAPTURE, onCapture ); + COMMAND_HANDLER( IDC_OUTPUT_FORMAT, CBN_SELCHANGE, OnOutFormatChange ) + MESSAGE_HANDLER( WM_CALLBACK_STATUS, onCallbackStatus ) + MSG_WM_CLOSE( onWmClose ) + END_MSG_MAP() + + BEGIN_DDX_MAP( LoadModelDlg ) + DDX_CONTROL_HANDLE( IDC_MODEL_DESC, modelDesc ) + DDX_CONTROL_HANDLE( IDC_PATH_MEDIA, sourceMediaPath ) + DDX_CONTROL_HANDLE( IDC_OUTPUT_FORMAT, transcribeOutFormat ) + DDX_CONTROL_HANDLE( IDC_PATH_RESULT, transcribeOutputPath ) + DDX_CONTROL_HANDLE( IDC_BROWSE_RESULT, transcribeOutputBrowse ); + DDX_CONTROL_HANDLE( IDC_TRANSCRIBE_PROGRESS, progressBar ); + END_DDX_MAP() + +private: + PendingState pendingState; + void setPending( bool nowPending ); + void transcribeError( LPCTSTR text, HRESULT hr = S_FALSE ); + + LRESULT OnInitDialog( UINT nMessage, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); + + void onClose() + { + ATLVERIFY( EndDialog( IDCANCEL ) ); + } + void onBack() + { + ATLVERIFY( EndDialog( IDC_BACK ) ); + } + + void printModelDescription(); + CStatic modelDesc; + ConsoleCheckbox cbConsole; + + LanguageDropdown languageSelector; + TranslateCheckbox cbTranslate; + + CEdit sourceMediaPath; + CEdit transcribeOutputPath; + CButton transcribeOutputBrowse; + CComboBox transcribeOutFormat; + CProgressBarCtrl progressBar; + void populateOutputFormats(); + + LRESULT OnOutFormatChange( UINT, INT, HWND, BOOL& bHandled ); + void onBrowseMedia(); + void onBrowseOutput(); + void onTranscribe(); + void onCapture() + { + EndDialog( IDC_CAPTURE ); + } + + ThreadPoolWork work; + + enum struct eOutputFormat : uint8_t; + + struct TranscribeArgs + { + CString pathMedia; + CString pathOutput; + uint32_t language; + bool translate; + eOutputFormat format; + Whisper::eResultFlags resultFlags; + uint64_t startTime; + int64_t mediaDuration; + CString errorMessage; + }; + TranscribeArgs transcribeArgs; + + void __stdcall poolCallback() noexcept override final; + + LRESULT onCallbackStatus( UINT, WPARAM wParam, LPARAM, BOOL& bHandled ); + + HRESULT transcribe(); + void getThreadError(); + + static HRESULT writeTextFile( const Whisper::sSegment* const segments, const size_t length, CAtlFile& file ); + static HRESULT writeSubRip( const Whisper::sSegment* const segments, const size_t length, CAtlFile& file ); + static HRESULT writeWebVTT( const Whisper::sSegment* const segments, const size_t length, CAtlFile& file ); + + static HRESULT __cdecl newSegmentCallbackStatic( Whisper::iContext* ctx, uint32_t n_new, void* user_data ) noexcept; + HRESULT newSegmentCallback( Whisper::iContext* ctx, uint32_t n_new ); + + static HRESULT __cdecl progressCallbackStatic( double p, Whisper::iContext* ctx, void* pv ) noexcept; + HRESULT progressCallback( double p ) noexcept; + + void onWmClose(); +};
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/DebugConsole.cpp b/Examples/WhisperDesktop/Utils/DebugConsole.cpp new file mode 100644 index 0000000..640efb0 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/DebugConsole.cpp @@ -0,0 +1,289 @@ +// https://github.com/Const-me/vis_avs_dx/blob/master/avs_dx/DxVisuals/Interop/ConsoleLogger.cpp +#include "stdafx.h" +#include "DebugConsole.h" +#include "miscUtils.h" +#include "../AppState.h" +#include "logger.h" + +namespace +{ + using Whisper::eLogLevel; + + constexpr uint16_t defaultAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + + inline uint16_t textAttributes( eLogLevel lvl ) + { + switch( lvl ) + { + case eLogLevel::Error: + return FOREGROUND_RED | FOREGROUND_INTENSITY; + case eLogLevel::Warning: + return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + case eLogLevel::Info: + return FOREGROUND_GREEN | FOREGROUND_INTENSITY; + case eLogLevel::Debug: + return FOREGROUND_BLUE | FOREGROUND_INTENSITY; + } + return defaultAttributes; + } + + // Background stuff: accumulate messages in a small buffer, in case user will want to see them in the console. + // Ideally, we should accumulate them in a more efficient data structure, maybe a circular buffer. + // However, we don't have that many messages per second, this simple solution that uses std::deque is probably good enough for the job. + static constexpr uint16_t bufferSize = 64; + + using Lock = CComCritSecLock<CComAutoCriticalSection>; +#define LOCK() Lock __lock{ critSec } + + thread_local CStringA threadError; +} + +HRESULT DebugConsole::Entry::print( HANDLE hConsole, CString& tempString ) const +{ + if( !SetConsoleTextAttribute( hConsole, textAttributes( level ) ) ) + return getLastHr(); + + makeUtf16( tempString, message ); + tempString += L"\r\n"; + if( !WriteConsoleW( hConsole, tempString, (DWORD)tempString.GetLength(), nullptr, nullptr ) ) + return getLastHr(); + return S_OK; +} + +void clearLastError() +{ + threadError = ""; +} + +bool getLastError( CString& rdi ) +{ + if( threadError.GetLength() <= 0 ) + { + rdi = L""; + return false; + } + else + { + makeUtf16( rdi, threadError ); + threadError = ""; + return true; + } +} + +inline void DebugConsole::logSink( eLogLevel lvl, const char* message ) +{ + LOCK(); + + // Add to the buffer + while( buffer.size() >= bufferSize ) + buffer.pop_front(); + buffer.emplace_back( Entry{ lvl, message } ); + + // If the console window is shown, print there, too. + if( output ) + buffer.rbegin()->print( output, tempString ); +} + +void __stdcall DebugConsole::logSinkStatic( void* context, eLogLevel lvl, const char* message ) +{ + if( lvl == eLogLevel::Error ) + threadError = message; + + DebugConsole* con = (DebugConsole*)context; + con->logSink( lvl, message ); +} + +HRESULT DebugConsole::initialize( Whisper::eLogLevel level ) +{ + if( nullptr != pGlobalInstance ) + return HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED ); + pGlobalInstance = this; + + Whisper::sLoggerSetup setup; + setup.sink = &logSinkStatic; + setup.context = this; + setup.level = level; + setup.flags = Whisper::eLoggerFlags::SkipFormatMessage; + return Whisper::setupLogger( setup ); +} + +DebugConsole::~DebugConsole() +{ + hide(); + + Whisper::sLoggerSetup setup; + memset( &setup, 0, sizeof( setup ) ); + Whisper::setupLogger( setup ); + + pGlobalInstance = nullptr; +} + +DebugConsole* DebugConsole::pGlobalInstance = nullptr; + +void DebugConsole::windowClosed() +{ + LOCK(); + if( FreeConsole() ) + { + // Apparently, FreeConsole already closes that handle: https://stackoverflow.com/q/12676312/126995 + output.Detach(); + } + output.Close(); + + for( CButton* b : checkboxes ) + { + if( !*b ) + continue; + if( !b->IsWindow() ) + continue; + PostMessage( *b, BM_SETCHECK, BST_UNCHECKED, 0 ); + } +} + +BOOL __stdcall DebugConsole::consoleHandlerRoutine( DWORD dwCtrlType ) +{ + switch( dwCtrlType ) + { + case CTRL_CLOSE_EVENT: + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + pGlobalInstance->windowClosed(); + return TRUE; + } + return TRUE; +} + +HRESULT DebugConsole::show() +{ + HWND hWnd = GetConsoleWindow(); + if( nullptr != hWnd ) + { + ShowWindow( hWnd, SW_RESTORE ); + SetForegroundWindow( hWnd ); + return S_FALSE; + } + + if( !AllocConsole() ) + return getLastHr(); + + output.Close(); + output.Attach( GetStdHandle( STD_OUTPUT_HANDLE ) ); + if( !output ) + return getLastHr(); + + constexpr UINT cp = CP_UTF8; + if( IsValidCodePage( cp ) ) + SetConsoleOutputCP( cp ); + + // Enable ANSI color coding + DWORD mode = 0; + if( !GetConsoleMode( output, &mode ) ) + return getLastHr(); + if( 0 == ( mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING ) ) + { + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if( !SetConsoleMode( output, mode ) ) + return getLastHr(); + } + + SetConsoleTitle( L"Whisper Desktop Debug Console" ); + + SetConsoleCtrlHandler( &consoleHandlerRoutine, TRUE ); + + // Disable close command in the sys.menu of the new console, otherwise the whole process will quit: https://stackoverflow.com/a/12015131/126995 + HWND hwnd = ::GetConsoleWindow(); + if( hwnd != nullptr ) + { + HMENU hMenu = ::GetSystemMenu( hwnd, FALSE ); + if( hMenu != NULL ) + DeleteMenu( hMenu, SC_CLOSE, MF_BYCOMMAND ); + } + + // Print old log entries + for( const auto& e : buffer ) + CHECK( e.print( output, tempString ) ); + + const CStringA msg = "Press Control+C or Control+Break to close this window\r\n"; + if( !SetConsoleTextAttribute( output, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY ) ) + return getLastHr(); + if( !WriteConsoleA( output, cstr( msg ), msg.GetLength(), nullptr, nullptr ) ) + return getLastHr(); + + return S_OK; +} + +HRESULT DebugConsole::hide() +{ + LOCK(); + if( !output ) + return S_FALSE; + windowClosed(); + return S_OK; +} + +void DebugConsole::addCheckbox( CButton& cb ) +{ + checkboxes.emplace( &cb ); +} +void DebugConsole::removeCheckbox( CButton& cb ) +{ + checkboxes.erase( &cb ); +} + +HRESULT ConsoleCheckbox::initialize( HWND dialog, int idc, AppState& state ) +{ + control = GetDlgItem( dialog, idc ); + assert( control ); + + console = &state.console; + if( state.console.isVisible() ) + control.SetCheck( BST_CHECKED ); + + state.console.addCheckbox( control ); + return S_OK; +} + +void ConsoleCheckbox::click() +{ + const int state = control.GetCheck(); + if( state == BST_CHECKED ) + console->show(); + else + console->hide(); +} + +void ConsoleCheckbox::ensureChecked() +{ + const int state = control.GetCheck(); + if( state == BST_CHECKED ) + return; + control.SetCheck( BST_CHECKED ); + console->show(); +} + +void DebugConsole::log( eLogLevel lvl, const char* pszFormat, va_list args ) +{ + LOCK(); + // Add to the buffer + while( buffer.size() >= bufferSize ) + buffer.pop_front(); + + tempStringA.FormatV( pszFormat, args ); + buffer.emplace_back( Entry{ lvl, tempStringA } ); + + // If the console window is shown, print there, too. + if( output ) + buffer.rbegin()->print( output, tempString ); +} + +void DebugConsole::logMessage( eLogLevel lvl, const char* pszFormat, va_list args ) +{ + if( nullptr == pGlobalInstance ) + return; + pGlobalInstance->log( lvl, pszFormat, args ); +} + +void logMessage( Whisper::eLogLevel lvl, const char8_t* pczFormat, va_list args ) +{ + DebugConsole::logMessage( lvl, (const char*)pczFormat, args ); +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/DebugConsole.h b/Examples/WhisperDesktop/Utils/DebugConsole.h new file mode 100644 index 0000000..a9ee8f2 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/DebugConsole.h @@ -0,0 +1,64 @@ +#pragma once +#include <whisperWindows.h> +#include <deque> +#include <unordered_set> + +class AppState; +class DebugConsole +{ + using eLogLevel = Whisper::eLogLevel; + + struct Entry + { + eLogLevel level; + CStringA message; + HRESULT print( HANDLE hConsole, CString& tempString ) const; + }; + + CComAutoCriticalSection critSec; + std::deque<Entry> buffer; + CString tempString; + CHandle output; + + inline void logSink( eLogLevel lvl, const char* message ); + static void __stdcall logSinkStatic( void* context, eLogLevel lvl, const char* message ); + + static BOOL __stdcall consoleHandlerRoutine( DWORD dwCtrlType ); + + static DebugConsole* pGlobalInstance; + void windowClosed(); + + std::unordered_set<CButton*> checkboxes; + + CStringA tempStringA; + void log( eLogLevel lvl, const char* pszFormat, va_list args ); + +public: + HRESULT initialize( eLogLevel level = eLogLevel::Debug ); + ~DebugConsole(); + + HRESULT show(); + HRESULT hide(); + bool isVisible() const { return output; } + + void addCheckbox( CButton& cb ); + void removeCheckbox( CButton& cb ); + + static void logMessage( eLogLevel lvl, const char* pszFormat, va_list args ); +}; + +class ConsoleCheckbox +{ + CButton control; + DebugConsole* console = nullptr; + +public: + HRESULT initialize( HWND dialog, int idc, AppState& state ); + void click(); + ~ConsoleCheckbox() + { + if( nullptr != console ) + console->removeCheckbox( control ); + } + void ensureChecked(); +};
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/LanguageDropdown.cpp b/Examples/WhisperDesktop/Utils/LanguageDropdown.cpp new file mode 100644 index 0000000..36cd1a8 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/LanguageDropdown.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include "LanguageDropdown.h" +#include "miscUtils.h" + +namespace +{ + inline wchar_t toUpper( wchar_t c ) + { + size_t st = (uint16_t)c; + st = reinterpret_cast<size_t>( CharUpperW( reinterpret_cast<LPWSTR>( st ) ) ); + return (wchar_t)(uint16_t)st; + } + + void makeTitleCase( CString& s ) + { + bool cap = true; + for( int i = 0; i < s.GetLength(); i++ ) + { + wchar_t c = s[ i ]; + if( cap ) + { + c = toUpper( c ); + s.SetAt( i, c ); + } + cap = false; + if( c == ' ' ) + cap = true; + } + } +} + +int LanguageDropdown::getInitialSelection( AppState& state ) const +{ + constexpr uint32_t english = 0x6E65; + + // Load preference from the registry + uint32_t id = state.languageRead(); + if( id == UINT_MAX ) + id = english; + + auto it = std::find( keys.begin(), keys.end(), id ); + if( it == keys.end() ) + { + id = english; + it = std::find( keys.begin(), keys.end(), id ); + assert( it != keys.end() ); + } + + ptrdiff_t idx = it - keys.begin(); + return (int)idx; +} + +void LanguageDropdown::initialize( HWND owner, int idc, AppState& state ) +{ + m_hWnd = GetDlgItem( owner, idc ); + assert( nullptr != m_hWnd ); + + Whisper::sLanguageList list; + Whisper::getSupportedLanguages( list ); + + const size_t length = list.length; + keys.resize( length ); + CString utf16; + for( size_t i = 0; i < length; i++ ) + { + keys[ i ] = list.pointer[ i ].key; + makeUtf16( utf16, list.pointer[ i ].name ); + makeTitleCase( utf16 ); + SendMessage( m_hWnd, CB_ADDSTRING, 0, (LPARAM)cstr( utf16 ) ); + } + + const int curSel = getInitialSelection( state ); + SendMessage( m_hWnd, CB_SETCURSEL, curSel, 0 ); +} + +uint32_t LanguageDropdown::selectedLanguage() +{ + const int cs = (int)SendMessage( m_hWnd, CB_GETCURSEL, 0, 0 ); + if( cs < 0 || cs >= keys.size() ) + return UINT_MAX; + return keys[ cs ]; +} + +void LanguageDropdown::saveSelection( AppState& state ) +{ + state.languageWrite( selectedLanguage() ); +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/LanguageDropdown.h b/Examples/WhisperDesktop/Utils/LanguageDropdown.h new file mode 100644 index 0000000..640b81e --- /dev/null +++ b/Examples/WhisperDesktop/Utils/LanguageDropdown.h @@ -0,0 +1,26 @@ +#pragma once +#include "../AppState.h" + +// Dropdown list which implements language selector control +class LanguageDropdown +{ + HWND m_hWnd = nullptr; + std::vector<uint32_t> keys; + int getInitialSelection( AppState& state ) const; + +public: + operator HWND() const + { + return m_hWnd; + } + + // Query language list form the native library, populate the combo box + // Then load the last saved language selection from registry, and preselect an item. + void initialize( HWND owner, int idc, AppState& state ); + + // Get the ID of the currently selected language, or UINT_MAX if nothing's selected + uint32_t selectedLanguage(); + + // Get the ID of the currently selected language, and store in registry + void saveSelection( AppState& state ); +};
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/PendingState.cpp b/Examples/WhisperDesktop/Utils/PendingState.cpp new file mode 100644 index 0000000..404ae4e --- /dev/null +++ b/Examples/WhisperDesktop/Utils/PendingState.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "PendingState.h" + +void PendingState::initialize( std::initializer_list<HWND> editors, std::initializer_list<HWND> pending ) +{ + editorsWindows = editors; + wasEnabled.resize( editorsWindows.size() ); + pendingWindows = pending; +} + +void PendingState::setPending( bool nowPending ) +{ + if( nowPending ) + { + for( size_t i = 0; i < editorsWindows.size(); i++ ) + { + BOOL e = IsWindowEnabled( editorsWindows[ i ] ); + if( e ) + { + wasEnabled[ i ] = true; + EnableWindow( editorsWindows[ i ], FALSE ); + } + else + wasEnabled[ i ] = false; + } + } + else + { + for( size_t i = 0; i < editorsWindows.size(); i++ ) + { + if( !wasEnabled[ i ] ) + continue; + EnableWindow( editorsWindows[ i ], TRUE ); + } + } + + const int show = nowPending ? SW_NORMAL : SW_HIDE; + for( HWND w : pendingWindows ) + ::ShowWindow( w, show ); +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/PendingState.h b/Examples/WhisperDesktop/Utils/PendingState.h new file mode 100644 index 0000000..0b34e13 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/PendingState.h @@ -0,0 +1,12 @@ +#pragma once + +// Utility class to switch visual state of dialog controls between idle and pending +class PendingState +{ + std::vector<HWND> editorsWindows; + std::vector<bool> wasEnabled; + std::vector<HWND> pendingWindows; +public: + void initialize( std::initializer_list<HWND> editors, std::initializer_list<HWND> pending ); + void setPending( bool nowPending ); +};
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/TranslateCheckbox.cpp b/Examples/WhisperDesktop/Utils/TranslateCheckbox.cpp new file mode 100644 index 0000000..c5e6ac0 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/TranslateCheckbox.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "TranslateCheckbox.h" + +static const LPCTSTR regValTranslate = L"translate"; + +void TranslateCheckbox::initialize( HWND owner, int idc, AppState& state ) +{ + m_hWnd = GetDlgItem( owner, idc ); + assert( nullptr != m_hWnd ); + + if( state.dwordLoad( regValTranslate, 0 ) != 0 ) + ::SendMessage( m_hWnd, BM_SETCHECK, BST_CHECKED, 0L ); +} + +bool TranslateCheckbox::checked() +{ + assert( nullptr != m_hWnd ); + const int state = ( int )::SendMessage( m_hWnd, BM_GETCHECK, 0, 0 ); + return state == BST_CHECKED; +} + +void TranslateCheckbox::saveSelection( AppState& state ) +{ + state.dwordStore( regValTranslate, checked() ? TRUE : FALSE ); +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/TranslateCheckbox.h b/Examples/WhisperDesktop/Utils/TranslateCheckbox.h new file mode 100644 index 0000000..2b1db12 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/TranslateCheckbox.h @@ -0,0 +1,18 @@ +#pragma once +#include "../AppState.h" + +class TranslateCheckbox +{ + HWND m_hWnd = nullptr; +public: + operator HWND() const + { + return m_hWnd; + } + + void initialize( HWND owner, int idc, AppState& state ); + + bool checked(); + + void saveSelection( AppState& state ); +};
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/WTL/atlapp.h b/Examples/WhisperDesktop/Utils/WTL/atlapp.h new file mode 100644 index 0000000..8be6edc --- /dev/null +++ b/Examples/WhisperDesktop/Utils/WTL/atlapp.h @@ -0,0 +1,1225 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLAPP_H__ +#define __ATLAPP_H__ + +#pragma once + +#ifndef __cplusplus + #error WTL requires C++ compilation (use a .cpp suffix) +#endif + +#ifndef __ATLBASE_H__ + #error atlapp.h requires atlbase.h to be included first +#endif + +#ifdef _WIN32_WCE + #error WTL10 doesn't support Windows CE +#endif + +#ifdef _ATL_NO_COMMODULE + #error WTL doesn't support _ATL_NO_COMMODULE +#endif + +#ifdef _ATL_NO_WIN_SUPPORT + #error WTL doesn't support _ATL_NO_WIN_SUPPORT +#endif + +#if (_MSC_VER < 1400) + #error WTL10 requires C++ compiler version 14 (Visual C++ 2005) or higher +#endif + +#if (WINVER < 0x0501) + #error WTL requires WINVER >= 0x0501 +#endif + +#if (_WIN32_WINNT < 0x0501) + #error WTL requires _WIN32_WINNT >= 0x0501 +#endif + +#if (_WIN32_IE < 0x0600) + #error WTL requires _WIN32_IE >= 0x0600 +#endif + +#if (_ATL_VER < 0x0800) + #error WTL10 requires ATL version 8 or higher +#endif + +#ifdef _ATL_MIN_CRT + #error WTL10 doesn't support _ATL_MIN_CRT +#endif + +#ifdef _ATL_NO_MSIMG + #error WTL10 doesn't support _ATL_NO_MSIMG +#endif + +#include <limits.h> +#ifdef _MT + #include <process.h> // for _beginthreadex +#endif + +#include <commctrl.h> +#pragma comment(lib, "comctl32.lib") + +#include <commdlg.h> +#include <shellapi.h> + +// Check for VS2005 without newer WinSDK +#if (_MSC_VER == 1400) && !defined(RB_GETEXTENDEDSTYLE) + #error WTL10 requires WinSDK 6.0 ot higher +#endif + +#include <uxtheme.h> +#pragma comment(lib, "uxtheme.lib") + +#if defined(_SYSINFOAPI_H_) && defined(NOT_BUILD_WINDOWS_DEPRECATE) + #include <VersionHelpers.h> +#endif + +#include "atlres.h" + + +/////////////////////////////////////////////////////////////////////////////// +// WTL version number + +#define _WTL_VER 0x1000 // version 10.0 + + +/////////////////////////////////////////////////////////////////////////////// +// Classes in this file: +// +// CMessageFilter +// CIdleHandler +// CMessageLoop +// +// CAppModule +// CServerAppModule +// +// Global functions: +// AtlInitCommonControls() +// AtlGetDefaultGuiFont() +// AtlCreateControlFont() +// AtlCreateBoldFont() +// AtlGetStringPtr() + + +/////////////////////////////////////////////////////////////////////////////// +// Miscellaneous global support + +// define useful macros from winuser.h +#ifndef IS_INTRESOURCE + #define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0) +#endif // IS_INTRESOURCE + +// protect template members from windowsx.h macros +#ifdef _INC_WINDOWSX + #undef SubclassWindow +#endif // _INC_WINDOWSX + +// define useful macros from windowsx.h +#ifndef GET_X_LPARAM + #define GET_X_LPARAM(lParam) ((int)(short)LOWORD(lParam)) +#endif +#ifndef GET_Y_LPARAM + #define GET_Y_LPARAM(lParam) ((int)(short)HIWORD(lParam)) +#endif + +// Dummy structs for compiling with /CLR +#ifdef _MANAGED + __if_not_exists(_IMAGELIST::_IMAGELIST) { struct _IMAGELIST { }; } + __if_not_exists(_TREEITEM::_TREEITEM) { struct _TREEITEM { }; } + __if_not_exists(_PSP::_PSP) { struct _PSP { }; } +#endif + +// Forward declaration for ATL11 fix +#if (_ATL_VER >= 0x0B00) + namespace ATL { HRESULT AtlGetCommCtrlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor); } +#endif + +#ifndef WM_MOUSEHWHEEL + #define WM_MOUSEHWHEEL 0x020E +#endif + +// Used for stack allocations with ATL::CTempBuffer +#ifndef _WTL_STACK_ALLOC_THRESHOLD + #define _WTL_STACK_ALLOC_THRESHOLD 512 +#endif + + +namespace WTL +{ + +DECLARE_TRACE_CATEGORY(atlTraceUI) +#ifdef _DEBUG + __declspec(selectany) ATL::CTraceCategory atlTraceUI(_T("atlTraceUI")); +#endif // _DEBUG + +// Common Controls initialization helper +inline BOOL AtlInitCommonControls(DWORD dwFlags) +{ + INITCOMMONCONTROLSEX iccx = { sizeof(INITCOMMONCONTROLSEX), dwFlags }; + BOOL bRet = ::InitCommonControlsEx(&iccx); + ATLASSERT(bRet); + return bRet; +} + +// Default GUI font helper - "MS Shell Dlg" stock font +inline HFONT AtlGetDefaultGuiFont() +{ + return (HFONT)::GetStockObject(DEFAULT_GUI_FONT); +} + +// Control font helper - default font for controls not in a dialog +// (NOTE: Caller owns the font, and should destroy it when it's no longer needed) +inline HFONT AtlCreateControlFont() +{ + LOGFONT lf = {}; + ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE); + HFONT hFont = ::CreateFontIndirect(&lf); + ATLASSERT(hFont != NULL); + return hFont; +} + +// Bold font helper +// (NOTE: Caller owns the font, and should destroy it when it's no longer needed) +inline HFONT AtlCreateBoldFont(HFONT hFont = NULL) +{ + LOGFONT lf = {}; + if(hFont == NULL) + ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE); + else + ATLVERIFY(::GetObject(hFont, sizeof(LOGFONT), &lf) == sizeof(LOGFONT)); + lf.lfWeight = FW_BOLD; + HFONT hFontBold = ::CreateFontIndirect(&lf); + ATLASSERT(hFontBold != NULL); + return hFontBold; +} + +// Resource string pointer +inline LPCWSTR AtlGetStringPtr(UINT uID, int* pch = NULL) +{ + LPCWSTR lpstr = NULL; + int nRet = ::LoadStringW(ATL::_AtlBaseModule.GetResourceInstance(), uID, (LPWSTR)&lpstr, 0); + if(pch != NULL) + *pch = nRet; + return lpstr; +} + + +/////////////////////////////////////////////////////////////////////////////// +// RunTimeHelper - helper functions for Windows version and structure sizes + +#ifndef _WTL_NO_RUNTIME_STRUCT_SIZE + +#ifndef _SIZEOF_STRUCT + #define _SIZEOF_STRUCT(structname, member) (((int)((LPBYTE)(&((structname*)0)->member) - ((LPBYTE)((structname*)0)))) + sizeof(((structname*)0)->member)) +#endif + +#if (_WIN32_WINNT >= 0x0600) && !defined(REBARBANDINFO_V6_SIZE) + #define REBARBANDINFO_V6_SIZE _SIZEOF_STRUCT(REBARBANDINFO, cxHeader) +#endif // (_WIN32_WINNT >= 0x0600) && !defined(REBARBANDINFO_V6_SIZE) + +#if (_WIN32_WINNT >= 0x0600) && !defined(LVGROUP_V5_SIZE) + #define LVGROUP_V5_SIZE _SIZEOF_STRUCT(LVGROUP, uAlign) +#endif // (_WIN32_WINNT >= 0x0600) && !defined(LVGROUP_V5_SIZE) + +#if (_WIN32_WINNT >= 0x0600) && !defined(LVTILEINFO_V5_SIZE) + #define LVTILEINFO_V5_SIZE _SIZEOF_STRUCT(LVTILEINFO, puColumns) +#endif // (_WIN32_WINNT >= 0x0600) && !defined(LVTILEINFO_V5_SIZE) + +#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) && !defined(MCHITTESTINFO_V1_SIZE) + #define MCHITTESTINFO_V1_SIZE _SIZEOF_STRUCT(MCHITTESTINFO, st) +#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) && !defined(MCHITTESTINFO_V1_SIZE) + +#if (WINVER >= 0x0600) && !defined(NONCLIENTMETRICS_V1_SIZE) + #define NONCLIENTMETRICS_V1_SIZE _SIZEOF_STRUCT(NONCLIENTMETRICS, lfMessageFont) +#endif // (WINVER >= 0x0600) && !defined(NONCLIENTMETRICS_V1_SIZE) + +#ifndef TTTOOLINFO_V2_SIZE + #define TTTOOLINFO_V2_SIZE _SIZEOF_STRUCT(TTTOOLINFO, lParam) +#endif + +#endif // !_WTL_NO_RUNTIME_STRUCT_SIZE + +namespace RunTimeHelper +{ + inline bool IsCommCtrl6() + { + DWORD dwMajor = 0, dwMinor = 0; + HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor); + return (SUCCEEDED(hRet) && (dwMajor >= 6)); + } + + inline bool IsVista() + { +#ifdef _versionhelpers_H_INCLUDED_ + return ::IsWindowsVistaOrGreater(); +#else // !_versionhelpers_H_INCLUDED_ + OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; + BOOL bRet = ::GetVersionEx(&ovi); + return ((bRet != FALSE) && (ovi.dwMajorVersion >= 6)); +#endif // _versionhelpers_H_INCLUDED_ + } + + inline bool IsThemeAvailable() + { + return IsCommCtrl6() && (::IsThemeActive() != FALSE) && (::IsAppThemed() != FALSE); + } + + inline bool IsWin7() + { +#ifdef _versionhelpers_H_INCLUDED_ + return ::IsWindows7OrGreater(); +#else // !_versionhelpers_H_INCLUDED_ + OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; + BOOL bRet = ::GetVersionEx(&ovi); + return ((bRet != FALSE) && ((ovi.dwMajorVersion > 6) || ((ovi.dwMajorVersion == 6) && (ovi.dwMinorVersion >= 1)))); +#endif // _versionhelpers_H_INCLUDED_ + } + + inline bool IsRibbonUIAvailable() + { + static INT iRibbonUI = -1; + +#if defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7) + if (iRibbonUI == -1) + { + HMODULE hRibbonDLL = ::LoadLibrary(_T("propsys.dll")); + if (hRibbonDLL != NULL) + { + const GUID CLSID_UIRibbonFramework = { 0x926749fa, 0x2615, 0x4987, { 0x88, 0x45, 0xc3, 0x3e, 0x65, 0xf2, 0xb9, 0x57 } }; + // block - create instance + { + ATL::CComPtr<IUnknown> pIUIFramework; + iRibbonUI = SUCCEEDED(pIUIFramework.CoCreateInstance(CLSID_UIRibbonFramework)) ? 1 : 0; + } + ::FreeLibrary(hRibbonDLL); + } + else + { + iRibbonUI = 0; + } + } +#endif // defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7) + + return (iRibbonUI == 1); + } + + inline UINT SizeOf_REBARBANDINFO() + { + UINT uSize = sizeof(REBARBANDINFO); +#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) + if(!(IsVista() && IsCommCtrl6())) + uSize = REBARBANDINFO_V6_SIZE; +#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) + return uSize; + } + + inline UINT SizeOf_LVGROUP() + { + UINT uSize = sizeof(LVGROUP); +#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) + if(!IsVista()) + uSize = LVGROUP_V5_SIZE; +#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) + return uSize; + } + + inline UINT SizeOf_LVTILEINFO() + { + UINT uSize = sizeof(LVTILEINFO); +#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) + if(!IsVista()) + uSize = LVTILEINFO_V5_SIZE; +#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) + return uSize; + } + + inline UINT SizeOf_MCHITTESTINFO() + { + UINT uSize = sizeof(MCHITTESTINFO); +#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) + if(!(IsVista() && IsCommCtrl6())) + uSize = MCHITTESTINFO_V1_SIZE; +#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) + return uSize; + } + + inline UINT SizeOf_NONCLIENTMETRICS() + { + UINT uSize = sizeof(NONCLIENTMETRICS); +#if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (WINVER >= 0x0600) + if(!IsVista()) + uSize = NONCLIENTMETRICS_V1_SIZE; +#endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (WINVER >= 0x0600) + return uSize; + } + + inline UINT SizeOf_TOOLINFO() + { + UINT uSize = sizeof(TOOLINFO); +#ifndef _WTL_NO_RUNTIME_STRUCT_SIZE + if(!IsVista()) + uSize = TTTOOLINFO_V2_SIZE; +#endif + return uSize; + } +} // namespace RunTimeHelper + + +/////////////////////////////////////////////////////////////////////////////// +// ModuleHelper - helper functions for ATL (deprecated) + +namespace ModuleHelper +{ + inline HINSTANCE GetModuleInstance() + { + return ATL::_AtlBaseModule.GetModuleInstance(); + } + + inline HINSTANCE GetResourceInstance() + { + return ATL::_AtlBaseModule.GetResourceInstance(); + } + + inline void AddCreateWndData(ATL::_AtlCreateWndData* pData, void* pObject) + { + ATL::_AtlWinModule.AddCreateWndData(pData, pObject); + } + + inline void* ExtractCreateWndData() + { + return ATL::_AtlWinModule.ExtractCreateWndData(); + } +} // namespace ModuleHelper + + +/////////////////////////////////////////////////////////////////////////////// +// SecureHelper - WTL10 requires use of secure functions +// these are here only for compatibility with existing projects + +namespace SecureHelper +{ + inline void strcpyA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc) + { + ATL::Checked::strcpy_s(lpstrDest, cchDest, lpstrSrc); + } + + inline void strcpyW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc) + { + ATL::Checked::wcscpy_s(lpstrDest, cchDest, lpstrSrc); + } + + inline void strcpy_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc) + { +#ifdef _UNICODE + strcpyW_x(lpstrDest, cchDest, lpstrSrc); +#else + strcpyA_x(lpstrDest, cchDest, lpstrSrc); +#endif + } + + inline errno_t strncpyA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc, size_t cchCount) + { + return ATL::Checked::strncpy_s(lpstrDest, cchDest, lpstrSrc, cchCount); + } + + inline errno_t strncpyW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc, size_t cchCount) + { + return ATL::Checked::wcsncpy_s(lpstrDest, cchDest, lpstrSrc, cchCount); + } + + inline errno_t strncpy_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc, size_t cchCount) + { +#ifdef _UNICODE + return strncpyW_x(lpstrDest, cchDest, lpstrSrc, cchCount); +#else + return strncpyA_x(lpstrDest, cchDest, lpstrSrc, cchCount); +#endif + } + + inline void strcatA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc) + { + ATL::Checked::strcat_s(lpstrDest, cchDest, lpstrSrc); + } + + inline void strcatW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc) + { + ATL::Checked::wcscat_s(lpstrDest, cchDest, lpstrSrc); + } + + inline void strcat_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc) + { +#ifdef _UNICODE + strcatW_x(lpstrDest, cchDest, lpstrSrc); +#else + strcatA_x(lpstrDest, cchDest, lpstrSrc); +#endif + } + + inline void memcpy_x(void* pDest, size_t cbDest, const void* pSrc, size_t cbSrc) + { + ATL::Checked::memcpy_s(pDest, cbDest, pSrc, cbSrc); + } + + inline void memmove_x(void* pDest, size_t cbDest, const void* pSrc, size_t cbSrc) + { + ATL::Checked::memmove_s(pDest, cbDest, pSrc, cbSrc); + } + + inline int vsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, va_list args) + { + return _vstprintf_s(lpstrBuff, cchBuff, lpstrFormat, args); + } + + inline int wvsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, va_list args) + { + return _vstprintf_s(lpstrBuff, cchBuff, lpstrFormat, args); + } + + inline int sprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, ...) + { + va_list args; + va_start(args, lpstrFormat); + int nRes = vsprintf_x(lpstrBuff, cchBuff, lpstrFormat, args); + va_end(args); + return nRes; + } + + inline int wsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, ...) + { + va_list args; + va_start(args, lpstrFormat); + int nRes = wvsprintf_x(lpstrBuff, cchBuff, lpstrFormat, args); + va_end(args); + return nRes; + } +} // namespace SecureHelper + + +/////////////////////////////////////////////////////////////////////////////// +// MinCrtHelper - WTL10 doesn't support _ATL_MIN_CRT, +// these are here only for compatibility with existing projects + +namespace MinCrtHelper +{ + inline int _isspace(TCHAR ch) + { + return _istspace(ch); + } + + inline int _isdigit(TCHAR ch) + { + return _istdigit(ch); + } + + inline int _atoi(LPCTSTR str) + { + return _ttoi(str); + } + + inline LPCTSTR _strrchr(LPCTSTR str, TCHAR ch) + { + return _tcsrchr(str, ch); + } + + inline LPTSTR _strrchr(LPTSTR str, TCHAR ch) + { + return _tcsrchr(str, ch); + } +} // namespace MinCrtHelper + + +/////////////////////////////////////////////////////////////////////////////// +// GenericWndClass - generic window class usable for subclassing + +// Use in dialog templates to specify a placeholder to be subclassed +// Specify as a custom control with class name WTL_GenericWindow +// Call Rregister() before creating dialog (for example, in WinMain) +namespace GenericWndClass +{ + inline LPCTSTR GetName() + { + return _T("WTL_GenericWindow"); + } + + inline ATOM Register() + { + WNDCLASSEX wc = { sizeof(WNDCLASSEX) }; + wc.lpfnWndProc = ::DefWindowProc; + wc.hInstance = ModuleHelper::GetModuleInstance(); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.lpszClassName = GetName(); + ATOM atom = ::RegisterClassEx(&wc); + ATLASSERT(atom != 0); + return atom; + } + + inline BOOL Unregister() // only needed for DLLs or tmp use + { + return ::UnregisterClass(GetName(), ModuleHelper::GetModuleInstance()); + } +} // namespace GenericWndClass + + +/////////////////////////////////////////////////////////////////////////////// +// CMessageFilter - Interface for message filter support + +class ATL_NO_VTABLE CMessageFilter +{ +public: + virtual BOOL PreTranslateMessage(MSG* pMsg) = 0; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CIdleHandler - Interface for idle processing + +class ATL_NO_VTABLE CIdleHandler +{ +public: + virtual BOOL OnIdle() = 0; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CMessageLoop - message loop implementation + +class CMessageLoop +{ +public: + ATL::CSimpleArray<CMessageFilter*> m_aMsgFilter; + ATL::CSimpleArray<CIdleHandler*> m_aIdleHandler; + MSG m_msg; + + CMessageLoop() + { + memset(&m_msg, 0, sizeof(m_msg)); + } + + virtual ~CMessageLoop() + { } + +// Message filter operations + BOOL AddMessageFilter(CMessageFilter* pMessageFilter) + { + return m_aMsgFilter.Add(pMessageFilter); + } + + BOOL RemoveMessageFilter(CMessageFilter* pMessageFilter) + { + return m_aMsgFilter.Remove(pMessageFilter); + } + +// Idle handler operations + BOOL AddIdleHandler(CIdleHandler* pIdleHandler) + { + return m_aIdleHandler.Add(pIdleHandler); + } + + BOOL RemoveIdleHandler(CIdleHandler* pIdleHandler) + { + return m_aIdleHandler.Remove(pIdleHandler); + } + +// message loop + int Run() + { + BOOL bDoIdle = TRUE; + int nIdleCount = 0; + BOOL bRet = FALSE; + + for(;;) + { + while(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE)) + { + if(!OnIdle(nIdleCount++)) + bDoIdle = FALSE; + } + + bRet = ::GetMessage(&m_msg, NULL, 0, 0); + + if(bRet == -1) + { + ATLTRACE2(atlTraceUI, 0, _T("::GetMessage returned -1 (error)\n")); + continue; // error, don't process + } + else if(!bRet) + { + ATLTRACE2(atlTraceUI, 0, _T("CMessageLoop::Run - exiting\n")); + break; // WM_QUIT, exit message loop + } + + if(!PreTranslateMessage(&m_msg)) + { + ::TranslateMessage(&m_msg); + ::DispatchMessage(&m_msg); + } + + if(IsIdleMessage(&m_msg)) + { + bDoIdle = TRUE; + nIdleCount = 0; + } + } + + return (int)m_msg.wParam; + } + +// Overrideables + // Override to change message filtering + virtual BOOL PreTranslateMessage(MSG* pMsg) + { + // loop backwards + for(int i = m_aMsgFilter.GetSize() - 1; i >= 0; i--) + { + CMessageFilter* pMessageFilter = m_aMsgFilter[i]; + if((pMessageFilter != NULL) && pMessageFilter->PreTranslateMessage(pMsg)) + return TRUE; + } + return FALSE; // not translated + } + + // override to change idle processing + virtual BOOL OnIdle(int /*nIdleCount*/) + { + for(int i = 0; i < m_aIdleHandler.GetSize(); i++) + { + CIdleHandler* pIdleHandler = m_aIdleHandler[i]; + if(pIdleHandler != NULL) + pIdleHandler->OnIdle(); + } + return FALSE; // don't continue + } + + // override to change non-idle messages + virtual BOOL IsIdleMessage(MSG* pMsg) const + { + // These messages should NOT cause idle processing + switch(pMsg->message) + { + case WM_MOUSEMOVE: + case WM_NCMOUSEMOVE: + case WM_PAINT: + case 0x0118: // WM_SYSTIMER (caret blink) + return FALSE; + } + + return TRUE; + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CStaticDataInitCriticalSectionLock and CWindowCreateCriticalSectionLock +// internal classes to manage critical sections for ATL (deprecated) + +class CStaticDataInitCriticalSectionLock +{ +public: + ATL::CComCritSecLock<ATL::CComCriticalSection> m_cslock; + + CStaticDataInitCriticalSectionLock() : m_cslock(ATL::_pAtlModule->m_csStaticDataInitAndTypeInfo, false) + { } + + HRESULT Lock() + { + return m_cslock.Lock(); + } + + void Unlock() + { + m_cslock.Unlock(); + } +}; + + +class CWindowCreateCriticalSectionLock +{ +public: + ATL::CComCritSecLock<ATL::CComCriticalSection> m_cslock; + + CWindowCreateCriticalSectionLock() : m_cslock(ATL::_AtlWinModule.m_csWindowCreate, false) + { } + + HRESULT Lock() + { + return m_cslock.Lock(); + } + + void Unlock() + { + m_cslock.Unlock(); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CAppModule - module class for an application + +#if (_MSC_VER == 1400) // VS2005 + #pragma warning(push) + #pragma warning(disable : 4244) + #pragma warning(disable : 4312) +#endif + +class CAppModule : public ATL::CComModule +{ +public: + DWORD m_dwMainThreadID; + ATL::CSimpleMap<DWORD, CMessageLoop*>* m_pMsgLoopMap; + ATL::CSimpleArray<HWND>* m_pSettingChangeNotify; + + CAppModule() : m_dwMainThreadID(0), m_pMsgLoopMap(NULL), m_pSettingChangeNotify(NULL) + { } + +// Overrides of CComModule::Init and Term + HRESULT Init(ATL::_ATL_OBJMAP_ENTRY* pObjMap, HINSTANCE hInstance, const GUID* pLibID = NULL) + { + HRESULT hRet = CComModule::Init(pObjMap, hInstance, pLibID); + if(FAILED(hRet)) + return hRet; + + m_dwMainThreadID = ::GetCurrentThreadId(); + typedef ATL::CSimpleMap<DWORD, CMessageLoop*> _mapClass; + m_pMsgLoopMap = NULL; + ATLTRY(m_pMsgLoopMap = new _mapClass); + if(m_pMsgLoopMap == NULL) + return E_OUTOFMEMORY; + m_pSettingChangeNotify = NULL; + + return hRet; + } + + void Term() + { + TermSettingChangeNotify(); + delete m_pMsgLoopMap; + CComModule::Term(); + } + +// Message loop map methods + BOOL AddMessageLoop(CMessageLoop* pMsgLoop) + { + CStaticDataInitCriticalSectionLock lock; + if(FAILED(lock.Lock())) + { + ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddMessageLoop.\n")); + ATLASSERT(FALSE); + return FALSE; + } + + ATLASSERT(pMsgLoop != NULL); + ATLASSERT(m_pMsgLoopMap->Lookup(::GetCurrentThreadId()) == NULL); // not in map yet + + BOOL bRet = m_pMsgLoopMap->Add(::GetCurrentThreadId(), pMsgLoop); + + lock.Unlock(); + + return bRet; + } + + BOOL RemoveMessageLoop() + { + CStaticDataInitCriticalSectionLock lock; + if(FAILED(lock.Lock())) + { + ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::RemoveMessageLoop.\n")); + ATLASSERT(FALSE); + return FALSE; + } + + BOOL bRet = m_pMsgLoopMap->Remove(::GetCurrentThreadId()); + + lock.Unlock(); + + return bRet; + } + + CMessageLoop* GetMessageLoop(DWORD dwThreadID = ::GetCurrentThreadId()) const + { + CStaticDataInitCriticalSectionLock lock; + if(FAILED(lock.Lock())) + { + ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::GetMessageLoop.\n")); + ATLASSERT(FALSE); + return NULL; + } + + CMessageLoop* pLoop = m_pMsgLoopMap->Lookup(dwThreadID); + + lock.Unlock(); + + return pLoop; + } + +// Setting change notify methods + // Note: Call this from the main thread for MSDI apps + BOOL InitSettingChangeNotify(DLGPROC pfnDlgProc = _SettingChangeDlgProc) + { + CStaticDataInitCriticalSectionLock lock; + if(FAILED(lock.Lock())) + { + ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::InitSettingChangeNotify.\n")); + ATLASSERT(FALSE); + return FALSE; + } + + if(m_pSettingChangeNotify == NULL) + { + typedef ATL::CSimpleArray<HWND> _notifyClass; + ATLTRY(m_pSettingChangeNotify = new _notifyClass); + ATLASSERT(m_pSettingChangeNotify != NULL); + } + + BOOL bRet = (m_pSettingChangeNotify != NULL); + if(bRet && (m_pSettingChangeNotify->GetSize() == 0)) + { + // init everything + _ATL_EMPTY_DLGTEMPLATE templ; + HWND hNtfWnd = ::CreateDialogIndirect(GetModuleInstance(), &templ, NULL, pfnDlgProc); + ATLASSERT(::IsWindow(hNtfWnd)); + if(::IsWindow(hNtfWnd)) + { + ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, (LONG_PTR)this); + bRet = m_pSettingChangeNotify->Add(hNtfWnd); + } + else + { + bRet = FALSE; + } + } + + lock.Unlock(); + + return bRet; + } + + void TermSettingChangeNotify() + { + CStaticDataInitCriticalSectionLock lock; + if(FAILED(lock.Lock())) + { + ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::TermSettingChangeNotify.\n")); + ATLASSERT(FALSE); + return; + } + + if((m_pSettingChangeNotify != NULL) && (m_pSettingChangeNotify->GetSize() > 0)) + ::DestroyWindow((*m_pSettingChangeNotify)[0]); + delete m_pSettingChangeNotify; + m_pSettingChangeNotify = NULL; + + lock.Unlock(); + } + + BOOL AddSettingChangeNotify(HWND hWnd) + { + CStaticDataInitCriticalSectionLock lock; + if(FAILED(lock.Lock())) + { + ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddSettingChangeNotify.\n")); + ATLASSERT(FALSE); + return FALSE; + } + + ATLASSERT(::IsWindow(hWnd)); + BOOL bRet = FALSE; + if(InitSettingChangeNotify() != FALSE) + bRet = m_pSettingChangeNotify->Add(hWnd); + + lock.Unlock(); + + return bRet; + } + + BOOL RemoveSettingChangeNotify(HWND hWnd) + { + CStaticDataInitCriticalSectionLock lock; + if(FAILED(lock.Lock())) + { + ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::RemoveSettingChangeNotify.\n")); + ATLASSERT(FALSE); + return FALSE; + } + + BOOL bRet = FALSE; + if(m_pSettingChangeNotify != NULL) + bRet = m_pSettingChangeNotify->Remove(hWnd); + + lock.Unlock(); + + return bRet; + } + +// Implementation - setting change notify dialog template and dialog procedure + struct _ATL_EMPTY_DLGTEMPLATE : DLGTEMPLATE + { + _ATL_EMPTY_DLGTEMPLATE() + { + memset(this, 0, sizeof(_ATL_EMPTY_DLGTEMPLATE)); + style = WS_POPUP; + } + WORD wMenu, wClass, wTitle; + }; + + static INT_PTR CALLBACK _SettingChangeDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + if(uMsg == WM_SETTINGCHANGE) + { + CAppModule* pModule = (CAppModule*)::GetWindowLongPtr(hWnd, GWLP_USERDATA); + ATLASSERT(pModule != NULL); + ATLASSERT(pModule->m_pSettingChangeNotify != NULL); + const UINT uTimeout = 1500; // ms + for(int i = 1; i < pModule->m_pSettingChangeNotify->GetSize(); i++) + ::SendMessageTimeout((*pModule->m_pSettingChangeNotify)[i], uMsg, wParam, lParam, SMTO_ABORTIFHUNG, uTimeout, NULL); + + return TRUE; + } + + return FALSE; + } +}; + +#if (_MSC_VER == 1400) // VS2005 + #pragma warning(pop) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// CServerAppModule - module class for a COM server application + +class CServerAppModule : public CAppModule +{ +public: + HANDLE m_hEventShutdown; + bool m_bActivity; + DWORD m_dwTimeOut; + DWORD m_dwPause; + + CServerAppModule() : m_hEventShutdown(NULL), m_bActivity(false), m_dwTimeOut(5000), m_dwPause(1000) + { } + +// Override of CAppModule::Init + HRESULT Init(ATL::_ATL_OBJMAP_ENTRY* pObjMap, HINSTANCE hInstance, const GUID* pLibID = NULL) + { + m_dwTimeOut = 5000; + m_dwPause = 1000; + return CAppModule::Init(pObjMap, hInstance, pLibID); + } + + void Term() + { + if((m_hEventShutdown != NULL) && ::CloseHandle(m_hEventShutdown)) + m_hEventShutdown = NULL; + CAppModule::Term(); + } + +// COM Server methods + LONG Unlock() throw() + { + LONG lRet = CComModule::Unlock(); + if(lRet == 0) + { + m_bActivity = true; + ::SetEvent(m_hEventShutdown); // tell monitor that we transitioned to zero + } + return lRet; + } + + void MonitorShutdown() + { + for(;;) + { + ::WaitForSingleObject(m_hEventShutdown, INFINITE); + DWORD dwWait = 0; + do + { + m_bActivity = false; + dwWait = ::WaitForSingleObject(m_hEventShutdown, m_dwTimeOut); + } + while(dwWait == WAIT_OBJECT_0); + // timed out + if(!m_bActivity && (m_nLockCnt == 0)) // if no activity let's really bail + { +#if defined(_WIN32_DCOM) && defined(_ATL_FREE_THREADED) + ::CoSuspendClassObjects(); + if(!m_bActivity && (m_nLockCnt == 0)) +#endif + break; + } + } + // This handle should be valid now. If it isn't, + // check if _Module.Term was called first (it shouldn't) + if(::CloseHandle(m_hEventShutdown)) + m_hEventShutdown = NULL; + ::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0); + } + + bool StartMonitor() + { + m_hEventShutdown = ::CreateEvent(NULL, false, false, NULL); + if(m_hEventShutdown == NULL) + return false; + DWORD dwThreadID = 0; +#ifdef _MT + HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, (UINT (WINAPI*)(void*))MonitorProc, this, 0, (UINT*)&dwThreadID); +#else + HANDLE hThread = ::CreateThread(NULL, 0, MonitorProc, this, 0, &dwThreadID); +#endif + bool bRet = (hThread != NULL); + if(bRet) + ::CloseHandle(hThread); + return bRet; + } + + static DWORD WINAPI MonitorProc(void* pv) + { + CServerAppModule* p = (CServerAppModule*)pv; + p->MonitorShutdown(); + return 0; + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CRegKeyEx - not used any more, here only for compatibility with old projects + +typedef ATL::CRegKey CRegKeyEx; + +} // namespace WTL + + +/////////////////////////////////////////////////////////////////////////////// +// CString forward reference (enables CString use in atluser.h and atlgdi.h) + +#if (defined(_WTL_USE_CSTRING) || defined(_WTL_FORWARD_DECLARE_CSTRING)) && !defined(__ATLSTR_H__) + #include <atlstr.h> +#endif + +// CString namespace +#define _CSTRING_NS ATL + +// Type classes namespace +#define _WTYPES_NS + + +/////////////////////////////////////////////////////////////////////////////// +// General DLL version helpers (removed in ATL11) + +#if (_ATL_VER >= 0x0B00) + +namespace ATL +{ + +inline HRESULT AtlGetDllVersion(HINSTANCE hInstDLL, DLLVERSIONINFO* pDllVersionInfo) +{ + ATLASSERT(pDllVersionInfo != NULL); + if(pDllVersionInfo == NULL) + return E_INVALIDARG; + + // We must get this function explicitly because some DLLs don't implement it. + DLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(hInstDLL, "DllGetVersion"); + if(pfnDllGetVersion == NULL) + return E_NOTIMPL; + + return (*pfnDllGetVersion)(pDllVersionInfo); +} + +inline HRESULT AtlGetDllVersion(LPCTSTR lpstrDllName, DLLVERSIONINFO* pDllVersionInfo) +{ + HINSTANCE hInstDLL = ::LoadLibrary(lpstrDllName); + if(hInstDLL == NULL) + return E_FAIL; + HRESULT hRet = AtlGetDllVersion(hInstDLL, pDllVersionInfo); + ::FreeLibrary(hInstDLL); + return hRet; +} + +// Common Control Versions: +// Win95/WinNT 4.0 maj=4 min=00 +// IE 3.x maj=4 min=70 +// IE 4.0 maj=4 min=71 +inline HRESULT AtlGetCommCtrlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor) +{ + ATLASSERT((pdwMajor != NULL) && (pdwMinor != NULL)); + if((pdwMajor == NULL) || (pdwMinor == NULL)) + return E_INVALIDARG; + + DLLVERSIONINFO dvi; + ::ZeroMemory(&dvi, sizeof(dvi)); + dvi.cbSize = sizeof(dvi); + HRESULT hRet = AtlGetDllVersion(_T("comctl32.dll"), &dvi); + + if(SUCCEEDED(hRet)) + { + *pdwMajor = dvi.dwMajorVersion; + *pdwMinor = dvi.dwMinorVersion; + } + else if(hRet == E_NOTIMPL) + { + // If DllGetVersion is not there, then the DLL is a version + // previous to the one shipped with IE 3.x + *pdwMajor = 4; + *pdwMinor = 0; + hRet = S_OK; + } + + return hRet; +} + +// Shell Versions: +// Win95/WinNT 4.0 maj=4 min=00 +// IE 3.x, IE 4.0 without Web Integrated Desktop maj=4 min=00 +// IE 4.0 with Web Integrated Desktop maj=4 min=71 +// IE 4.01 with Web Integrated Desktop maj=4 min=72 +inline HRESULT AtlGetShellVersion(LPDWORD pdwMajor, LPDWORD pdwMinor) +{ + ATLASSERT((pdwMajor != NULL) && (pdwMinor != NULL)); + if((pdwMajor == NULL) || (pdwMinor == NULL)) + return E_INVALIDARG; + + DLLVERSIONINFO dvi; + ::ZeroMemory(&dvi, sizeof(dvi)); + dvi.cbSize = sizeof(dvi); + HRESULT hRet = AtlGetDllVersion(_T("shell32.dll"), &dvi); + + if(SUCCEEDED(hRet)) + { + *pdwMajor = dvi.dwMajorVersion; + *pdwMinor = dvi.dwMinorVersion; + } + else if(hRet == E_NOTIMPL) + { + // If DllGetVersion is not there, then the DLL is a version + // previous to the one shipped with IE 4.x + *pdwMajor = 4; + *pdwMinor = 0; + hRet = S_OK; + } + + return hRet; +} + +} // namespace ATL + +#endif // (_ATL_VER >= 0x0B00) + + +// These are always included +#include "atlwinx.h" +#include "atluser.h" +#include "atlgdi.h" + +#ifndef _WTL_NO_AUTOMATIC_NAMESPACE +using namespace WTL; +#endif // !_WTL_NO_AUTOMATIC_NAMESPACE + +#endif // __ATLAPP_H__ diff --git a/Examples/WhisperDesktop/Utils/WTL/atlcrack.h b/Examples/WhisperDesktop/Utils/WTL/atlcrack.h new file mode 100644 index 0000000..da6a896 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/WTL/atlcrack.h @@ -0,0 +1,2480 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLCRACK_H__ +#define __ATLCRACK_H__ + +#pragma once + +#ifndef __ATLAPP_H__ + #error atlcrack.h requires atlapp.h to be included first +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Message map macro for cracked handlers + +// Note about message maps with cracked handlers: +// You can use BEGIN_MSG_MAP for classes that derive from CWindowImpl/CDialogImpl, +// but must use BEGIN_MSG_MAP_EX for classes that don't. + +#define BEGIN_MSG_MAP_EX(theClass) \ +public: \ + BOOL m_bMsgHandled; \ + /* "handled" management for cracked handlers */ \ + BOOL IsMsgHandled() const \ + { \ + return m_bMsgHandled; \ + } \ + void SetMsgHandled(BOOL bHandled) \ + { \ + m_bMsgHandled = bHandled; \ + } \ + BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \ + { \ + BOOL bOldMsgHandled = m_bMsgHandled; \ + BOOL bRet = _ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); \ + m_bMsgHandled = bOldMsgHandled; \ + return bRet; \ + } \ + BOOL _ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID) \ + { \ + BOOL bHandled = TRUE; \ + (hWnd); \ + (uMsg); \ + (wParam); \ + (lParam); \ + (lResult); \ + (bHandled); \ + switch(dwMsgMapID) \ + { \ + case 0: + + +/////////////////////////////////////////////////////////////////////////////// +// Standard Windows message macros + +// int OnCreate(LPCREATESTRUCT lpCreateStruct) +#define MSG_WM_CREATE(func) \ + if (uMsg == WM_CREATE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam) +#define MSG_WM_INITDIALOG(func) \ + if (uMsg == WM_INITDIALOG) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnCopyData(CWindow wnd, PCOPYDATASTRUCT pCopyDataStruct) +#define MSG_WM_COPYDATA(func) \ + if (uMsg == WM_COPYDATA) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, (PCOPYDATASTRUCT)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDestroy() +#define MSG_WM_DESTROY(func) \ + if (uMsg == WM_DESTROY) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMove(CPoint ptPos) +#define MSG_WM_MOVE(func) \ + if (uMsg == WM_MOVE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSize(UINT nType, CSize size) +#define MSG_WM_SIZE(func) \ + if (uMsg == WM_SIZE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnActivate(UINT nState, BOOL bMinimized, CWindow wndOther) +#define MSG_WM_ACTIVATE(func) \ + if (uMsg == WM_ACTIVATE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (BOOL)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSetFocus(CWindow wndOld) +#define MSG_WM_SETFOCUS(func) \ + if (uMsg == WM_SETFOCUS) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnKillFocus(CWindow wndFocus) +#define MSG_WM_KILLFOCUS(func) \ + if (uMsg == WM_KILLFOCUS) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEnable(BOOL bEnable) +#define MSG_WM_ENABLE(func) \ + if (uMsg == WM_ENABLE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaint(CDCHandle dc) +#define MSG_WM_PAINT(func) \ + if (uMsg == WM_PAINT) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HDC)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnClose() +#define MSG_WM_CLOSE(func) \ + if (uMsg == WM_CLOSE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnQueryEndSession(UINT nSource, UINT uLogOff) +#define MSG_WM_QUERYENDSESSION(func) \ + if (uMsg == WM_QUERYENDSESSION) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (UINT)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnQueryOpen() +#define MSG_WM_QUERYOPEN(func) \ + if (uMsg == WM_QUERYOPEN) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnEraseBkgnd(CDCHandle dc) +#define MSG_WM_ERASEBKGND(func) \ + if (uMsg == WM_ERASEBKGND) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysColorChange() +#define MSG_WM_SYSCOLORCHANGE(func) \ + if (uMsg == WM_SYSCOLORCHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEndSession(BOOL bEnding, UINT uLogOff) +#define MSG_WM_ENDSESSION(func) \ + if (uMsg == WM_ENDSESSION) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)wParam, (UINT)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnShowWindow(BOOL bShow, UINT nStatus) +#define MSG_WM_SHOWWINDOW(func) \ + if (uMsg == WM_SHOWWINDOW) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)wParam, (int)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorEdit(CDCHandle dc, CEdit edit) +#define MSG_WM_CTLCOLOREDIT(func) \ + if (uMsg == WM_CTLCOLOREDIT) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorListBox(CDCHandle dc, CListBox listBox) +#define MSG_WM_CTLCOLORLISTBOX(func) \ + if (uMsg == WM_CTLCOLORLISTBOX) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorBtn(CDCHandle dc, CButton button) +#define MSG_WM_CTLCOLORBTN(func) \ + if (uMsg == WM_CTLCOLORBTN) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorDlg(CDCHandle dc, CWindow wnd) +#define MSG_WM_CTLCOLORDLG(func) \ + if (uMsg == WM_CTLCOLORDLG) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar) +#define MSG_WM_CTLCOLORSCROLLBAR(func) \ + if (uMsg == WM_CTLCOLORSCROLLBAR) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnCtlColorStatic(CDCHandle dc, CStatic wndStatic) +#define MSG_WM_CTLCOLORSTATIC(func) \ + if (uMsg == WM_CTLCOLORSTATIC) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSettingChange(UINT uFlags, LPCTSTR lpszSection) +#define MSG_WM_SETTINGCHANGE(func) \ + if (uMsg == WM_SETTINGCHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPCTSTR)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDevModeChange(LPCTSTR lpDeviceName) +#define MSG_WM_DEVMODECHANGE(func) \ + if (uMsg == WM_DEVMODECHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((LPCTSTR)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnActivateApp(BOOL bActive, DWORD dwThreadID) +#define MSG_WM_ACTIVATEAPP(func) \ + if (uMsg == WM_ACTIVATEAPP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)wParam, (DWORD)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnFontChange() +#define MSG_WM_FONTCHANGE(func) \ + if (uMsg == WM_FONTCHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnTimeChange() +#define MSG_WM_TIMECHANGE(func) \ + if (uMsg == WM_TIMECHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCancelMode() +#define MSG_WM_CANCELMODE(func) \ + if (uMsg == WM_CANCELMODE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnSetCursor(CWindow wnd, UINT nHitTest, UINT message) +#define MSG_WM_SETCURSOR(func) \ + if (uMsg == WM_SETCURSOR) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnMouseActivate(CWindow wndTopLevel, UINT nHitTest, UINT message) +#define MSG_WM_MOUSEACTIVATE(func) \ + if (uMsg == WM_MOUSEACTIVATE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnChildActivate() +#define MSG_WM_CHILDACTIVATE(func) \ + if (uMsg == WM_CHILDACTIVATE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnGetMinMaxInfo(LPMINMAXINFO lpMMI) +#define MSG_WM_GETMINMAXINFO(func) \ + if (uMsg == WM_GETMINMAXINFO) \ + { \ + this->SetMsgHandled(TRUE); \ + func((LPMINMAXINFO)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnIconEraseBkgnd(CDCHandle dc) +#define MSG_WM_ICONERASEBKGND(func) \ + if (uMsg == WM_ICONERASEBKGND) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HDC)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSpoolerStatus(UINT nStatus, UINT nJobs) +#define MSG_WM_SPOOLERSTATUS(func) \ + if (uMsg == WM_SPOOLERSTATUS) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (UINT)LOWORD(lParam)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) +#define MSG_WM_DRAWITEM(func) \ + if (uMsg == WM_DRAWITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) +#define MSG_WM_MEASUREITEM(func) \ + if (uMsg == WM_MEASUREITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct) +#define MSG_WM_DELETEITEM(func) \ + if (uMsg == WM_DELETEITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +//int OnCharToItem(UINT nChar, UINT nIndex, CListBox listBox) +#define MSG_WM_CHARTOITEM(func) \ + if (uMsg == WM_CHARTOITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox) +#define MSG_WM_VKEYTOITEM(func) \ + if (uMsg == WM_VKEYTOITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HCURSOR OnQueryDragIcon() +#define MSG_WM_QUERYDRAGICON(func) \ + if (uMsg == WM_QUERYDRAGICON) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT lpCompareItemStruct) +#define MSG_WM_COMPAREITEM(func) \ + if (uMsg == WM_COMPAREITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCompacting(UINT nCpuTime) +#define MSG_WM_COMPACTING(func) \ + if (uMsg == WM_COMPACTING) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnNcCreate(LPCREATESTRUCT lpCreateStruct) +#define MSG_WM_NCCREATE(func) \ + if (uMsg == WM_NCCREATE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcDestroy() +#define MSG_WM_NCDESTROY(func) \ + if (uMsg == WM_NCDESTROY) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNcCalcSize(BOOL bCalcValidRects, LPARAM lParam) +#define MSG_WM_NCCALCSIZE(func) \ + if (uMsg == WM_NCCALCSIZE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((BOOL)wParam, lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// UINT OnNcHitTest(CPoint point) +#define MSG_WM_NCHITTEST(func) \ + if (uMsg == WM_NCHITTEST) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcPaint(CRgnHandle rgn) +#define MSG_WM_NCPAINT(func) \ + if (uMsg == WM_NCPAINT) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HRGN)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnNcActivate(BOOL bActive) +#define MSG_WM_NCACTIVATE(func) \ + if (uMsg == WM_NCACTIVATE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((BOOL)wParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// UINT OnGetDlgCode(LPMSG lpMsg) +#define MSG_WM_GETDLGCODE(func) \ + if (uMsg == WM_GETDLGCODE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPMSG)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMouseMove(UINT nHitTest, CPoint point) +#define MSG_WM_NCMOUSEMOVE(func) \ + if (uMsg == WM_NCMOUSEMOVE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcLButtonDown(UINT nHitTest, CPoint point) +#define MSG_WM_NCLBUTTONDOWN(func) \ + if (uMsg == WM_NCLBUTTONDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcLButtonUp(UINT nHitTest, CPoint point) +#define MSG_WM_NCLBUTTONUP(func) \ + if (uMsg == WM_NCLBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcLButtonDblClk(UINT nHitTest, CPoint point) +#define MSG_WM_NCLBUTTONDBLCLK(func) \ + if (uMsg == WM_NCLBUTTONDBLCLK) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcRButtonDown(UINT nHitTest, CPoint point) +#define MSG_WM_NCRBUTTONDOWN(func) \ + if (uMsg == WM_NCRBUTTONDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcRButtonUp(UINT nHitTest, CPoint point) +#define MSG_WM_NCRBUTTONUP(func) \ + if (uMsg == WM_NCRBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcRButtonDblClk(UINT nHitTest, CPoint point) +#define MSG_WM_NCRBUTTONDBLCLK(func) \ + if (uMsg == WM_NCRBUTTONDBLCLK) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMButtonDown(UINT nHitTest, CPoint point) +#define MSG_WM_NCMBUTTONDOWN(func) \ + if (uMsg == WM_NCMBUTTONDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMButtonUp(UINT nHitTest, CPoint point) +#define MSG_WM_NCMBUTTONUP(func) \ + if (uMsg == WM_NCMBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMButtonDblClk(UINT nHitTest, CPoint point) +#define MSG_WM_NCMBUTTONDBLCLK(func) \ + if (uMsg == WM_NCMBUTTONDBLCLK) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_KEYDOWN(func) \ + if (uMsg == WM_KEYDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_KEYUP(func) \ + if (uMsg == WM_KEYUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnChar(TCHAR chChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_CHAR(func) \ + if (uMsg == WM_CHAR) \ + { \ + this->SetMsgHandled(TRUE); \ + func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDeadChar(TCHAR chChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_DEADCHAR(func) \ + if (uMsg == WM_DEADCHAR) \ + { \ + this->SetMsgHandled(TRUE); \ + func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_SYSKEYDOWN(func) \ + if (uMsg == WM_SYSKEYDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_SYSKEYUP(func) \ + if (uMsg == WM_SYSKEYUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysChar(TCHAR chChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_SYSCHAR(func) \ + if (uMsg == WM_SYSCHAR) \ + { \ + this->SetMsgHandled(TRUE); \ + func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysDeadChar(TCHAR chChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_SYSDEADCHAR(func) \ + if (uMsg == WM_SYSDEADCHAR) \ + { \ + this->SetMsgHandled(TRUE); \ + func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSysCommand(UINT nID, CPoint point) +#define MSG_WM_SYSCOMMAND(func) \ + if (uMsg == WM_SYSCOMMAND) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnTCard(UINT idAction, DWORD dwActionData) +#define MSG_WM_TCARD(func) \ + if (uMsg == WM_TCARD) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (DWORD)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnTimer(UINT_PTR nIDEvent) +#define MSG_WM_TIMER(func) \ + if (uMsg == WM_TIMER) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT_PTR)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +#define MSG_WM_HSCROLL(func) \ + if (uMsg == WM_HSCROLL) \ + { \ + this->SetMsgHandled(TRUE); \ + func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +#define MSG_WM_VSCROLL(func) \ + if (uMsg == WM_VSCROLL) \ + { \ + this->SetMsgHandled(TRUE); \ + func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInitMenu(CMenuHandle menu) +#define MSG_WM_INITMENU(func) \ + if (uMsg == WM_INITMENU) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HMENU)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInitMenuPopup(CMenuHandle menuPopup, UINT nIndex, BOOL bSysMenu) +#define MSG_WM_INITMENUPOPUP(func) \ + if (uMsg == WM_INITMENUPOPUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HMENU)wParam, (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMenuSelect(UINT nItemID, UINT nFlags, CMenuHandle menu) +#define MSG_WM_MENUSELECT(func) \ + if (uMsg == WM_MENUSELECT) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenuHandle menu) +#define MSG_WM_MENUCHAR(func) \ + if (uMsg == WM_MENUCHAR) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((TCHAR)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotify(int idCtrl, LPNMHDR pnmh) +#define MSG_WM_NOTIFY(func) \ + if (uMsg == WM_NOTIFY) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((int)wParam, (LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEnterIdle(UINT nWhy, CWindow wndWho) +#define MSG_WM_ENTERIDLE(func) \ + if (uMsg == WM_ENTERIDLE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMouseMove(UINT nFlags, CPoint point) +#define MSG_WM_MOUSEMOVE(func) \ + if (uMsg == WM_MOUSEMOVE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) +#define MSG_WM_MOUSEWHEEL(func) \ + if (uMsg == WM_MOUSEWHEEL) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)LOWORD(wParam), (short)HIWORD(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnLButtonDown(UINT nFlags, CPoint point) +#define MSG_WM_LBUTTONDOWN(func) \ + if (uMsg == WM_LBUTTONDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnLButtonUp(UINT nFlags, CPoint point) +#define MSG_WM_LBUTTONUP(func) \ + if (uMsg == WM_LBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnLButtonDblClk(UINT nFlags, CPoint point) +#define MSG_WM_LBUTTONDBLCLK(func) \ + if (uMsg == WM_LBUTTONDBLCLK) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRButtonDown(UINT nFlags, CPoint point) +#define MSG_WM_RBUTTONDOWN(func) \ + if (uMsg == WM_RBUTTONDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRButtonUp(UINT nFlags, CPoint point) +#define MSG_WM_RBUTTONUP(func) \ + if (uMsg == WM_RBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRButtonDblClk(UINT nFlags, CPoint point) +#define MSG_WM_RBUTTONDBLCLK(func) \ + if (uMsg == WM_RBUTTONDBLCLK) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMButtonDown(UINT nFlags, CPoint point) +#define MSG_WM_MBUTTONDOWN(func) \ + if (uMsg == WM_MBUTTONDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMButtonUp(UINT nFlags, CPoint point) +#define MSG_WM_MBUTTONUP(func) \ + if (uMsg == WM_MBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMButtonDblClk(UINT nFlags, CPoint point) +#define MSG_WM_MBUTTONDBLCLK(func) \ + if (uMsg == WM_MBUTTONDBLCLK) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnParentNotify(UINT message, UINT nChildID, LPARAM lParam) +#define MSG_WM_PARENTNOTIFY(func) \ + if (uMsg == WM_PARENTNOTIFY) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMDIActivate(CWindow wndDeactivate, CWindow wndActivate) +#define MSG_WM_MDIACTIVATE(func) \ + if (uMsg == WM_MDIACTIVATE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam, (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRenderFormat(UINT nFormat) +#define MSG_WM_RENDERFORMAT(func) \ + if (uMsg == WM_RENDERFORMAT) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRenderAllFormats() +#define MSG_WM_RENDERALLFORMATS(func) \ + if (uMsg == WM_RENDERALLFORMATS) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDestroyClipboard() +#define MSG_WM_DESTROYCLIPBOARD(func) \ + if (uMsg == WM_DESTROYCLIPBOARD) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDrawClipboard() +#define MSG_WM_DRAWCLIPBOARD(func) \ + if (uMsg == WM_DRAWCLIPBOARD) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaintClipboard(CWindow wndViewer, const LPPAINTSTRUCT lpPaintStruct) +#define MSG_WM_PAINTCLIPBOARD(func) \ + if (uMsg == WM_PAINTCLIPBOARD) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam, (const LPPAINTSTRUCT)::GlobalLock((HGLOBAL)lParam)); \ + ::GlobalUnlock((HGLOBAL)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnVScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos) +#define MSG_WM_VSCROLLCLIPBOARD(func) \ + if (uMsg == WM_VSCROLLCLIPBOARD) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnContextMenu(CWindow wnd, CPoint point) +#define MSG_WM_CONTEXTMENU(func) \ + if (uMsg == WM_CONTEXTMENU) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSizeClipboard(CWindow wndViewer, const LPRECT lpRect) +#define MSG_WM_SIZECLIPBOARD(func) \ + if (uMsg == WM_SIZECLIPBOARD) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam, (const LPRECT)::GlobalLock((HGLOBAL)lParam)); \ + ::GlobalUnlock((HGLOBAL)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnAskCbFormatName(UINT nMaxCount, LPTSTR lpszString) +#define MSG_WM_ASKCBFORMATNAME(func) \ + if (uMsg == WM_ASKCBFORMATNAME) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPTSTR)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnChangeCbChain(CWindow wndRemove, CWindow wndAfter) +#define MSG_WM_CHANGECBCHAIN(func) \ + if (uMsg == WM_CHANGECBCHAIN) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam, (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnHScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos) +#define MSG_WM_HSCROLLCLIPBOARD(func) \ + if (uMsg == WM_HSCROLLCLIPBOARD) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnQueryNewPalette() +#define MSG_WM_QUERYNEWPALETTE(func) \ + if (uMsg == WM_QUERYNEWPALETTE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaletteChanged(CWindow wndFocus) +#define MSG_WM_PALETTECHANGED(func) \ + if (uMsg == WM_PALETTECHANGED) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaletteIsChanging(CWindow wndPalChg) +#define MSG_WM_PALETTEISCHANGING(func) \ + if (uMsg == WM_PALETTEISCHANGING) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDropFiles(HDROP hDropInfo) +#define MSG_WM_DROPFILES(func) \ + if (uMsg == WM_DROPFILES) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HDROP)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnWindowPosChanging(LPWINDOWPOS lpWndPos) +#define MSG_WM_WINDOWPOSCHANGING(func) \ + if (uMsg == WM_WINDOWPOSCHANGING) \ + { \ + this->SetMsgHandled(TRUE); \ + func((LPWINDOWPOS)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnWindowPosChanged(LPWINDOWPOS lpWndPos) +#define MSG_WM_WINDOWPOSCHANGED(func) \ + if (uMsg == WM_WINDOWPOSCHANGED) \ + { \ + this->SetMsgHandled(TRUE); \ + func((LPWINDOWPOS)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnExitMenuLoop(BOOL fIsTrackPopupMenu) +#define MSG_WM_EXITMENULOOP(func) \ + if (uMsg == WM_EXITMENULOOP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEnterMenuLoop(BOOL fIsTrackPopupMenu) +#define MSG_WM_ENTERMENULOOP(func) \ + if (uMsg == WM_ENTERMENULOOP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnStyleChanged(int nStyleType, LPSTYLESTRUCT lpStyleStruct) +#define MSG_WM_STYLECHANGED(func) \ + if (uMsg == WM_STYLECHANGED) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPSTYLESTRUCT)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnStyleChanging(int nStyleType, LPSTYLESTRUCT lpStyleStruct) +#define MSG_WM_STYLECHANGING(func) \ + if (uMsg == WM_STYLECHANGING) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPSTYLESTRUCT)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSizing(UINT fwSide, LPRECT pRect) +#define MSG_WM_SIZING(func) \ + if (uMsg == WM_SIZING) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPRECT)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMoving(UINT fwSide, LPRECT pRect) +#define MSG_WM_MOVING(func) \ + if (uMsg == WM_MOVING) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPRECT)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCaptureChanged(CWindow wnd) +#define MSG_WM_CAPTURECHANGED(func) \ + if (uMsg == WM_CAPTURECHANGED) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnDeviceChange(UINT nEventType, DWORD_PTR dwData) +#define MSG_WM_DEVICECHANGE(func) \ + if (uMsg == WM_DEVICECHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (DWORD_PTR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl) +#define MSG_WM_COMMAND(func) \ + if (uMsg == WM_COMMAND) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDisplayChange(UINT uBitsPerPixel, CSize sizeScreen) +#define MSG_WM_DISPLAYCHANGE(func) \ + if (uMsg == WM_DISPLAYCHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnEnterSizeMove() +#define MSG_WM_ENTERSIZEMOVE(func) \ + if (uMsg == WM_ENTERSIZEMOVE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnExitSizeMove() +#define MSG_WM_EXITSIZEMOVE(func) \ + if (uMsg == WM_EXITSIZEMOVE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HFONT OnGetFont() +#define MSG_WM_GETFONT(func) \ + if (uMsg == WM_GETFONT) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnGetHotKey() +#define MSG_WM_GETHOTKEY(func) \ + if (uMsg == WM_GETHOTKEY) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func(); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HICON OnGetIcon() +#define MSG_WM_GETICON(func) \ + if (uMsg == WM_GETICON) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnGetText(int cchTextMax, LPTSTR lpszText) +#define MSG_WM_GETTEXT(func) \ + if (uMsg == WM_GETTEXT) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((int)wParam, (LPTSTR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnGetTextLength() +#define MSG_WM_GETTEXTLENGTH(func) \ + if (uMsg == WM_GETTEXTLENGTH) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func(); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnHelp(LPHELPINFO lpHelpInfo) +#define MSG_WM_HELP(func) \ + if (uMsg == WM_HELP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((LPHELPINFO)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnHotKey(int nHotKeyID, UINT uModifiers, UINT uVirtKey) +#define MSG_WM_HOTKEY(func) \ + if (uMsg == WM_HOTKEY) \ + { \ + this->SetMsgHandled(TRUE); \ + func((int)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInputLangChange(DWORD dwCharSet, HKL hKbdLayout) +#define MSG_WM_INPUTLANGCHANGE(func) \ + if (uMsg == WM_INPUTLANGCHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((DWORD)wParam, (HKL)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInputLangChangeRequest(BOOL bSysCharSet, HKL hKbdLayout) +#define MSG_WM_INPUTLANGCHANGEREQUEST(func) \ + if (uMsg == WM_INPUTLANGCHANGEREQUEST) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)wParam, (HKL)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNextDlgCtl(BOOL bHandle, WPARAM wCtlFocus) +#define MSG_WM_NEXTDLGCTL(func) \ + if (uMsg == WM_NEXTDLGCTL) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)LOWORD(lParam), wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNextMenu(int nVirtKey, LPMDINEXTMENU lpMdiNextMenu) +#define MSG_WM_NEXTMENU(func) \ + if (uMsg == WM_NEXTMENU) \ + { \ + this->SetMsgHandled(TRUE); \ + func((int)wParam, (LPMDINEXTMENU)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnNotifyFormat(CWindow wndFrom, int nCommand) +#define MSG_WM_NOTIFYFORMAT(func) \ + if (uMsg == WM_NOTIFYFORMAT) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, (int)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnPowerBroadcast(DWORD dwPowerEvent, DWORD_PTR dwData) +#define MSG_WM_POWERBROADCAST(func) \ + if (uMsg == WM_POWERBROADCAST) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((DWORD)wParam, (DWORD_PTR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPrint(CDCHandle dc, UINT uFlags) +#define MSG_WM_PRINT(func) \ + if (uMsg == WM_PRINT) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HDC)wParam, (UINT)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPrintClient(CDCHandle dc, UINT uFlags) +#define MSG_WM_PRINTCLIENT(func) \ + if (uMsg == WM_PRINTCLIENT) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HDC)wParam, (UINT)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnRasDialEvent(RASCONNSTATE rasconnstate, DWORD dwError) +#define MSG_WM_RASDIALEVENT(func) \ + if (uMsg == WM_RASDIALEVENT) \ + { \ + this->SetMsgHandled(TRUE); \ + func((RASCONNSTATE)wParam, (DWORD)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSetFont(CFontHandle font, BOOL bRedraw) +#define MSG_WM_SETFONT(func) \ + if (uMsg == WM_SETFONT) \ + { \ + this->SetMsgHandled(TRUE); \ + func((HFONT)wParam, (BOOL)LOWORD(lParam)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnSetHotKey(int nVirtKey, UINT uFlags) +#define MSG_WM_SETHOTKEY(func) \ + if (uMsg == WM_SETHOTKEY) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((int)LOBYTE(LOWORD(wParam)), (UINT)HIBYTE(LOWORD(wParam))); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HICON OnSetIcon(UINT uType, HICON hIcon) +#define MSG_WM_SETICON(func) \ + if (uMsg == WM_SETICON) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (HICON)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnSetRedraw(BOOL bRedraw) +#define MSG_WM_SETREDRAW(func) \ + if (uMsg == WM_SETREDRAW) \ + { \ + this->SetMsgHandled(TRUE); \ + func((BOOL)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnSetText(LPCTSTR lpstrText) +#define MSG_WM_SETTEXT(func) \ + if (uMsg == WM_SETTEXT) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPCTSTR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUserChanged() +#define MSG_WM_USERCHANGED(func) \ + if (uMsg == WM_USERCHANGED) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Newer Windows messages + +// void OnMouseHover(WPARAM wParam, CPoint ptPos) +#define MSG_WM_MOUSEHOVER(func) \ + if (uMsg == WM_MOUSEHOVER) \ + { \ + this->SetMsgHandled(TRUE); \ + func(wParam, ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMouseLeave() +#define MSG_WM_MOUSELEAVE(func) \ + if (uMsg == WM_MOUSELEAVE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMouseHover(UINT nHitTest, CPoint ptPos) +#define MSG_WM_NCMOUSEHOVER(func) \ + if (uMsg == WM_NCMOUSEHOVER) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, ::CPoint(MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNcMouseLeave() +#define MSG_WM_NCMOUSELEAVE(func) \ + if (uMsg == WM_NCMOUSELEAVE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMenuRButtonUp(WPARAM wParam, CMenuHandle menu) +#define MSG_WM_MENURBUTTONUP(func) \ + if (uMsg == WM_MENURBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func(wParam, (HMENU)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnMenuDrag(WPARAM wParam, CMenuHandle menu) +#define MSG_WM_MENUDRAG(func) \ + if (uMsg == WM_MENUDRAG) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func(wParam, (HMENU)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnMenuGetObject(PMENUGETOBJECTINFO info) +#define MSG_WM_MENUGETOBJECT(func) \ + if (uMsg == WM_MENUGETOBJECT) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((PMENUGETOBJECTINFO)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUnInitMenuPopup(UINT nID, CMenuHandle menu) +#define MSG_WM_UNINITMENUPOPUP(func) \ + if (uMsg == WM_UNINITMENUPOPUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(lParam), (HMENU)wParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnMenuCommand(WPARAM nIndex, CMenuHandle menu) +#define MSG_WM_MENUCOMMAND(func) \ + if (uMsg == WM_MENUCOMMAND) \ + { \ + this->SetMsgHandled(TRUE); \ + func(wParam, (HMENU)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnAppCommand(CWindow wndFocus, short cmd, WORD uDevice, int dwKeys) +#define MSG_WM_APPCOMMAND(func) \ + if (uMsg == WM_APPCOMMAND) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HWND)wParam, GET_APPCOMMAND_LPARAM(lParam), GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam)); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNCXButtonDown(int fwButton, short nHittest, CPoint ptPos) +#define MSG_WM_NCXBUTTONDOWN(func) \ + if (uMsg == WM_NCXBUTTONDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = (LRESULT)TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNCXButtonUp(int fwButton, short nHittest, CPoint ptPos) +#define MSG_WM_NCXBUTTONUP(func) \ + if (uMsg == WM_NCXBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = (LRESULT)TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnNCXButtonDblClk(int fwButton, short nHittest, CPoint ptPos) +#define MSG_WM_NCXBUTTONDBLCLK(func) \ + if (uMsg == WM_NCXBUTTONDBLCLK) \ + { \ + this->SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = (LRESULT)TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnXButtonDown(int fwButton, int dwKeys, CPoint ptPos) +#define MSG_WM_XBUTTONDOWN(func) \ + if (uMsg == WM_XBUTTONDOWN) \ + { \ + this->SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = (LRESULT)TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnXButtonUp(int fwButton, int dwKeys, CPoint ptPos) +#define MSG_WM_XBUTTONUP(func) \ + if (uMsg == WM_XBUTTONUP) \ + { \ + this->SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = (LRESULT)TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnXButtonDblClk(int fwButton, int dwKeys, CPoint ptPos) +#define MSG_WM_XBUTTONDBLCLK(func) \ + if (uMsg == WM_XBUTTONDBLCLK) \ + { \ + this->SetMsgHandled(TRUE); \ + func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + lResult = (LRESULT)TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnChangeUIState(WORD nAction, WORD nState) +#define MSG_WM_CHANGEUISTATE(func) \ + if (uMsg == WM_CHANGEUISTATE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(LOWORD(wParam), HIWORD(wParam)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUpdateUIState(WORD nAction, WORD nState) +#define MSG_WM_UPDATEUISTATE(func) \ + if (uMsg == WM_UPDATEUISTATE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(LOWORD(wParam), HIWORD(wParam)); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnQueryUIState() +#define MSG_WM_QUERYUISTATE(func) \ + if (uMsg == WM_QUERYUISTATE) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func(); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnInput(WPARAM RawInputCode, HRAWINPUT hRawInput) +#define MSG_WM_INPUT(func) \ + if (uMsg == WM_INPUT) \ + { \ + this->SetMsgHandled(TRUE); \ + func(GET_RAWINPUT_CODE_WPARAM(wParam), (HRAWINPUT)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUniChar(TCHAR nChar, UINT nRepCnt, UINT nFlags) +#define MSG_WM_UNICHAR(func) \ + if (uMsg == WM_UNICHAR) \ + { \ + this->SetMsgHandled(TRUE); \ + func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ + if(this->IsMsgHandled()) \ + { \ + lResult = (wParam == UNICODE_NOCHAR) ? TRUE : FALSE; \ + return TRUE; \ + } \ + } + +// void OnWTSSessionChange(WPARAM nStatusCode, DWORD dwSessionID) +#define MSG_WM_WTSSESSION_CHANGE(func) \ + if (uMsg == WM_WTSSESSION_CHANGE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(wParam, (DWORD)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnThemeChanged() +#define MSG_WM_THEMECHANGED(func) \ + if (uMsg == WM_THEMECHANGED) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +#if (_WIN32_WINNT >= 0x0600) + +// BOOL OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt) +#define MSG_WM_MOUSEHWHEEL(func) \ + if (uMsg == WM_MOUSEHWHEEL) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)LOWORD(wParam), (short)HIWORD(wParam), ::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +#endif // (_WIN32_WINNT >= 0x0600) + +#if (WINVER >= 0x0601) + +// void OnGesture(ULONGLONG ullArguments, HGESTUREINFO hGestureInfo) +#define MSG_WM_GESTURE(func) \ + if (uMsg == WM_GESTURE) \ + { \ + this->SetMsgHandled(TRUE); \ + func((ULONGLONG)wParam, (HGESTUREINFO)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnGestureNotify(PGESTURENOTIFYSTRUCT pGestureNotifyStruct) +#define MSG_WM_GESTURENOTIFY(func) \ + if (uMsg == WM_GESTURENOTIFY) \ + { \ + func((PGESTURENOTIFYSTRUCT)lParam); \ + } + +// void OnDpiChanged(UINT nDpiX, UINT nDpiY, PRECT pRect) +#define MSG_WM_DPICHANGED(func) \ + if (uMsg == WM_DPICHANGED) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (PRECT)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +#endif // (WINVER >= 0x0601) + +#if (WINVER >= 0x0605) + +// void OnDpiChangedBeforeParent() +#define MSG_WM_DPICHANGED_BEFOREPARENT(func) \ + if (uMsg == WM_DPICHANGED_BEFOREPARENT) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDpiChangedAfterParent() +#define MSG_WM_DPICHANGED_AFTERPARENT(func) \ + if (uMsg == WM_DPICHANGED_AFTERPARENT) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// BOOL OnGetDpiScaledSize(UINT uDpi, PSIZE pSize) +#define MSG_WM_GETDPISCALEDSIZE(func) \ +if (uMsg == WM_GETDPISCALEDSIZE) \ +{ \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (PSIZE)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ +} + +#endif // (WINVER >= 0x0605) + +/////////////////////////////////////////////////////////////////////////////// +// ATL defined messages + +// BOOL OnForwardMsg(LPMSG Msg, DWORD nUserData) +#define MSG_WM_FORWARDMSG(func) \ + if (uMsg == WM_FORWARDMSG) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((LPMSG)lParam, (DWORD)wParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Dialog specific messages + +// LRESULT OnDMGetDefID() +#define MSG_DM_GETDEFID(func) \ + if (uMsg == DM_GETDEFID) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func(); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDMSetDefID(UINT DefID) +#define MSG_DM_SETDEFID(func) \ + if (uMsg == DM_SETDEFID) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnDMReposition() +#define MSG_DM_REPOSITION(func) \ + if (uMsg == DM_REPOSITION) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Reflected messages + +// void OnReflectedCommand(UINT uNotifyCode, int nID, CWindow wndCtl) +#define MSG_OCM_COMMAND(func) \ + if (uMsg == OCM_COMMAND) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotify(int idCtrl, LPNMHDR pnmh) +#define MSG_OCM_NOTIFY(func) \ + if (uMsg == OCM_NOTIFY) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((int)wParam, (LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedParentNotify(UINT message, UINT nChildID, LPARAM lParam) +#define MSG_OCM_PARENTNOTIFY(func) \ + if (uMsg == OCM_PARENTNOTIFY) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) +#define MSG_OCM_DRAWITEM(func) \ + if (uMsg == OCM_DRAWITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) +#define MSG_OCM_MEASUREITEM(func) \ + if (uMsg == OCM_MEASUREITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnReflectedCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT lpCompareItemStruct) +#define MSG_OCM_COMPAREITEM(func) \ + if (uMsg == OCM_COMPAREITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct) +#define MSG_OCM_DELETEITEM(func) \ + if (uMsg == OCM_DELETEITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// int OnReflectedVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox) +#define MSG_OCM_VKEYTOITEM(func) \ + if (uMsg == OCM_VKEYTOITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +//int OnReflectedCharToItem(UINT nChar, UINT nIndex, CListBox listBox) +#define MSG_OCM_CHARTOITEM(func) \ + if (uMsg == OCM_CHARTOITEM) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +#define MSG_OCM_HSCROLL(func) \ + if (uMsg == OCM_HSCROLL) \ + { \ + this->SetMsgHandled(TRUE); \ + func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +#define MSG_OCM_VSCROLL(func) \ + if (uMsg == OCM_VSCROLL) \ + { \ + this->SetMsgHandled(TRUE); \ + func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorEdit(CDCHandle dc, CEdit edit) +#define MSG_OCM_CTLCOLOREDIT(func) \ + if (uMsg == OCM_CTLCOLOREDIT) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorListBox(CDCHandle dc, CListBox listBox) +#define MSG_OCM_CTLCOLORLISTBOX(func) \ + if (uMsg == OCM_CTLCOLORLISTBOX) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorBtn(CDCHandle dc, CButton button) +#define MSG_OCM_CTLCOLORBTN(func) \ + if (uMsg == OCM_CTLCOLORBTN) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorDlg(CDCHandle dc, CWindow wnd) +#define MSG_OCM_CTLCOLORDLG(func) \ + if (uMsg == OCM_CTLCOLORDLG) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar) +#define MSG_OCM_CTLCOLORSCROLLBAR(func) \ + if (uMsg == OCM_CTLCOLORSCROLLBAR) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// HBRUSH OnReflectedCtlColorStatic(CDCHandle dc, CStatic wndStatic) +#define MSG_OCM_CTLCOLORSTATIC(func) \ + if (uMsg == OCM_CTLCOLORSTATIC) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Edit specific messages + +// void OnClear() +#define MSG_WM_CLEAR(func) \ + if (uMsg == WM_CLEAR) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCopy() +#define MSG_WM_COPY(func) \ + if (uMsg == WM_COPY) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCut() +#define MSG_WM_CUT(func) \ + if (uMsg == WM_CUT) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnPaste() +#define MSG_WM_PASTE(func) \ + if (uMsg == WM_PASTE) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnUndo() +#define MSG_WM_UNDO(func) \ + if (uMsg == WM_UNDO) \ + { \ + this->SetMsgHandled(TRUE); \ + func(); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Generic message handlers + +// LRESULT OnMessageHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam) +#define MESSAGE_HANDLER_EX(msg, func) \ + if(uMsg == msg) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func(uMsg, wParam, lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnMessageRangeHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam) +#define MESSAGE_RANGE_HANDLER_EX(msgFirst, msgLast, func) \ + if((uMsg >= msgFirst) && (uMsg <= msgLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func(uMsg, wParam, lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Commands and notifications + +// void OnCommandHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define COMMAND_HANDLER_EX(id, code, func) \ + if ((uMsg == WM_COMMAND) && (code == HIWORD(wParam)) && (id == LOWORD(wParam))) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define COMMAND_ID_HANDLER_EX(id, func) \ + if ((uMsg == WM_COMMAND) && (id == LOWORD(wParam))) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define COMMAND_CODE_HANDLER_EX(code, func) \ + if ((uMsg == WM_COMMAND) && (code == HIWORD(wParam))) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyHandlerEX(LPNMHDR pnmh) +#define NOTIFY_HANDLER_EX(id, cd, func) \ + if ((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (id == ((LPNMHDR)lParam)->idFrom)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyIDHandlerEX(LPNMHDR pnmh) +#define NOTIFY_ID_HANDLER_EX(id, func) \ + if ((uMsg == WM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyCodeHandlerEX(LPNMHDR pnmh) +#define NOTIFY_CODE_HANDLER_EX(cd, func) \ + if ((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \ + if((uMsg == WM_COMMAND) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, func) \ + if((uMsg == WM_COMMAND) && (code == HIWORD(wParam)) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyRangeHandlerEX(LPNMHDR pnmh) +#define NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \ + if((uMsg == WM_NOTIFY) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnNotifyRangeCodeHandlerEX(LPNMHDR pnmh) +#define NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \ + if((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedCommandHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define REFLECTED_COMMAND_HANDLER_EX(id, code, func) \ + if ((uMsg == OCM_COMMAND) && (code == HIWORD(wParam)) && (id == LOWORD(wParam))) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define REFLECTED_COMMAND_ID_HANDLER_EX(id, func) \ + if ((uMsg == OCM_COMMAND) && (id == LOWORD(wParam))) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define REFLECTED_COMMAND_CODE_HANDLER_EX(code, func) \ + if ((uMsg == OCM_COMMAND) && (code == HIWORD(wParam))) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyHandlerEX(LPNMHDR pnmh) +#define REFLECTED_NOTIFY_HANDLER_EX(id, cd, func) \ + if ((uMsg == OCM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (id == ((LPNMHDR)lParam)->idFrom)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyIDHandlerEX(LPNMHDR pnmh) +#define REFLECTED_NOTIFY_ID_HANDLER_EX(id, func) \ + if ((uMsg == OCM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyCodeHandlerEX(LPNMHDR pnmh) +#define REFLECTED_NOTIFY_CODE_HANDLER_EX(cd, func) \ + if ((uMsg == OCM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define REFLECTED_COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \ + if((uMsg == OCM_COMMAND) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnReflectedCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) +#define REFLECTED_COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, func) \ + if((uMsg == OCM_COMMAND) && (code == HIWORD(wParam)) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ + lResult = 0; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyRangeHandlerEX(LPNMHDR pnmh) +#define REFLECTED_NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \ + if((uMsg == OCM_NOTIFY) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// LRESULT OnReflectedNotifyRangeCodeHandlerEX(LPNMHDR pnmh) +#define REFLECTED_NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \ + if((uMsg == OCM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \ + { \ + this->SetMsgHandled(TRUE); \ + lResult = func((LPNMHDR)lParam); \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +// void OnAppCommandHandler(UINT uDevice, DWORD dwKeys, CWindow wndFocus) +#define APPCOMMAND_HANDLER_EX(cmd, func) \ + if((uMsg == WM_APPCOMMAND) && (cmd == GET_APPCOMMAND_LPARAM(lParam))) \ + { \ + this->SetMsgHandled(TRUE); \ + func(GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam), (HWND)wParam); \ + lResult = TRUE; \ + if(this->IsMsgHandled()) \ + return TRUE; \ + } + +#endif // __ATLCRACK_H__ diff --git a/Examples/WhisperDesktop/Utils/WTL/atlctrls.h b/Examples/WhisperDesktop/Utils/WTL/atlctrls.h new file mode 100644 index 0000000..61df427 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/WTL/atlctrls.h @@ -0,0 +1,9764 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLCTRLS_H__ +#define __ATLCTRLS_H__ + +#pragma once + +#ifndef __ATLAPP_H__ + #error atlctrls.h requires atlapp.h to be included first +#endif + +#ifndef __ATLWIN_H__ + #error atlctrls.h requires atlwin.h to be included first +#endif + +#include <richedit.h> +#include <richole.h> + +#if (_RICHEDIT_VER < 0x0300) + #error WTL10 requires _RICHEDIT_VER >= 0x0300 +#endif + +// protect template members from windowsx.h macros +#ifdef _INC_WINDOWSX + #undef GetNextSibling + #undef GetPrevSibling +#endif // _INC_WINDOWSX + + +/////////////////////////////////////////////////////////////////////////////// +// Classes in this file: +// +// CStaticT<TBase> - CStatic +// CButtonT<TBase> - CButton +// CListBoxT<TBase> - CListBox +// CComboBoxT<TBase> - CComboBox +// CEditT<TBase> - CEdit +// CEditCommands<T> +// CScrollBarT<TBase> - CScrollBar +// +// CImageListT<t_bManaged> - CImageList, CImageListManaged +// CListViewCtrlT<TBase> - CListViewCtrl +// CTreeViewCtrlT<TBase> - CTreeViewCtrl +// CTreeItemT<TBase> - CTreeItem +// CTreeViewCtrlExT<TBase> - CTreeViewCtrlEx +// CHeaderCtrlT<TBase> - CHeaderCtrl +// CToolBarCtrlT<TBase> - CToolBarCtrl +// CStatusBarCtrlT<TBase> - CStatusBarCtrl +// CTabCtrlT<TBase> - CTabCtrl +// CToolInfo +// CToolTipCtrlT<TBase> - CToolTipCtrl +// CTrackBarCtrlT<TBase> - CTrackBarCtrl +// CUpDownCtrlT<TBase> - CUpDownCtrl +// CProgressBarCtrlT<TBase> - CProgressBarCtrl +// CHotKeyCtrlT<TBase> - CHotKeyCtrl +// CAnimateCtrlT<TBase> - CAnimateCtrl +// CRichEditCtrlT<TBase> - CRichEditCtrl +// CRichEditCommands<T> +// CDragListBoxT<TBase> - CDragListBox +// CDragListNotifyImpl<T> +// CReBarCtrlT<TBase> - CReBarCtrl +// CComboBoxExT<TBase> - CComboBoxEx +// CDateTimePickerCtrlT<TBase> - CDateTimePickerCtrl +// CMonthCalendarCtrlT<TBase> - CMonthCalendarCtrl +// CFlatScrollBarImpl<T> +// CFlatScrollBarT<TBase> - CFlatScrollBar +// CIPAddressCtrlT<TBase> - CIPAddressCtrl +// CPagerCtrlT<TBase> - CPagerCtrl +// CLinkCtrlT<TBase> - CLinkCtrl +// +// CCustomDraw<T> + + +namespace WTL +{ + +// These are wrapper classes for Windows standard and common controls. +// To implement a window based on a control, use following: +// Example: Implementing a window based on a list box +// +// class CMyListBox : CWindowImpl<CMyListBox, CListBox> +// { +// public: +// BEGIN_MSG_MAP(CMyListBox) +// // put your message handler entries here +// END_MSG_MAP() +// }; + + + +// --- Standard Windows controls --- + +/////////////////////////////////////////////////////////////////////////////// +// CStatic - client side for a Windows STATIC control + +template <class TBase> +class CStaticT : public TBase +{ +public: +// Constructors + CStaticT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CStaticT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return _T("STATIC"); + } + + HICON GetIcon() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HICON)::SendMessage(this->m_hWnd, STM_GETICON, 0, 0L); + } + + HICON SetIcon(HICON hIcon) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HICON)::SendMessage(this->m_hWnd, STM_SETICON, (WPARAM)hIcon, 0L); + } + + HENHMETAFILE GetEnhMetaFile() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HENHMETAFILE)::SendMessage(this->m_hWnd, STM_GETIMAGE, IMAGE_ENHMETAFILE, 0L); + } + + HENHMETAFILE SetEnhMetaFile(HENHMETAFILE hMetaFile) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HENHMETAFILE)::SendMessage(this->m_hWnd, STM_SETIMAGE, IMAGE_ENHMETAFILE, (LPARAM)hMetaFile); + } + + CBitmapHandle GetBitmap() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CBitmapHandle((HBITMAP)::SendMessage(this->m_hWnd, STM_GETIMAGE, IMAGE_BITMAP, 0L)); + } + + CBitmapHandle SetBitmap(HBITMAP hBitmap) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CBitmapHandle((HBITMAP)::SendMessage(this->m_hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap)); + } + + HCURSOR GetCursor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HCURSOR)::SendMessage(this->m_hWnd, STM_GETIMAGE, IMAGE_CURSOR, 0L); + } + + HCURSOR SetCursor(HCURSOR hCursor) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HCURSOR)::SendMessage(this->m_hWnd, STM_SETIMAGE, IMAGE_CURSOR, (LPARAM)hCursor); + } +}; + +typedef CStaticT<ATL::CWindow> CStatic; + + +/////////////////////////////////////////////////////////////////////////////// +// CButton - client side for a Windows BUTTON control + +template <class TBase> +class CButtonT : public TBase +{ +public: +// Constructors + CButtonT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CButtonT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return _T("BUTTON"); + } + + UINT GetState() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, BM_GETSTATE, 0, 0L); + } + + void SetState(BOOL bHighlight) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, BM_SETSTATE, bHighlight, 0L); + } + + int GetCheck() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, BM_GETCHECK, 0, 0L); + } + + void SetCheck(int nCheck) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, BM_SETCHECK, nCheck, 0L); + } + + UINT GetButtonStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::GetWindowLong(this->m_hWnd, GWL_STYLE) & 0xFFFF; + } + + void SetButtonStyle(UINT nStyle, BOOL bRedraw = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, BM_SETSTYLE, nStyle, (LPARAM)bRedraw); + } + + HICON GetIcon() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HICON)::SendMessage(this->m_hWnd, BM_GETIMAGE, IMAGE_ICON, 0L); + } + + HICON SetIcon(HICON hIcon) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HICON)::SendMessage(this->m_hWnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); + } + + CBitmapHandle GetBitmap() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CBitmapHandle((HBITMAP)::SendMessage(this->m_hWnd, BM_GETIMAGE, IMAGE_BITMAP, 0L)); + } + + CBitmapHandle SetBitmap(HBITMAP hBitmap) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CBitmapHandle((HBITMAP)::SendMessage(this->m_hWnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap)); + } + + BOOL GetIdealSize(LPSIZE lpSize) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, BCM_GETIDEALSIZE, 0, (LPARAM)lpSize); + } + + BOOL GetImageList(PBUTTON_IMAGELIST pButtonImagelist) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, BCM_GETIMAGELIST, 0, (LPARAM)pButtonImagelist); + } + + BOOL SetImageList(PBUTTON_IMAGELIST pButtonImagelist) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, BCM_SETIMAGELIST, 0, (LPARAM)pButtonImagelist); + } + + BOOL GetTextMargin(LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, BCM_GETTEXTMARGIN, 0, (LPARAM)lpRect); + } + + BOOL SetTextMargin(LPRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, BCM_SETTEXTMARGIN, 0, (LPARAM)lpRect); + } + +#if (WINVER >= 0x0600) + void SetDontClick(BOOL bDontClick) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, BM_SETDONTCLICK, (WPARAM)bDontClick, 0L); + } +#endif // (WINVER >= 0x0600) + +#if (_WIN32_WINNT >= 0x0600) + BOOL SetDropDownState(BOOL bDropDown) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0); + return (BOOL)::SendMessage(this->m_hWnd, BCM_SETDROPDOWNSTATE, (WPARAM)bDropDown, 0L); + } + + BOOL GetSplitInfo(PBUTTON_SPLITINFO pSplitInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0); + return (BOOL)::SendMessage(this->m_hWnd, BCM_GETSPLITINFO, 0, (LPARAM)pSplitInfo); + } + + BOOL SetSplitInfo(PBUTTON_SPLITINFO pSplitInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0); + return (BOOL)::SendMessage(this->m_hWnd, BCM_SETSPLITINFO, 0, (LPARAM)pSplitInfo); + } + + int GetNoteLength() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0); + return (int)::SendMessage(this->m_hWnd, BCM_GETNOTELENGTH, 0, 0L); + } + + BOOL GetNote(LPWSTR lpstrNoteText, int cchNoteText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0); + return (BOOL)::SendMessage(this->m_hWnd, BCM_GETNOTE, cchNoteText, (LPARAM)lpstrNoteText); + } + + BOOL SetNote(LPCWSTR lpstrNoteText) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0); + return (BOOL)::SendMessage(this->m_hWnd, BCM_SETNOTE, 0, (LPARAM)lpstrNoteText); + } + + LRESULT SetElevationRequiredState(BOOL bSet) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SendMessage(this->m_hWnd, BCM_SETSHIELD, 0, (LPARAM)bSet); + } +#endif // (_WIN32_WINNT >= 0x0600) + +// Operations + void Click() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, BM_CLICK, 0, 0L); + } +}; + +typedef CButtonT<ATL::CWindow> CButton; + + +/////////////////////////////////////////////////////////////////////////////// +// CListBox - client side for a Windows LISTBOX control + +template <class TBase> +class CListBoxT : public TBase +{ +public: +// Constructors + CListBoxT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CListBoxT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return _T("LISTBOX"); + } + + // for entire listbox + int GetCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETCOUNT, 0, 0L); + } + + int SetCount(int cItems) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(((this->GetStyle() & LBS_NODATA) != 0) && ((this->GetStyle() & LBS_HASSTRINGS) == 0)); + return (int)::SendMessage(this->m_hWnd, LB_SETCOUNT, cItems, 0L); + } + + int GetHorizontalExtent() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETHORIZONTALEXTENT, 0, 0L); + } + + void SetHorizontalExtent(int cxExtent) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LB_SETHORIZONTALEXTENT, cxExtent, 0L); + } + + int GetTopIndex() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETTOPINDEX, 0, 0L); + } + + int SetTopIndex(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_SETTOPINDEX, nIndex, 0L); + } + + LCID GetLocale() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LCID)::SendMessage(this->m_hWnd, LB_GETLOCALE, 0, 0L); + } + + LCID SetLocale(LCID nNewLocale) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LCID)::SendMessage(this->m_hWnd, LB_SETLOCALE, (WPARAM)nNewLocale, 0L); + } + + DWORD GetListBoxInfo() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, LB_GETLISTBOXINFO, 0, 0L); + } + + // for single-selection listboxes + int GetCurSel() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0); + return (int)::SendMessage(this->m_hWnd, LB_GETCURSEL, 0, 0L); + } + + int SetCurSel(int nSelect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0); + return (int)::SendMessage(this->m_hWnd, LB_SETCURSEL, nSelect, 0L); + } + + // for multiple-selection listboxes + int GetSel(int nIndex) const // also works for single-selection + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETSEL, nIndex, 0L); + } + + int SetSel(int nIndex, BOOL bSelect = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); + return (int)::SendMessage(this->m_hWnd, LB_SETSEL, bSelect, nIndex); + } + + int GetSelCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); + return (int)::SendMessage(this->m_hWnd, LB_GETSELCOUNT, 0, 0L); + } + + int GetSelItems(int nMaxItems, LPINT rgIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); + return (int)::SendMessage(this->m_hWnd, LB_GETSELITEMS, nMaxItems, (LPARAM)rgIndex); + } + + int GetAnchorIndex() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); + return (int)::SendMessage(this->m_hWnd, LB_GETANCHORINDEX, 0, 0L); + } + + void SetAnchorIndex(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); + ::SendMessage(this->m_hWnd, LB_SETANCHORINDEX, nIndex, 0L); + } + + int GetCaretIndex() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETCARETINDEX, 0, 0); + } + + int SetCaretIndex(int nIndex, BOOL bScroll = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_SETCARETINDEX, nIndex, MAKELONG(bScroll, 0)); + } + + // for listbox items + DWORD_PTR GetItemData(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD_PTR)::SendMessage(this->m_hWnd, LB_GETITEMDATA, nIndex, 0L); + } + + int SetItemData(int nIndex, DWORD_PTR dwItemData) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_SETITEMDATA, nIndex, (LPARAM)dwItemData); + } + + void* GetItemDataPtr(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (void*)::SendMessage(this->m_hWnd, LB_GETITEMDATA, nIndex, 0L); + } + + int SetItemDataPtr(int nIndex, void* pData) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItemData(nIndex, (DWORD_PTR)pData); + } + + int GetItemRect(int nIndex, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETITEMRECT, nIndex, (LPARAM)lpRect); + } + + int GetText(int nIndex, LPTSTR lpszBuffer) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETTEXT, nIndex, (LPARAM)lpszBuffer); + } + +#ifdef _OLEAUTO_H_ + BOOL GetTextBSTR(int nIndex, BSTR& bstrText) const + { + USES_CONVERSION; + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(bstrText == NULL); + + int nLen = GetTextLen(nIndex); + if(nLen == LB_ERR) + return FALSE; + + ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff; + LPTSTR lpstrText = buff.Allocate(nLen + 1); + if(lpstrText == NULL) + return FALSE; + + if(GetText(nIndex, lpstrText) == LB_ERR) + return FALSE; + + bstrText = ::SysAllocString(T2OLE(lpstrText)); + return (bstrText != NULL) ? TRUE : FALSE; + } +#endif // _OLEAUTO_H_ + +#ifdef __ATLSTR_H__ + int GetText(int nIndex, ATL::CString& strText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + int cchLen = GetTextLen(nIndex); + if(cchLen == LB_ERR) + return LB_ERR; + int nRet = LB_ERR; + LPTSTR lpstr = strText.GetBufferSetLength(cchLen); + if(lpstr != NULL) + { + nRet = GetText(nIndex, lpstr); + strText.ReleaseBuffer(); + } + return nRet; + } +#endif // __ATLSTR_H__ + + int GetTextLen(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETTEXTLEN, nIndex, 0L); + } + + int GetItemHeight(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_GETITEMHEIGHT, nIndex, 0L); + } + + int SetItemHeight(int nIndex, UINT cyItemHeight) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_SETITEMHEIGHT, nIndex, MAKELONG(cyItemHeight, 0)); + } + + // Settable only attributes + void SetColumnWidth(int cxWidth) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LB_SETCOLUMNWIDTH, cxWidth, 0L); + } + + BOOL SetTabStops(int nTabStops, LPINT rgTabStops) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & LBS_USETABSTOPS) != 0); + return (BOOL)::SendMessage(this->m_hWnd, LB_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops); + } + + BOOL SetTabStops() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & LBS_USETABSTOPS) != 0); + return (BOOL)::SendMessage(this->m_hWnd, LB_SETTABSTOPS, 0, 0L); + } + + BOOL SetTabStops(const int& cxEachStop) // takes an 'int' + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & LBS_USETABSTOPS) != 0); + return (BOOL)::SendMessage(this->m_hWnd, LB_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop); + } + +// Operations + int InitStorage(int nItems, UINT nBytes) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_INITSTORAGE, (WPARAM)nItems, nBytes); + } + + void ResetContent() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LB_RESETCONTENT, 0, 0L); + } + + UINT ItemFromPoint(POINT pt, BOOL& bOutside) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dw = (DWORD)::SendMessage(this->m_hWnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)); + bOutside = (BOOL)HIWORD(dw); + return (UINT)LOWORD(dw); + } + + // manipulating listbox items + int AddString(LPCTSTR lpszItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_ADDSTRING, 0, (LPARAM)lpszItem); + } + + int DeleteString(UINT nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_DELETESTRING, nIndex, 0L); + } + + int InsertString(int nIndex, LPCTSTR lpszItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_INSERTSTRING, nIndex, (LPARAM)lpszItem); + } + + int Dir(UINT attr, LPCTSTR lpszWildCard) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_DIR, attr, (LPARAM)lpszWildCard); + } + + int AddFile(LPCTSTR lpstrFileName) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_ADDFILE, 0, (LPARAM)lpstrFileName); + } + + // selection helpers + int FindString(int nStartAfter, LPCTSTR lpszItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_FINDSTRING, nStartAfter, (LPARAM)lpszItem); + } + + int FindStringExact(int nIndexStart, LPCTSTR lpszFind) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_FINDSTRINGEXACT, nIndexStart, (LPARAM)lpszFind); + } + + int SelectString(int nStartAfter, LPCTSTR lpszItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LB_SELECTSTRING, nStartAfter, (LPARAM)lpszItem); + } + + int SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); + ATLASSERT(nFirstItem <= nLastItem); + return bSelect ? (int)::SendMessage(this->m_hWnd, LB_SELITEMRANGEEX, nFirstItem, nLastItem) : (int)::SendMessage(this->m_hWnd, LB_SELITEMRANGEEX, nLastItem, nFirstItem); + } +}; + +typedef CListBoxT<ATL::CWindow> CListBox; + + +/////////////////////////////////////////////////////////////////////////////// +// CComboBox - client side for a Windows COMBOBOX control + +template <class TBase> +class CComboBoxT : public TBase +{ +public: +// Constructors + CComboBoxT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CComboBoxT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return _T("COMBOBOX"); + } + + // for entire combo box + int GetCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_GETCOUNT, 0, 0L); + } + + int GetCurSel() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_GETCURSEL, 0, 0L); + } + + int SetCurSel(int nSelect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_SETCURSEL, nSelect, 0L); + } + + LCID GetLocale() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LCID)::SendMessage(this->m_hWnd, CB_GETLOCALE, 0, 0L); + } + + LCID SetLocale(LCID nNewLocale) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LCID)::SendMessage(this->m_hWnd, CB_SETLOCALE, (WPARAM)nNewLocale, 0L); + } + + int GetTopIndex() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_GETTOPINDEX, 0, 0L); + } + + int SetTopIndex(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_SETTOPINDEX, nIndex, 0L); + } + + UINT GetHorizontalExtent() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, CB_GETHORIZONTALEXTENT, 0, 0L); + } + + void SetHorizontalExtent(UINT nExtent) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, CB_SETHORIZONTALEXTENT, nExtent, 0L); + } + + int GetDroppedWidth() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_GETDROPPEDWIDTH, 0, 0L); + } + + int SetDroppedWidth(UINT nWidth) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_SETDROPPEDWIDTH, nWidth, 0L); + } + + BOOL GetComboBoxInfo(PCOMBOBOXINFO pComboBoxInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CB_GETCOMBOBOXINFO, 0, (LPARAM)pComboBoxInfo); + } + + // for edit control + DWORD GetEditSel() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, CB_GETEDITSEL, 0, 0L); + } + + BOOL SetEditSel(int nStartChar, int nEndChar) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CB_SETEDITSEL, 0, MAKELONG(nStartChar, nEndChar)); + } + + // for combobox item + DWORD_PTR GetItemData(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD_PTR)::SendMessage(this->m_hWnd, CB_GETITEMDATA, nIndex, 0L); + } + + int SetItemData(int nIndex, DWORD_PTR dwItemData) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_SETITEMDATA, nIndex, (LPARAM)dwItemData); + } + + void* GetItemDataPtr(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (void*)GetItemData(nIndex); + } + + int SetItemDataPtr(int nIndex, void* pData) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItemData(nIndex, (DWORD_PTR)pData); + } + + int GetLBText(int nIndex, LPTSTR lpszText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_GETLBTEXT, nIndex, (LPARAM)lpszText); + } + + BOOL GetLBTextBSTR(int nIndex, BSTR& bstrText) const + { + USES_CONVERSION; + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(bstrText == NULL); + + int nLen = GetLBTextLen(nIndex); + if(nLen == CB_ERR) + return FALSE; + + ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff; + LPTSTR lpstrText = buff.Allocate(nLen + 1); + if(lpstrText == NULL) + return FALSE; + + if(GetLBText(nIndex, lpstrText) == CB_ERR) + return FALSE; + + bstrText = ::SysAllocString(T2OLE(lpstrText)); + return (bstrText != NULL) ? TRUE : FALSE; + } + +#ifdef __ATLSTR_H__ + int GetLBText(int nIndex, ATL::CString& strText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + int cchLen = GetLBTextLen(nIndex); + if(cchLen == CB_ERR) + return CB_ERR; + int nRet = CB_ERR; + LPTSTR lpstr = strText.GetBufferSetLength(cchLen); + if(lpstr != NULL) + { + nRet = GetLBText(nIndex, lpstr); + strText.ReleaseBuffer(); + } + return nRet; + } +#endif // __ATLSTR_H__ + + int GetLBTextLen(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_GETLBTEXTLEN, nIndex, 0L); + } + + int GetItemHeight(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_GETITEMHEIGHT, nIndex, 0L); + } + + int SetItemHeight(int nIndex, UINT cyItemHeight) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_SETITEMHEIGHT, nIndex, MAKELONG(cyItemHeight, 0)); + } + + BOOL GetExtendedUI() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CB_GETEXTENDEDUI, 0, 0L); + } + + int SetExtendedUI(BOOL bExtended = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_SETEXTENDEDUI, bExtended, 0L); + } + + void GetDroppedControlRect(LPRECT lprect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)lprect); + } + + BOOL GetDroppedState() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CB_GETDROPPEDSTATE, 0, 0L); + } + + int GetMinVisible() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_GETMINVISIBLE, 0, 0L); + } + + BOOL SetMinVisible(int nMinVisible) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CB_SETMINVISIBLE, nMinVisible, 0L); + } + + // Vista only + BOOL GetCueBannerText(LPWSTR lpwText, int cchText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CB_GETCUEBANNER, (WPARAM)lpwText, cchText); + } + + // Vista only + BOOL SetCueBannerText(LPCWSTR lpcwText) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CB_SETCUEBANNER, 0, (LPARAM)lpcwText); + } + +// Operations + int InitStorage(int nItems, UINT nBytes) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_INITSTORAGE, (WPARAM)nItems, nBytes); + } + + void ResetContent() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, CB_RESETCONTENT, 0, 0L); + } + + // for edit control + BOOL LimitText(int nMaxChars) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CB_LIMITTEXT, nMaxChars, 0L); + } + + // for drop-down combo boxes + void ShowDropDown(BOOL bShowIt = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, CB_SHOWDROPDOWN, bShowIt, 0L); + } + + // manipulating listbox items + int AddString(LPCTSTR lpszString) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_ADDSTRING, 0, (LPARAM)lpszString); + } + + int DeleteString(UINT nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_DELETESTRING, nIndex, 0L); + } + + int InsertString(int nIndex, LPCTSTR lpszString) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_INSERTSTRING, nIndex, (LPARAM)lpszString); + } + + int Dir(UINT attr, LPCTSTR lpszWildCard) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_DIR, attr, (LPARAM)lpszWildCard); + } + + // selection helpers + int FindString(int nStartAfter, LPCTSTR lpszString) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_FINDSTRING, nStartAfter, (LPARAM)lpszString); + } + + int FindStringExact(int nIndexStart, LPCTSTR lpszFind) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_FINDSTRINGEXACT, nIndexStart, (LPARAM)lpszFind); + } + + int SelectString(int nStartAfter, LPCTSTR lpszString) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CB_SELECTSTRING, nStartAfter, (LPARAM)lpszString); + } + + // Clipboard operations + void Clear() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_CLEAR, 0, 0L); + } + + void Copy() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_COPY, 0, 0L); + } + + void Cut() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_CUT, 0, 0L); + } + + void Paste() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_PASTE, 0, 0L); + } +}; + +typedef CComboBoxT<ATL::CWindow> CComboBox; + + +/////////////////////////////////////////////////////////////////////////////// +// CEdit - client side for a Windows EDIT control + +template <class TBase> +class CEditT : public TBase +{ +public: +// Constructors + CEditT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CEditT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return _T("EDIT"); + } + + BOOL CanUndo() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_CANUNDO, 0, 0L); + } + + int GetLineCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETLINECOUNT, 0, 0L); + } + + BOOL GetModify() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_GETMODIFY, 0, 0L); + } + + void SetModify(BOOL bModified = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETMODIFY, bModified, 0L); + } + + void GetRect(LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_GETRECT, 0, (LPARAM)lpRect); + } + + DWORD GetSel() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETSEL, 0, 0L); + } + + void GetSel(int& nStartChar, int& nEndChar) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); + } + + HLOCAL GetHandle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HLOCAL)::SendMessage(this->m_hWnd, EM_GETHANDLE, 0, 0L); + } + + void SetHandle(HLOCAL hBuffer) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETHANDLE, (WPARAM)hBuffer, 0L); + } + + DWORD GetMargins() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETMARGINS, 0, 0L); + } + + void GetMargins(UINT& nLeft, UINT& nRight) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, EM_GETMARGINS, 0, 0L); + nLeft = LOWORD(dwRet); + nRight = HIWORD(dwRet); + } + + void SetMargins(UINT nLeft, UINT nRight, WORD wFlags = EC_LEFTMARGIN | EC_RIGHTMARGIN) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETMARGINS, wFlags, MAKELONG(nLeft, nRight)); + } + + UINT GetLimitText() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, EM_GETLIMITTEXT, 0, 0L); + } + + void SetLimitText(UINT nMax) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETLIMITTEXT, nMax, 0L); + } + + POINT PosFromChar(UINT nChar) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, EM_POSFROMCHAR, nChar, 0); + POINT point = { GET_X_LPARAM(dwRet), GET_Y_LPARAM(dwRet) }; + return point; + } + + int CharFromPos(POINT pt, int* pLine = NULL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y)); + if(pLine != NULL) + *pLine = (int)(short)HIWORD(dwRet); + return (int)(short)LOWORD(dwRet); + } + + // NOTE: first word in lpszBuffer must contain the size of the buffer! + int GetLine(int nIndex, LPTSTR lpszBuffer) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); + } + + int GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + *(LPWORD)lpszBuffer = (WORD)nMaxLength; + return (int)::SendMessage(this->m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); + } + + TCHAR GetPasswordChar() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (TCHAR)::SendMessage(this->m_hWnd, EM_GETPASSWORDCHAR, 0, 0L); + } + + void SetPasswordChar(TCHAR ch) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETPASSWORDCHAR, ch, 0L); + } + + EDITWORDBREAKPROC GetWordBreakProc() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (EDITWORDBREAKPROC)::SendMessage(this->m_hWnd, EM_GETWORDBREAKPROC, 0, 0L); + } + + void SetWordBreakProc(EDITWORDBREAKPROC ewbprc) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETWORDBREAKPROC, 0, (LPARAM)ewbprc); + } + + int GetFirstVisibleLine() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETFIRSTVISIBLELINE, 0, 0L); + } + + int GetThumb() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & ES_MULTILINE) != 0); + return (int)::SendMessage(this->m_hWnd, EM_GETTHUMB, 0, 0L); + } + + BOOL SetReadOnly(BOOL bReadOnly = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETREADONLY, bReadOnly, 0L); + } + + UINT GetImeStatus(UINT uStatus) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, EM_GETIMESTATUS, uStatus, 0L); + } + + UINT SetImeStatus(UINT uStatus, UINT uData) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, EM_SETIMESTATUS, uStatus, uData); + } + + BOOL GetCueBannerText(LPCWSTR lpstrText, int cchText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_GETCUEBANNER, (WPARAM)lpstrText, cchText); + } + + // bKeepWithFocus - Vista only + BOOL SetCueBannerText(LPCWSTR lpstrText, BOOL bKeepWithFocus = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCUEBANNER, (WPARAM)bKeepWithFocus, (LPARAM)(lpstrText)); + } + +// Operations + void EmptyUndoBuffer() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_EMPTYUNDOBUFFER, 0, 0L); + } + + BOOL FmtLines(BOOL bAddEOL) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_FMTLINES, bAddEOL, 0L); + } + + void LimitText(int nChars = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_LIMITTEXT, nChars, 0L); + } + + int LineFromChar(int nIndex = -1) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_LINEFROMCHAR, nIndex, 0L); + } + + int LineIndex(int nLine = -1) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_LINEINDEX, nLine, 0L); + } + + int LineLength(int nLine = -1) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_LINELENGTH, nLine, 0L); + } + + void LineScroll(int nLines, int nChars = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_LINESCROLL, nChars, nLines); + } + + void ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText); + } + + void SetRect(LPCRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETRECT, 0, (LPARAM)lpRect); + } + + void SetRectNP(LPCRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETRECTNP, 0, (LPARAM)lpRect); + } + + void SetSel(DWORD dwSelection, BOOL bNoScroll = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETSEL, LOWORD(dwSelection), HIWORD(dwSelection)); + if(!bNoScroll) + ::SendMessage(this->m_hWnd, EM_SCROLLCARET, 0, 0L); + } + + void SetSel(int nStartChar, int nEndChar, BOOL bNoScroll = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETSEL, nStartChar, nEndChar); + if(!bNoScroll) + ::SendMessage(this->m_hWnd, EM_SCROLLCARET, 0, 0L); + } + + void SetSelAll(BOOL bNoScroll = FALSE) + { + SetSel(0, -1, bNoScroll); + } + + void SetSelNone(BOOL bNoScroll = FALSE) + { + SetSel(-1, 0, bNoScroll); + } + + BOOL SetTabStops(int nTabStops, LPINT rgTabStops) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops); + } + + BOOL SetTabStops() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, 0, 0L); + } + + BOOL SetTabStops(const int& cxEachStop) // takes an 'int' + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop); + } + + void ScrollCaret() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SCROLLCARET, 0, 0L); + } + + int Scroll(int nScrollAction) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & ES_MULTILINE) != 0); + LRESULT lRet = ::SendMessage(this->m_hWnd, EM_SCROLL, nScrollAction, 0L); + if(!(BOOL)HIWORD(lRet)) + return -1; // failed + return (int)(short)LOWORD(lRet); + + } + + void InsertText(int nInsertAfterChar, LPCTSTR lpstrText, BOOL bNoScroll = FALSE, BOOL bCanUndo = FALSE) + { + SetSel(nInsertAfterChar, nInsertAfterChar, bNoScroll); + ReplaceSel(lpstrText, bCanUndo); + } + + void AppendText(LPCTSTR lpstrText, BOOL bNoScroll = FALSE, BOOL bCanUndo = FALSE) + { + InsertText(this->GetWindowTextLength(), lpstrText, bNoScroll, bCanUndo); + } + + BOOL ShowBalloonTip(PEDITBALLOONTIP pEditBaloonTip) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SHOWBALLOONTIP, 0, (LPARAM)pEditBaloonTip); + } + + BOOL HideBalloonTip() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_HIDEBALLOONTIP, 0, 0L); + } + +#if (_WIN32_WINNT >= 0x0600) + DWORD GetHilite() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETHILITE, 0, 0L); + } + + void GetHilite(int& nStartChar, int& nEndChar) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, EM_GETHILITE, 0, 0L); + nStartChar = (int)(short)LOWORD(dwRet); + nEndChar = (int)(short)HIWORD(dwRet); + } + + void SetHilite(int nStartChar, int nEndChar) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETHILITE, nStartChar, nEndChar); + } +#endif // (_WIN32_WINNT >= 0x0600) + + // Clipboard operations + BOOL Undo() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_UNDO, 0, 0L); + } + + void Clear() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_CLEAR, 0, 0L); + } + + void Copy() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_COPY, 0, 0L); + } + + void Cut() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_CUT, 0, 0L); + } + + void Paste() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_PASTE, 0, 0L); + } + + // New messages added in Windows 10.0.17763 +#if defined(NTDDI_VERSION) && defined(NTDDI_WIN10_RS5) && (NTDDI_VERSION >= NTDDI_WIN10_RS5) + DWORD SetExtendedStyle(DWORD dwStyle, DWORD dwMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SendMessage(this->m_hWnd, EM_SETEXTENDEDSTYLE, dwMask, dwStyle); + } + + DWORD GetExtendedStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SendMessage(this->m_hWnd, EM_GETEXTENDEDSTYLE, 0, 0L); + } + + BOOL SetEndOfLine(EC_ENDOFLINE eolType) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETENDOFLINE, eolType, 0L); + } + + EC_ENDOFLINE GetEndOfLine() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (EC_ENDOFLINE)::SendMessage(this->m_hWnd, EM_GETENDOFLINE, 0, 0L); + } + + BOOL EnableSearchWeb(BOOL bEnable) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_ENABLESEARCHWEB, (WPARAM)bEnable, 0L); + } + + void SearchWeb() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SEARCHWEB, 0, 0L); + } + + BOOL SetCaretIndex(DWORD dwCaretIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCARETINDEX, dwCaretIndex, 0L); + } + + DWORD GetCaretIndex() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SendMessage(this->m_hWnd, EM_GETCARETINDEX, 0, 0L); + } + + BOOL GetZoom(int& nNum, int& nDen) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_GETZOOM, (WPARAM)&nNum, (LPARAM)&nDen); + } + + BOOL SetZoom(int nNum, int nDen) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((nNum >= 0) && (nNum <= 64)); + ATLASSERT((nDen >= 0) && (nDen <= 64)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETZOOM, nNum, nDen); + } + + DWORD GetFileLineFromChar(DWORD dwCharIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SendMessage(this->m_hWnd, EM_FILELINEFROMCHAR, dwCharIndex, 0L); + } + + DWORD GetFileLineIndex(DWORD dwLineNum) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SendMessage(this->m_hWnd, EM_FILELINEINDEX, dwLineNum, 0L); + } + + DWORD GetFileLineLength(DWORD dwCharIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SendMessage(this->m_hWnd, EM_FILELINELENGTH, dwCharIndex, 0L); + } + + DWORD GetFileLine(DWORD dwLineNum, LPTSTR lpstrLine, WORD wLen) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + WORD* pw = (WORD*)lpstrLine; + *pw = wLen; + return ::SendMessage(this->m_hWnd, EM_GETFILELINE, dwLineNum, (LPARAM)lpstrLine); + } + +#ifdef __ATLSTR_H__ + ATL::CString GetFileLine(DWORD dwLineNum) const + { + ATL::CString strLine; + DWORD dwCharIndex = GetFileLineIndex(dwLineNum); + if(dwCharIndex != (DWORD)-1) + { + DWORD dwLen = GetFileLineLength(dwCharIndex); + if(dwLen > 0) + { + LPTSTR lpstrLine = strLine.GetBufferSetLength(dwLen); + ATLVERIFY(GetFileLine(dwLineNum, lpstrLine, (WORD)dwLen) == dwLen); + strLine.ReleaseBuffer(); + } + } + + return strLine; + } +#endif // __ATLSTR_H__ + + DWORD GetFileLineCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SendMessage(this->m_hWnd, EM_GETFILELINECOUNT, 0, 0L); + } +#endif // defined(NTDDI_VERSION) && defined(NTDDI_WIN10_RS5) && (NTDDI_VERSION >= NTDDI_WIN10_RS5) +}; + +typedef CEditT<ATL::CWindow> CEdit; + + +/////////////////////////////////////////////////////////////////////////////// +// CEditCommands - message handlers for standard EDIT commands + +// Chain to CEditCommands message map. Your class must also derive from CEdit. +// Example: +// class CMyEdit : public CWindowImpl<CMyEdit, CEdit>, +// public CEditCommands<CMyEdit> +// { +// public: +// BEGIN_MSG_MAP(CMyEdit) +// // your handlers... +// CHAIN_MSG_MAP_ALT(CEditCommands<CMyEdit>, 1) +// END_MSG_MAP() +// // other stuff... +// }; + +template <class T> +class CEditCommands +{ +public: + BEGIN_MSG_MAP(CEditCommands< T >) + ALT_MSG_MAP(1) + COMMAND_ID_HANDLER(ID_EDIT_CLEAR, OnEditClear) + COMMAND_ID_HANDLER(ID_EDIT_CLEAR_ALL, OnEditClearAll) + COMMAND_ID_HANDLER(ID_EDIT_COPY, OnEditCopy) + COMMAND_ID_HANDLER(ID_EDIT_CUT, OnEditCut) + COMMAND_ID_HANDLER(ID_EDIT_PASTE, OnEditPaste) + COMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, OnEditSelectAll) + COMMAND_ID_HANDLER(ID_EDIT_UNDO, OnEditUndo) + END_MSG_MAP() + + LRESULT OnEditClear(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->Clear(); + return 0; + } + + LRESULT OnEditClearAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->SetSel(0, -1); + pT->Clear(); + return 0; + } + + LRESULT OnEditCopy(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->Copy(); + return 0; + } + + LRESULT OnEditCut(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->Cut(); + return 0; + } + + LRESULT OnEditPaste(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->Paste(); + return 0; + } + + LRESULT OnEditSelectAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->SetSel(0, -1); + return 0; + } + + LRESULT OnEditUndo(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->Undo(); + return 0; + } + +// State (update UI) helpers + BOOL CanCut() const + { return HasSelection(); } + + BOOL CanCopy() const + { return HasSelection(); } + + BOOL CanClear() const + { return HasSelection(); } + + BOOL CanSelectAll() const + { return HasText(); } + + BOOL CanFind() const + { return HasText(); } + + BOOL CanRepeat() const + { return HasText(); } + + BOOL CanReplace() const + { return HasText(); } + + BOOL CanClearAll() const + { return HasText(); } + +// Implementation + BOOL HasSelection() const + { + const T* pT = static_cast<const T*>(this); + int nMin = 0, nMax = 0; + ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nMin, (LPARAM)&nMax); + return (nMin != nMax); + } + + BOOL HasText() const + { + const T* pT = static_cast<const T*>(this); + return (pT->GetWindowTextLength() > 0); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CScrollBar - client side for a Windows SCROLLBAR control + +template <class TBase> +class CScrollBarT : public TBase +{ +public: +// Constructors + CScrollBarT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CScrollBarT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return _T("SCROLLBAR"); + } + + int GetScrollPos() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::GetScrollPos(this->m_hWnd, SB_CTL); + } + + int SetScrollPos(int nPos, BOOL bRedraw = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SetScrollPos(this->m_hWnd, SB_CTL, nPos, bRedraw); + } + + void GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::GetScrollRange(this->m_hWnd, SB_CTL, lpMinPos, lpMaxPos); + } + + void SetScrollRange(int nMinPos, int nMaxPos, BOOL bRedraw = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SetScrollRange(this->m_hWnd, SB_CTL, nMinPos, nMaxPos, bRedraw); + } + + BOOL GetScrollInfo(LPSCROLLINFO lpScrollInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::GetScrollInfo(this->m_hWnd, SB_CTL, lpScrollInfo); + } + + int SetScrollInfo(LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::SetScrollInfo(this->m_hWnd, SB_CTL, lpScrollInfo, bRedraw); + } + + int GetScrollLimit() const + { + SCROLLINFO info = { sizeof(SCROLLINFO), SIF_RANGE | SIF_PAGE }; + ::GetScrollInfo(this->m_hWnd, SB_CTL, &info); + if(info.nPage > 1) + info.nMax -= info.nPage - 1; + + return info.nMax; + } + + BOOL GetScrollBarInfo(PSCROLLBARINFO pScrollBarInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)pScrollBarInfo); + } + +// Operations + void ShowScrollBar(BOOL bShow = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::ShowScrollBar(this->m_hWnd, SB_CTL, bShow); + } + + BOOL EnableScrollBar(UINT nArrowFlags = ESB_ENABLE_BOTH) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::EnableScrollBar(this->m_hWnd, SB_CTL, nArrowFlags); + } +}; + +typedef CScrollBarT<ATL::CWindow> CScrollBar; + + +// --- Windows Common Controls --- + +/////////////////////////////////////////////////////////////////////////////// +// CImageList + +// forward declarations +template <bool t_bManaged> class CImageListT; +typedef CImageListT<false> CImageList; +typedef CImageListT<true> CImageListManaged; + + +template <bool t_bManaged> +class CImageListT +{ +public: +// Data members + HIMAGELIST m_hImageList; + +// Constructor/destructor/operators + CImageListT(HIMAGELIST hImageList = NULL) : m_hImageList(hImageList) + { } + + ~CImageListT() + { + if(t_bManaged && (m_hImageList != NULL)) + Destroy(); + } + + CImageListT<t_bManaged>& operator =(HIMAGELIST hImageList) + { + Attach(hImageList); + return *this; + } + + void Attach(HIMAGELIST hImageList) + { + if(t_bManaged && (m_hImageList != NULL) && (m_hImageList != hImageList)) + ImageList_Destroy(m_hImageList); + m_hImageList = hImageList; + } + + HIMAGELIST Detach() + { + HIMAGELIST hImageList = m_hImageList; + m_hImageList = NULL; + return hImageList; + } + + operator HIMAGELIST() const { return m_hImageList; } + + bool IsNull() const { return (m_hImageList == NULL); } + +// Attributes + int GetImageCount() const + { + ATLASSERT(m_hImageList != NULL); + return ImageList_GetImageCount(m_hImageList); + } + + COLORREF GetBkColor() const + { + ATLASSERT(m_hImageList != NULL); + return ImageList_GetBkColor(m_hImageList); + } + + COLORREF SetBkColor(COLORREF cr) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_SetBkColor(m_hImageList, cr); + } + + BOOL GetImageInfo(int nImage, IMAGEINFO* pImageInfo) const + { + ATLASSERT(m_hImageList != NULL); + return ImageList_GetImageInfo(m_hImageList, nImage, pImageInfo); + } + + HICON GetIcon(int nIndex, UINT uFlags = ILD_NORMAL) const + { + ATLASSERT(m_hImageList != NULL); + return ImageList_GetIcon(m_hImageList, nIndex, uFlags); + } + + BOOL GetIconSize(int& cx, int& cy) const + { + ATLASSERT(m_hImageList != NULL); + return ImageList_GetIconSize(m_hImageList, &cx, &cy); + } + + BOOL GetIconSize(SIZE& size) const + { + ATLASSERT(m_hImageList != NULL); + return ImageList_GetIconSize(m_hImageList, (int*)&size.cx, (int*)&size.cy); + } + + BOOL SetIconSize(int cx, int cy) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_SetIconSize(m_hImageList, cx, cy); + } + + BOOL SetIconSize(SIZE size) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_SetIconSize(m_hImageList, size.cx, size.cy); + } + + BOOL SetImageCount(UINT uNewCount) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_SetImageCount(m_hImageList, uNewCount); + } + + BOOL SetOverlayImage(int nImage, int nOverlay) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_SetOverlayImage(m_hImageList, nImage, nOverlay); + } + +// Operations + BOOL Create(int cx, int cy, UINT nFlags, int nInitial, int nGrow) + { + ATLASSERT(m_hImageList == NULL); + m_hImageList = ImageList_Create(cx, cy, nFlags, nInitial, nGrow); + return (m_hImageList != NULL) ? TRUE : FALSE; + } + + BOOL Create(ATL::_U_STRINGorID bitmap, int cx, int nGrow, COLORREF crMask) + { + ATLASSERT(m_hImageList == NULL); + m_hImageList = ImageList_LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, cx, nGrow, crMask); + return (m_hImageList != NULL) ? TRUE : FALSE; + } + + BOOL CreateFromImage(ATL::_U_STRINGorID image, int cx, int nGrow, COLORREF crMask, UINT uType, UINT uFlags = LR_DEFAULTCOLOR | LR_DEFAULTSIZE) + { + ATLASSERT(m_hImageList == NULL); + m_hImageList = ImageList_LoadImage(ModuleHelper::GetResourceInstance(), image.m_lpstr, cx, nGrow, crMask, uType, uFlags); + return (m_hImageList != NULL) ? TRUE : FALSE; + } + + BOOL Merge(HIMAGELIST hImageList1, int nImage1, HIMAGELIST hImageList2, int nImage2, int dx, int dy) + { + ATLASSERT(m_hImageList == NULL); + m_hImageList = ImageList_Merge(hImageList1, nImage1, hImageList2, nImage2, dx, dy); + return (m_hImageList != NULL) ? TRUE : FALSE; + } + +#ifdef __IStream_INTERFACE_DEFINED__ + BOOL CreateFromStream(LPSTREAM lpStream) + { + ATLASSERT(m_hImageList == NULL); + m_hImageList = ImageList_Read(lpStream); + return (m_hImageList != NULL) ? TRUE : FALSE; + } +#endif // __IStream_INTERFACE_DEFINED__ + + BOOL Destroy() + { + if (m_hImageList == NULL) + return FALSE; + BOOL bRet = ImageList_Destroy(m_hImageList); + if(bRet) + m_hImageList = NULL; + return bRet; + } + + int Add(HBITMAP hBitmap, HBITMAP hBitmapMask = NULL) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_Add(m_hImageList, hBitmap, hBitmapMask); + } + + int Add(HBITMAP hBitmap, COLORREF crMask) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_AddMasked(m_hImageList, hBitmap, crMask); + } + + BOOL Remove(int nImage) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_Remove(m_hImageList, nImage); + } + + BOOL RemoveAll() + { + ATLASSERT(m_hImageList != NULL); + return ImageList_RemoveAll(m_hImageList); + } + + BOOL Replace(int nImage, HBITMAP hBitmap, HBITMAP hBitmapMask) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_Replace(m_hImageList, nImage, hBitmap, hBitmapMask); + } + + int AddIcon(HICON hIcon) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_AddIcon(m_hImageList, hIcon); + } + + int ReplaceIcon(int nImage, HICON hIcon) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_ReplaceIcon(m_hImageList, nImage, hIcon); + } + + HICON ExtractIcon(int nImage) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_ExtractIcon(NULL, m_hImageList, nImage); + } + + BOOL Draw(HDC hDC, int nImage, int x, int y, UINT nStyle) + { + ATLASSERT(m_hImageList != NULL); + ATLASSERT(hDC != NULL); + return ImageList_Draw(m_hImageList, nImage, hDC, x, y, nStyle); + } + + BOOL Draw(HDC hDC, int nImage, POINT pt, UINT nStyle) + { + ATLASSERT(m_hImageList != NULL); + ATLASSERT(hDC != NULL); + return ImageList_Draw(m_hImageList, nImage, hDC, pt.x, pt.y, nStyle); + } + + BOOL DrawEx(int nImage, HDC hDC, int x, int y, int dx, int dy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle) + { + ATLASSERT(m_hImageList != NULL); + ATLASSERT(hDC != NULL); + return ImageList_DrawEx(m_hImageList, nImage, hDC, x, y, dx, dy, rgbBk, rgbFg, fStyle); + } + + BOOL DrawEx(int nImage, HDC hDC, RECT& rect, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle) + { + ATLASSERT(m_hImageList != NULL); + ATLASSERT(hDC != NULL); + return ImageList_DrawEx(m_hImageList, nImage, hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, rgbBk, rgbFg, fStyle); + } + + static BOOL DrawIndirect(IMAGELISTDRAWPARAMS* pimldp) + { + return ImageList_DrawIndirect(pimldp); + } + + BOOL Copy(int nSrc, int nDst, UINT uFlags = ILCF_MOVE) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_Copy(m_hImageList, nDst, m_hImageList, nSrc, uFlags); + } + +#ifdef __IStream_INTERFACE_DEFINED__ + static HIMAGELIST Read(LPSTREAM lpStream) + { + return ImageList_Read(lpStream); + } + + BOOL Write(LPSTREAM lpStream) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_Write(m_hImageList, lpStream); + } + + static HRESULT ReadEx(DWORD dwFlags, LPSTREAM lpStream, REFIID riid, PVOID* ppv) + { + return ImageList_ReadEx(dwFlags, lpStream, riid, ppv); + } + + HRESULT WriteEx(DWORD dwFlags, LPSTREAM lpStream) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_WriteEx(m_hImageList, dwFlags, lpStream); + } +#endif // __IStream_INTERFACE_DEFINED__ + + // Drag operations + BOOL BeginDrag(int nImage, POINT ptHotSpot) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_BeginDrag(m_hImageList, nImage, ptHotSpot.x, ptHotSpot.y); + } + + BOOL BeginDrag(int nImage, int xHotSpot, int yHotSpot) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_BeginDrag(m_hImageList, nImage, xHotSpot, yHotSpot); + } + + static void EndDrag() + { + ImageList_EndDrag(); + } + + static BOOL DragMove(POINT pt) + { + return ImageList_DragMove(pt.x, pt.y); + } + + static BOOL DragMove(int x, int y) + { + return ImageList_DragMove(x, y); + } + + BOOL SetDragCursorImage(int nDrag, POINT ptHotSpot) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_SetDragCursorImage(m_hImageList, nDrag, ptHotSpot.x, ptHotSpot.y); + } + + BOOL SetDragCursorImage(int nDrag, int xHotSpot, int yHotSpot) + { + ATLASSERT(m_hImageList != NULL); + return ImageList_SetDragCursorImage(m_hImageList, nDrag, xHotSpot, yHotSpot); + } + + static BOOL DragShowNolock(BOOL bShow = TRUE) + { + return ImageList_DragShowNolock(bShow); + } + + static CImageList GetDragImage(LPPOINT lpPoint, LPPOINT lpPointHotSpot) + { + return CImageList(ImageList_GetDragImage(lpPoint, lpPointHotSpot)); + } + + static BOOL DragEnter(HWND hWnd, POINT point) + { + return ImageList_DragEnter(hWnd, point.x, point.y); + } + + static BOOL DragEnter(HWND hWnd, int x, int y) + { + return ImageList_DragEnter(hWnd, x, y); + } + + static BOOL DragLeave(HWND hWnd) + { + return ImageList_DragLeave(hWnd); + } + + CImageList Duplicate() const + { + ATLASSERT(m_hImageList != NULL); + return CImageList(ImageList_Duplicate(m_hImageList)); + } + + static CImageList Duplicate(HIMAGELIST hImageList) + { + ATLASSERT(hImageList != NULL); + return CImageList(ImageList_Duplicate(hImageList)); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CToolTipCtrl + +class CToolInfo : public TOOLINFO +{ +public: + CToolInfo(UINT nFlags, HWND hWnd, UINT_PTR nIDTool = 0, LPRECT lpRect = NULL, LPTSTR lpstrText = LPSTR_TEXTCALLBACK, LPARAM lUserParam = NULL) + { + Init(nFlags, hWnd, nIDTool, lpRect, lpstrText, lUserParam); + } + + operator LPTOOLINFO() { return this; } + + operator LPARAM() { return (LPARAM)this; } + + void Init(UINT nFlags, HWND hWnd, UINT_PTR nIDTool = 0, LPRECT lpRect = NULL, LPTSTR lpstrText = LPSTR_TEXTCALLBACK, LPARAM lUserParam = NULL) + { + ATLASSERT(::IsWindow(hWnd)); + memset(this, 0, sizeof(TOOLINFO)); + cbSize = RunTimeHelper::SizeOf_TOOLINFO(); + uFlags = nFlags; + if(nIDTool == 0) + { + hwnd = ::GetParent(hWnd); + uFlags |= TTF_IDISHWND; + uId = (UINT_PTR)hWnd; + } + else + { + hwnd = hWnd; + uId = nIDTool; + } + if(lpRect != NULL) + rect = *lpRect; + hinst = ModuleHelper::GetResourceInstance(); + lpszText = lpstrText; + lParam = lUserParam; + } +}; + +template <class TBase> +class CToolTipCtrlT : public TBase +{ +public: +// Constructors + CToolTipCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CToolTipCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return TOOLTIPS_CLASS; + } + + void GetText(LPTOOLINFO lpToolInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_GETTEXT, 0, (LPARAM)&lpToolInfo); + } + + void GetText(LPTSTR lpstrText, HWND hWnd, UINT_PTR nIDTool = 0) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(hWnd != NULL); + CToolInfo ti(0, hWnd, nIDTool, NULL, lpstrText); + ::SendMessage(this->m_hWnd, TTM_GETTEXT, 0, ti); + } + + BOOL GetToolInfo(LPTOOLINFO lpToolInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TTM_GETTOOLINFO, 0, (LPARAM)lpToolInfo); + } + + BOOL GetToolInfo(HWND hWnd, UINT_PTR nIDTool, UINT* puFlags, LPRECT lpRect, LPTSTR lpstrText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(hWnd != NULL); + ATLASSERT(puFlags != NULL); + ATLASSERT(lpRect != NULL); + CToolInfo ti(0, hWnd, nIDTool, NULL, lpstrText); + BOOL bRet = (BOOL)::SendMessage(this->m_hWnd, TTM_GETTOOLINFO, 0, ti); + if(bRet != FALSE) + { + *puFlags = ti.uFlags; + *lpRect = ti.rect; + } + return bRet; + } + + void SetToolInfo(LPTOOLINFO lpToolInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_SETTOOLINFO, 0, (LPARAM)lpToolInfo); + } + + void SetToolRect(LPTOOLINFO lpToolInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_NEWTOOLRECT, 0, (LPARAM)lpToolInfo); + } + + void SetToolRect(HWND hWnd, UINT_PTR nIDTool, LPCRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(hWnd != NULL); + ATLASSERT(nIDTool != 0); + + CToolInfo ti(0, hWnd, nIDTool, (LPRECT)lpRect, NULL); + ::SendMessage(this->m_hWnd, TTM_NEWTOOLRECT, 0, ti); + } + + int GetToolCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TTM_GETTOOLCOUNT, 0, 0L); + } + + int GetDelayTime(DWORD dwType) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TTM_GETDELAYTIME, dwType, 0L); + } + + void SetDelayTime(DWORD dwType, int nTime) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_SETDELAYTIME, dwType, MAKELPARAM(nTime, 0)); + } + + void GetMargin(LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_GETMARGIN, 0, (LPARAM)lpRect); + } + + void SetMargin(LPRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_SETMARGIN, 0, (LPARAM)lpRect); + } + + int GetMaxTipWidth() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TTM_GETMAXTIPWIDTH, 0, 0L); + } + + int SetMaxTipWidth(int nWidth) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TTM_SETMAXTIPWIDTH, 0, nWidth); + } + + COLORREF GetTipBkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TTM_GETTIPBKCOLOR, 0, 0L); + } + + void SetTipBkColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_SETTIPBKCOLOR, (WPARAM)clr, 0L); + } + + COLORREF GetTipTextColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TTM_GETTIPTEXTCOLOR, 0, 0L); + } + + void SetTipTextColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_SETTIPTEXTCOLOR, (WPARAM)clr, 0L); + } + + BOOL GetCurrentTool(LPTOOLINFO lpToolInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TTM_GETCURRENTTOOL, 0, (LPARAM)lpToolInfo); + } + + SIZE GetBubbleSize(LPTOOLINFO lpToolInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, TTM_GETBUBBLESIZE, 0, (LPARAM)lpToolInfo); + SIZE size = { GET_X_LPARAM(dwRet), GET_Y_LPARAM(dwRet) }; + return size; + } + + BOOL SetTitle(UINT_PTR uIcon, LPCTSTR lpstrTitle) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TTM_SETTITLE, uIcon, (LPARAM)lpstrTitle); + } + + + BOOL SetTitle(HICON hIcon, LPCTSTR lpstrTitle) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TTM_SETTITLE, (WPARAM)hIcon, (LPARAM)lpstrTitle); + } + + void GetTitle(PTTGETTITLE pTTGetTitle) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_GETTITLE, 0, (LPARAM)pTTGetTitle); + } + + void SetWindowTheme(LPCWSTR lpstrTheme) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme); + } + +// Operations + void Activate(BOOL bActivate) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_ACTIVATE, bActivate, 0L); + } + + BOOL AddTool(LPTOOLINFO lpToolInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TTM_ADDTOOL, 0, (LPARAM)lpToolInfo); + } + + BOOL AddTool(HWND hWnd, ATL::_U_STRINGorID text = LPSTR_TEXTCALLBACK, LPCRECT lpRectTool = NULL, UINT_PTR nIDTool = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(hWnd != NULL); + // the toolrect and toolid must both be zero or both valid + ATLASSERT(((lpRectTool != NULL) && (nIDTool != 0)) || ((lpRectTool == NULL) && (nIDTool == 0))); + + CToolInfo ti(0, hWnd, nIDTool, (LPRECT)lpRectTool, (LPTSTR)text.m_lpstr); + return (BOOL)::SendMessage(this->m_hWnd, TTM_ADDTOOL, 0, ti); + } + + void DelTool(LPTOOLINFO lpToolInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_DELTOOL, 0, (LPARAM)lpToolInfo); + } + + void DelTool(HWND hWnd, UINT_PTR nIDTool = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(hWnd != NULL); + + CToolInfo ti(0, hWnd, nIDTool, NULL, NULL); + ::SendMessage(this->m_hWnd, TTM_DELTOOL, 0, ti); + } + + BOOL HitTest(LPTTHITTESTINFO lpHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TTM_HITTEST, 0, (LPARAM)lpHitTestInfo); + } + + BOOL HitTest(HWND hWnd, POINT pt, LPTOOLINFO lpToolInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(hWnd != NULL); + ATLASSERT(lpToolInfo != NULL); + + TTHITTESTINFO hti = {}; + hti.ti.cbSize = RunTimeHelper::SizeOf_TOOLINFO(); + hti.hwnd = hWnd; + hti.pt.x = pt.x; + hti.pt.y = pt.y; + if((BOOL)::SendMessage(this->m_hWnd, TTM_HITTEST, 0, (LPARAM)&hti) != FALSE) + { + *lpToolInfo = hti.ti; + return TRUE; + } + return FALSE; + } + + void RelayEvent(LPMSG lpMsg) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_RELAYEVENT, 0, (LPARAM)lpMsg); + } + + void UpdateTipText(LPTOOLINFO lpToolInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_UPDATETIPTEXT, 0, (LPARAM)lpToolInfo); + } + + void UpdateTipText(ATL::_U_STRINGorID text, HWND hWnd, UINT_PTR nIDTool = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(hWnd != NULL); + + CToolInfo ti(0, hWnd, nIDTool, NULL, (LPTSTR)text.m_lpstr); + ::SendMessage(this->m_hWnd, TTM_UPDATETIPTEXT, 0, ti); + } + + BOOL EnumTools(UINT_PTR nTool, LPTOOLINFO lpToolInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TTM_ENUMTOOLS, nTool, (LPARAM)lpToolInfo); + } + + void Pop() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_POP, 0, 0L); + } + + void TrackActivate(LPTOOLINFO lpToolInfo, BOOL bActivate) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_TRACKACTIVATE, bActivate, (LPARAM)lpToolInfo); + } + + void TrackActivate(HWND hWnd, UINT_PTR nIDTool, BOOL bActivate) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(hWnd != NULL); + + CToolInfo ti(0, hWnd, nIDTool); + ::SendMessage(this->m_hWnd, TTM_TRACKACTIVATE, bActivate, ti); + } + + void TrackPosition(int xPos, int yPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_TRACKPOSITION, 0, MAKELPARAM(xPos, yPos)); + } + + void Update() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_UPDATE, 0, 0L); + } + + BOOL AdjustRect(LPRECT lpRect, BOOL bLarger /*= TRUE*/) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TTM_ADJUSTRECT, bLarger, (LPARAM)lpRect); + } + + void Popup() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TTM_POPUP, 0, 0L); + } +}; + +typedef CToolTipCtrlT<ATL::CWindow> CToolTipCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CHeaderCtrl + +template <class TBase> +class CHeaderCtrlT : public TBase +{ +public: +// Constructors + CHeaderCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CHeaderCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return WC_HEADER; + } + + int GetItemCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_GETITEMCOUNT, 0, 0L); + } + + BOOL GetItem(int nIndex, LPHDITEM pHeaderItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_GETITEM, nIndex, (LPARAM)pHeaderItem); + } + + BOOL SetItem(int nIndex, LPHDITEM pHeaderItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_SETITEM, nIndex, (LPARAM)pHeaderItem); + } + + CImageList GetImageList() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, HDM_GETIMAGELIST, 0, 0L)); + } + + CImageList SetImageList(HIMAGELIST hImageList) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, HDM_SETIMAGELIST, 0, (LPARAM)hImageList)); + } + + BOOL GetOrderArray(int nSize, int* lpnArray) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_GETORDERARRAY, nSize, (LPARAM)lpnArray); + } + + BOOL SetOrderArray(int nSize, int* lpnArray) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_SETORDERARRAY, nSize, (LPARAM)lpnArray); + } + + BOOL GetItemRect(int nIndex, LPRECT lpItemRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_GETITEMRECT, nIndex, (LPARAM)lpItemRect); + } + + int SetHotDivider(BOOL bPos, DWORD dwInputValue) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_SETHOTDIVIDER, bPos, dwInputValue); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_SETUNICODEFORMAT, bUnicode, 0L); + } + + int GetBitmapMargin() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_GETBITMAPMARGIN, 0, 0L); + } + + int SetBitmapMargin(int nWidth) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_SETBITMAPMARGIN, nWidth, 0L); + } + + int SetFilterChangeTimeout(DWORD dwTimeOut) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_SETFILTERCHANGETIMEOUT, 0, dwTimeOut); + } + +#if (_WIN32_WINNT >= 0x0600) + BOOL GetItemDropDownRect(int nIndex, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_GETITEMDROPDOWNRECT, nIndex, (LPARAM)lpRect); + } + + BOOL GetOverflowRect(LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_GETOVERFLOWRECT, 0, (LPARAM)lpRect); + } + + int GetFocusedItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_GETFOCUSEDITEM, 0, 0L); + } + + BOOL SetFocusedItem(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_SETFOCUSEDITEM, 0, nIndex); + } +#endif // (_WIN32_WINNT >= 0x0600) + +// Operations + int InsertItem(int nIndex, LPHDITEM phdi) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_INSERTITEM, nIndex, (LPARAM)phdi); + } + + int AddItem(LPHDITEM phdi) + { + return InsertItem(GetItemCount(), phdi); + } + + BOOL DeleteItem(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_DELETEITEM, nIndex, 0L); + } + + BOOL Layout(HD_LAYOUT* pHeaderLayout) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, HDM_LAYOUT, 0, (LPARAM)pHeaderLayout); + } + + int HitTest(LPHDHITTESTINFO lpHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_HITTEST, 0, (LPARAM)lpHitTestInfo); + } + + int OrderToIndex(int nOrder) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_ORDERTOINDEX, nOrder, 0L); + } + + CImageList CreateDragImage(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, HDM_CREATEDRAGIMAGE, nIndex, 0L)); + } + + int EditFilter(int nColumn, BOOL bDiscardChanges) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_EDITFILTER, nColumn, MAKELPARAM(bDiscardChanges, 0)); + } + + int ClearFilter(int nColumn) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_CLEARFILTER, nColumn, 0L); + } + + int ClearAllFilters() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, HDM_CLEARFILTER, (WPARAM)-1, 0L); + } +}; + +typedef CHeaderCtrlT<ATL::CWindow> CHeaderCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CListViewCtrl + +template <class TBase> +class CListViewCtrlT : public TBase +{ +public: +// Constructors + CListViewCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CListViewCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return WC_LISTVIEW; + } + + COLORREF GetBkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, LVM_GETBKCOLOR, 0, 0L); + } + + BOOL SetBkColor(COLORREF cr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETBKCOLOR, 0, cr); + } + + CImageList GetImageList(int nImageListType) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, LVM_GETIMAGELIST, nImageListType, 0L)); + } + + CImageList SetImageList(HIMAGELIST hImageList, int nImageList) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, LVM_SETIMAGELIST, nImageList, (LPARAM)hImageList)); + } + + int GetItemCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETITEMCOUNT, 0, 0L); + } + + BOOL SetItemCount(int nItems) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMCOUNT, nItems, 0L); + } + + BOOL GetItem(LPLVITEM pItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEM, 0, (LPARAM)pItem); + } + + BOOL SetItem(const LVITEM* pItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEM, 0, (LPARAM)pItem); + } + + BOOL SetItem(int nItem, int nSubItem, UINT nMask, LPCTSTR lpszItem, + int nImage, UINT nState, UINT nStateMask, LPARAM lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVITEM lvi = {}; + lvi.mask = nMask; + lvi.iItem = nItem; + lvi.iSubItem = nSubItem; + lvi.stateMask = nStateMask; + lvi.state = nState; + lvi.pszText = (LPTSTR) lpszItem; + lvi.iImage = nImage; + lvi.lParam = lParam; + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEM, 0, (LPARAM)&lvi); + } + + UINT GetItemState(int nItem, UINT nMask) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, LVM_GETITEMSTATE, nItem, nMask); + } + + BOOL SetItemState(int nItem, UINT nState, UINT nStateMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVITEM lvi = {}; + lvi.state = nState; + lvi.stateMask = nStateMask; + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMSTATE, nItem, (LPARAM)&lvi); + } + + BOOL SetItemState(int nItem, LPLVITEM pItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMSTATE, nItem, (LPARAM)pItem); + } + + BOOL GetItemText(int nItem, int nSubItem, BSTR& bstrText) const + { + USES_CONVERSION; + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(bstrText == NULL); + LVITEM lvi = {}; + lvi.iSubItem = nSubItem; + + LPTSTR lpstrText = NULL; + int nRes = 0; + for(int nLen = 256; ; nLen *= 2) + { + ATLTRY(lpstrText = new TCHAR[nLen]); + if(lpstrText == NULL) + break; + lpstrText[0] = NULL; + lvi.cchTextMax = nLen; + lvi.pszText = lpstrText; + nRes = (int)::SendMessage(this->m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi); + if(nRes < nLen - 1) + break; + delete [] lpstrText; + lpstrText = NULL; + } + + if(lpstrText != NULL) + { + if(nRes != 0) + bstrText = ::SysAllocString(T2OLE(lpstrText)); + delete [] lpstrText; + } + + return (bstrText != NULL) ? TRUE : FALSE; + } + +#ifdef __ATLSTR_H__ + int GetItemText(int nItem, int nSubItem, ATL::CString& strText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVITEM lvi = {}; + lvi.iSubItem = nSubItem; + + strText.Empty(); + int nRes = 0; + for(int nLen = 256; ; nLen *= 2) + { + lvi.cchTextMax = nLen; + lvi.pszText = strText.GetBufferSetLength(nLen); + if(lvi.pszText == NULL) + { + nRes = 0; + break; + } + nRes = (int)::SendMessage(this->m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi); + if(nRes < nLen - 1) + break; + } + strText.ReleaseBuffer(); + return nRes; + } +#endif // __ATLSTR_H__ + + int GetItemText(int nItem, int nSubItem, LPTSTR lpszText, int nLen) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVITEM lvi = {}; + lvi.iSubItem = nSubItem; + lvi.cchTextMax = nLen; + lvi.pszText = lpszText; + return (int)::SendMessage(this->m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi); + } + + BOOL SetItemText(int nItem, int nSubItem, LPCTSTR lpszText) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItem(nItem, nSubItem, LVIF_TEXT, lpszText, 0, 0, 0, 0); + } + + DWORD_PTR GetItemData(int nItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVITEM lvi = {}; + lvi.iItem = nItem; + lvi.mask = LVIF_PARAM; + BOOL bRet = (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEM, 0, (LPARAM)&lvi); + return (DWORD_PTR)(bRet ? lvi.lParam : NULL); + } + + BOOL SetItemData(int nItem, DWORD_PTR dwData) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItem(nItem, 0, LVIF_PARAM, NULL, 0, 0, 0, (LPARAM)dwData); + } + + UINT GetCallbackMask() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, LVM_GETCALLBACKMASK, 0, 0L); + } + + BOOL SetCallbackMask(UINT nMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETCALLBACKMASK, nMask, 0L); + } + + BOOL GetItemPosition(int nItem, LPPOINT lpPoint) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEMPOSITION, nItem, (LPARAM)lpPoint); + } + + BOOL SetItemPosition(int nItem, POINT pt) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(((this->GetStyle() & LVS_TYPEMASK) == LVS_ICON) || ((this->GetStyle() & LVS_TYPEMASK) == LVS_SMALLICON)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMPOSITION32, nItem, (LPARAM)&pt); + } + + BOOL SetItemPosition(int nItem, int x, int y) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(((this->GetStyle() & LVS_TYPEMASK) == LVS_ICON) || ((this->GetStyle() & LVS_TYPEMASK) == LVS_SMALLICON)); + POINT pt = { x, y }; + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMPOSITION32, nItem, (LPARAM)&pt); + } + + int GetStringWidth(LPCTSTR lpsz) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETSTRINGWIDTH, 0, (LPARAM)lpsz); + } + + CEdit GetEditControl() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CEdit((HWND)::SendMessage(this->m_hWnd, LVM_GETEDITCONTROL, 0, 0L)); + } + + BOOL GetColumn(int nCol, LVCOLUMN* pColumn) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETCOLUMN, nCol, (LPARAM)pColumn); + } + + BOOL SetColumn(int nCol, const LVCOLUMN* pColumn) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETCOLUMN, nCol, (LPARAM)pColumn); + } + + int GetColumnWidth(int nCol) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETCOLUMNWIDTH, nCol, 0L); + } + + BOOL SetColumnWidth(int nCol, int cx) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETCOLUMNWIDTH, nCol, MAKELPARAM(cx, 0)); + } + + BOOL GetViewRect(LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETVIEWRECT, 0, (LPARAM)lpRect); + } + + COLORREF GetTextColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, LVM_GETTEXTCOLOR, 0, 0L); + } + + BOOL SetTextColor(COLORREF cr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETTEXTCOLOR, 0, cr); + } + + COLORREF GetTextBkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, LVM_GETTEXTBKCOLOR, 0, 0L); + } + + BOOL SetTextBkColor(COLORREF cr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETTEXTBKCOLOR, 0, cr); + } + + int GetTopIndex() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETTOPINDEX, 0, 0L); + } + + int GetCountPerPage() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETCOUNTPERPAGE, 0, 0L); + } + + BOOL GetOrigin(LPPOINT lpPoint) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETORIGIN, 0, (LPARAM)lpPoint); + } + + UINT GetSelectedCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, LVM_GETSELECTEDCOUNT, 0, 0L); + } + + BOOL GetItemRect(int nItem, LPRECT lpRect, UINT nCode) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + lpRect->left = nCode; + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEMRECT, (WPARAM)nItem, (LPARAM)lpRect); + } + + HCURSOR GetHotCursor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HCURSOR)::SendMessage(this->m_hWnd, LVM_GETHOTCURSOR, 0, 0L); + } + + HCURSOR SetHotCursor(HCURSOR hHotCursor) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HCURSOR)::SendMessage(this->m_hWnd, LVM_SETHOTCURSOR, 0, (LPARAM)hHotCursor); + } + + int GetHotItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETHOTITEM, 0, 0L); + } + + int SetHotItem(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_SETHOTITEM, nIndex, 0L); + } + + BOOL GetColumnOrderArray(int nCount, int* lpnArray) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETCOLUMNORDERARRAY, nCount, (LPARAM)lpnArray); + } + + BOOL SetColumnOrderArray(int nCount, int* lpnArray) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETCOLUMNORDERARRAY, nCount, (LPARAM)lpnArray); + } + + CHeaderCtrl GetHeader() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CHeaderCtrl((HWND)::SendMessage(this->m_hWnd, LVM_GETHEADER, 0, 0L)); + } + + BOOL GetSubItemRect(int nItem, int nSubItem, int nFlag, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & LVS_TYPEMASK) == LVS_REPORT); + ATLASSERT(lpRect != NULL); + lpRect->top = nSubItem; + lpRect->left = nFlag; + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETSUBITEMRECT, nItem, (LPARAM)lpRect); + } + + DWORD SetIconSpacing(int cx, int cy) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & LVS_TYPEMASK) == LVS_ICON); + return (DWORD)::SendMessage(this->m_hWnd, LVM_SETICONSPACING, 0, MAKELPARAM(cx, cy)); + } + + int GetISearchString(LPTSTR lpstr) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETISEARCHSTRING, 0, (LPARAM)lpstr); + } + + void GetItemSpacing(SIZE& sizeSpacing, BOOL bSmallIconView = FALSE) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, LVM_GETITEMSPACING, bSmallIconView, 0L); + sizeSpacing.cx = GET_X_LPARAM(dwRet); + sizeSpacing.cy = GET_Y_LPARAM(dwRet); + } + + // single-selection only + int GetSelectedIndex() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & LVS_SINGLESEL) != 0); + return (int)::SendMessage(this->m_hWnd, LVM_GETNEXTITEM, (WPARAM)-1, MAKELPARAM(LVNI_ALL | LVNI_SELECTED, 0)); + } + + BOOL GetSelectedItem(LPLVITEM pItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & LVS_SINGLESEL) != 0); + ATLASSERT(pItem != NULL); + pItem->iItem = (int)::SendMessage(this->m_hWnd, LVM_GETNEXTITEM, (WPARAM)-1, MAKELPARAM(LVNI_ALL | LVNI_SELECTED, 0)); + if(pItem->iItem == -1) + return FALSE; + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEM, 0, (LPARAM)pItem); + } + + // extended list view styles + DWORD GetExtendedListViewStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0L); + } + + // dwExMask = 0 means all styles + DWORD SetExtendedListViewStyle(DWORD dwExStyle, DWORD dwExMask = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, dwExMask, dwExStyle); + } + + // checkboxes only + BOOL GetCheckState(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((GetExtendedListViewStyle() & LVS_EX_CHECKBOXES) != 0); + UINT uRet = GetItemState(nIndex, LVIS_STATEIMAGEMASK); + return (uRet >> 12) - 1; + } + + BOOL SetCheckState(int nItem, BOOL bCheck) + { + int nCheck = bCheck ? 2 : 1; // one based index + return SetItemState(nItem, INDEXTOSTATEIMAGEMASK(nCheck), LVIS_STATEIMAGEMASK); + } + + // view type + DWORD GetViewType() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (this->GetStyle() & LVS_TYPEMASK); + } + + DWORD SetViewType(DWORD dwType) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((dwType == LVS_ICON) || (dwType == LVS_SMALLICON) || (dwType == LVS_LIST) || (dwType == LVS_REPORT)); + DWORD dwOldType = GetViewType(); + if(dwType != dwOldType) + this->ModifyStyle(LVS_TYPEMASK, (dwType & LVS_TYPEMASK)); + return dwOldType; + } + + BOOL GetBkImage(LPLVBKIMAGE plvbki) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETBKIMAGE, 0, (LPARAM)plvbki); + } + + BOOL SetBkImage(LPLVBKIMAGE plvbki) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETBKIMAGE, 0, (LPARAM)plvbki); + } + + int GetSelectionMark() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETSELECTIONMARK, 0, 0L); + } + + int SetSelectionMark(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_SETSELECTIONMARK, 0, nIndex); + } + + BOOL GetWorkAreas(int nWorkAreas, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETWORKAREAS, nWorkAreas, (LPARAM)lpRect); + } + + BOOL SetWorkAreas(int nWorkAreas, LPRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETWORKAREAS, nWorkAreas, (LPARAM)lpRect); + } + + DWORD GetHoverTime() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((GetExtendedListViewStyle() & (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE)) != 0); + return (DWORD)::SendMessage(this->m_hWnd, LVM_GETHOVERTIME, 0, 0L); + } + + DWORD SetHoverTime(DWORD dwHoverTime) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((GetExtendedListViewStyle() & (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE)) != 0); + return (DWORD)::SendMessage(this->m_hWnd, LVM_SETHOVERTIME, 0, dwHoverTime); + } + + BOOL GetNumberOfWorkAreas(int* pnWorkAreas) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETNUMBEROFWORKAREAS, 0, (LPARAM)pnWorkAreas); + } + + BOOL SetItemCountEx(int nItems, DWORD dwFlags) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(((this->GetStyle() & LVS_OWNERDATA) != 0) && (((this->GetStyle() & LVS_TYPEMASK) == LVS_REPORT) || ((this->GetStyle() & LVS_TYPEMASK) == LVS_LIST))); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMCOUNT, nItems, dwFlags); + } + + CToolTipCtrl GetToolTips() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, LVM_GETTOOLTIPS, 0, 0L)); + } + + CToolTipCtrl SetToolTips(HWND hWndTT) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, LVM_SETTOOLTIPS, (WPARAM)hWndTT, 0L)); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETUNICODEFORMAT, bUnicode, 0L); + } + + int GetSelectedColumn() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETSELECTEDCOLUMN, 0, 0L); + } + + void SetSelectedColumn(int nColumn) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_SETSELECTEDCOLUMN, nColumn, 0L); + } + + DWORD GetView() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, LVM_GETVIEW, 0, 0L); + } + + int SetView(DWORD dwView) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_SETVIEW, dwView, 0L); + } + + BOOL IsGroupViewEnabled() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_ISGROUPVIEWENABLED, 0, 0L); + } + + int GetGroupInfo(int nGroupID, PLVGROUP pGroup) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETGROUPINFO, nGroupID, (LPARAM)pGroup); + } + + int SetGroupInfo(int nGroupID, PLVGROUP pGroup) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_SETGROUPINFO, nGroupID, (LPARAM)pGroup); + } + + void GetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_GETGROUPMETRICS, 0, (LPARAM)pGroupMetrics); + } + + void SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_SETGROUPMETRICS, 0, (LPARAM)pGroupMetrics); + } + + void GetTileViewInfo(PLVTILEVIEWINFO pTileViewInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_GETTILEVIEWINFO, 0, (LPARAM)pTileViewInfo); + } + + BOOL SetTileViewInfo(PLVTILEVIEWINFO pTileViewInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETTILEVIEWINFO, 0, (LPARAM)pTileViewInfo); + } + + void GetTileInfo(PLVTILEINFO pTileInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_GETTILEINFO, 0, (LPARAM)pTileInfo); + } + + BOOL SetTileInfo(PLVTILEINFO pTileInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETTILEINFO, 0, (LPARAM)pTileInfo); + } + + BOOL GetInsertMark(LPLVINSERTMARK pInsertMark) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETINSERTMARK, 0, (LPARAM)pInsertMark); + } + + BOOL SetInsertMark(LPLVINSERTMARK pInsertMark) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETINSERTMARK, 0, (LPARAM)pInsertMark); + } + + int GetInsertMarkRect(LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETINSERTMARKRECT, 0, (LPARAM)lpRect); + } + + COLORREF GetInsertMarkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, LVM_GETINSERTMARKCOLOR, 0, 0L); + } + + COLORREF SetInsertMarkColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, LVM_SETINSERTMARKCOLOR, 0, clr); + } + + COLORREF GetOutlineColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, LVM_GETOUTLINECOLOR, 0, 0L); + } + + COLORREF SetOutlineColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, LVM_SETOUTLINECOLOR, 0, clr); + } + +#if (_WIN32_WINNT >= 0x0600) + int GetGroupCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETGROUPCOUNT, 0, 0L); + } + + BOOL GetGroupInfoByIndex(int nIndex, PLVGROUP pGroup) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETGROUPINFOBYINDEX, nIndex, (LPARAM)pGroup); + } + + BOOL GetGroupRect(int nGroupID, int nType, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpRect != NULL); + if(lpRect != NULL) + lpRect->top = nType; + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETGROUPRECT, nGroupID, (LPARAM)lpRect); + } + + UINT GetGroupState(int nGroupID, UINT uMask) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, LVM_GETGROUPSTATE, nGroupID, (LPARAM)uMask); + } + + int GetFocusedGroup() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETFOCUSEDGROUP, 0, 0L); + } + + BOOL GetEmptyText(LPWSTR lpstrText, int cchText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETEMPTYTEXT, cchText, (LPARAM)lpstrText); + } + + BOOL GetFooterRect(LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETFOOTERRECT, 0, (LPARAM)lpRect); + } + + BOOL GetFooterInfo(LPLVFOOTERINFO lpFooterInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETFOOTERINFO, 0, (LPARAM)lpFooterInfo); + } + + BOOL GetFooterItemRect(int nItem, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETFOOTERITEMRECT, nItem, (LPARAM)lpRect); + } + + BOOL GetFooterItem(int nItem, LPLVFOOTERITEM lpFooterItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETFOOTERITEM, nItem, (LPARAM)lpFooterItem); + } + + BOOL GetItemIndexRect(PLVITEMINDEX pItemIndex, int nSubItem, int nType, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(pItemIndex != NULL); + ATLASSERT(lpRect != NULL); + if(lpRect != NULL) + { + lpRect->top = nSubItem; + lpRect->left = nType; + } + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETITEMINDEXRECT, (WPARAM)pItemIndex, (LPARAM)lpRect); + } + + BOOL SetItemIndexState(PLVITEMINDEX pItemIndex, UINT uState, UINT dwMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVITEM lvi = {}; + lvi.state = uState; + lvi.stateMask = dwMask; + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETITEMINDEXSTATE, (WPARAM)pItemIndex, (LPARAM)&lvi); + } + + BOOL GetNextItemIndex(PLVITEMINDEX pItemIndex, WORD wFlags) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_GETNEXTITEMINDEX, (WPARAM)pItemIndex, MAKELPARAM(wFlags, 0)); + } +#endif // (_WIN32_WINNT >= 0x0600) + +// Operations + int InsertColumn(int nCol, const LVCOLUMN* pColumn) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_INSERTCOLUMN, nCol, (LPARAM)pColumn); + } + + int InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, + int nWidth = -1, int nSubItem = -1, int iImage = -1, int iOrder = -1) + { + LVCOLUMN column = {}; + column.mask = LVCF_TEXT | LVCF_FMT; + column.pszText = (LPTSTR)lpszColumnHeading; + column.fmt = nFormat; + if (nWidth != -1) + { + column.mask |= LVCF_WIDTH; + column.cx = nWidth; + } + if (nSubItem != -1) + { + column.mask |= LVCF_SUBITEM; + column.iSubItem = nSubItem; + } + if (iImage != -1) + { + column.mask |= LVCF_IMAGE; + column.iImage = iImage; + } + if (iOrder != -1) + { + column.mask |= LVCF_ORDER; + column.iOrder = iOrder; + } + return InsertColumn(nCol, &column); + } + + BOOL DeleteColumn(int nCol) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_DELETECOLUMN, nCol, 0L); + } + + int InsertItem(UINT nMask, int nItem, LPCTSTR lpszItem, UINT nState, UINT nStateMask, int nImage, LPARAM lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVITEM item = {}; + item.mask = nMask; + item.iItem = nItem; + item.iSubItem = 0; + item.pszText = (LPTSTR)lpszItem; + item.state = nState; + item.stateMask = nStateMask; + item.iImage = nImage; + item.lParam = lParam; + return InsertItem(&item); + } + + int InsertItem(const LVITEM* pItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_INSERTITEM, 0, (LPARAM)pItem); + } + + int InsertItem(int nItem, LPCTSTR lpszItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return InsertItem(LVIF_TEXT, nItem, lpszItem, 0, 0, 0, 0); + } + + int InsertItem(int nItem, LPCTSTR lpszItem, int nImage) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return InsertItem(LVIF_TEXT|LVIF_IMAGE, nItem, lpszItem, 0, 0, nImage, 0); + } + + int GetNextItem(int nItem, int nFlags) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_GETNEXTITEM, nItem, MAKELPARAM(nFlags, 0)); + } + + BOOL DeleteItem(int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_DELETEITEM, nItem, 0L); + } + + BOOL DeleteAllItems() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_DELETEALLITEMS, 0, 0L); + } + + int FindItem(LVFINDINFO* pFindInfo, int nStart = -1) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_FINDITEM, nStart, (LPARAM)pFindInfo); + } + + int FindItem(LPCTSTR lpstrFind, bool bPartial = true, bool bWrap = false, int nStart = -1) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVFINDINFO lvfi = {}; + lvfi.flags = LVFI_STRING | (bWrap ? LVFI_WRAP : 0) | (bPartial ? LVFI_PARTIAL : 0); + lvfi.psz = lpstrFind; + return (int)::SendMessage(this->m_hWnd, LVM_FINDITEM, nStart, (LPARAM)&lvfi); + } + + int HitTest(LVHITTESTINFO* pHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_HITTEST, 0, (LPARAM)pHitTestInfo); + } + + int HitTest(POINT pt, UINT* pFlags) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVHITTESTINFO hti = {}; + hti.pt = pt; + int nRes = (int)::SendMessage(this->m_hWnd, LVM_HITTEST, 0, (LPARAM)&hti); + if (pFlags != NULL) + *pFlags = hti.flags; + return nRes; + } + + BOOL EnsureVisible(int nItem, BOOL bPartialOK) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_ENSUREVISIBLE, nItem, MAKELPARAM(bPartialOK, 0)); + } + + BOOL Scroll(int cx, int cy) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SCROLL, cx, cy); + } + + BOOL Scroll(SIZE size) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SCROLL, size.cx, size.cy); + } + + BOOL RedrawItems(int nFirst, int nLast) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_REDRAWITEMS, nFirst, nLast); + } + + BOOL Arrange(UINT nCode) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_ARRANGE, nCode, 0L); + } + + CEdit EditLabel(int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CEdit((HWND)::SendMessage(this->m_hWnd, LVM_EDITLABEL, nItem, 0L)); + } + + BOOL Update(int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_UPDATE, nItem, 0L); + } + + BOOL SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SORTITEMS, (WPARAM)lParamSort, (LPARAM)pfnCompare); + } + + CImageList RemoveImageList(int nImageList) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, LVM_SETIMAGELIST, (WPARAM)nImageList, NULL)); + } + + CImageList CreateDragImage(int nItem, LPPOINT lpPoint) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, LVM_CREATEDRAGIMAGE, nItem, (LPARAM)lpPoint)); + } + + DWORD ApproximateViewRect(int cx = -1, int cy = -1, int nCount = -1) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, LVM_APPROXIMATEVIEWRECT, nCount, MAKELPARAM(cx, cy)); + } + + int SubItemHitTest(LPLVHITTESTINFO lpInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_SUBITEMHITTEST, 0, (LPARAM)lpInfo); + } + + int AddColumn(LPCTSTR strColumn, int nItem, int nSubItem = -1, + int nMask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, + int nFmt = LVCFMT_LEFT) + { + const int cxOffset = 15; + ATLASSERT(::IsWindow(this->m_hWnd)); + LVCOLUMN lvc = {}; + lvc.mask = nMask; + lvc.fmt = nFmt; + lvc.pszText = (LPTSTR)strColumn; + lvc.cx = GetStringWidth(lvc.pszText) + cxOffset; + if(nMask & LVCF_SUBITEM) + lvc.iSubItem = (nSubItem != -1) ? nSubItem : nItem; + return InsertColumn(nItem, &lvc); + } + + int AddItem(int nItem, int nSubItem, LPCTSTR strItem, int nImageIndex = -3) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVITEM lvItem = {}; + lvItem.mask = LVIF_TEXT; + lvItem.iItem = nItem; + lvItem.iSubItem = nSubItem; + lvItem.pszText = (LPTSTR)strItem; + if(nImageIndex != -3) + { + lvItem.mask |= LVIF_IMAGE; + lvItem.iImage = nImageIndex; + } + if(nSubItem == 0) + return InsertItem(&lvItem); + return SetItem(&lvItem) ? nItem : -1; + } + + BOOL SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SORTITEMSEX, (WPARAM)lParamSort, (LPARAM)pfnCompare); + } + + int InsertGroup(int nItem, PLVGROUP pGroup) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_INSERTGROUP, nItem, (LPARAM)pGroup); + } + + int AddGroup(PLVGROUP pGroup) + { + return InsertGroup(-1, pGroup); + } + + int RemoveGroup(int nGroupID) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_REMOVEGROUP, nGroupID, 0L); + } + + void MoveGroup(int nGroupID, int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_MOVEGROUP, nGroupID, nItem); + } + + void MoveItemToGroup(int nItem, int nGroupID) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_MOVEITEMTOGROUP, nItem, nGroupID); + } + + int EnableGroupView(BOOL bEnable) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_ENABLEGROUPVIEW, bEnable, 0L); + } + + int SortGroups(PFNLVGROUPCOMPARE pCompareFunc, LPVOID lpVoid = NULL) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_SORTGROUPS, (WPARAM)pCompareFunc, (LPARAM)lpVoid); + } + + void InsertGroupSorted(PLVINSERTGROUPSORTED pInsertGroupSorted) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_INSERTGROUPSORTED, (WPARAM)pInsertGroupSorted, 0L); + } + + void RemoveAllGroups() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_REMOVEALLGROUPS, 0, 0L); + } + + BOOL HasGroup(int nGroupID) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_HASGROUP, nGroupID, 0L); + } + + BOOL InsertMarkHitTest(LPPOINT lpPoint, LPLVINSERTMARK pInsertMark) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_INSERTMARKHITTEST, (WPARAM)lpPoint, (LPARAM)pInsertMark); + } + + BOOL SetInfoTip(PLVSETINFOTIP pSetInfoTip) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_SETINFOTIP, 0, (LPARAM)pSetInfoTip); + } + + void CancelEditLabel() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, LVM_CANCELEDITLABEL, 0, 0L); + } + + UINT MapIndexToID(int nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, LVM_MAPINDEXTOID, nIndex, 0L); + } + + int MapIDToIndex(UINT uID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_MAPIDTOINDEX, uID, 0L); + } + + BOOL IsItemVisible(int nItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LVM_ISITEMVISIBLE, nItem, 0L); + } + +#if (_WIN32_WINNT >= 0x0600) + int HitTestEx(LPLVHITTESTINFO lpHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_HITTEST, (WPARAM)-1, (LPARAM)lpHitTestInfo); + } + + int HitTestEx(POINT pt, UINT* pFlags) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + LVHITTESTINFO hti = {}; + hti.pt = pt; + int nRes = (int)::SendMessage(this->m_hWnd, LVM_HITTEST, (WPARAM)-1, (LPARAM)&hti); + if (pFlags != NULL) + *pFlags = hti.flags; + return nRes; + } + + int SubItemHitTestEx(LPLVHITTESTINFO lpHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LVM_SUBITEMHITTEST, (WPARAM)-1, (LPARAM)lpHitTestInfo); + } +#endif // (_WIN32_WINNT >= 0x0600) + + // Note: selects only one item + BOOL SelectItem(int nIndex) // -1 to select none + { + ATLASSERT(::IsWindow(this->m_hWnd)); + + BOOL bRet = FALSE; + if(nIndex != -1) + { + // multi-selection only: de-select all items + if((this->GetStyle() & LVS_SINGLESEL) == 0) + SetItemState(-1, 0, LVIS_SELECTED); + + bRet = SetItemState(nIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + if(bRet) + { + SetSelectionMark(nIndex); + bRet = EnsureVisible(nIndex, FALSE); + } + } + else // no item specified, just de-select + { + bRet = SetItemState(-1, 0, LVIS_SELECTED); + } + + return bRet; + } + + // multi-selection only + BOOL SelectAllItems(bool bSelect = true) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & LVS_SINGLESEL) == 0); + + return SetItemState(-1, bSelect ? LVIS_SELECTED : 0, LVIS_SELECTED); + } +}; + +typedef CListViewCtrlT<ATL::CWindow> CListViewCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CTreeViewCtrl + +template <class TBase> +class CTreeViewCtrlT : public TBase +{ +public: +// Constructors + CTreeViewCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CTreeViewCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return WC_TREEVIEW; + } + + UINT GetCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, TVM_GETCOUNT, 0, 0L); + } + + UINT GetIndent() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, TVM_GETINDENT, 0, 0L); + } + + void SetIndent(UINT nIndent) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TVM_SETINDENT, nIndent, 0L); + } + + CImageList GetImageList(int nImageListType = TVSIL_NORMAL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TVM_GETIMAGELIST, (WPARAM)nImageListType, 0L)); + } + + CImageList SetImageList(HIMAGELIST hImageList, int nImageListType = TVSIL_NORMAL) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TVM_SETIMAGELIST, (WPARAM)nImageListType, (LPARAM)hImageList)); + } + + BOOL GetItem(LPTVITEM pItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)pItem); + } + + BOOL SetItem(LPTVITEM pItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SETITEM, 0, (LPARAM)pItem); + } + + BOOL SetItem(HTREEITEM hItem, UINT nMask, LPCTSTR lpszItem, int nImage, + int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVITEM item = {}; + item.hItem = hItem; + item.mask = nMask; + item.pszText = (LPTSTR) lpszItem; + item.iImage = nImage; + item.iSelectedImage = nSelectedImage; + item.state = nState; + item.stateMask = nStateMask; + item.lParam = lParam; + return (BOOL)::SendMessage(this->m_hWnd, TVM_SETITEM, 0, (LPARAM)&item); + } + + BOOL GetItemText(HTREEITEM hItem, LPTSTR lpstrText, int nLen) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpstrText != NULL); + + TVITEM item = {}; + item.hItem = hItem; + item.mask = TVIF_TEXT; + item.pszText = lpstrText; + item.cchTextMax = nLen; + + return (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); + } + + BOOL GetItemText(HTREEITEM hItem, BSTR& bstrText) const + { + USES_CONVERSION; + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(bstrText == NULL); + TVITEM item = {}; + item.hItem = hItem; + item.mask = TVIF_TEXT; + + LPTSTR lpstrText = NULL; + BOOL bRet = FALSE; + for(int nLen = 256; ; nLen *= 2) + { + ATLTRY(lpstrText = new TCHAR[nLen]); + if(lpstrText == NULL) + break; + lpstrText[0] = NULL; + item.pszText = lpstrText; + item.cchTextMax = nLen; + bRet = (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); + if(!bRet || (lstrlen(item.pszText) < (nLen - 1))) + break; + delete [] lpstrText; + lpstrText = NULL; + } + + if(lpstrText != NULL) + { + if(bRet) + bstrText = ::SysAllocString(T2OLE(lpstrText)); + delete [] lpstrText; + } + + return (bstrText != NULL) ? TRUE : FALSE; + } + +#ifdef __ATLSTR_H__ + BOOL GetItemText(HTREEITEM hItem, ATL::CString& strText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVITEM item = {}; + item.hItem = hItem; + item.mask = TVIF_TEXT; + + strText.Empty(); + BOOL bRet = FALSE; + for(int nLen = 256; ; nLen *= 2) + { + item.pszText = strText.GetBufferSetLength(nLen); + if(item.pszText == NULL) + { + bRet = FALSE; + break; + } + item.cchTextMax = nLen; + bRet = (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); + if(!bRet || (lstrlen(item.pszText) < (nLen - 1))) + break; + } + strText.ReleaseBuffer(); + return bRet; + } +#endif // __ATLSTR_H__ + + BOOL SetItemText(HTREEITEM hItem, LPCTSTR lpszItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItem(hItem, TVIF_TEXT, lpszItem, 0, 0, 0, 0, NULL); + } + + BOOL GetItemImage(HTREEITEM hItem, int& nImage, int& nSelectedImage) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVITEM item = {}; + item.hItem = hItem; + item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE; + BOOL bRes = (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); + if (bRes) + { + nImage = item.iImage; + nSelectedImage = item.iSelectedImage; + } + return bRes; + } + + BOOL SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItem(hItem, TVIF_IMAGE|TVIF_SELECTEDIMAGE, NULL, nImage, nSelectedImage, 0, 0, NULL); + } + + UINT GetItemState(HTREEITEM hItem, UINT nStateMask) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (((UINT)::SendMessage(this->m_hWnd, TVM_GETITEMSTATE, (WPARAM)hItem, (LPARAM)nStateMask)) & nStateMask); + } + + BOOL SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItem(hItem, TVIF_STATE, NULL, 0, 0, nState, nStateMask, NULL); + } + + DWORD_PTR GetItemData(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVITEM item = {}; + item.hItem = hItem; + item.mask = TVIF_PARAM; + BOOL bRet = (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); + return (DWORD_PTR)(bRet ? item.lParam : NULL); + } + + BOOL SetItemData(HTREEITEM hItem, DWORD_PTR dwData) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItem(hItem, TVIF_PARAM, NULL, 0, 0, 0, 0, (LPARAM)dwData); + } + + CEdit GetEditControl() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CEdit((HWND)::SendMessage(this->m_hWnd, TVM_GETEDITCONTROL, 0, 0L)); + } + + UINT GetVisibleCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, TVM_GETVISIBLECOUNT, 0, 0L); + } + + BOOL GetItemRect(HTREEITEM hItem, LPRECT lpRect, BOOL bTextOnly) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + *(HTREEITEM*)lpRect = hItem; + return (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEMRECT, (WPARAM)bTextOnly, (LPARAM)lpRect); + } + + BOOL ItemHasChildren(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVITEM item = {}; + item.hItem = hItem; + item.mask = TVIF_CHILDREN; + ::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); + return item.cChildren; + } + + CToolTipCtrl GetToolTips() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TVM_GETTOOLTIPS, 0, 0L)); + } + + CToolTipCtrl SetToolTips(HWND hWndTT) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TVM_SETTOOLTIPS, (WPARAM)hWndTT, 0L)); + } + + int GetISearchString(LPTSTR lpstr) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TVM_GETISEARCHSTRING, 0, (LPARAM)lpstr); + } + + // checkboxes only + BOOL GetCheckState(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & TVS_CHECKBOXES) != 0); + UINT uRet = GetItemState(hItem, TVIS_STATEIMAGEMASK); + return (uRet >> 12) - 1; + } + + BOOL SetCheckState(HTREEITEM hItem, BOOL bCheck) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & TVS_CHECKBOXES) != 0); + int nCheck = bCheck ? 2 : 1; // one based index + return SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nCheck), TVIS_STATEIMAGEMASK); + } + + // for standard and extended checkboxes (0 = no checkbox, 1 = unchecked, 2 = checked, >2 = optional extended check states) + UINT GetCheckStateEx(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(this->GetImageList(TVSIL_STATE) != NULL); + UINT uRet = GetItemState(hItem, TVIS_STATEIMAGEMASK); + return (uRet >> 12); + } + + BOOL SetCheckStateEx(HTREEITEM hItem, UINT uCheckState) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(this->GetImageList(TVSIL_STATE) != NULL); + ATLASSERT(uCheckState < (UINT)::ImageList_GetImageCount(this->GetImageList(TVSIL_STATE))); + return SetItemState(hItem, INDEXTOSTATEIMAGEMASK(uCheckState), TVIS_STATEIMAGEMASK); + } + + COLORREF GetBkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TVM_GETBKCOLOR, 0, 0L); + } + + COLORREF SetBkColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TVM_SETBKCOLOR, 0, (LPARAM)clr); + } + + COLORREF GetInsertMarkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TVM_GETINSERTMARKCOLOR, 0, 0L); + } + + COLORREF SetInsertMarkColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TVM_SETINSERTMARKCOLOR, 0, (LPARAM)clr); + } + + int GetItemHeight() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TVM_GETITEMHEIGHT, 0, 0L); + } + + int SetItemHeight(int cyHeight) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TVM_SETITEMHEIGHT, cyHeight, 0L); + } + + int GetScrollTime() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TVM_GETSCROLLTIME, 0, 0L); + } + + int SetScrollTime(int nScrollTime) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TVM_SETSCROLLTIME, nScrollTime, 0L); + } + + COLORREF GetTextColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TVM_GETTEXTCOLOR, 0, 0L); + } + + COLORREF SetTextColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TVM_SETTEXTCOLOR, 0, (LPARAM)clr); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SETUNICODEFORMAT, bUnicode, 0L); + } + + COLORREF GetLineColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TVM_GETLINECOLOR, 0, 0L); + } + + COLORREF SetLineColor(COLORREF clrNew /*= CLR_DEFAULT*/) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TVM_SETLINECOLOR, 0, (LPARAM)clrNew); + } + + BOOL GetItem(LPTVITEMEX pItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEM, 0, (LPARAM)pItem); + } + + BOOL SetItem(LPTVITEMEX pItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SETITEM, 0, (LPARAM)pItem); + } + + DWORD GetExtendedStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TVM_GETEXTENDEDSTYLE, 0, 0L); + } + + DWORD SetExtendedStyle(DWORD dwStyle, DWORD dwMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TVM_SETEXTENDEDSTYLE, dwMask, dwStyle); + } + +#if (_WIN32_WINNT >= 0x0600) + BOOL SetAutoScrollInfo(UINT uPixPerSec, UINT uUpdateTime) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SETAUTOSCROLLINFO, (WPARAM)uPixPerSec, (LPARAM)uUpdateTime); + } + + DWORD GetSelectedCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TVM_GETSELECTEDCOUNT, 0, 0L); + } + + BOOL GetItemPartRect(HTREEITEM hItem, TVITEMPART partID, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVGETITEMPARTRECTINFO gipri = { hItem, lpRect, partID }; + return (BOOL)::SendMessage(this->m_hWnd, TVM_GETITEMPARTRECT, 0, (LPARAM)&gipri); + } +#endif // (_WIN32_WINNT >= 0x0600) + +// Operations + HTREEITEM InsertItem(LPTVINSERTSTRUCT lpInsertStruct) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_INSERTITEM, 0, (LPARAM)lpInsertStruct); + } + + HTREEITEM InsertItem(LPCTSTR lpszItem, int nImage, + int nSelectedImage, HTREEITEM hParent, HTREEITEM hInsertAfter) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE, lpszItem, nImage, nSelectedImage, 0, 0, 0, hParent, hInsertAfter); + } + + HTREEITEM InsertItem(LPCTSTR lpszItem, HTREEITEM hParent, HTREEITEM hInsertAfter) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return InsertItem(TVIF_TEXT, lpszItem, 0, 0, 0, 0, 0, hParent, hInsertAfter); + } + + HTREEITEM InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage, + int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam, + HTREEITEM hParent, HTREEITEM hInsertAfter) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVINSERTSTRUCT tvis = {}; + tvis.hParent = hParent; + tvis.hInsertAfter = hInsertAfter; + tvis.item.mask = nMask; + tvis.item.pszText = (LPTSTR) lpszItem; + tvis.item.iImage = nImage; + tvis.item.iSelectedImage = nSelectedImage; + tvis.item.state = nState; + tvis.item.stateMask = nStateMask; + tvis.item.lParam = lParam; + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis); + } + + BOOL DeleteItem(HTREEITEM hItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_DELETEITEM, 0, (LPARAM)hItem); + } + + BOOL DeleteAllItems() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT); + } + + BOOL Expand(HTREEITEM hItem, UINT nCode = TVE_EXPAND) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_EXPAND, nCode, (LPARAM)hItem); + } + + HTREEITEM GetNextItem(HTREEITEM hItem, UINT nCode) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, nCode, (LPARAM)hItem); + } + + HTREEITEM GetChildItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); + } + + HTREEITEM GetNextSiblingItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem); + } + + HTREEITEM GetPrevSiblingItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUS, (LPARAM)hItem); + } + + HTREEITEM GetParentItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem); + } + + HTREEITEM GetFirstVisibleItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE, 0L); + } + + HTREEITEM GetNextVisibleItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, (LPARAM)hItem); + } + + HTREEITEM GetPrevVisibleItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUSVISIBLE, (LPARAM)hItem); + } + + HTREEITEM GetSelectedItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_CARET, 0L); + } + + HTREEITEM GetDropHilightItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_DROPHILITE, 0L); + } + + HTREEITEM GetRootItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0L); + } + + HTREEITEM GetLastVisibleItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_LASTVISIBLE, 0L); + } + + HTREEITEM GetNextSelectedItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, (LPARAM)hItem); + } + + BOOL Select(HTREEITEM hItem, UINT nCode) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SELECTITEM, nCode, (LPARAM)hItem); + } + + BOOL SelectItem(HTREEITEM hItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hItem); + } + + BOOL SelectDropTarget(HTREEITEM hItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SELECTITEM, TVGN_DROPHILITE, (LPARAM)hItem); + } + + BOOL SelectSetFirstVisible(HTREEITEM hItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SELECTITEM, TVGN_FIRSTVISIBLE, (LPARAM)hItem); + } + + CEdit EditLabel(HTREEITEM hItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CEdit((HWND)::SendMessage(this->m_hWnd, TVM_EDITLABEL, 0, (LPARAM)hItem)); + } + + BOOL EndEditLabelNow(BOOL bCancel) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_ENDEDITLABELNOW, bCancel, 0L); + } + + HTREEITEM HitTest(TVHITTESTINFO* pHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_HITTEST, 0, (LPARAM)pHitTestInfo); + } + + HTREEITEM HitTest(POINT pt, UINT* pFlags) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVHITTESTINFO hti = {}; + hti.pt = pt; + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_HITTEST, 0, (LPARAM)&hti); + if (pFlags != NULL) + *pFlags = hti.flags; + return hTreeItem; + } + + BOOL SortChildren(HTREEITEM hItem, BOOL bRecurse = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SORTCHILDREN, (WPARAM)bRecurse, (LPARAM)hItem); + } + + BOOL EnsureVisible(HTREEITEM hItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_ENSUREVISIBLE, 0, (LPARAM)hItem); + } + + BOOL SortChildrenCB(LPTVSORTCB pSort, BOOL bRecurse = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SORTCHILDRENCB, (WPARAM)bRecurse, (LPARAM)pSort); + } + + CImageList RemoveImageList(int nImageList) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TVM_SETIMAGELIST, (WPARAM)nImageList, NULL)); + } + + CImageList CreateDragImage(HTREEITEM hItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TVM_CREATEDRAGIMAGE, 0, (LPARAM)hItem)); + } + + BOOL SetInsertMark(HTREEITEM hTreeItem, BOOL bAfter) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SETINSERTMARK, bAfter, (LPARAM)hTreeItem); + } + + BOOL RemoveInsertMark() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TVM_SETINSERTMARK, 0, 0L); + } + + HTREEITEM MapAccIDToHTREEITEM(UINT uID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HTREEITEM)::SendMessage(this->m_hWnd, TVM_MAPACCIDTOHTREEITEM, uID, 0L); + } + + UINT MapHTREEITEMToAccID(HTREEITEM hTreeItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, TVM_MAPHTREEITEMTOACCID, (WPARAM)hTreeItem, 0L); + } + +#if (_WIN32_WINNT >= 0x0600) + void ShowInfoTip(HTREEITEM hItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TVM_SHOWINFOTIP, 0, (LPARAM)hItem); + } +#endif // (_WIN32_WINNT >= 0x0600) +}; + +typedef CTreeViewCtrlT<ATL::CWindow> CTreeViewCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CTreeViewCtrlEx + +// forward declaration +template <class TBase> class CTreeViewCtrlExT; + +// Note: TBase here is for CTreeViewCtrlExT, and not for CTreeItemT itself +template <class TBase> +class CTreeItemT +{ +public: + HTREEITEM m_hTreeItem; + CTreeViewCtrlExT<TBase>* m_pTreeView; + +// Construction + CTreeItemT(HTREEITEM hTreeItem = NULL, CTreeViewCtrlExT<TBase>* pTreeView = NULL) : m_hTreeItem(hTreeItem), m_pTreeView(pTreeView) + { } + + CTreeItemT(const CTreeItemT<TBase>& posSrc) + { + *this = posSrc; + } + + operator HTREEITEM() { return m_hTreeItem; } + + CTreeItemT<TBase>& operator =(const CTreeItemT<TBase>& itemSrc) + { + m_hTreeItem = itemSrc.m_hTreeItem; + m_pTreeView = itemSrc.m_pTreeView; + return *this; + } + +// Attributes + CTreeViewCtrlExT<TBase>* GetTreeView() const { return m_pTreeView; } + + BOOL operator !() const { return m_hTreeItem == NULL; } + + BOOL IsNull() const { return m_hTreeItem == NULL; } + + BOOL GetRect(LPRECT lpRect, BOOL bTextOnly) const; + BOOL GetText(LPTSTR lpstrText, int nLen) const; + BOOL GetText(BSTR& bstrText) const; +#ifdef __ATLSTR_H__ + BOOL GetText(ATL::CString& strText) const; +#endif // __ATLSTR_H__ + BOOL SetText(LPCTSTR lpszItem); + BOOL GetImage(int& nImage, int& nSelectedImage) const; + BOOL SetImage(int nImage, int nSelectedImage); + UINT GetState(UINT nStateMask) const; + BOOL SetState(UINT nState, UINT nStateMask); + DWORD_PTR GetData() const; + BOOL SetData(DWORD_PTR dwData); + BOOL SetItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam); + +// Operations + CTreeItemT<TBase> InsertAfter(LPCTSTR lpstrItem, HTREEITEM hItemAfter, int nImageIndex) + { + return _Insert(lpstrItem, nImageIndex, hItemAfter); + } + + CTreeItemT<TBase> AddHead(LPCTSTR lpstrItem, int nImageIndex) + { + return _Insert(lpstrItem, nImageIndex, TVI_FIRST); + } + + CTreeItemT<TBase> AddTail(LPCTSTR lpstrItem, int nImageIndex) + { + return _Insert(lpstrItem, nImageIndex, TVI_LAST); + } + + CTreeItemT<TBase> GetChild() const; + CTreeItemT<TBase> GetNext(UINT nCode) const; + CTreeItemT<TBase> GetNextSibling() const; + CTreeItemT<TBase> GetPrevSibling() const; + CTreeItemT<TBase> GetParent() const; + CTreeItemT<TBase> GetFirstVisible() const; + CTreeItemT<TBase> GetNextVisible() const; + CTreeItemT<TBase> GetPrevVisible() const; + CTreeItemT<TBase> GetSelected() const; + CTreeItemT<TBase> GetDropHilight() const; + CTreeItemT<TBase> GetRoot() const; + CTreeItemT<TBase> GetLastVisible() const; + CTreeItemT<TBase> GetNextSelected() const; + BOOL HasChildren() const; + BOOL Delete(); + BOOL Expand(UINT nCode = TVE_EXPAND); + BOOL Select(UINT nCode); + BOOL Select(); + BOOL SelectDropTarget(); + BOOL SelectSetFirstVisible(); + HWND EditLabel(); + HIMAGELIST CreateDragImage(); + BOOL SortChildren(BOOL bRecurse = FALSE); + BOOL EnsureVisible(); + CTreeItemT<TBase> _Insert(LPCTSTR lpstrItem, int nImageIndex, HTREEITEM hItemAfter); + int GetImageIndex() const; + BOOL SetInsertMark(BOOL bAfter); + UINT MapHTREEITEMToAccID() const; +#if (_WIN32_WINNT >= 0x0600) + void ShowInfoTip(); + BOOL GetPartRect(TVITEMPART partID, LPRECT lpRect) const; +#endif // (_WIN32_WINNT >= 0x0600) +}; + +typedef CTreeItemT<ATL::CWindow> CTreeItem; + + +template <class TBase> +class CTreeViewCtrlExT : public CTreeViewCtrlT< TBase > +{ +public: +// Constructors + CTreeViewCtrlExT(HWND hWnd = NULL) : CTreeViewCtrlT< TBase >(hWnd) + { } + + CTreeViewCtrlExT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + +// Operations (overides that return CTreeItem) + CTreeItemT<TBase> InsertItem(LPTVINSERTSTRUCT lpInsertStruct) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_INSERTITEM, 0, (LPARAM)lpInsertStruct); + return CTreeItemT<TBase>(hTreeItem, this); + } + + CTreeItemT<TBase> InsertItem(LPCTSTR lpszItem, int nImage, + int nSelectedImage, HTREEITEM hParent, HTREEITEM hInsertAfter) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE, lpszItem, nImage, nSelectedImage, 0, 0, 0, hParent, hInsertAfter); + } + + CTreeItemT<TBase> InsertItem(LPCTSTR lpszItem, HTREEITEM hParent, HTREEITEM hInsertAfter) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return InsertItem(TVIF_TEXT, lpszItem, 0, 0, 0, 0, 0, hParent, hInsertAfter); + } + + CTreeItemT<TBase> GetNextItem(HTREEITEM hItem, UINT nCode) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, nCode, (LPARAM)hItem); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetChildItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetNextSiblingItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetPrevSiblingItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUS, (LPARAM)hItem); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetParentItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetFirstVisibleItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE, 0L); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetNextVisibleItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, (LPARAM)hItem); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetPrevVisibleItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUSVISIBLE, (LPARAM)hItem); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetSelectedItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_CARET, 0L); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetDropHilightItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_DROPHILITE, 0L); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetRootItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0L); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetLastVisibleItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_LASTVISIBLE, 0L); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> GetNextSelectedItem(HTREEITEM hItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, (LPARAM)hItem); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> HitTest(TVHITTESTINFO* pHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_HITTEST, 0, (LPARAM)pHitTestInfo); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage, + int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam, + HTREEITEM hParent, HTREEITEM hInsertAfter) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVINSERTSTRUCT tvis = {}; + tvis.hParent = hParent; + tvis.hInsertAfter = hInsertAfter; + tvis.item.mask = nMask; + tvis.item.pszText = (LPTSTR) lpszItem; + tvis.item.iImage = nImage; + tvis.item.iSelectedImage = nSelectedImage; + tvis.item.state = nState; + tvis.item.stateMask = nStateMask; + tvis.item.lParam = lParam; + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis); + return CTreeItemT<TBase>(hTreeItem, this); + } + + CTreeItemT<TBase> HitTest(POINT pt, UINT* pFlags) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TVHITTESTINFO hti = {}; + hti.pt = pt; + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_HITTEST, 0, (LPARAM)&hti); + if (pFlags != NULL) + *pFlags = hti.flags; + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } + + CTreeItemT<TBase> MapAccIDToHTREEITEM(UINT uID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(this->m_hWnd, TVM_MAPACCIDTOHTREEITEM, uID, 0L); + return CTreeItemT<TBase>(hTreeItem, (CTreeViewCtrlExT<TBase>*)this); + } +}; + +typedef CTreeViewCtrlExT<ATL::CWindow> CTreeViewCtrlEx; + + +// CTreeItem inline methods +template <class TBase> +inline BOOL CTreeItemT<TBase>::GetRect(LPRECT lpRect, BOOL bTextOnly) const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetItemRect(m_hTreeItem,lpRect,bTextOnly); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetNext(UINT nCode) const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetNextItem(m_hTreeItem,nCode); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetChild() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetChildItem(m_hTreeItem); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetNextSibling() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetNextSiblingItem(m_hTreeItem); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetPrevSibling() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetPrevSiblingItem(m_hTreeItem); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetParent() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetParentItem(m_hTreeItem); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetFirstVisible() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetFirstVisibleItem(); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetNextVisible() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetNextVisibleItem(m_hTreeItem); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetPrevVisible() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetPrevVisibleItem(m_hTreeItem); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetSelected() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetSelectedItem(); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetDropHilight() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetDropHilightItem(); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetRoot() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetRootItem(); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetLastVisible() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetLastVisibleItem(); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::GetNextSelected() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetNextSelectedItem(m_hTreeItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::GetText(LPTSTR lpstrText, int nLen) const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetItemText(m_hTreeItem, lpstrText, nLen); +} + +#ifdef _OLEAUTO_H_ +template <class TBase> +inline BOOL CTreeItemT<TBase>::GetText(BSTR& bstrText) const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetItemText(m_hTreeItem, bstrText); +} +#endif // _OLEAUTO_H_ + +#ifdef __ATLSTR_H__ +template <class TBase> +inline BOOL CTreeItemT<TBase>::GetText(ATL::CString& strText) const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetItemText(m_hTreeItem, strText); +} +#endif // __ATLSTR_H__ + +template <class TBase> +inline BOOL CTreeItemT<TBase>::GetImage(int& nImage, int& nSelectedImage) const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetItemImage(m_hTreeItem,nImage,nSelectedImage); +} + +template <class TBase> +inline UINT CTreeItemT<TBase>::GetState(UINT nStateMask) const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetItemState(m_hTreeItem,nStateMask); +} + +template <class TBase> +inline DWORD_PTR CTreeItemT<TBase>::GetData() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetItemData(m_hTreeItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SetItem(UINT nMask, LPCTSTR lpszItem, int nImage, + int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SetItem(m_hTreeItem, nMask, lpszItem, nImage, nSelectedImage, nState, nStateMask, lParam); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SetText(LPCTSTR lpszItem) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SetItemText(m_hTreeItem,lpszItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SetImage(int nImage, int nSelectedImage) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SetItemImage(m_hTreeItem,nImage,nSelectedImage); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SetState(UINT nState, UINT nStateMask) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SetItemState(m_hTreeItem,nState,nStateMask); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SetData(DWORD_PTR dwData) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SetItemData(m_hTreeItem,dwData); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::HasChildren() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->ItemHasChildren(m_hTreeItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::Delete() +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->DeleteItem(m_hTreeItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::Expand(UINT nCode /*= TVE_EXPAND*/) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->Expand(m_hTreeItem,nCode); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::Select(UINT nCode) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->Select(m_hTreeItem,nCode); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::Select() +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SelectItem(m_hTreeItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SelectDropTarget() +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SelectDropTarget(m_hTreeItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SelectSetFirstVisible() +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SelectSetFirstVisible(m_hTreeItem); +} + +template <class TBase> +inline HWND CTreeItemT<TBase>::EditLabel() +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->EditLabel(m_hTreeItem); +} + +template <class TBase> +inline HIMAGELIST CTreeItemT<TBase>::CreateDragImage() +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->CreateDragImage(m_hTreeItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SortChildren(BOOL bRecurse /*= FALSE*/) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SortChildren(m_hTreeItem, bRecurse); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::EnsureVisible() +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->EnsureVisible(m_hTreeItem); +} + +template <class TBase> +inline CTreeItemT<TBase> CTreeItemT<TBase>::_Insert(LPCTSTR lpstrItem, int nImageIndex, HTREEITEM hItemAfter) +{ + ATLASSERT(m_pTreeView != NULL); + TVINSERTSTRUCT ins = {}; + ins.hParent = m_hTreeItem; + ins.hInsertAfter = hItemAfter; + ins.item.mask = TVIF_TEXT; + ins.item.pszText = (LPTSTR)lpstrItem; + if(nImageIndex != -1) + { + ins.item.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; + ins.item.iImage = nImageIndex; + ins.item.iSelectedImage = nImageIndex; + } + return CTreeItemT<TBase>(m_pTreeView->InsertItem(&ins), m_pTreeView); +} + +template <class TBase> +inline int CTreeItemT<TBase>::GetImageIndex() const +{ + ATLASSERT(m_pTreeView != NULL); + TVITEM item = {}; + item.mask = TVIF_HANDLE | TVIF_IMAGE; + item.hItem = m_hTreeItem; + m_pTreeView->GetItem(&item); + return item.iImage; +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::SetInsertMark(BOOL bAfter) +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->SetInsertMark(m_hTreeItem, bAfter); +} + +template <class TBase> +inline UINT CTreeItemT<TBase>::MapHTREEITEMToAccID() const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->MapHTREEITEMToAccID(m_hTreeItem); +} + +#if (_WIN32_WINNT >= 0x0600) +template <class TBase> +inline void CTreeItemT<TBase>::ShowInfoTip() +{ + ATLASSERT(m_pTreeView != NULL); + m_pTreeView->ShowInfoTip(m_hTreeItem); +} + +template <class TBase> +inline BOOL CTreeItemT<TBase>::GetPartRect(TVITEMPART partID, LPRECT lpRect) const +{ + ATLASSERT(m_pTreeView != NULL); + return m_pTreeView->GetItemPartRect(m_hTreeItem, partID, lpRect); +} +#endif // (_WIN32_WINNT >= 0x0600) + + +/////////////////////////////////////////////////////////////////////////////// +// CToolBarCtrl + +template <class TBase> +class CToolBarCtrlT : public TBase +{ +public: +// Construction + CToolBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CToolBarCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return TOOLBARCLASSNAME; + } + + BOOL IsButtonEnabled(int nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONENABLED, nID, 0L); + } + + BOOL IsButtonChecked(int nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONCHECKED, nID, 0L); + } + + BOOL IsButtonPressed(int nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONPRESSED, nID, 0L); + } + + BOOL IsButtonHidden(int nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return(BOOL) ::SendMessage(this->m_hWnd, TB_ISBUTTONHIDDEN, nID, 0L); + } + + BOOL IsButtonIndeterminate(int nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONINDETERMINATE, nID, 0L); + } + + int GetState(int nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_GETSTATE, nID, 0L); + } + + BOOL SetState(int nID, UINT nState) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETSTATE, nID, MAKELPARAM(nState, 0)); + } + + BOOL GetButton(int nIndex, LPTBBUTTON lpButton) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_GETBUTTON, nIndex, (LPARAM)lpButton); + } + + int GetButtonCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_BUTTONCOUNT, 0, 0L); + } + + BOOL GetItemRect(int nIndex, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_GETITEMRECT, nIndex, (LPARAM)lpRect); + } + + void SetButtonStructSize(int nSize = sizeof(TBBUTTON)) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_BUTTONSTRUCTSIZE, nSize, 0L); + } + + BOOL SetButtonSize(SIZE size) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONSIZE, 0, MAKELPARAM(size.cx, size.cy)); + } + + BOOL SetButtonSize(int cx, int cy) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONSIZE, 0, MAKELPARAM(cx, cy)); + } + + BOOL SetBitmapSize(SIZE size) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETBITMAPSIZE, 0, MAKELPARAM(size.cx, size.cy)); + } + + BOOL SetBitmapSize(int cx, int cy) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETBITMAPSIZE, 0, MAKELPARAM(cx, cy)); + } + + CToolTipCtrl GetToolTips() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TB_GETTOOLTIPS, 0, 0L)); + } + + void SetToolTips(HWND hWndToolTip) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_SETTOOLTIPS, (WPARAM)hWndToolTip, 0L); + } + + void SetNotifyWnd(HWND hWnd) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_SETPARENT, (WPARAM)hWnd, 0L); + } + + int GetRows() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_GETROWS, 0, 0L); + } + + void SetRows(int nRows, BOOL bLarger, LPRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_SETROWS, MAKELPARAM(nRows, bLarger), (LPARAM)lpRect); + } + + BOOL SetCmdID(int nIndex, UINT nID) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETCMDID, nIndex, nID); + } + + DWORD GetBitmapFlags() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TB_GETBITMAPFLAGS, 0, 0L); + } + + int GetBitmap(int nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_GETBITMAP, nID, 0L); + } + + int GetButtonText(int nID, LPTSTR lpstrText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_GETBUTTONTEXT, nID, (LPARAM)lpstrText); + } + + // nIndex - IE5 or higher only + CImageList GetImageList(int nIndex = 0) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_GETIMAGELIST, nIndex, 0L)); + } + + // nIndex - IE5 or higher only + CImageList SetImageList(HIMAGELIST hImageList, int nIndex = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_SETIMAGELIST, nIndex, (LPARAM)hImageList)); + } + + // nIndex - IE5 or higher only + CImageList GetDisabledImageList(int nIndex = 0) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_GETDISABLEDIMAGELIST, nIndex, 0L)); + } + + // nIndex - IE5 or higher only + CImageList SetDisabledImageList(HIMAGELIST hImageList, int nIndex = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_SETDISABLEDIMAGELIST, nIndex, (LPARAM)hImageList)); + } + + // nIndex - IE5 or higher only + CImageList GetHotImageList(int nIndex = 0) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_GETHOTIMAGELIST, nIndex, 0L)); + } + + // nIndex - IE5 or higher only + CImageList SetHotImageList(HIMAGELIST hImageList, int nIndex = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_SETHOTIMAGELIST, nIndex, (LPARAM)hImageList)); + } + + DWORD GetStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TB_GETSTYLE, 0, 0L); + } + + void SetStyle(DWORD dwStyle) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_SETSTYLE, 0, dwStyle); + } + + DWORD GetButtonSize() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TB_GETBUTTONSIZE, 0, 0L); + } + + void GetButtonSize(SIZE& size) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, TB_GETBUTTONSIZE, 0, 0L); + size.cx = LOWORD(dwRet); + size.cy = HIWORD(dwRet); + } + + BOOL GetRect(int nID, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_GETRECT, nID, (LPARAM)lpRect); + } + + int GetTextRows() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_GETTEXTROWS, 0, 0L); + } + + BOOL SetButtonWidth(int cxMin, int cxMax) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONWIDTH, 0, MAKELPARAM(cxMin, cxMax)); + } + + BOOL SetIndent(int nIndent) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETINDENT, nIndent, 0L); + } + + BOOL SetMaxTextRows(int nMaxTextRows) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETMAXTEXTROWS, nMaxTextRows, 0L); + } + + BOOL GetAnchorHighlight() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_GETANCHORHIGHLIGHT, 0, 0L); + } + + BOOL SetAnchorHighlight(BOOL bEnable = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETANCHORHIGHLIGHT, bEnable, 0L); + } + + int GetButtonInfo(int nID, LPTBBUTTONINFO lptbbi) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_GETBUTTONINFO, nID, (LPARAM)lptbbi); + } + + BOOL SetButtonInfo(int nID, LPTBBUTTONINFO lptbbi) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONINFO, nID, (LPARAM)lptbbi); + } + + BOOL SetButtonInfo(int nID, DWORD dwMask, BYTE Style, BYTE State, LPCTSTR lpszItem, + int iImage, WORD cx, int iCommand, DWORD_PTR lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TBBUTTONINFO tbbi = {}; + tbbi.cbSize = sizeof(TBBUTTONINFO); + tbbi.dwMask = dwMask; + tbbi.idCommand = iCommand; + tbbi.iImage = iImage; + tbbi.fsState = State; + tbbi.fsStyle = Style; + tbbi.cx = cx; + tbbi.pszText = (LPTSTR) lpszItem; + tbbi.lParam = lParam; + return (BOOL)::SendMessage(this->m_hWnd, TB_SETBUTTONINFO, nID, (LPARAM)&tbbi); + } + + int GetHotItem() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_GETHOTITEM, 0, 0L); + } + + int SetHotItem(int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_SETHOTITEM, nItem, 0L); + } + + BOOL IsButtonHighlighted(int nButtonID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_ISBUTTONHIGHLIGHTED, nButtonID, 0L); + } + + DWORD SetDrawTextFlags(DWORD dwMask, DWORD dwFlags) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TB_SETDRAWTEXTFLAGS, dwMask, dwFlags); + } + + BOOL GetColorScheme(LPCOLORSCHEME lpcs) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_GETCOLORSCHEME, 0, (LPARAM)lpcs); + } + + void SetColorScheme(LPCOLORSCHEME lpcs) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_SETCOLORSCHEME, 0, (LPARAM)lpcs); + } + + DWORD GetExtendedStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TB_GETEXTENDEDSTYLE, 0, 0L); + } + + DWORD SetExtendedStyle(DWORD dwStyle) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TB_SETEXTENDEDSTYLE, 0, dwStyle); + } + + void GetInsertMark(LPTBINSERTMARK lptbim) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_GETINSERTMARK, 0, (LPARAM)lptbim); + } + + void SetInsertMark(LPTBINSERTMARK lptbim) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_SETINSERTMARK, 0, (LPARAM)lptbim); + } + + COLORREF GetInsertMarkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TB_GETINSERTMARKCOLOR, 0, 0L); + } + + COLORREF SetInsertMarkColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, TB_SETINSERTMARKCOLOR, 0, (LPARAM)clr); + } + + BOOL GetMaxSize(LPSIZE lpSize) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_GETMAXSIZE, 0, (LPARAM)lpSize); + } + + void GetPadding(LPSIZE lpSizePadding) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpSizePadding != NULL); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, TB_GETPADDING, 0, 0L); + lpSizePadding->cx = GET_X_LPARAM(dwRet); + lpSizePadding->cy = GET_Y_LPARAM(dwRet); + } + + void SetPadding(int cx, int cy, LPSIZE lpSizePadding = NULL) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, TB_SETPADDING, 0, MAKELPARAM(cx, cy)); + if(lpSizePadding != NULL) + { + lpSizePadding->cx = GET_X_LPARAM(dwRet); + lpSizePadding->cy = GET_Y_LPARAM(dwRet); + } + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_SETUNICODEFORMAT, bUnicode, 0L); + } + + int GetString(int nString, LPTSTR lpstrString, int cchMaxLen) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(cchMaxLen, nString), (LPARAM)lpstrString); + } + + int GetStringBSTR(int nString, BSTR& bstrString) const + { + USES_CONVERSION; + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(bstrString == NULL); + int nLength = (int)(short)LOWORD(::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(0, nString), NULL)); + if(nLength != -1) + { + ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff; + LPTSTR lpstrText = buff.Allocate(nLength + 1); + if(lpstrText != NULL) + { + nLength = (int)::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(nLength + 1, nString), (LPARAM)lpstrText); + if(nLength != -1) + bstrString = ::SysAllocString(T2OLE(lpstrText)); + } + else + { + nLength = -1; + } + } + + return nLength; + } + +#ifdef __ATLSTR_H__ + int GetString(int nString, ATL::CString& str) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + int nLength = (int)(short)LOWORD(::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(0, nString), NULL)); + if(nLength != -1) + { + LPTSTR lpstr = str.GetBufferSetLength(nLength + 1); + if(lpstr != NULL) + nLength = (int)::SendMessage(this->m_hWnd, TB_GETSTRING, MAKEWPARAM(nLength + 1, nString), (LPARAM)lpstr); + else + nLength = -1; + str.ReleaseBuffer(); + } + return nLength; + } +#endif // __ATLSTR_H__ + + void GetMetrics(LPTBMETRICS lptbm) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_GETMETRICS, 0, (LPARAM)lptbm); + } + + void SetMetrics(LPTBMETRICS lptbm) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_SETMETRICS, 0, (LPARAM)lptbm); + } + + void SetWindowTheme(LPCWSTR lpstrTheme) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme); + } + +#if (_WIN32_WINNT >= 0x0600) + CImageList GetPressedImageList(int nIndex = 0) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_GETPRESSEDIMAGELIST, nIndex, 0L)); + } + + CImageList SetPressedImageList(HIMAGELIST hImageList, int nIndex = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TB_SETPRESSEDIMAGELIST, nIndex, (LPARAM)hImageList)); + } + + void GetItemDropDownRect(int nIndex, LPRECT lpRect) const + { +#ifndef TB_GETITEMDROPDOWNRECT + const int TB_GETITEMDROPDOWNRECT = WM_USER + 103; +#endif + ATLASSERT(::IsWindow(this->m_hWnd)); + BOOL bRet = (BOOL)::SendMessage(this->m_hWnd, TB_GETITEMDROPDOWNRECT, nIndex, (LPARAM)lpRect); + (void)bRet; // avoid level 4 warning + ATLASSERT(bRet != FALSE); + } +#endif // (_WIN32_WINNT >= 0x0600) + +// Operations + BOOL EnableButton(int nID, BOOL bEnable = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_ENABLEBUTTON, nID, MAKELPARAM(bEnable, 0)); + } + + BOOL CheckButton(int nID, BOOL bCheck = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_CHECKBUTTON, nID, MAKELPARAM(bCheck, 0)); + } + + BOOL PressButton(int nID, BOOL bPress = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_PRESSBUTTON, nID, MAKELPARAM(bPress, 0)); + } + + BOOL HideButton(int nID, BOOL bHide = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_HIDEBUTTON, nID, MAKELPARAM(bHide, 0)); + } + + BOOL Indeterminate(int nID, BOOL bIndeterminate = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_INDETERMINATE, nID, MAKELPARAM(bIndeterminate, 0)); + } + + int AddBitmap(int nNumButtons, UINT nBitmapID) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TBADDBITMAP tbab = {}; + tbab.hInst = ModuleHelper::GetResourceInstance(); + ATLASSERT(tbab.hInst != NULL); + tbab.nID = nBitmapID; + return (int)::SendMessage(this->m_hWnd, TB_ADDBITMAP, (WPARAM)nNumButtons, (LPARAM)&tbab); + } + + int AddBitmap(int nNumButtons, HBITMAP hBitmap) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TBADDBITMAP tbab = {}; + tbab.hInst = NULL; + tbab.nID = (UINT_PTR)hBitmap; + return (int)::SendMessage(this->m_hWnd, TB_ADDBITMAP, (WPARAM)nNumButtons, (LPARAM)&tbab); + } + + BOOL AddButtons(int nNumButtons, LPCTBBUTTON lpButtons) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_ADDBUTTONS, nNumButtons, (LPARAM)lpButtons); + } + + BOOL InsertButton(int nIndex, LPCTBBUTTON lpButton) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_INSERTBUTTON, nIndex, (LPARAM)lpButton); + } + + BOOL InsertButton(int nIndex, int iCommand, BYTE Style, BYTE State, int iBitmap, + INT_PTR iString, DWORD_PTR lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TBBUTTON tbb = {}; + tbb.fsStyle = Style; + tbb.fsState = State; + tbb.idCommand = iCommand; + tbb.iBitmap = iBitmap; + tbb.iString = iString; + tbb.dwData = lParam; + return (BOOL)::SendMessage(this->m_hWnd, TB_INSERTBUTTON, nIndex, (LPARAM)&tbb); + } + + BOOL InsertButton(int nIndex, int iCommand, BYTE Style, BYTE State, int iBitmap, + LPCTSTR lpszItem, DWORD_PTR lParam) + { + return InsertButton(nIndex, iCommand, Style, State, iBitmap, (INT_PTR)lpszItem, lParam); + } + + BOOL AddButton(LPTBBUTTON lpButton) + { + return InsertButton(-1, lpButton); + } + + BOOL AddButton(int iCommand, BYTE Style, BYTE State, int iBitmap, INT_PTR iString, DWORD_PTR lParam) + { + return InsertButton(-1, iCommand, Style, State, iBitmap, iString, lParam); + } + + BOOL AddButton(int iCommand, BYTE Style, BYTE State, int iBitmap, LPCTSTR lpszItem, DWORD_PTR lParam) + { + return InsertButton(-1, iCommand, Style, State, iBitmap, lpszItem, lParam); + } + + BOOL DeleteButton(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_DELETEBUTTON, nIndex, 0L); + } + + BOOL InsertSeparator(int nIndex, int cxWidth = 8) + { + return InsertButton(nIndex, 0, BTNS_SEP, 0, cxWidth, (INT_PTR)0, 0); + } + + BOOL AddSeparator(int cxWidth = 8) + { + return AddButton(0, BTNS_SEP, 0, cxWidth, (INT_PTR)0, 0); + } + + int CommandToIndex(UINT nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_COMMANDTOINDEX, nID, 0L); + } + + void SaveState(HKEY hKeyRoot, LPCTSTR lpszSubKey, LPCTSTR lpszValueName) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TBSAVEPARAMS tbs = {}; + tbs.hkr = hKeyRoot; + tbs.pszSubKey = lpszSubKey; + tbs.pszValueName = lpszValueName; + ::SendMessage(this->m_hWnd, TB_SAVERESTORE, (WPARAM)TRUE, (LPARAM)&tbs); + } + + void RestoreState(HKEY hKeyRoot, LPCTSTR lpszSubKey, LPCTSTR lpszValueName) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TBSAVEPARAMS tbs = {}; + tbs.hkr = hKeyRoot; + tbs.pszSubKey = lpszSubKey; + tbs.pszValueName = lpszValueName; + ::SendMessage(this->m_hWnd, TB_SAVERESTORE, (WPARAM)FALSE, (LPARAM)&tbs); + } + + void Customize() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_CUSTOMIZE, 0, 0L); + } + + int AddString(UINT nStringID) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_ADDSTRING, (WPARAM)ModuleHelper::GetResourceInstance(), (LPARAM)nStringID); + } + + int AddStrings(LPCTSTR lpszStrings) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_ADDSTRING, 0, (LPARAM)lpszStrings); + } + + void AutoSize() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TB_AUTOSIZE, 0, 0L); + } + + BOOL ChangeBitmap(int nID, int nBitmap) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_CHANGEBITMAP, nID, MAKELPARAM(nBitmap, 0)); + } + + int LoadImages(int nBitmapID) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_LOADIMAGES, nBitmapID, (LPARAM)ModuleHelper::GetResourceInstance()); + } + + int LoadStdImages(int nBitmapID) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_LOADIMAGES, nBitmapID, (LPARAM)HINST_COMMCTRL); + } + + BOOL ReplaceBitmap(LPTBREPLACEBITMAP ptbrb) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_REPLACEBITMAP, 0, (LPARAM)ptbrb); + } + + int HitTest(LPPOINT lpPoint) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TB_HITTEST, 0, (LPARAM)lpPoint); + } + + BOOL InsertMarkHitTest(LPPOINT lpPoint, LPTBINSERTMARK lptbim) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_INSERTMARKHITTEST, (WPARAM)lpPoint, (LPARAM)lptbim); + } + + BOOL InsertMarkHitTest(int x, int y, LPTBINSERTMARK lptbim) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + POINT pt = { x, y }; + return (BOOL)::SendMessage(this->m_hWnd, TB_INSERTMARKHITTEST, (WPARAM)&pt, (LPARAM)lptbim); + } + + BOOL MapAccelerator(TCHAR chAccel, int& nID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_MAPACCELERATOR, (WPARAM)chAccel, (LPARAM)&nID); + } + + BOOL MarkButton(int nID, BOOL bHighlight = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_MARKBUTTON, nID, MAKELPARAM(bHighlight, 0)); + } + + BOOL MoveButton(int nOldPos, int nNewPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TB_MOVEBUTTON, nOldPos, nNewPos); + } + + HRESULT GetObject(REFIID iid, LPVOID* ppvObject) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HRESULT)::SendMessage(this->m_hWnd, TB_GETOBJECT, (WPARAM)&iid, (LPARAM)ppvObject); + } +}; + +typedef CToolBarCtrlT<ATL::CWindow> CToolBarCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CStatusBarCtrl + +template <class TBase> +class CStatusBarCtrlT : public TBase +{ +public: +// Constructors + CStatusBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CStatusBarCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Methods + static LPCTSTR GetWndClassName() + { + return STATUSCLASSNAME; + } + + int GetParts(int nParts, int* pParts) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, SB_GETPARTS, nParts, (LPARAM)pParts); + } + + BOOL SetParts(int nParts, int* pWidths) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, SB_SETPARTS, nParts, (LPARAM)pWidths); + } + + int GetTextLength(int nPane, int* pType = NULL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L); + if (pType != NULL) + *pType = (int)(short)HIWORD(dwRet); + return (int)(short)LOWORD(dwRet); + } + + int GetText(int nPane, LPTSTR lpszText, int* pType = NULL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, SB_GETTEXT, (WPARAM)nPane, (LPARAM)lpszText); + if(pType != NULL) + *pType = (int)(short)HIWORD(dwRet); + return (int)(short)LOWORD(dwRet); + } + + BOOL GetTextBSTR(int nPane, BSTR& bstrText, int* pType = NULL) const + { + USES_CONVERSION; + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + ATLASSERT(bstrText == NULL); + int nLength = (int)(short)LOWORD(::SendMessage(this->m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L)); + if(nLength == 0) + return FALSE; + + ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff; + LPTSTR lpstrText = buff.Allocate(nLength + 1); + if(lpstrText == NULL) + return FALSE; + + if(!GetText(nPane, lpstrText, pType)) + return FALSE; + + bstrText = ::SysAllocString(T2OLE(lpstrText)); + return (bstrText != NULL) ? TRUE : FALSE; + } + +#ifdef __ATLSTR_H__ + int GetText(int nPane, ATL::CString& strText, int* pType = NULL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + int nLength = (int)(short)LOWORD(::SendMessage(this->m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L)); + if(nLength == 0) + return 0; + + LPTSTR lpstr = strText.GetBufferSetLength(nLength); + if(lpstr == NULL) + return 0; + return GetText(nPane, lpstr, pType); + } +#endif // __ATLSTR_H__ + + BOOL SetText(int nPane, LPCTSTR lpszText, int nType = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + return (BOOL)::SendMessage(this->m_hWnd, SB_SETTEXT, (nPane | nType), (LPARAM)lpszText); + } + + BOOL GetRect(int nPane, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + return (BOOL)::SendMessage(this->m_hWnd, SB_GETRECT, nPane, (LPARAM)lpRect); + } + + BOOL GetBorders(int* pBorders) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, SB_GETBORDERS, 0, (LPARAM)pBorders); + } + + BOOL GetBorders(int& nHorz, int& nVert, int& nSpacing) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + int borders[3] = {}; + BOOL bResult = (BOOL)::SendMessage(this->m_hWnd, SB_GETBORDERS, 0, (LPARAM)&borders); + if(bResult) + { + nHorz = borders[0]; + nVert = borders[1]; + nSpacing = borders[2]; + } + return bResult; + } + + void SetMinHeight(int nMin) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, SB_SETMINHEIGHT, nMin, 0L); + } + + BOOL SetSimple(BOOL bSimple = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, SB_SIMPLE, bSimple, 0L); + } + + BOOL IsSimple() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, SB_ISSIMPLE, 0, 0L); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, SB_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, SB_SETUNICODEFORMAT, bUnicode, 0L); + } + + void GetTipText(int nPane, LPTSTR lpstrText, int nSize) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + ::SendMessage(this->m_hWnd, SB_GETTIPTEXT, MAKEWPARAM(nPane, nSize), (LPARAM)lpstrText); + } + + void SetTipText(int nPane, LPCTSTR lpstrText) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + ::SendMessage(this->m_hWnd, SB_SETTIPTEXT, nPane, (LPARAM)lpstrText); + } + + COLORREF SetBkColor(COLORREF clrBk) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, SB_SETBKCOLOR, 0, (LPARAM)clrBk); + } + + HICON GetIcon(int nPane) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + return (HICON)::SendMessage(this->m_hWnd, SB_GETICON, nPane, 0L); + } + + BOOL SetIcon(int nPane, HICON hIcon) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(nPane < 256); + return (BOOL)::SendMessage(this->m_hWnd, SB_SETICON, nPane, (LPARAM)hIcon); + } +}; + +typedef CStatusBarCtrlT<ATL::CWindow> CStatusBarCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CTabCtrl + +template <class TBase> +class CTabCtrlT : public TBase +{ +public: +// Constructors + CTabCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CTabCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return WC_TABCONTROL; + } + + CImageList GetImageList() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TCM_GETIMAGELIST, 0, 0L)); + } + + CImageList SetImageList(HIMAGELIST hImageList) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, TCM_SETIMAGELIST, 0, (LPARAM)hImageList)); + } + + int GetItemCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TCM_GETITEMCOUNT, 0, 0L); + } + + BOOL GetItem(int nItem, LPTCITEM pTabCtrlItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TCM_GETITEM, nItem, (LPARAM)pTabCtrlItem); + } + + BOOL SetItem(int nItem, LPTCITEM pTabCtrlItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TCM_SETITEM, nItem, (LPARAM)pTabCtrlItem); + } + + int SetItem(int nItem, UINT mask, LPCTSTR lpszItem, DWORD dwState, DWORD dwStateMask, int iImage, LPARAM lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TCITEM tci = {}; + tci.mask = mask; + tci.pszText = (LPTSTR) lpszItem; + tci.dwState = dwState; + tci.dwStateMask = dwStateMask; + tci.iImage = iImage; + tci.lParam = lParam; + return (int)::SendMessage(this->m_hWnd, TCM_SETITEM, nItem, (LPARAM)&tci); + } + + BOOL GetItemRect(int nItem, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TCM_GETITEMRECT, nItem, (LPARAM)lpRect); + } + + int GetCurSel() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TCM_GETCURSEL, 0, 0L); + } + + int SetCurSel(int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TCM_SETCURSEL, nItem, 0L); + } + + SIZE SetItemSize(SIZE size) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwSize = (DWORD)::SendMessage(this->m_hWnd, TCM_SETITEMSIZE, 0, MAKELPARAM(size.cx, size.cy)); + SIZE sizeRet = { GET_X_LPARAM(dwSize), GET_Y_LPARAM(dwSize) }; + return sizeRet; + } + + void SetItemSize(int cx, int cy) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TCM_SETITEMSIZE, 0, MAKELPARAM(cx, cy)); + } + + void SetPadding(SIZE size) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TCM_SETPADDING, 0, MAKELPARAM(size.cx, size.cy)); + } + + int GetRowCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TCM_GETROWCOUNT, 0, 0L); + } + + CToolTipCtrl GetToolTips() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TCM_GETTOOLTIPS, 0, 0L)); + } + + // this method is deprecated, please use GetToolTips + CToolTipCtrl GetTooltips() const { return GetToolTips(); } + + void SetToolTips(HWND hWndToolTip) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TCM_SETTOOLTIPS, (WPARAM)hWndToolTip, 0L); + } + + // this method is deprecated, please use SetToolTips + void SetTooltips(HWND hWndToolTip) { SetToolTips(hWndToolTip); } + + int GetCurFocus() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TCM_GETCURFOCUS, 0, 0L); + } + + void SetCurFocus(int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TCM_SETCURFOCUS, nItem, 0L); + } + + BOOL SetItemExtra(int cbExtra) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(GetItemCount() == 0); // must be empty + return (BOOL)::SendMessage(this->m_hWnd, TCM_SETITEMEXTRA, cbExtra, 0L); + } + + int SetMinTabWidth(int nWidth = -1) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TCM_SETMINTABWIDTH, 0, nWidth); + } + + DWORD GetExtendedStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TCM_GETEXTENDEDSTYLE, 0, 0L); + } + + DWORD SetExtendedStyle(DWORD dwExMask, DWORD dwExStyle) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, TCM_SETEXTENDEDSTYLE, dwExMask, dwExStyle); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TCM_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TCM_SETUNICODEFORMAT, bUnicode, 0L); + } + +// Operations + int InsertItem(int nItem, LPTCITEM pTabCtrlItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)pTabCtrlItem); + } + + int InsertItem(int nItem, UINT mask, LPCTSTR lpszItem, int iImage, LPARAM lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TCITEM tci = {}; + tci.mask = mask; + tci.pszText = (LPTSTR) lpszItem; + tci.iImage = iImage; + tci.lParam = lParam; + return (int)::SendMessage(this->m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)&tci); + } + + int InsertItem(int nItem, LPCTSTR lpszItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TCITEM tci = {}; + tci.mask = TCIF_TEXT; + tci.pszText = (LPTSTR) lpszItem; + return (int)::SendMessage(this->m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)&tci); + } + + int AddItem(LPTCITEM pTabCtrlItem) + { + return InsertItem(GetItemCount(), pTabCtrlItem); + } + + int AddItem(UINT mask, LPCTSTR lpszItem, int iImage, LPARAM lParam) + { + return InsertItem(GetItemCount(), mask, lpszItem, iImage, lParam); + } + + int AddItem(LPCTSTR lpszItem) + { + return InsertItem(GetItemCount(), lpszItem); + } + + BOOL DeleteItem(int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TCM_DELETEITEM, nItem, 0L); + } + + BOOL DeleteAllItems() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TCM_DELETEALLITEMS, 0, 0L); + } + + void AdjustRect(BOOL bLarger, LPRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TCM_ADJUSTRECT, bLarger, (LPARAM)lpRect); + } + + void RemoveImage(int nImage) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TCM_REMOVEIMAGE, nImage, 0L); + } + + int HitTest(TC_HITTESTINFO* pHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TCM_HITTEST, 0, (LPARAM)pHitTestInfo); + } + + void DeselectAll(BOOL bExcludeFocus = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TCM_DESELECTALL, bExcludeFocus, 0L); + } + + BOOL HighlightItem(int nIndex, BOOL bHighlight = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TCM_HIGHLIGHTITEM, nIndex, MAKELPARAM(bHighlight, 0)); + } +}; + +typedef CTabCtrlT<ATL::CWindow> CTabCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CTrackBarCtrl + +template <class TBase> +class CTrackBarCtrlT : public TBase +{ +public: +// Constructors + CTrackBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CTrackBarCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return TRACKBAR_CLASS; + } + + int GetLineSize() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETLINESIZE, 0, 0L); + } + + int SetLineSize(int nSize) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_SETLINESIZE, 0, nSize); + } + + int GetPageSize() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETPAGESIZE, 0, 0L); + } + + int SetPageSize(int nSize) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_SETPAGESIZE, 0, nSize); + } + + int GetRangeMin() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETRANGEMIN, 0, 0L); + } + + void SetRangeMin(int nMin, BOOL bRedraw = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETRANGEMIN, bRedraw, nMin); + } + + int GetRangeMax() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETRANGEMAX, 0, 0L); + } + + void SetRangeMax(int nMax, BOOL bRedraw = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETRANGEMAX, bRedraw, nMax); + } + + void GetRange(int& nMin, int& nMax) const + { + nMin = GetRangeMin(); + nMax = GetRangeMax(); + } + + void SetRange(int nMin, int nMax, BOOL bRedraw = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETRANGE, bRedraw, MAKELPARAM(nMin, nMax)); + } + + int GetSelStart() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETSELSTART, 0, 0L); + } + + void SetSelStart(int nMin, BOOL bRedraw = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETSELSTART, bRedraw, (LPARAM)nMin); + } + + int GetSelEnd() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETSELEND, 0, 0L); + } + + void SetSelEnd(int nMax, BOOL bRedraw = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETSELEND, bRedraw, (LPARAM)nMax); + } + + void GetSelection(int& nMin, int& nMax) const + { + nMin = GetSelStart(); + nMax = GetSelEnd(); + } + + void SetSelection(int nMin, int nMax, BOOL bRedraw = TRUE) + { + SetSelStart(nMin, FALSE); + SetSelEnd(nMax, bRedraw); + } + + void GetChannelRect(LPRECT lprc) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_GETCHANNELRECT, 0, (LPARAM)lprc); + } + + void GetThumbRect(LPRECT lprc) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_GETTHUMBRECT, 0, (LPARAM)lprc); + } + + int GetPos() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETPOS, 0, 0L); + } + + void SetPos(int nPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETPOS, TRUE, nPos); + } + + UINT GetNumTics() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, TBM_GETNUMTICS, 0, 0L); + } + + DWORD* GetTicArray() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD*)::SendMessage(this->m_hWnd, TBM_GETPTICS, 0, 0L); + } + + int GetTic(int nTic) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETTIC, nTic, 0L); + } + + BOOL SetTic(int nTic) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TBM_SETTIC, 0, nTic); + } + + int GetTicPos(int nTic) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETTICPOS, nTic, 0L); + } + + void SetTicFreq(int nFreq) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETTICFREQ, nFreq, 0L); + } + + int GetThumbLength() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_GETTHUMBLENGTH, 0, 0L); + } + + void SetThumbLength(int nLength) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETTHUMBLENGTH, nLength, 0L); + } + + void SetSel(int nStart, int nEnd, BOOL bRedraw = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & TBS_ENABLESELRANGE) != 0); + ::SendMessage(this->m_hWnd, TBM_SETSEL, bRedraw, MAKELPARAM(nStart, nEnd)); + } + + ATL::CWindow GetBuddy(BOOL bLeft = TRUE) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ATL::CWindow((HWND)::SendMessage(this->m_hWnd, TBM_GETBUDDY, bLeft, 0L)); + } + + ATL::CWindow SetBuddy(HWND hWndBuddy, BOOL bLeft = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ATL::CWindow((HWND)::SendMessage(this->m_hWnd, TBM_SETBUDDY, bLeft, (LPARAM)hWndBuddy)); + } + + CToolTipCtrl GetToolTips() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, TBM_GETTOOLTIPS, 0, 0L)); + } + + void SetToolTips(HWND hWndTT) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETTOOLTIPS, (WPARAM)hWndTT, 0L); + } + + int SetTipSide(int nSide) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, TBM_SETTIPSIDE, nSide, 0L); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TBM_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, TBM_SETUNICODEFORMAT, bUnicode, 0L); + } + +// Operations + void ClearSel(BOOL bRedraw = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_CLEARSEL, bRedraw, 0L); + } + + void VerifyPos() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_SETPOS, FALSE, 0L); + } + + void ClearTics(BOOL bRedraw = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, TBM_CLEARTICS, bRedraw, 0L); + } +}; + +typedef CTrackBarCtrlT<ATL::CWindow> CTrackBarCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CUpDownCtrl + +template <class TBase> +class CUpDownCtrlT : public TBase +{ +public: +// Constructors + CUpDownCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CUpDownCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return UPDOWN_CLASS; + } + + UINT GetAccel(int nAccel, UDACCEL* pAccel) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)LOWORD(::SendMessage(this->m_hWnd, UDM_GETACCEL, nAccel, (LPARAM)pAccel)); + } + + BOOL SetAccel(int nAccel, UDACCEL* pAccel) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)LOWORD(::SendMessage(this->m_hWnd, UDM_SETACCEL, nAccel, (LPARAM)pAccel)); + } + + UINT GetBase() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)LOWORD(::SendMessage(this->m_hWnd, UDM_GETBASE, 0, 0L)); + } + + int SetBase(int nBase) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, UDM_SETBASE, nBase, 0L); + } + + ATL::CWindow GetBuddy() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ATL::CWindow((HWND)::SendMessage(this->m_hWnd, UDM_GETBUDDY, 0, 0L)); + } + + ATL::CWindow SetBuddy(HWND hWndBuddy) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ATL::CWindow((HWND)::SendMessage(this->m_hWnd, UDM_SETBUDDY, (WPARAM)hWndBuddy, 0L)); + } + + int GetPos(LPBOOL lpbError = NULL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, UDM_GETPOS, 0, 0L); + // Note: Seems that Windows always sets error to TRUE if + // UDS_SETBUDDYINT style is not used + if(lpbError != NULL) + *lpbError = (HIWORD(dwRet) != 0) ? TRUE : FALSE; + return (int)(short)LOWORD(dwRet); + } + + int SetPos(int nPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)(short)LOWORD(::SendMessage(this->m_hWnd, UDM_SETPOS, 0, MAKELPARAM(nPos, 0))); + } + + DWORD GetRange() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, UDM_GETRANGE, 0, 0L); + } + + void GetRange(int& nLower, int& nUpper) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, UDM_GETRANGE, 0, 0L); + nLower = (int)(short)HIWORD(dwRet); + nUpper = (int)(short)LOWORD(dwRet); + } + + void SetRange(int nLower, int nUpper) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, UDM_SETRANGE, 0, MAKELPARAM(nUpper, nLower)); + } + + void SetRange32(int nLower, int nUpper) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, UDM_SETRANGE32, nLower, nUpper); + } + + void GetRange32(int& nLower, int& nUpper) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, UDM_GETRANGE32, (WPARAM)&nLower, (LPARAM)&nUpper); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, UDM_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, UDM_SETUNICODEFORMAT, bUnicode, 0L); + } + + int GetPos32(LPBOOL lpbError = NULL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + // Note: Seems that Windows always sets error to TRUE if + // UDS_SETBUDDYINT style is not used + return (int)::SendMessage(this->m_hWnd, UDM_GETPOS32, 0, (LPARAM)lpbError); + } + + int SetPos32(int nPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, UDM_SETPOS32, 0, (LPARAM)nPos); + } +}; + +typedef CUpDownCtrlT<ATL::CWindow> CUpDownCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CProgressBarCtrl + +template <class TBase> +class CProgressBarCtrlT : public TBase +{ +public: +// Constructors + CProgressBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CProgressBarCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return PROGRESS_CLASS; + } + + DWORD SetRange(int nLower, int nUpper) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, PBM_SETRANGE, 0, MAKELPARAM(nLower, nUpper)); + } + + int SetPos(int nPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)(short)LOWORD(::SendMessage(this->m_hWnd, PBM_SETPOS, nPos, 0L)); + } + + int OffsetPos(int nPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)(short)LOWORD(::SendMessage(this->m_hWnd, PBM_DELTAPOS, nPos, 0L)); + } + + int SetStep(int nStep) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)(short)LOWORD(::SendMessage(this->m_hWnd, PBM_SETSTEP, nStep, 0L)); + } + + UINT GetPos() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, PBM_GETPOS, 0, 0L); + } + + void GetRange(PPBRANGE pPBRange) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(pPBRange != NULL); + ::SendMessage(this->m_hWnd, PBM_GETRANGE, TRUE, (LPARAM)pPBRange); + } + + void GetRange(int& nLower, int& nUpper) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + PBRANGE range = {}; + ::SendMessage(this->m_hWnd, PBM_GETRANGE, TRUE, (LPARAM)&range); + nLower = range.iLow; + nUpper = range.iHigh; + } + + int GetRangeLimit(BOOL bLowLimit) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PBM_GETRANGE, bLowLimit, (LPARAM)NULL); + } + + DWORD SetRange32(int nMin, int nMax) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, PBM_SETRANGE32, nMin, nMax); + } + + COLORREF SetBarColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, PBM_SETBARCOLOR, 0, (LPARAM)clr); + } + + COLORREF SetBkColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, PBM_SETBKCOLOR, 0, (LPARAM)clr); + } + +#ifdef PBM_SETMARQUEE + BOOL SetMarquee(BOOL bMarquee, UINT uUpdateTime = 0U) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, PBM_SETMARQUEE, (WPARAM)bMarquee, (LPARAM)uUpdateTime); + } +#endif + +#if (_WIN32_WINNT >= 0x0600) + int GetStep() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PBM_GETSTEP, 0, 0L); + } + + COLORREF GetBkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, PBM_GETBKCOLOR, 0, 0L); + } + + COLORREF GetBarColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, PBM_GETBARCOLOR, 0, 0L); + } + + int GetState() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PBM_GETSTATE, 0, 0L); + } + + int SetState(int nState) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PBM_SETSTATE, nState, 0L); + } +#endif // (_WIN32_WINNT >= 0x0600) + +// Operations + int StepIt() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)(short)LOWORD(::SendMessage(this->m_hWnd, PBM_STEPIT, 0, 0L)); + } +}; + +typedef CProgressBarCtrlT<ATL::CWindow> CProgressBarCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CHotKeyCtrl + +template <class TBase> +class CHotKeyCtrlT : public TBase +{ +public: +// Constructors + CHotKeyCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CHotKeyCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return HOTKEY_CLASS; + } + + DWORD GetHotKey() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, HKM_GETHOTKEY, 0, 0L); + } + + void GetHotKey(WORD &wVirtualKeyCode, WORD &wModifiers) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dw = (DWORD)::SendMessage(this->m_hWnd, HKM_GETHOTKEY, 0, 0L); + wVirtualKeyCode = LOBYTE(LOWORD(dw)); + wModifiers = HIBYTE(LOWORD(dw)); + } + + void SetHotKey(WORD wVirtualKeyCode, WORD wModifiers) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, HKM_SETHOTKEY, MAKEWORD(wVirtualKeyCode, wModifiers), 0L); + } + + void SetRules(WORD wInvalidComb, WORD wModifiers) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, HKM_SETRULES, wInvalidComb, MAKELPARAM(wModifiers, 0)); + } +}; + +typedef CHotKeyCtrlT<ATL::CWindow> CHotKeyCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CAnimateCtrl + +template <class TBase> +class CAnimateCtrlT : public TBase +{ +public: +// Constructors + CAnimateCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CAnimateCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return ANIMATE_CLASS; + } + +// Operations + BOOL Open(ATL::_U_STRINGorID FileName) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, ACM_OPEN, 0, (LPARAM)FileName.m_lpstr); + } + + BOOL Play(UINT nFrom, UINT nTo, UINT nRep) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, ACM_PLAY, nRep, MAKELPARAM(nFrom, nTo)); + } + + BOOL Stop() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, ACM_STOP, 0, 0L); + } + + BOOL Close() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, ACM_OPEN, 0, 0L); + } + + BOOL Seek(UINT nTo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, ACM_PLAY, 0, MAKELPARAM(nTo, nTo)); + } + + // Vista only + BOOL IsPlaying() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, ACM_ISPLAYING, 0, 0L); + } +}; + +typedef CAnimateCtrlT<ATL::CWindow> CAnimateCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CRichEditCtrl + +#if !defined(_UNICODE) && (_RICHEDIT_VER >= 0x0500) + #undef MSFTEDIT_CLASS + #define MSFTEDIT_CLASS "RICHEDIT50W" +#endif + +template <class TBase> +class CRichEditCtrlT : public TBase +{ +public: +// Constructors + CRichEditCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CRichEditCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { +#if (_RICHEDIT_VER >= 0x0500) + return MSFTEDIT_CLASS; +#else + return RICHEDIT_CLASS; +#endif + } + + static LPCTSTR GetLibraryName() + { +#if (_RICHEDIT_VER >= 0x0500) + return _T("MSFTEDIT.DLL"); +#else + return _T("RICHED20.DLL"); +#endif + } + + int GetLineCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETLINECOUNT, 0, 0L); + } + + BOOL GetModify() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_GETMODIFY, 0, 0L); + } + + void SetModify(BOOL bModified = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETMODIFY, bModified, 0L); + } + + void GetRect(LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_GETRECT, 0, (LPARAM)lpRect); + } + + DWORD GetOptions() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETOPTIONS, 0, 0L); + } + + DWORD SetOptions(WORD wOperation, DWORD dwOptions) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_SETOPTIONS, wOperation, dwOptions); + } + + // NOTE: first word in lpszBuffer must contain the size of the buffer! + int GetLine(int nIndex, LPTSTR lpszBuffer) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); + } + + int GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + *(LPWORD)lpszBuffer = (WORD)nMaxLength; + return (int)::SendMessage(this->m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); + } + + BOOL CanUndo() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_CANUNDO, 0, 0L); + } + + BOOL CanPaste(UINT nFormat = 0) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_CANPASTE, nFormat, 0L); + } + + void GetSel(LONG& nStartChar, LONG& nEndChar) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + CHARRANGE cr = {}; + ::SendMessage(this->m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr); + nStartChar = cr.cpMin; + nEndChar = cr.cpMax; + } + + void GetSel(CHARRANGE &cr) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr); + } + + int SetSel(LONG nStartChar, LONG nEndChar) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + CHARRANGE cr = { nStartChar, nEndChar }; + return (int)::SendMessage(this->m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cr); + } + + int SetSel(CHARRANGE &cr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cr); + } + + int SetSelAll() + { + return SetSel(0, -1); + } + + int SetSelNone() + { + return SetSel(-1, 0); + } + + DWORD GetDefaultCharFormat(CHARFORMAT& cf) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETCHARFORMAT, 0, (LPARAM)&cf); + } + + DWORD GetSelectionCharFormat(CHARFORMAT& cf) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETCHARFORMAT, 1, (LPARAM)&cf); + } + + DWORD GetEventMask() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETEVENTMASK, 0, 0L); + } + + LONG GetLimitText() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LONG)::SendMessage(this->m_hWnd, EM_GETLIMITTEXT, 0, 0L); + } + + DWORD GetParaFormat(PARAFORMAT& pf) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + pf.cbSize = sizeof(PARAFORMAT); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM)&pf); + } + + LONG GetSelText(LPTSTR lpstrBuff) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LONG)::SendMessage(this->m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrBuff); + } + + BOOL GetSelTextBSTR(BSTR& bstrText) const + { + USES_CONVERSION; + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(bstrText == NULL); + + CHARRANGE cr = {}; + ::SendMessage(this->m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr); + + ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff; + LPTSTR lpstrText = buff.Allocate(cr.cpMax - cr.cpMin + 1); + if(lpstrText == NULL) + return FALSE; + if(::SendMessage(this->m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrText) == 0) + return FALSE; + + bstrText = ::SysAllocString(T2W(lpstrText)); + + return (bstrText != NULL) ? TRUE : FALSE; + } + +#ifdef __ATLSTR_H__ + LONG GetSelText(ATL::CString& strText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + + CHARRANGE cr = {}; + ::SendMessage(this->m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr); + + LONG lLen = 0; + LPTSTR lpstrText = strText.GetBufferSetLength(cr.cpMax - cr.cpMin); + if(lpstrText != NULL) + { + lLen = (LONG)::SendMessage(this->m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrText); + strText.ReleaseBuffer(); + } + + return lLen; + } +#endif // __ATLSTR_H__ + + WORD GetSelectionType() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (WORD)::SendMessage(this->m_hWnd, EM_SELECTIONTYPE, 0, 0L); + } + + COLORREF SetBackgroundColor(COLORREF cr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, EM_SETBKGNDCOLOR, 0, cr); + } + + COLORREF SetBackgroundColor() // sets to system background + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, EM_SETBKGNDCOLOR, 1, 0); + } + + BOOL SetCharFormat(CHARFORMAT& cf, WORD wFlags) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, (WPARAM)wFlags, (LPARAM)&cf); + } + + BOOL SetDefaultCharFormat(CHARFORMAT& cf) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf); + } + + BOOL SetSelectionCharFormat(CHARFORMAT& cf) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + } + + BOOL SetWordCharFormat(CHARFORMAT& cf) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, (LPARAM)&cf); + } + + DWORD SetEventMask(DWORD dwEventMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_SETEVENTMASK, 0, dwEventMask); + } + + BOOL SetParaFormat(PARAFORMAT& pf) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + pf.cbSize = sizeof(PARAFORMAT); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf); + } + + BOOL SetTargetDevice(HDC hDC, int cxLineWidth) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETTARGETDEVICE, (WPARAM)hDC, cxLineWidth); + } + + int GetTextLength() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, WM_GETTEXTLENGTH, 0, 0L); + } + + BOOL SetReadOnly(BOOL bReadOnly = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETREADONLY, bReadOnly, 0L); + } + + int GetFirstVisibleLine() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETFIRSTVISIBLELINE, 0, 0L); + } + + int GetTextRange(TEXTRANGE* pTextRange) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETTEXTRANGE, 0, (LPARAM)pTextRange); + } + + int GetTextRange(LONG nStartChar, LONG nEndChar, LPTSTR lpstrText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + TEXTRANGE tr = {}; + tr.chrg.cpMin = nStartChar; + tr.chrg.cpMax = nEndChar; + tr.lpstrText = lpstrText; + return (int)::SendMessage(this->m_hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr); + } + + DWORD GetDefaultCharFormat(CHARFORMAT2& cf) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT2); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETCHARFORMAT, 0, (LPARAM)&cf); + } + + BOOL SetCharFormat(CHARFORMAT2& cf, WORD wFlags) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT2); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, (WPARAM)wFlags, (LPARAM)&cf); + } + + BOOL SetDefaultCharFormat(CHARFORMAT2& cf) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT2); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf); + } + + DWORD GetSelectionCharFormat(CHARFORMAT2& cf) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT2); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETCHARFORMAT, 1, (LPARAM)&cf); + } + + BOOL SetSelectionCharFormat(CHARFORMAT2& cf) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT2); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + } + + BOOL SetWordCharFormat(CHARFORMAT2& cf) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + cf.cbSize = sizeof(CHARFORMAT2); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, (LPARAM)&cf); + } + + DWORD GetParaFormat(PARAFORMAT2& pf) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + pf.cbSize = sizeof(PARAFORMAT2); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM)&pf); + } + + BOOL SetParaFormat(PARAFORMAT2& pf) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + pf.cbSize = sizeof(PARAFORMAT2); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf); + } + + TEXTMODE GetTextMode() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (TEXTMODE)::SendMessage(this->m_hWnd, EM_GETTEXTMODE, 0, 0L); + } + + BOOL SetTextMode(TEXTMODE enumTextMode) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return !(BOOL)::SendMessage(this->m_hWnd, EM_SETTEXTMODE, enumTextMode, 0L); + } + + UNDONAMEID GetUndoName() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UNDONAMEID)::SendMessage(this->m_hWnd, EM_GETUNDONAME, 0, 0L); + } + + UNDONAMEID GetRedoName() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UNDONAMEID)::SendMessage(this->m_hWnd, EM_GETREDONAME, 0, 0L); + } + + BOOL CanRedo() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_CANREDO, 0, 0L); + } + + BOOL GetAutoURLDetect() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_GETAUTOURLDETECT, 0, 0L); + } + + BOOL SetAutoURLDetect(BOOL bAutoDetect = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return !(BOOL)::SendMessage(this->m_hWnd, EM_AUTOURLDETECT, bAutoDetect, 0L); + } + + // this method is deprecated, please use SetAutoURLDetect + BOOL EnableAutoURLDetect(BOOL bEnable = TRUE) { return SetAutoURLDetect(bEnable); } + + UINT SetUndoLimit(UINT uUndoLimit) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, EM_SETUNDOLIMIT, uUndoLimit, 0L); + } + + void SetPalette(HPALETTE hPalette) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETPALETTE, (WPARAM)hPalette, 0L); + } + + int GetTextEx(GETTEXTEX* pGetTextEx, LPTSTR lpstrText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETTEXTEX, (WPARAM)pGetTextEx, (LPARAM)lpstrText); + } + + int GetTextEx(LPTSTR lpstrText, int nTextLen, DWORD dwFlags = GT_DEFAULT, UINT uCodePage = CP_ACP, LPCSTR lpDefaultChar = NULL, LPBOOL lpUsedDefChar = NULL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + GETTEXTEX gte = {}; + gte.cb = nTextLen * sizeof(TCHAR); + gte.codepage = uCodePage; + gte.flags = dwFlags; + gte.lpDefaultChar = lpDefaultChar; + gte.lpUsedDefChar = lpUsedDefChar; + return (int)::SendMessage(this->m_hWnd, EM_GETTEXTEX, (WPARAM)>e, (LPARAM)lpstrText); + } + + int GetTextLengthEx(GETTEXTLENGTHEX* pGetTextLengthEx) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETTEXTLENGTHEX, (WPARAM)pGetTextLengthEx, 0L); + } + + int GetTextLengthEx(DWORD dwFlags = GTL_DEFAULT, UINT uCodePage = CP_ACP) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + GETTEXTLENGTHEX gtle = {}; + gtle.codepage = uCodePage; + gtle.flags = dwFlags; + return (int)::SendMessage(this->m_hWnd, EM_GETTEXTLENGTHEX, (WPARAM)>le, 0L); + } + + EDITWORDBREAKPROC GetWordBreakProc() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (EDITWORDBREAKPROC)::SendMessage(this->m_hWnd, EM_GETWORDBREAKPROC, 0, 0L); + } + + void SetWordBreakProc(EDITWORDBREAKPROC ewbprc) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETWORDBREAKPROC, 0, (LPARAM)ewbprc); + } + + int SetTextEx(SETTEXTEX* pSetTextEx, LPCTSTR lpstrText) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_SETTEXTEX, (WPARAM)pSetTextEx, (LPARAM)lpstrText); + } + + int SetTextEx(LPCTSTR lpstrText, DWORD dwFlags = ST_DEFAULT, UINT uCodePage = CP_ACP) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + SETTEXTEX ste = {}; + ste.flags = dwFlags; + ste.codepage = uCodePage; + return (int)::SendMessage(this->m_hWnd, EM_SETTEXTEX, (WPARAM)&ste, (LPARAM)lpstrText); + } + + int GetEditStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_GETEDITSTYLE, 0, 0L); + } + + int SetEditStyle(int nStyle, int nMask = -1) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + if(nMask == -1) + nMask = nStyle; // set everything specified + return (int)::SendMessage(this->m_hWnd, EM_SETEDITSTYLE, nStyle, nMask); + } + + BOOL SetFontSize(int nFontSizeDelta) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((nFontSizeDelta >= -1637) && (nFontSizeDelta <= 1638)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETFONTSIZE, nFontSizeDelta, 0L); + } + + void GetScrollPos(LPPOINT lpPoint) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpPoint != NULL); + ::SendMessage(this->m_hWnd, EM_GETSCROLLPOS, 0, (LPARAM)lpPoint); + } + + void SetScrollPos(LPPOINT lpPoint) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpPoint != NULL); + ::SendMessage(this->m_hWnd, EM_SETSCROLLPOS, 0, (LPARAM)lpPoint); + } + + BOOL GetZoom(int& nNum, int& nDen) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_GETZOOM, (WPARAM)&nNum, (LPARAM)&nDen); + } + + BOOL SetZoom(int nNum, int nDen) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((nNum >= 0) && (nNum <= 64)); + ATLASSERT((nDen >= 0) && (nDen <= 64)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETZOOM, nNum, nDen); + } + + BOOL SetZoomOff() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETZOOM, 0, 0L); + } + + void SetMargins(UINT nLeft, UINT nRight, WORD wFlags = EC_LEFTMARGIN | EC_RIGHTMARGIN) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETMARGINS, wFlags, MAKELONG(nLeft, nRight)); + } + + WORD GetTypographyOptions() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (WORD)::SendMessage(this->m_hWnd, EM_GETTYPOGRAPHYOPTIONS, 0, 0L); + } + + BOOL SetTypographyOptions(WORD wOptions, WORD wMask) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETTYPOGRAPHYOPTIONS, wOptions, wMask); + } + +// Operations + void LimitText(LONG nChars = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_EXLIMITTEXT, 0, nChars); + } + + int LineFromChar(LONG nIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_EXLINEFROMCHAR, 0, nIndex); + } + + POINT PosFromChar(LONG nChar) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + POINT point = {}; + ::SendMessage(this->m_hWnd, EM_POSFROMCHAR, (WPARAM)&point, nChar); + return point; + } + + int CharFromPos(POINT pt) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + POINTL ptl = { pt.x, pt.y }; + return (int)::SendMessage(this->m_hWnd, EM_CHARFROMPOS, 0, (LPARAM)&ptl); + } + + void EmptyUndoBuffer() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_EMPTYUNDOBUFFER, 0, 0L); + } + + int LineIndex(int nLine = -1) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_LINEINDEX, nLine, 0L); + } + + int LineLength(int nLine = -1) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, EM_LINELENGTH, nLine, 0L); + } + + BOOL LineScroll(int nLines) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_LINESCROLL, 0, nLines); + } + + void ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText); + } + + void SetRect(LPCRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETRECT, 0, (LPARAM)lpRect); + } + + BOOL DisplayBand(LPRECT pDisplayRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_DISPLAYBAND, 0, (LPARAM)pDisplayRect); + } + + LONG FindText(DWORD dwFlags, FINDTEXT& ft) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); +#ifdef _UNICODE + return (LONG)::SendMessage(this->m_hWnd, EM_FINDTEXTW, dwFlags, (LPARAM)&ft); +#else + return (LONG)::SendMessage(this->m_hWnd, EM_FINDTEXT, dwFlags, (LPARAM)&ft); +#endif + } + + LONG FindText(DWORD dwFlags, FINDTEXTEX& ft) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); +#ifdef _UNICODE + return (LONG)::SendMessage(this->m_hWnd, EM_FINDTEXTEXW, dwFlags, (LPARAM)&ft); +#else + return (LONG)::SendMessage(this->m_hWnd, EM_FINDTEXTEX, dwFlags, (LPARAM)&ft); +#endif + } + + LONG FormatRange(FORMATRANGE& fr, BOOL bDisplay = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LONG)::SendMessage(this->m_hWnd, EM_FORMATRANGE, bDisplay, (LPARAM)&fr); + } + + LONG FormatRange(FORMATRANGE* pFormatRange, BOOL bDisplay = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LONG)::SendMessage(this->m_hWnd, EM_FORMATRANGE, bDisplay, (LPARAM)pFormatRange); + } + + void HideSelection(BOOL bHide = TRUE, BOOL bChangeStyle = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_HIDESELECTION, bHide, bChangeStyle); + } + + void PasteSpecial(UINT uClipFormat, DWORD dwAspect = 0, HMETAFILE hMF = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + REPASTESPECIAL reps = { dwAspect, (DWORD_PTR)hMF }; + ::SendMessage(this->m_hWnd, EM_PASTESPECIAL, uClipFormat, (LPARAM)&reps); + } + + void RequestResize() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_REQUESTRESIZE, 0, 0L); + } + + LONG StreamIn(UINT uFormat, EDITSTREAM& es) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LONG)::SendMessage(this->m_hWnd, EM_STREAMIN, uFormat, (LPARAM)&es); + } + + LONG StreamOut(UINT uFormat, EDITSTREAM& es) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (LONG)::SendMessage(this->m_hWnd, EM_STREAMOUT, uFormat, (LPARAM)&es); + } + + DWORD FindWordBreak(int nCode, LONG nStartChar) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_FINDWORDBREAK, nCode, nStartChar); + } + + // Additional operations + void ScrollCaret() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SCROLLCARET, 0, 0L); + } + + int InsertText(long nInsertAfterChar, LPCTSTR lpstrText, BOOL bCanUndo = FALSE) + { + int nRet = SetSel(nInsertAfterChar, nInsertAfterChar); + ReplaceSel(lpstrText, bCanUndo); + return nRet; + } + + int AppendText(LPCTSTR lpstrText, BOOL bCanUndo = FALSE) + { + return InsertText(this->GetWindowTextLength(), lpstrText, bCanUndo); + } + + // Clipboard operations + BOOL Undo() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_UNDO, 0, 0L); + } + + void Clear() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_CLEAR, 0, 0L); + } + + void Copy() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_COPY, 0, 0L); + } + + void Cut() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_CUT, 0, 0L); + } + + void Paste() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, WM_PASTE, 0, 0L); + } + + // OLE support + IRichEditOle* GetOleInterface() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + IRichEditOle *pRichEditOle = NULL; + ::SendMessage(this->m_hWnd, EM_GETOLEINTERFACE, 0, (LPARAM)&pRichEditOle); + return pRichEditOle; + } + + BOOL SetOleCallback(IRichEditOleCallback* pCallback) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETOLECALLBACK, 0, (LPARAM)pCallback); + } + + BOOL Redo() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_REDO, 0, 0L); + } + + void StopGroupTyping() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_STOPGROUPTYPING, 0, 0L); + } + + void ShowScrollBar(int nBarType, BOOL bVisible = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SHOWSCROLLBAR, nBarType, bVisible); + } + + BOOL SetTabStops(int nTabStops, LPINT rgTabStops) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops); + } + + BOOL SetTabStops() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, 0, 0L); + } + + BOOL SetTabStops(const int& cxEachStop) // takes an 'int' + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop); + } + +#if (_RICHEDIT_VER >= 0x0800) + AutoCorrectProc GetAutoCorrectProc() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (AutoCorrectProc)::SendMessage(this->m_hWnd, EM_GETAUTOCORRECTPROC, 0, 0L); + } + + BOOL SetAutoCorrectProc(AutoCorrectProc pfn) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETAUTOCORRECTPROC, (WPARAM)pfn, 0L); + } + + BOOL CallAutoCorrectProc(WCHAR ch) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_CALLAUTOCORRECTPROC, (WPARAM)ch, 0L); + } + + DWORD GetEditStyleEx() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETEDITSTYLEEX, 0, 0L); + } + + DWORD SetEditStyleEx(DWORD dwStyleEx, DWORD dwMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_SETEDITSTYLEEX, dwStyleEx, dwMask); + } + + DWORD GetStoryType(int nStoryIndex) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_GETSTORYTYPE, nStoryIndex, 0L); + } + + DWORD SetStoryType(int nStoryIndex, DWORD dwStoryType) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, EM_SETSTORYTYPE, nStoryIndex, dwStoryType); + } + + DWORD GetEllipsisMode() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + + DWORD dwMode = 0; + BOOL bRet = (BOOL)::SendMessage(this->m_hWnd, EM_GETELLIPSISMODE, 0, (LPARAM)&dwMode); + (void)bRet; // avoid level 4 warning + ATLASSERT(bRet != FALSE); + + return dwMode; + } + + BOOL SetEllipsisMode(DWORD dwEllipsisMode) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETELLIPSISMODE, 0, dwEllipsisMode); + } + + BOOL GetEllipsisState() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_GETELLIPSISSTATE, 0, 0L); + } + + BOOL GetTouchOptions(int nTouchOptions) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_GETTOUCHOPTIONS, nTouchOptions, 0L); + } + + void SetTouchOptions(int nTouchOptions, BOOL bEnable) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, EM_SETTOUCHOPTIONS, nTouchOptions, bEnable); + } + + HRESULT InsertTable(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HRESULT)::SendMessage(this->m_hWnd, EM_INSERTTABLE, (WPARAM)pRowParams, (LPARAM)pCellParams); + } + + HRESULT GetTableParams(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HRESULT)::SendMessage(this->m_hWnd, EM_GETTABLEPARMS, (WPARAM)pRowParams, (LPARAM)pCellParams); + } + + HRESULT SetTableParams(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HRESULT)::SendMessage(this->m_hWnd, EM_SETTABLEPARMS, (WPARAM)pRowParams, (LPARAM)pCellParams); + } + + HRESULT InsertImage(RICHEDIT_IMAGE_PARAMETERS* pParams) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HRESULT)::SendMessage(this->m_hWnd, EM_INSERTIMAGE, 0, (LPARAM)pParams); + } + + BOOL SetUiaName(LPCTSTR lpstrName) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, EM_SETUIANAME, 0, (LPARAM)lpstrName); + } +#endif // (_RICHEDIT_VER >= 0x0800) +}; + +typedef CRichEditCtrlT<ATL::CWindow> CRichEditCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CRichEditCommands - message handlers for standard EDIT commands + +// Chain to CRichEditCommands message map. Your class must also derive from CRichEditCtrl. +// Example: +// class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>, +// public CRichEditCommands<CMyRichEdit> +// { +// public: +// BEGIN_MSG_MAP(CMyRichEdit) +// // your handlers... +// CHAIN_MSG_MAP_ALT(CRichEditCommands<CMyRichEdit>, 1) +// END_MSG_MAP() +// // other stuff... +// }; + +template <class T> +class CRichEditCommands : public CEditCommands< T > +{ +public: + BEGIN_MSG_MAP(CRichEditCommands< T >) + ALT_MSG_MAP(1) + COMMAND_ID_HANDLER(ID_EDIT_CLEAR, CEditCommands< T >::OnEditClear) + COMMAND_ID_HANDLER(ID_EDIT_CLEAR_ALL, CEditCommands< T >::OnEditClearAll) + COMMAND_ID_HANDLER(ID_EDIT_COPY, CEditCommands< T >::OnEditCopy) + COMMAND_ID_HANDLER(ID_EDIT_CUT, CEditCommands< T >::OnEditCut) + COMMAND_ID_HANDLER(ID_EDIT_PASTE, CEditCommands< T >::OnEditPaste) + COMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, CEditCommands< T >::OnEditSelectAll) + COMMAND_ID_HANDLER(ID_EDIT_UNDO, CEditCommands< T >::OnEditUndo) + COMMAND_ID_HANDLER(ID_EDIT_REDO, OnEditRedo) + END_MSG_MAP() + + LRESULT OnEditRedo(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->Redo(); + return 0; + } + +// State (update UI) helpers + BOOL CanCut() const + { return HasSelection(); } + + BOOL CanCopy() const + { return HasSelection(); } + + BOOL CanClear() const + { return HasSelection(); } + +// Implementation + BOOL HasSelection() const + { + const T* pT = static_cast<const T*>(this); + return (pT->GetSelectionType() != SEL_EMPTY); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CDragListBox + +template <class TBase> +class CDragListBoxT : public CListBoxT< TBase > +{ +public: +// Constructors + CDragListBoxT(HWND hWnd = NULL) : CListBoxT< TBase >(hWnd) + { } + + CDragListBoxT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + HWND hWnd = TBase::Create(TBase::GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + if(hWnd != NULL) + MakeDragList(); + return hWnd; + } + +// Operations + BOOL MakeDragList() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((this->GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0); + return ::MakeDragList(this->m_hWnd); + } + + int LBItemFromPt(POINT pt, BOOL bAutoScroll = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ::LBItemFromPt(this->m_hWnd, pt, bAutoScroll); + } + + void DrawInsert(int nItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::DrawInsert(this->GetParent(), this->m_hWnd, nItem); + } + + static UINT GetDragListMessage() + { + static UINT uDragListMessage = 0; + if(uDragListMessage == 0) + { + CStaticDataInitCriticalSectionLock lock; + if(FAILED(lock.Lock())) + { + ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CDragListBox::GetDragListMessage.\n")); + ATLASSERT(FALSE); + return 0; + } + + if(uDragListMessage == 0) + uDragListMessage = ::RegisterWindowMessage(DRAGLISTMSGSTRING); + + lock.Unlock(); + } + ATLASSERT(uDragListMessage != 0); + return uDragListMessage; + } +}; + +typedef CDragListBoxT<ATL::CWindow> CDragListBox; + +template <class T> +class CDragListNotifyImpl +{ +public: + BEGIN_MSG_MAP(CDragListNotifyImpl< T >) + MESSAGE_HANDLER(CDragListBox::GetDragListMessage(), OnDragListNotify) + END_MSG_MAP() + + LRESULT OnDragListNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + (void)uMsg; // avoid level 4 warning + ATLASSERT(uMsg == CDragListBox::GetDragListMessage()); + T* pT = static_cast<T*>(this); + LPDRAGLISTINFO lpDragListInfo = (LPDRAGLISTINFO)lParam; + LRESULT lRet = 0; + switch(lpDragListInfo->uNotification) + { + case DL_BEGINDRAG: + lRet = (LPARAM)pT->OnBeginDrag((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor); + break; + case DL_CANCELDRAG: + pT->OnCancelDrag((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor); + break; + case DL_DRAGGING: + lRet = (LPARAM)pT->OnDragging((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor); + break; + case DL_DROPPED: + pT->OnDropped((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor); + break; + default: + ATLTRACE2(atlTraceUI, 0, _T("Unknown DragListBox notification\n")); + bHandled = FALSE; // don't handle it + break; + } + return lRet; + } + +// Overrideables + BOOL OnBeginDrag(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/) + { + return TRUE; // allow dragging + } + + void OnCancelDrag(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/) + { + // nothing to do + } + + int OnDragging(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/) + { + return 0; // don't change cursor + } + + void OnDropped(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/) + { + // nothing to do + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CReBarCtrl + +template <class TBase> +class CReBarCtrlT : public TBase +{ +public: +// Constructors + CReBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CReBarCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return REBARCLASSNAME; + } + + UINT GetBandCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, RB_GETBANDCOUNT, 0, 0L); + } + + BOOL GetBandInfo(int nBand, LPREBARBANDINFO lprbbi) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_GETBANDINFO, nBand, (LPARAM)lprbbi); + } + + BOOL SetBandInfo(int nBand, LPREBARBANDINFO lprbbi) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_SETBANDINFO, nBand, (LPARAM)lprbbi); + } + + BOOL GetBarInfo(LPREBARINFO lprbi) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_GETBARINFO, 0, (LPARAM)lprbi); + } + + BOOL SetBarInfo(LPREBARINFO lprbi) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_SETBARINFO, 0, (LPARAM)lprbi); + } + + CImageList GetImageList() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + REBARINFO rbi = {}; + rbi.cbSize = sizeof(REBARINFO); + rbi.fMask = RBIM_IMAGELIST; + BOOL bRet = (BOOL)::SendMessage(this->m_hWnd, RB_GETBARINFO, 0, (LPARAM)&rbi); + return CImageList((bRet != FALSE) ? rbi.himl : NULL); + } + + BOOL SetImageList(HIMAGELIST hImageList) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + REBARINFO rbi = {}; + rbi.cbSize = sizeof(REBARINFO); + rbi.fMask = RBIM_IMAGELIST; + rbi.himl = hImageList; + return (BOOL)::SendMessage(this->m_hWnd, RB_SETBARINFO, 0, (LPARAM)&rbi); + } + + UINT GetRowCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, RB_GETROWCOUNT, 0, 0L); + } + + UINT GetRowHeight(int nBand) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, RB_GETROWHEIGHT, nBand, 0L); + } + + COLORREF GetTextColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, RB_GETTEXTCOLOR, 0, 0L); + } + + COLORREF SetTextColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, RB_SETTEXTCOLOR, 0, (LPARAM)clr); + } + + COLORREF GetBkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, RB_GETBKCOLOR, 0, 0L); + } + + COLORREF SetBkColor(COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, RB_SETBKCOLOR, 0, (LPARAM)clr); + } + + UINT GetBarHeight() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (UINT)::SendMessage(this->m_hWnd, RB_GETBARHEIGHT, 0, 0L); + } + + BOOL GetRect(int nBand, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_GETRECT, nBand, (LPARAM)lpRect); + } + + CToolTipCtrl GetToolTips() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CToolTipCtrl((HWND)::SendMessage(this->m_hWnd, RB_GETTOOLTIPS, 0, 0L)); + } + + void SetToolTips(HWND hwndToolTip) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_SETTOOLTIPS, (WPARAM)hwndToolTip, 0L); + } + + void GetBandBorders(int nBand, LPRECT lpRect) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpRect != NULL); + ::SendMessage(this->m_hWnd, RB_GETBANDBORDERS, nBand, (LPARAM)lpRect); + } + + BOOL GetColorScheme(LPCOLORSCHEME lpColorScheme) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpColorScheme != NULL); + return (BOOL)::SendMessage(this->m_hWnd, RB_GETCOLORSCHEME, 0, (LPARAM)lpColorScheme); + } + + void SetColorScheme(LPCOLORSCHEME lpColorScheme) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpColorScheme != NULL); + ::SendMessage(this->m_hWnd, RB_SETCOLORSCHEME, 0, (LPARAM)lpColorScheme); + } + + HPALETTE GetPalette() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HPALETTE)::SendMessage(this->m_hWnd, RB_GETPALETTE, 0, 0L); + } + + HPALETTE SetPalette(HPALETTE hPalette) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (HPALETTE)::SendMessage(this->m_hWnd, RB_SETPALETTE, 0, (LPARAM)hPalette); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_SETUNICODEFORMAT, bUnicode, 0L); + } + + // requires uxtheme.h to be included to use MARGINS struct +#ifndef _UXTHEME_H_ + typedef struct _MARGINS* PMARGINS; +#endif // !_UXTHEME_H_ + void GetBandMargins(PMARGINS pMargins) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_GETBANDMARGINS, 0, (LPARAM)pMargins); + } + + void SetWindowTheme(LPCWSTR lpstrTheme) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme); + } + + DWORD GetExtendedStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, RB_GETEXTENDEDSTYLE, 0, 0L); + } + + DWORD SetExtendedStyle(DWORD dwStyle, DWORD dwMask) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, RB_SETEXTENDEDSTYLE, dwMask, dwStyle); + } + +// Operations + BOOL InsertBand(int nBand, LPREBARBANDINFO lprbbi) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_INSERTBAND, nBand, (LPARAM)lprbbi); + } + + BOOL AddBand(LPREBARBANDINFO lprbbi) + { + return InsertBand(-1, lprbbi); + } + + BOOL DeleteBand(int nBand) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_DELETEBAND, nBand, 0L); + } + + ATL::CWindow SetNotifyWnd(HWND hWnd) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return ATL::CWindow((HWND)::SendMessage(this->m_hWnd, RB_SETPARENT, (WPARAM)hWnd, 0L)); + } + + void BeginDrag(int nBand, DWORD dwPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_BEGINDRAG, nBand, dwPos); + } + + void BeginDrag(int nBand, int xPos, int yPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_BEGINDRAG, nBand, MAKELPARAM(xPos, yPos)); + } + + void EndDrag() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_ENDDRAG, 0, 0L); + } + + void DragMove(DWORD dwPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_DRAGMOVE, 0, dwPos); + } + + void DragMove(int xPos, int yPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_DRAGMOVE, 0, MAKELPARAM(xPos, yPos)); + } + + void GetDropTarget(IDropTarget** ppDropTarget) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_GETDROPTARGET, 0, (LPARAM)ppDropTarget); + } + + void MaximizeBand(int nBand, BOOL bIdeal = FALSE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_MAXIMIZEBAND, nBand, bIdeal); + } + + void MinimizeBand(int nBand) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_MINIMIZEBAND, nBand, 0L); + } + + BOOL SizeToRect(LPRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_SIZETORECT, 0, (LPARAM)lpRect); + } + + int IdToIndex(UINT uBandID) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, RB_IDTOINDEX, uBandID, 0L); + } + + int HitTest(LPRBHITTESTINFO lprbht) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, RB_HITTEST, 0, (LPARAM)lprbht); + } + + BOOL ShowBand(int nBand, BOOL bShow) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_SHOWBAND, nBand, bShow); + } + + BOOL MoveBand(int nBand, int nNewPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((nNewPos >= 0) && (nNewPos <= ((int)GetBandCount() - 1))); + return (BOOL)::SendMessage(this->m_hWnd, RB_MOVEBAND, nBand, nNewPos); + } + + void PushChevron(int nBand, LPARAM lAppValue) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, RB_PUSHCHEVRON, nBand, lAppValue); + } + +// Extra operations + void LockBands(bool bLock) + { + int nBandCount = GetBandCount(); + for(int i =0; i < nBandCount; i++) + { + REBARBANDINFO rbbi = { RunTimeHelper::SizeOf_REBARBANDINFO() }; + rbbi.fMask = RBBIM_STYLE; + BOOL bRet = GetBandInfo(i, &rbbi); + ATLASSERT(bRet); + + if((rbbi.fStyle & RBBS_GRIPPERALWAYS) == 0) + { + rbbi.fStyle |= RBBS_GRIPPERALWAYS; + bRet = SetBandInfo(i, &rbbi); + ATLASSERT(bRet); + rbbi.fStyle &= ~RBBS_GRIPPERALWAYS; + } + + if(bLock) + rbbi.fStyle |= RBBS_NOGRIPPER; + else + rbbi.fStyle &= ~RBBS_NOGRIPPER; + + bRet = SetBandInfo(i, &rbbi); + ATLASSERT(bRet); + } + } + +#if (_WIN32_WINNT >= 0x0600) + BOOL SetBandWidth(int nBand, int cxWidth) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, RB_SETBANDWIDTH, nBand, cxWidth); + } +#endif // (_WIN32_WINNT >= 0x0600) +}; + +typedef CReBarCtrlT<ATL::CWindow> CReBarCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CComboBoxEx + +template <class TBase> +class CComboBoxExT : public CComboBoxT< TBase > +{ +public: +// Constructors + CComboBoxExT(HWND hWnd = NULL) : CComboBoxT< TBase >(hWnd) + { } + + CComboBoxExT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return WC_COMBOBOXEX; + } + + CImageList GetImageList() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, CBEM_GETIMAGELIST, 0, 0L)); + } + + CImageList SetImageList(HIMAGELIST hImageList) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CImageList((HIMAGELIST)::SendMessage(this->m_hWnd, CBEM_SETIMAGELIST, 0, (LPARAM)hImageList)); + } + + DWORD GetExtendedStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, CBEM_GETEXTENDEDSTYLE, 0, 0L); + } + + DWORD SetExtendedStyle(DWORD dwExMask, DWORD dwExStyle) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, CBEM_SETEXTENDEDSTYLE, dwExMask, dwExStyle); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CBEM_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CBEM_SETUNICODEFORMAT, bUnicode, 0L); + } + + void SetWindowTheme(LPCWSTR lpstrTheme) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, CBEM_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme); + } + +// Operations + int InsertItem(const COMBOBOXEXITEM* lpcCBItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)lpcCBItem); + } + + int InsertItem(UINT nMask, int nIndex, LPCTSTR lpszItem, int nImage, int nSelImage, + int iIndent, int iOverlay, LPARAM lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + COMBOBOXEXITEM cbex = {}; + cbex.mask = nMask; + cbex.iItem = nIndex; + cbex.pszText = (LPTSTR) lpszItem; + cbex.iImage = nImage; + cbex.iSelectedImage = nSelImage; + cbex.iIndent = iIndent; + cbex.iOverlay = iOverlay; + cbex.lParam = lParam; + return (int)::SendMessage(this->m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)&cbex); + } + + int InsertItem(int nIndex, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, LPARAM lParam = 0) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + COMBOBOXEXITEM cbex = {}; + cbex.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT | CBEIF_LPARAM; + cbex.iItem = nIndex; + cbex.pszText = (LPTSTR) lpszItem; + cbex.iImage = nImage; + cbex.iSelectedImage = nSelImage; + cbex.iIndent = iIndent; + cbex.lParam = lParam; + return (int)::SendMessage(this->m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)&cbex); + } + + int AddItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, int iOverlay, LPARAM lParam) + { + return InsertItem(nMask, -1, lpszItem, nImage, nSelImage, iIndent, iOverlay, lParam); + } + + int AddItem(LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, LPARAM lParam = 0) + { + return InsertItem(-1, lpszItem, nImage, nSelImage, iIndent, lParam); + } + + int DeleteItem(int nIndex) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, CBEM_DELETEITEM, nIndex, 0L); + } + + BOOL GetItem(PCOMBOBOXEXITEM pCBItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CBEM_GETITEM, 0, (LPARAM)pCBItem); + } + + BOOL SetItem(const COMBOBOXEXITEM* lpcCBItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CBEM_SETITEM, 0, (LPARAM)lpcCBItem); + } + + int SetItem(int nIndex, UINT nMask, LPCTSTR lpszItem, int nImage, int nSelImage, + int iIndent, int iOverlay, LPARAM lParam) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + COMBOBOXEXITEM cbex = {}; + cbex.mask = nMask; + cbex.iItem = nIndex; + cbex.pszText = (LPTSTR) lpszItem; + cbex.iImage = nImage; + cbex.iSelectedImage = nSelImage; + cbex.iIndent = iIndent; + cbex.iOverlay = iOverlay; + cbex.lParam = lParam; + return (int)::SendMessage(this->m_hWnd, CBEM_SETITEM, 0, (LPARAM)&cbex); + } + + BOOL GetItemText(int nIndex, LPTSTR lpszItem, int nLen) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(lpszItem != NULL); + + COMBOBOXEXITEM cbex = {}; + cbex.mask = CBEIF_TEXT; + cbex.iItem = nIndex; + cbex.pszText = lpszItem; + cbex.cchTextMax = nLen; + + return (BOOL)::SendMessage(this->m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex); + } + + BOOL GetItemText(int nIndex, BSTR& bstrText) const + { + USES_CONVERSION; + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(bstrText == NULL); + + COMBOBOXEXITEM cbex = {}; + cbex.mask = CBEIF_TEXT; + cbex.iItem = nIndex; + + LPTSTR lpstrText = NULL; + BOOL bRet = FALSE; + for(int nLen = 256; ; nLen *= 2) + { + ATLTRY(lpstrText = new TCHAR[nLen]); + if(lpstrText == NULL) + break; + lpstrText[0] = NULL; + cbex.pszText = lpstrText; + cbex.cchTextMax = nLen; + bRet = (BOOL)::SendMessage(this->m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex); + if(!bRet || (lstrlen(cbex.pszText) < (nLen - 1))) + break; + delete [] lpstrText; + lpstrText = NULL; + } + + if(lpstrText != NULL) + { + if(bRet) + bstrText = ::SysAllocString(T2OLE(lpstrText)); + delete [] lpstrText; + } + + return (bstrText != NULL) ? TRUE : FALSE; + } + +#ifdef __ATLSTR_H__ + BOOL GetItemText(int nIndex, ATL::CString& strText) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + + COMBOBOXEXITEM cbex = {}; + cbex.mask = CBEIF_TEXT; + cbex.iItem = nIndex; + + strText.Empty(); + BOOL bRet = FALSE; + for(int nLen = 256; ; nLen *= 2) + { + cbex.pszText = strText.GetBufferSetLength(nLen); + if(cbex.pszText == NULL) + { + bRet = FALSE; + break; + } + cbex.cchTextMax = nLen; + bRet = (BOOL)::SendMessage(this->m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex); + if(!bRet || (lstrlen(cbex.pszText) < (nLen - 1))) + break; + } + strText.ReleaseBuffer(); + return bRet; + } +#endif // __ATLSTR_H__ + + BOOL SetItemText(int nIndex, LPCTSTR lpszItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return SetItem(nIndex, CBEIF_TEXT, lpszItem, 0, 0, 0, 0, 0); + } + + CComboBox GetComboCtrl() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CComboBox((HWND)::SendMessage(this->m_hWnd, CBEM_GETCOMBOCONTROL, 0, 0L)); + } + + CEdit GetEditCtrl() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CEdit((HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0L)); + } + + BOOL HasEditChanged() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, CBEM_HASEDITCHANGED, 0, 0L); + } + +// Non-functional + int AddString(LPCTSTR /*lpszItem*/) + { + ATLASSERT(FALSE); // Not available in CComboBoxEx; use InsertItem + return 0; + } + + int InsertString(int /*nIndex*/, LPCTSTR /*lpszString*/) + { + ATLASSERT(FALSE); // Not available in CComboBoxEx; use InsertItem + return 0; + } + + int Dir(UINT /*attr*/, LPCTSTR /*lpszWildCard*/) + { + ATLASSERT(FALSE); // Not available in CComboBoxEx + return 0; + } + + int FindString(int /*nStartAfter*/, LPCTSTR /*lpszString*/) const + { + ATLASSERT(FALSE); // Not available in CComboBoxEx; try FindStringExact + return 0; + } +}; + +typedef CComboBoxExT<ATL::CWindow> CComboBoxEx; + + +/////////////////////////////////////////////////////////////////////////////// +// CMonthCalendarCtrl + +template <class TBase> +class CMonthCalendarCtrlT : public TBase +{ +public: +// Constructors + CMonthCalendarCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CMonthCalendarCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return MONTHCAL_CLASS; + } + + COLORREF GetColor(int nColorType) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, MCM_GETCOLOR, nColorType, 0L); + } + + COLORREF SetColor(int nColorType, COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, MCM_SETCOLOR, nColorType, clr); + } + + BOOL GetCurSel(LPSYSTEMTIME lpSysTime) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_GETCURSEL, 0, (LPARAM)lpSysTime); + } + + BOOL SetCurSel(LPSYSTEMTIME lpSysTime) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_SETCURSEL, 0, (LPARAM)lpSysTime); + } + + int GetFirstDayOfWeek(BOOL* pbLocaleVal = NULL) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, MCM_GETFIRSTDAYOFWEEK, 0, 0L); + if(pbLocaleVal != NULL) + *pbLocaleVal = (BOOL)HIWORD(dwRet); + return (int)(short)LOWORD(dwRet); + } + + int SetFirstDayOfWeek(int nDay, BOOL* pbLocaleVal = NULL) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + DWORD dwRet = (DWORD)::SendMessage(this->m_hWnd, MCM_SETFIRSTDAYOFWEEK, 0, nDay); + if(pbLocaleVal != NULL) + *pbLocaleVal = (BOOL)HIWORD(dwRet); + return (int)(short)LOWORD(dwRet); + } + + int GetMaxSelCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, MCM_GETMAXSELCOUNT, 0, 0L); + } + + BOOL SetMaxSelCount(int nMax) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_SETMAXSELCOUNT, nMax, 0L); + } + + int GetMonthDelta() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, MCM_GETMONTHDELTA, 0, 0L); + } + + int SetMonthDelta(int nDelta) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, MCM_SETMONTHDELTA, nDelta, 0L); + } + + DWORD GetRange(LPSYSTEMTIME lprgSysTimeArray) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, MCM_GETRANGE, 0, (LPARAM)lprgSysTimeArray); + } + + BOOL SetRange(DWORD dwFlags, LPSYSTEMTIME lprgSysTimeArray) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_SETRANGE, dwFlags, (LPARAM)lprgSysTimeArray); + } + + BOOL GetSelRange(LPSYSTEMTIME lprgSysTimeArray) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_GETSELRANGE, 0, (LPARAM)lprgSysTimeArray); + } + + BOOL SetSelRange(LPSYSTEMTIME lprgSysTimeArray) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_SETSELRANGE, 0, (LPARAM)lprgSysTimeArray); + } + + BOOL GetToday(LPSYSTEMTIME lpSysTime) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_GETTODAY, 0, (LPARAM)lpSysTime); + } + + void SetToday(LPSYSTEMTIME lpSysTime) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, MCM_SETTODAY, 0, (LPARAM)lpSysTime); + } + + BOOL GetMinReqRect(LPRECT lpRectInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_GETMINREQRECT, 0, (LPARAM)lpRectInfo); + } + + int GetMaxTodayWidth() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, MCM_GETMAXTODAYWIDTH, 0, 0L); + } + + BOOL GetUnicodeFormat() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_GETUNICODEFORMAT, 0, 0L); + } + + BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_SETUNICODEFORMAT, bUnicode, 0L); + } + +#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) + DWORD GetCurrentView() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, MCM_GETCURRENTVIEW, 0, 0L); + } + + BOOL SetCurrentView(DWORD dwView) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_SETCURRENTVIEW, 0, dwView); + } + + DWORD GetCalendarCount() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, MCM_GETCALENDARCOUNT, 0, 0L); + } + + BOOL GetCalendarGridInfo(PMCGRIDINFO pGridInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_GETCALENDARGRIDINFO, 0, (LPARAM)pGridInfo); + } + + CALID GetCALID() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (CALID)::SendMessage(this->m_hWnd, MCM_GETCALID, 0, 0L); + } + + void SetCALID(CALID calid) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, MCM_SETCALID, (LPARAM)calid, 0L); + } + + int GetCalendarBorder() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, MCM_GETCALENDARBORDER, 0, 0L); + } + + void SetCalendarBorder(int cxyBorder, BOOL bSet = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, MCM_SETCALENDARBORDER, (WPARAM)bSet, (LPARAM)cxyBorder); + } +#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) + +// Operations + int GetMonthRange(DWORD dwFlags, LPSYSTEMTIME lprgSysTimeArray) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, MCM_GETMONTHRANGE, dwFlags, (LPARAM)lprgSysTimeArray); + } + + BOOL SetDayState(int nMonths, LPMONTHDAYSTATE lpDayStateArray) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, MCM_SETDAYSTATE, nMonths, (LPARAM)lpDayStateArray); + } + + DWORD HitTest(PMCHITTESTINFO pMCHitTest) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, MCM_HITTEST, 0, (LPARAM)pMCHitTest); + } + +#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) + void SizeRectToMin(LPRECT lpRect) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, MCM_SIZERECTTOMIN, 0, (LPARAM)lpRect); + } +#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) +}; + +typedef CMonthCalendarCtrlT<ATL::CWindow> CMonthCalendarCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CDateTimePickerCtrl + +template <class TBase> +class CDateTimePickerCtrlT : public TBase +{ +public: +// Constructors + CDateTimePickerCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CDateTimePickerCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Operations + static LPCTSTR GetWndClassName() + { + return DATETIMEPICK_CLASS; + } + + BOOL SetFormat(LPCTSTR lpszFormat) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, DTM_SETFORMAT, 0, (LPARAM)lpszFormat); + } + + COLORREF GetMonthCalColor(int nColorType) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, DTM_GETMCCOLOR, nColorType, 0L); + } + + COLORREF SetMonthCalColor(int nColorType, COLORREF clr) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, DTM_SETMCCOLOR, nColorType, clr); + } + + DWORD GetRange(LPSYSTEMTIME lpSysTimeArray) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, DTM_GETRANGE, 0, (LPARAM)lpSysTimeArray); + } + + BOOL SetRange(DWORD dwFlags, LPSYSTEMTIME lpSysTimeArray) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, DTM_SETRANGE, dwFlags, (LPARAM)lpSysTimeArray); + } + + DWORD GetSystemTime(LPSYSTEMTIME lpSysTime) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)lpSysTime); + } + + BOOL SetSystemTime(DWORD dwFlags, LPSYSTEMTIME lpSysTime) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, DTM_SETSYSTEMTIME, dwFlags, (LPARAM)lpSysTime); + } + + CMonthCalendarCtrl GetMonthCal() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CMonthCalendarCtrl((HWND)::SendMessage(this->m_hWnd, DTM_GETMONTHCAL, 0, 0L)); + } + + CFontHandle GetMonthCalFont() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return CFontHandle((HFONT)::SendMessage(this->m_hWnd, DTM_GETMCFONT, 0, 0L)); + } + + void SetMonthCalFont(HFONT hFont, BOOL bRedraw = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, DTM_SETMCFONT, (WPARAM)hFont, MAKELPARAM(bRedraw, 0)); + } + +#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) + DWORD GetMonthCalStyle() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, DTM_GETMCSTYLE, 0, 0L); + } + + DWORD SetMonthCalStyle(DWORD dwStyle) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (DWORD)::SendMessage(this->m_hWnd, DTM_SETMCSTYLE, 0, (LPARAM)dwStyle); + } + + void GetDateTimePickerInfo(LPDATETIMEPICKERINFO lpPickerInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, DTM_GETDATETIMEPICKERINFO, 0, (LPARAM)lpPickerInfo); + } + + BOOL GetIdealSize(LPSIZE lpSize) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, DTM_GETIDEALSIZE, 0, (LPARAM)lpSize); + } + + void CloseMonthCal() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, DTM_CLOSEMONTHCAL, 0, 0L); + } +#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) +}; + +typedef CDateTimePickerCtrlT<ATL::CWindow> CDateTimePickerCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CFlatScrollBarImpl - support for flat scroll bars + +template <class T> +class CFlatScrollBarImpl +{ +public: +// Initialization + BOOL FlatSB_Initialize() + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::InitializeFlatSB(pT->m_hWnd); + } + + HRESULT FlatSB_Uninitialize() + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::UninitializeFlatSB(pT->m_hWnd); + } + +// Flat scroll bar properties + BOOL FlatSB_GetScrollProp(UINT uIndex, LPINT lpnValue) const + { + const T* pT = static_cast<const T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_GetScrollProp(pT->m_hWnd, uIndex, lpnValue); + } + + BOOL FlatSB_SetScrollProp(UINT uIndex, int nValue, BOOL bRedraw = TRUE) + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_SetScrollProp(pT->m_hWnd, uIndex, nValue, bRedraw); + } + +// Attributes + int FlatSB_GetScrollPos(int nBar) const + { + const T* pT = static_cast<const T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_GetScrollPos(pT->m_hWnd, nBar); + } + + int FlatSB_SetScrollPos(int nBar, int nPos, BOOL bRedraw = TRUE) + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_SetScrollPos(pT->m_hWnd, nBar, nPos, bRedraw); + } + + BOOL FlatSB_GetScrollRange(int nBar, LPINT lpMinPos, LPINT lpMaxPos) const + { + const T* pT = static_cast<const T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_GetScrollRange(pT->m_hWnd, nBar, lpMinPos, lpMaxPos); + } + + BOOL FlatSB_SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw = TRUE) + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_SetScrollRange(pT->m_hWnd, nBar, nMinPos, nMaxPos, bRedraw); + } + + BOOL FlatSB_GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo) const + { + const T* pT = static_cast<const T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_GetScrollInfo(pT->m_hWnd, nBar, lpScrollInfo); + } + + int FlatSB_SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE) + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_SetScrollInfo(pT->m_hWnd, nBar, lpScrollInfo, bRedraw); + } + +// Operations + BOOL FlatSB_ShowScrollBar(UINT nBar, BOOL bShow = TRUE) + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_ShowScrollBar(pT->m_hWnd, nBar, bShow); + } + + BOOL FlatSB_EnableScrollBar(UINT uSBFlags, UINT uArrowFlags = ESB_ENABLE_BOTH) + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + return ::FlatSB_EnableScrollBar(pT->m_hWnd, uSBFlags, uArrowFlags); + } +}; + +template <class TBase> +class CFlatScrollBarT : public TBase, public CFlatScrollBarImpl<CFlatScrollBarT< TBase > > +{ +public: + CFlatScrollBarT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CFlatScrollBarT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } +}; + +typedef CFlatScrollBarT<ATL::CWindow> CFlatScrollBar; + + +/////////////////////////////////////////////////////////////////////////////// +// CIPAddressCtrl + +template <class TBase> +class CIPAddressCtrlT : public TBase +{ +public: +// Constructors + CIPAddressCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CIPAddressCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Atteributes + static LPCTSTR GetWndClassName() + { + return WC_IPADDRESS; + } + + BOOL IsBlank() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, IPM_ISBLANK, 0, 0L); + } + + int GetAddress(LPDWORD lpdwAddress) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, IPM_GETADDRESS, 0, (LPARAM)lpdwAddress); + } + + void SetAddress(DWORD dwAddress) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, IPM_SETADDRESS, 0, dwAddress); + } + + void ClearAddress() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, IPM_CLEARADDRESS, 0, 0L); + } + + void SetRange(int nField, WORD wRange) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, IPM_SETRANGE, nField, wRange); + } + + void SetRange(int nField, BYTE nMin, BYTE nMax) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, IPM_SETRANGE, nField, MAKEIPRANGE(nMin, nMax)); + } + + void SetFocus(int nField) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, IPM_SETFOCUS, nField, 0L); + } +}; + +typedef CIPAddressCtrlT<ATL::CWindow> CIPAddressCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CPagerCtrl + +template <class TBase> +class CPagerCtrlT : public TBase +{ +public: +// Constructors + CPagerCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CPagerCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { + return WC_PAGESCROLLER; + } + + int GetButtonSize() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PGM_GETBUTTONSIZE, 0, 0L); + } + + int SetButtonSize(int nButtonSize) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PGM_SETBUTTONSIZE, 0, nButtonSize); + } + + DWORD GetButtonState(int nButton) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT((nButton == PGB_TOPORLEFT) || (nButton == PGB_BOTTOMORRIGHT)); + return (DWORD)::SendMessage(this->m_hWnd, PGM_GETBUTTONSTATE, 0, nButton); + } + + COLORREF GetBkColor() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, PGM_GETBKCOLOR, 0, 0L); + } + + COLORREF SetBkColor(COLORREF clrBk) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (COLORREF)::SendMessage(this->m_hWnd, PGM_SETBKCOLOR, 0, (LPARAM)clrBk); + } + + int GetBorder() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PGM_GETBORDER, 0, 0L); + } + + int SetBorder(int nBorderSize) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PGM_SETBORDER, 0, nBorderSize); + } + + int GetPos() const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PGM_GETPOS, 0, 0L); + } + + int SetPos(int nPos) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, PGM_SETPOS, 0, nPos); + } + +// Operations + void SetChild(HWND hWndChild) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, PGM_SETCHILD, 0, (LPARAM)hWndChild); + } + + void ForwardMouse(BOOL bForward = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, PGM_FORWARDMOUSE, bForward, 0L); + } + + void RecalcSize() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ::SendMessage(this->m_hWnd, PGM_RECALCSIZE, 0, 0L); + } + + void GetDropTarget(IDropTarget** ppDropTarget) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + ATLASSERT(ppDropTarget != NULL); + ::SendMessage(this->m_hWnd, PGM_GETDROPTARGET, 0, (LPARAM)ppDropTarget); + } +}; + +typedef CPagerCtrlT<ATL::CWindow> CPagerCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CLinkCtrl - Windows SYSLINK control + +template <class TBase> +class CLinkCtrlT : public TBase +{ +public: +// Constructors + CLinkCtrlT(HWND hWnd = NULL) : TBase(hWnd) + { } + + CLinkCtrlT< TBase >& operator =(HWND hWnd) + { + this->m_hWnd = hWnd; + return *this; + } + + HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, + DWORD dwStyle = 0, DWORD dwExStyle = 0, + ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) + { + return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); + } + +// Attributes + static LPCTSTR GetWndClassName() + { +#ifdef _UNICODE + return WC_LINK; +#else // !_UNICODE + return "SysLink"; +#endif // !_UNICODE + } + + int GetIdealHeight(int cxMaxWidth = 0) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LM_GETIDEALHEIGHT, cxMaxWidth, 0L); + } + + BOOL GetItem(PLITEM pLItem) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LM_GETITEM, 0, (LPARAM)pLItem); + } + + BOOL SetItem(PLITEM pLItem) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LM_SETITEM, 0, (LPARAM)pLItem); + } + + // Vista only + int GetIdealSize(SIZE& size, int cxMaxWidth = 0) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (int)::SendMessage(this->m_hWnd, LM_GETIDEALSIZE, cxMaxWidth, (LPARAM)&size); + } + +// Operations + BOOL HitTest(PLHITTESTINFO pLHitTestInfo) const + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return (BOOL)::SendMessage(this->m_hWnd, LM_HITTEST, 0, (LPARAM)pLHitTestInfo); + } +}; + +typedef CLinkCtrlT<ATL::CWindow> CLinkCtrl; + + +/////////////////////////////////////////////////////////////////////////////// +// CCustomDraw - MI class for custom-draw support + +template <class T> +class CCustomDraw +{ +public: +// Message map and handlers + BEGIN_MSG_MAP(CCustomDraw< T >) + NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw) + ALT_MSG_MAP(1) + REFLECTED_NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw) + END_MSG_MAP() + +// message handler + LRESULT OnCustomDraw(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) + { + T* pT = static_cast<T*>(this); + pT->SetMsgHandled(TRUE); + LPNMCUSTOMDRAW lpNMCustomDraw = (LPNMCUSTOMDRAW)pnmh; + DWORD dwRet = 0; + switch(lpNMCustomDraw->dwDrawStage) + { + case CDDS_PREPAINT: + dwRet = pT->OnPrePaint(idCtrl, lpNMCustomDraw); + break; + case CDDS_POSTPAINT: + dwRet = pT->OnPostPaint(idCtrl, lpNMCustomDraw); + break; + case CDDS_PREERASE: + dwRet = pT->OnPreErase(idCtrl, lpNMCustomDraw); + break; + case CDDS_POSTERASE: + dwRet = pT->OnPostErase(idCtrl, lpNMCustomDraw); + break; + case CDDS_ITEMPREPAINT: + dwRet = pT->OnItemPrePaint(idCtrl, lpNMCustomDraw); + break; + case CDDS_ITEMPOSTPAINT: + dwRet = pT->OnItemPostPaint(idCtrl, lpNMCustomDraw); + break; + case CDDS_ITEMPREERASE: + dwRet = pT->OnItemPreErase(idCtrl, lpNMCustomDraw); + break; + case CDDS_ITEMPOSTERASE: + dwRet = pT->OnItemPostErase(idCtrl, lpNMCustomDraw); + break; + case (CDDS_ITEMPREPAINT | CDDS_SUBITEM): + dwRet = pT->OnSubItemPrePaint(idCtrl, lpNMCustomDraw); + break; + default: + pT->SetMsgHandled(FALSE); + break; + } + bHandled = pT->IsMsgHandled(); + return dwRet; + } + +// Overrideables + DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } + + DWORD OnPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } + + DWORD OnPreErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } + + DWORD OnPostErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } + + DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } + + DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } + + DWORD OnItemPreErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } + + DWORD OnItemPostErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } + + DWORD OnSubItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) + { + return CDRF_DODEFAULT; + } +}; + +} // namespace WTL + +#endif // __ATLCTRLS_H__ diff --git a/Examples/WhisperDesktop/Utils/WTL/atlddx.h b/Examples/WhisperDesktop/Utils/WTL/atlddx.h new file mode 100644 index 0000000..7723b99 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/WTL/atlddx.h @@ -0,0 +1,667 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLDDX_H__ +#define __ATLDDX_H__ + +#pragma once + +#ifndef __ATLAPP_H__ + #error atlddx.h requires atlapp.h to be included first +#endif + +#include <float.h> + + +/////////////////////////////////////////////////////////////////////////////// +// Classes in this file: +// +// CWinDataExchange<T> + + +namespace WTL +{ + +// Constants +#define DDX_LOAD FALSE +#define DDX_SAVE TRUE + +// DDX map macros +#define BEGIN_DDX_MAP(thisClass) \ + BOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) \ + { \ + (bSaveAndValidate); \ + (nCtlID); + +#define DDX_TEXT(nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \ + return FALSE; \ + } + +#define DDX_TEXT_LEN(nID, var, len) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \ + return FALSE; \ + } + +#define DDX_INT(nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Int(nID, var, TRUE, bSaveAndValidate)) \ + return FALSE; \ + } + +#define DDX_INT_RANGE(nID, var, min, max) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Int(nID, var, TRUE, bSaveAndValidate, TRUE, min, max)) \ + return FALSE; \ + } + +#define DDX_UINT(nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Int(nID, var, FALSE, bSaveAndValidate)) \ + return FALSE; \ + } + +#define DDX_UINT_RANGE(nID, var, min, max) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Int(nID, var, FALSE, bSaveAndValidate, TRUE, min, max)) \ + return FALSE; \ + } + +#define DDX_FLOAT(nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Float(nID, var, bSaveAndValidate)) \ + return FALSE; \ + } + +#define DDX_FLOAT_RANGE(nID, var, min, max) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max)) \ + return FALSE; \ + } +#define DDX_FLOAT_P(nID, var, precision) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Float(nID, var, bSaveAndValidate, FALSE, 0, 0, precision)) \ + return FALSE; \ + } + +#define DDX_FLOAT_P_RANGE(nID, var, min, max, precision) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max, precision)) \ + return FALSE; \ + } + +#define DDX_CONTROL(nID, obj) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + DDX_Control(nID, obj, bSaveAndValidate); + +#define DDX_CONTROL_HANDLE(nID, obj) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + DDX_Control_Handle(nID, obj, bSaveAndValidate); + +#define DDX_CHECK(nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + DDX_Check(nID, var, bSaveAndValidate); + +#define DDX_RADIO(nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + DDX_Radio(nID, var, bSaveAndValidate); + +#define END_DDX_MAP() \ + return TRUE; \ + } + +// DDX support for Tab, Combo, ListBox and ListView selection index +// Note: Specialized versions require atlctrls.h to be included first + +#define DDX_INDEX(CtrlClass, nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + DDX_Index<CtrlClass>(nID, var, bSaveAndValidate); + +#ifdef __ATLCTRLS_H__ + #define DDX_TAB_INDEX(nID, var) DDX_INDEX(WTL::CTabCtrl, nID, var) + #define DDX_COMBO_INDEX(nID, var) DDX_INDEX(WTL::CComboBox, nID, var) + #define DDX_LISTBOX_INDEX(nID, var) DDX_INDEX(WTL::CListBox, nID, var) + #define DDX_LISTVIEW_INDEX(nID, var) DDX_INDEX(WTL::CListViewCtrl, nID, var) +#endif // __ATLCTRLS_H__ + + +/////////////////////////////////////////////////////////////////////////////// +// CWinDataExchange - provides support for DDX + +template <class T> +class CWinDataExchange +{ +public: +// Data exchange method - override in your derived class + BOOL DoDataExchange(BOOL /*bSaveAndValidate*/ = FALSE, UINT /*nCtlID*/ = (UINT)-1) + { + // this one should never be called, override it in + // your derived class by implementing DDX map + ATLASSERT(FALSE); + return FALSE; + } + +// Helpers for validation error reporting + enum _XDataType + { + ddxDataNull = 0, + ddxDataText = 1, + ddxDataInt = 2, + ddxDataFloat = 3, + ddxDataDouble = 4 + }; + + struct _XTextData + { + int nLength; + int nMaxLength; + }; + + struct _XIntData + { + long nVal; + long nMin; + long nMax; + }; + + struct _XFloatData + { + double nVal; + double nMin; + double nMax; + }; + + struct _XData + { + _XDataType nDataType; + union + { + _XTextData textData; + _XIntData intData; + _XFloatData floatData; + }; + }; + +// Text exchange + BOOL DDX_Text(UINT nID, LPTSTR lpstrText, int cbSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) + { + T* pT = static_cast<T*>(this); + BOOL bSuccess = TRUE; + + if(bSave) + { + HWND hWndCtrl = pT->GetDlgItem(nID); + int nRetLen = ::GetWindowText(hWndCtrl, lpstrText, cbSize / sizeof(TCHAR)); + if(nRetLen < ::GetWindowTextLength(hWndCtrl)) + bSuccess = FALSE; + } + else + { + ATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength)); + bSuccess = pT->SetDlgItemText(nID, lpstrText); + } + + if(!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if(bSave && bValidate) // validation + { + ATLASSERT(nLength > 0); + if(lstrlen(lpstrText) > nLength) + { + _XData data = { ddxDataText }; + data.textData.nLength = lstrlen(lpstrText); + data.textData.nMaxLength = nLength; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } + } + return bSuccess; + } + + BOOL DDX_Text(UINT nID, BSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) + { + T* pT = static_cast<T*>(this); + BOOL bSuccess = TRUE; + + if(bSave) + { + bSuccess = pT->GetDlgItemText(nID, bstrText); + } + else + { + USES_CONVERSION; + LPTSTR lpstrText = OLE2T(bstrText); + ATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength)); + bSuccess = pT->SetDlgItemText(nID, lpstrText); + } + + if(!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if(bSave && bValidate) // validation + { + ATLASSERT(nLength > 0); + if((int)::SysStringLen(bstrText) > nLength) + { + _XData data = { ddxDataText }; + data.textData.nLength = (int)::SysStringLen(bstrText); + data.textData.nMaxLength = nLength; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } + } + return bSuccess; + } + + BOOL DDX_Text(UINT nID, ATL::CComBSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) + { + T* pT = static_cast<T*>(this); + BOOL bSuccess = TRUE; + + if(bSave) + { + bSuccess = pT->GetDlgItemText(nID, (BSTR&)bstrText); + } + else + { + USES_CONVERSION; + LPTSTR lpstrText = OLE2T(bstrText); + ATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength)); + bSuccess = pT->SetDlgItemText(nID, lpstrText); + } + + if(!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if(bSave && bValidate) // validation + { + ATLASSERT(nLength > 0); + if((int)bstrText.Length() > nLength) + { + _XData data = { ddxDataText }; + data.textData.nLength = (int)bstrText.Length(); + data.textData.nMaxLength = nLength; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } + } + return bSuccess; + } + +#ifdef __ATLSTR_H__ + BOOL DDX_Text(UINT nID, ATL::CString& strText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) + { + T* pT = static_cast<T*>(this); + BOOL bSuccess = TRUE; + + if(bSave) + { + HWND hWndCtrl = pT->GetDlgItem(nID); + int nLen = ::GetWindowTextLength(hWndCtrl); + int nRetLen = -1; + LPTSTR lpstr = strText.GetBufferSetLength(nLen); + if(lpstr != NULL) + { + nRetLen = ::GetWindowText(hWndCtrl, lpstr, nLen + 1); + strText.ReleaseBuffer(); + } + if(nRetLen < nLen) + bSuccess = FALSE; + } + else + { + bSuccess = pT->SetDlgItemText(nID, strText); + } + + if(!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if(bSave && bValidate) // validation + { + ATLASSERT(nLength > 0); + if(strText.GetLength() > nLength) + { + _XData data = { ddxDataText }; + data.textData.nLength = strText.GetLength(); + data.textData.nMaxLength = nLength; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } + } + return bSuccess; + } +#endif // __ATLSTR_H__ + +// Numeric exchange + template <class Type> + BOOL DDX_Int(UINT nID, Type& nVal, BOOL bSigned, BOOL bSave, BOOL bValidate = FALSE, Type nMin = 0, Type nMax = 0) + { + T* pT = static_cast<T*>(this); + BOOL bSuccess = TRUE; + + if(bSave) + { + nVal = (Type)pT->GetDlgItemInt(nID, &bSuccess, bSigned); + } + else + { + ATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax))); + bSuccess = pT->SetDlgItemInt(nID, nVal, bSigned); + } + + if(!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if(bSave && bValidate) // validation + { + ATLASSERT(nMin != nMax); + if((nVal < nMin) || (nVal > nMax)) + { + _XData data = { ddxDataInt }; + data.intData.nVal = (long)nVal; + data.intData.nMin = (long)nMin; + data.intData.nMax = (long)nMax; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } + } + return bSuccess; + } + +// Float exchange + static BOOL _AtlSimpleFloatParse(LPCTSTR lpszText, double& d) + { + ATLASSERT(lpszText != NULL); + while ((*lpszText == _T(' ')) || (*lpszText == _T('\t'))) + lpszText++; + + TCHAR chFirst = lpszText[0]; + d = _tcstod(lpszText, (LPTSTR*)&lpszText); + if ((d == 0.0) && (chFirst != _T('0'))) + return FALSE; // could not convert + while ((*lpszText == _T(' ')) || (*lpszText == _T('\t'))) + lpszText++; + + if (*lpszText != _T('\0')) + return FALSE; // not terminated properly + + return TRUE; + } + + BOOL DDX_Float(UINT nID, float& nVal, BOOL bSave, BOOL bValidate = FALSE, float nMin = 0.F, float nMax = 0.F, int nPrecision = FLT_DIG) + { + T* pT = static_cast<T*>(this); + BOOL bSuccess = TRUE; + const int cchBuff = 32; + TCHAR szBuff[cchBuff] = {}; + + if(bSave) + { + pT->GetDlgItemText(nID, szBuff, cchBuff); + double d = 0; + if(_AtlSimpleFloatParse(szBuff, d)) + nVal = (float)d; + else + bSuccess = FALSE; + } + else + { + ATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax))); + _stprintf_s(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal); + bSuccess = pT->SetDlgItemText(nID, szBuff); + } + + if(!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if(bSave && bValidate) // validation + { + ATLASSERT(nMin != nMax); + if((nVal < nMin) || (nVal > nMax)) + { + _XData data = { ddxDataFloat }; + data.floatData.nVal = (double)nVal; + data.floatData.nMin = (double)nMin; + data.floatData.nMax = (double)nMax; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } + } + return bSuccess; + } + + BOOL DDX_Float(UINT nID, double& nVal, BOOL bSave, BOOL bValidate = FALSE, double nMin = 0., double nMax = 0., int nPrecision = DBL_DIG) + { + T* pT = static_cast<T*>(this); + BOOL bSuccess = TRUE; + const int cchBuff = 32; + TCHAR szBuff[cchBuff] = {}; + + if(bSave) + { + pT->GetDlgItemText(nID, szBuff, cchBuff); + double d = 0; + if(_AtlSimpleFloatParse(szBuff, d)) + nVal = d; + else + bSuccess = FALSE; + } + else + { + ATLASSERT(!bValidate || ((nVal >= nMin) && (nVal <= nMax))); + _stprintf_s(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal); + bSuccess = pT->SetDlgItemText(nID, szBuff); + } + + if(!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if(bSave && bValidate) // validation + { + ATLASSERT(nMin != nMax); + if((nVal < nMin) || (nVal > nMax)) + { + _XData data = { ddxDataFloat }; + data.floatData.nVal = nVal; + data.floatData.nMin = nMin; + data.floatData.nMax = nMax; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } + } + return bSuccess; + } + +// Full control subclassing (for CWindowImpl derived controls) + template <class TControl> + void DDX_Control(UINT nID, TControl& ctrl, BOOL bSave) + { + if(!bSave && (ctrl.m_hWnd == NULL)) + { + T* pT = static_cast<T*>(this); + ctrl.SubclassWindow(pT->GetDlgItem(nID)); + } + } + +// Simple control attaching (for HWND wrapper controls) + template <class TControl> + void DDX_Control_Handle(UINT nID, TControl& ctrl, BOOL bSave) + { + if(!bSave && (ctrl.m_hWnd == NULL)) + { + T* pT = static_cast<T*>(this); + ctrl = pT->GetDlgItem(nID); + } + } + +// Control state + void DDX_Check(UINT nID, int& nValue, BOOL bSave) + { + T* pT = static_cast<T*>(this); + HWND hWndCtrl = pT->GetDlgItem(nID); + if(bSave) + { + nValue = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L); + ATLASSERT((nValue >= 0) && (nValue <= 2)); + } + else + { + if((nValue < 0) || (nValue > 2)) + { + ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - dialog data checkbox value (%d) out of range.\n"), nValue); + nValue = 0; // default to off + } + ::SendMessage(hWndCtrl, BM_SETCHECK, nValue, 0L); + } + } + + // variant that supports bool (checked/not-checked, no intermediate state) + void DDX_Check(UINT nID, bool& bCheck, BOOL bSave) + { + int nValue = bCheck ? 1 : 0; + DDX_Check(nID, nValue, bSave); + + if(bSave) + { + if(nValue == 2) + ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - checkbox state (%d) out of supported range.\n"), nValue); + bCheck = (nValue == 1); + } + } + + void DDX_Radio(UINT nID, int& nValue, BOOL bSave) + { + T* pT = static_cast<T*>(this); + HWND hWndCtrl = pT->GetDlgItem(nID); + ATLASSERT(hWndCtrl != NULL); + + // must be first in a group of auto radio buttons + ATLASSERT(::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP); + ATLASSERT(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON); + + if(bSave) + nValue = -1; // value if none found + + // walk all children in group + int nButton = 0; + do + { + if(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON) + { + // control in group is a radio button + if(bSave) + { + if(::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0) + { + ATLASSERT(nValue == -1); // only set once + nValue = nButton; + } + } + else + { + // select button + ::SendMessage(hWndCtrl, BM_SETCHECK, (nButton == nValue), 0L); + } + nButton++; + } + else + { + ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - skipping non-radio button in group.\n")); + } + hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT); + } + while ((hWndCtrl != NULL) && !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP)); + } + +// DDX support for Tab, Combo, ListBox and ListView selection index + template <class TCtrl> + INT _getSel(TCtrl& tCtrl) + { + return tCtrl.GetCurSel(); + } + + template <class TCtrl> + void _setSel(TCtrl& tCtrl, INT iSel) + { + if(iSel < 0) + tCtrl.SetCurSel(-1); + else + tCtrl.SetCurSel(iSel); + } + +#ifdef __ATLCTRLS_H__ + // ListViewCtrl specialization + template <> + INT _getSel(WTL::CListViewCtrl& tCtrl) + { + return tCtrl.GetSelectedIndex(); + } + + template <> + void _setSel(WTL::CListViewCtrl& tCtrl, INT iSel) + { + if(iSel < 0) + tCtrl.SelectItem(-1); + else + tCtrl.SelectItem(iSel); + } +#endif // __ATLCTRLS_H__ + + template <class TCtrl> + void DDX_Index(UINT nID, INT& nVal, BOOL bSave) + { + T* pT = static_cast<T*>(this); + TCtrl ctrl(pT->GetDlgItem(nID)); + + if(bSave) + nVal = _getSel(ctrl); + else + _setSel(ctrl, nVal); + } + +// Overrideables + void OnDataExchangeError(UINT nCtrlID, BOOL /*bSave*/) + { + // Override to display an error message + ::MessageBeep((UINT)-1); + T* pT = static_cast<T*>(this); + ::SetFocus(pT->GetDlgItem(nCtrlID)); + } + + void OnDataValidateError(UINT nCtrlID, BOOL /*bSave*/, _XData& /*data*/) + { + // Override to display an error message + ::MessageBeep((UINT)-1); + T* pT = static_cast<T*>(this); + ::SetFocus(pT->GetDlgItem(nCtrlID)); + } +}; + +} // namespace WTL + +#endif // __ATLDDX_H__ diff --git a/Examples/WhisperDesktop/Utils/WTL/atlgdi.h b/Examples/WhisperDesktop/Utils/WTL/atlgdi.h new file mode 100644 index 0000000..14e1518 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/WTL/atlgdi.h @@ -0,0 +1,3445 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLGDI_H__ +#define __ATLGDI_H__ + +#pragma once + +#ifndef __ATLAPP_H__ + #error atlgdi.h requires atlapp.h to be included first +#endif + + +// protect template members from windowsx.h macros +#ifdef _INC_WINDOWSX + #undef CopyRgn + #undef CreateBrush + #undef CreatePen + #undef SelectBrush + #undef SelectPen + #undef SelectFont + #undef SelectBitmap +#endif // _INC_WINDOWSX + +// required libraries +#pragma comment(lib, "msimg32.lib") +#if !defined(_ATL_NO_OPENGL) + #pragma comment(lib, "opengl32.lib") +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Classes in this file: +// +// CPenT<t_bManaged> +// CBrushT<t_bManaged> +// CLogFont +// CFontT<t_bManaged> +// CBitmapT<t_bManaged> +// CPaletteT<t_bManaged> +// CRgnT<t_bManaged> +// CDCT<t_bManaged> +// CPaintDC +// CClientDC +// CWindowDC +// CMemoryDC +// CEnhMetaFileInfo +// CEnhMetaFileT<t_bManaged> +// CEnhMetaFileDC + + +namespace WTL +{ + +/////////////////////////////////////////////////////////////////////////////// +// Bitmap resource helpers to extract bitmap information for a bitmap resource + +inline LPBITMAPINFOHEADER AtlGetBitmapResourceInfo(HMODULE hModule, ATL::_U_STRINGorID image) +{ + HRSRC hResource = ::FindResource(hModule, image.m_lpstr, RT_BITMAP); + ATLASSERT(hResource != NULL); + HGLOBAL hGlobal = ::LoadResource(hModule, hResource); + ATLASSERT(hGlobal != NULL); + LPBITMAPINFOHEADER pBitmapInfoHeader = (LPBITMAPINFOHEADER)::LockResource(hGlobal); + ATLASSERT(pBitmapInfoHeader != NULL); + return pBitmapInfoHeader; +} + +inline WORD AtlGetBitmapResourceBitsPerPixel(HMODULE hModule, ATL::_U_STRINGorID image) +{ + LPBITMAPINFOHEADER pBitmapInfoHeader = AtlGetBitmapResourceInfo(hModule, image); + ATLASSERT(pBitmapInfoHeader != NULL); + return pBitmapInfoHeader->biBitCount; +} + +inline WORD AtlGetBitmapResourceBitsPerPixel(ATL::_U_STRINGorID image) +{ + return AtlGetBitmapResourceBitsPerPixel(ModuleHelper::GetResourceInstance(), image); +} + +/////////////////////////////////////////////////////////////////////////////// +// 32-bit (alpha channel) bitmap resource helper + +// Note: 32-bit (alpha channel) images work only on Windows XP with Common Controls version 6. +// If you want your app to work on older version of Windows, load non-alpha images if Common +// Controls version is less than 6. + +inline bool AtlIsAlphaBitmapResource(ATL::_U_STRINGorID image) +{ + return (AtlGetBitmapResourceBitsPerPixel(image) == 32); +} + + +/////////////////////////////////////////////////////////////////////////////// +// CPen + +template <bool t_bManaged> +class CPenT +{ +public: +// Data members + HPEN m_hPen; + +// Constructor/destructor/operators + CPenT(HPEN hPen = NULL) : m_hPen(hPen) + { } + + ~CPenT() + { + if(t_bManaged && (m_hPen != NULL)) + DeleteObject(); + } + + CPenT<t_bManaged>& operator =(HPEN hPen) + { + Attach(hPen); + return *this; + } + + void Attach(HPEN hPen) + { + if(t_bManaged && (m_hPen != NULL) && (m_hPen != hPen)) + ::DeleteObject(m_hPen); + m_hPen = hPen; + } + + HPEN Detach() + { + HPEN hPen = m_hPen; + m_hPen = NULL; + return hPen; + } + + operator HPEN() const { return m_hPen; } + + bool IsNull() const { return (m_hPen == NULL); } + +// Create methods + HPEN CreatePen(int nPenStyle, int nWidth, COLORREF crColor) + { + ATLASSERT(m_hPen == NULL); + m_hPen = ::CreatePen(nPenStyle, nWidth, crColor); + return m_hPen; + } + + HPEN CreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL) + { + ATLASSERT(m_hPen == NULL); + m_hPen = ::ExtCreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount, lpStyle); + return m_hPen; + } + + HPEN CreatePenIndirect(LPLOGPEN lpLogPen) + { + ATLASSERT(m_hPen == NULL); + m_hPen = ::CreatePenIndirect(lpLogPen); + return m_hPen; + } + + BOOL DeleteObject() + { + ATLASSERT(m_hPen != NULL); + BOOL bRet = ::DeleteObject(m_hPen); + if(bRet) + m_hPen = NULL; + return bRet; + } + +// Attributes + int GetLogPen(LOGPEN* pLogPen) const + { + ATLASSERT(m_hPen != NULL); + return ::GetObject(m_hPen, sizeof(LOGPEN), pLogPen); + } + + bool GetLogPen(LOGPEN& LogPen) const + { + ATLASSERT(m_hPen != NULL); + return (::GetObject(m_hPen, sizeof(LOGPEN), &LogPen) == sizeof(LOGPEN)); + } + + int GetExtLogPen(EXTLOGPEN* pLogPen, int nSize = sizeof(EXTLOGPEN)) const + { + ATLASSERT(m_hPen != NULL); + return ::GetObject(m_hPen, nSize, pLogPen); + } + + bool GetExtLogPen(EXTLOGPEN& ExtLogPen, int nSize = sizeof(EXTLOGPEN)) const + { + ATLASSERT(m_hPen != NULL); + int nRet = ::GetObject(m_hPen, nSize, &ExtLogPen); + return ((nRet > 0) && (nRet <= nSize)); + } +}; + +typedef CPenT<false> CPenHandle; +typedef CPenT<true> CPen; + + +/////////////////////////////////////////////////////////////////////////////// +// CBrush + +template <bool t_bManaged> +class CBrushT +{ +public: +// Data members + HBRUSH m_hBrush; + +// Constructor/destructor/operators + CBrushT(HBRUSH hBrush = NULL) : m_hBrush(hBrush) + { } + + ~CBrushT() + { + if(t_bManaged && (m_hBrush != NULL)) + DeleteObject(); + } + + CBrushT<t_bManaged>& operator =(HBRUSH hBrush) + { + Attach(hBrush); + return *this; + } + + void Attach(HBRUSH hBrush) + { + if(t_bManaged && (m_hBrush != NULL) && (m_hBrush != hBrush)) + ::DeleteObject(m_hBrush); + m_hBrush = hBrush; + } + + HBRUSH Detach() + { + HBRUSH hBrush = m_hBrush; + m_hBrush = NULL; + return hBrush; + } + + operator HBRUSH() const { return m_hBrush; } + + bool IsNull() const { return (m_hBrush == NULL); } + +// Create methods + HBRUSH CreateSolidBrush(COLORREF crColor) + { + ATLASSERT(m_hBrush == NULL); + m_hBrush = ::CreateSolidBrush(crColor); + return m_hBrush; + } + + HBRUSH CreateHatchBrush(int nIndex, COLORREF crColor) + { + ATLASSERT(m_hBrush == NULL); + m_hBrush = ::CreateHatchBrush(nIndex, crColor); + return m_hBrush; + } + + HBRUSH CreateBrushIndirect(const LOGBRUSH* lpLogBrush) + { + ATLASSERT(m_hBrush == NULL); + m_hBrush = ::CreateBrushIndirect(lpLogBrush); + return m_hBrush; + } + + HBRUSH CreatePatternBrush(HBITMAP hBitmap) + { + ATLASSERT(m_hBrush == NULL); + m_hBrush = ::CreatePatternBrush(hBitmap); + return m_hBrush; + } + + HBRUSH CreateDIBPatternBrush(HGLOBAL hPackedDIB, UINT nUsage) + { + ATLASSERT(hPackedDIB != NULL); + const void* lpPackedDIB = GlobalLock(hPackedDIB); + ATLASSERT(lpPackedDIB != NULL); + m_hBrush = ::CreateDIBPatternBrushPt(lpPackedDIB, nUsage); + GlobalUnlock(hPackedDIB); + return m_hBrush; + } + + HBRUSH CreateDIBPatternBrush(const void* lpPackedDIB, UINT nUsage) + { + ATLASSERT(m_hBrush == NULL); + m_hBrush = ::CreateDIBPatternBrushPt(lpPackedDIB, nUsage); + return m_hBrush; + } + + HBRUSH CreateSysColorBrush(int nIndex) + { + ATLASSERT(m_hBrush == NULL); + m_hBrush = ::GetSysColorBrush(nIndex); + return m_hBrush; + } + + BOOL DeleteObject() + { + ATLASSERT(m_hBrush != NULL); + BOOL bRet = ::DeleteObject(m_hBrush); + if(bRet) + m_hBrush = NULL; + return bRet; + } + +// Attributes + int GetLogBrush(LOGBRUSH* pLogBrush) const + { + ATLASSERT(m_hBrush != NULL); + return ::GetObject(m_hBrush, sizeof(LOGBRUSH), pLogBrush); + } + + bool GetLogBrush(LOGBRUSH& LogBrush) const + { + ATLASSERT(m_hBrush != NULL); + return (::GetObject(m_hBrush, sizeof(LOGBRUSH), &LogBrush) == sizeof(LOGBRUSH)); + } +}; + +typedef CBrushT<false> CBrushHandle; +typedef CBrushT<true> CBrush; + + +/////////////////////////////////////////////////////////////////////////////// +// CFont + +class CLogFont : public LOGFONT +{ +public: + CLogFont() + { + memset(this, 0, sizeof(LOGFONT)); + } + + CLogFont(const LOGFONT& lf) + { + Copy(&lf); + } + + CLogFont(HFONT hFont) + { + ATLASSERT(::GetObjectType(hFont) == OBJ_FONT); + ::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*)this); + } + + HFONT CreateFontIndirect() + { + return ::CreateFontIndirect(this); + } + + void SetBold() + { + lfWeight = FW_BOLD; + } + + bool IsBold() const + { + return (lfWeight >= FW_BOLD); + } + + void MakeBolder(int iScale = 1) + { + lfWeight += FW_BOLD * iScale; + } + + void MakeLarger(int iScale) + { + if(lfHeight > 0) + lfHeight += iScale; + else + lfHeight -= iScale; + } + + void SetHeight(LONG nPointSize, HDC hDC = NULL) + { + HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); + // For MM_TEXT mapping mode + lfHeight = -::MulDiv(nPointSize, ::GetDeviceCaps(hDC1, LOGPIXELSY), 72); + if(hDC == NULL) + ::ReleaseDC(NULL, hDC1); + } + + LONG GetHeight(HDC hDC = NULL) const + { + HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); + // For MM_TEXT mapping mode + LONG nPointSize = ::MulDiv(-lfHeight, 72, ::GetDeviceCaps(hDC1, LOGPIXELSY)); + if(hDC == NULL) + ::ReleaseDC(NULL, hDC1); + + return nPointSize; + } + + LONG GetDeciPointHeight(HDC hDC = NULL) const + { + HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); + POINT ptOrg = { 0, 0 }; + ::DPtoLP(hDC1, &ptOrg, 1); + POINT pt = { 0, 0 }; + pt.y = abs(lfHeight) + ptOrg.y; + ::LPtoDP(hDC1, &pt, 1); + LONG nDeciPoint = ::MulDiv(pt.y, 720, ::GetDeviceCaps(hDC1, LOGPIXELSY)); // 72 points/inch, 10 decipoints/point + if(hDC == NULL) + ::ReleaseDC(NULL, hDC1); + + return nDeciPoint; + } + + void SetHeightFromDeciPoint(LONG nDeciPtHeight, HDC hDC = NULL) + { + HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); + POINT pt = { 0, 0 }; + pt.y = ::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), nDeciPtHeight, 720); // 72 points/inch, 10 decipoints/point + ::DPtoLP(hDC1, &pt, 1); + POINT ptOrg = { 0, 0 }; + ::DPtoLP(hDC1, &ptOrg, 1); + lfHeight = -abs(pt.y - ptOrg.y); + if(hDC == NULL) + ::ReleaseDC(NULL, hDC1); + } + + void SetCaptionFont() + { + NONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; + ATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)); + Copy(&ncm.lfCaptionFont); + } + + void SetMenuFont() + { + NONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; + ATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)); + Copy(&ncm.lfMenuFont); + } + + void SetStatusFont() + { + NONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; + ATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)); + Copy(&ncm.lfStatusFont); + } + + void SetMessageBoxFont() + { + NONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; + ATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)); + Copy(&ncm.lfMessageFont); + } + + void Copy(const LOGFONT* pLogFont) + { + ATLASSERT(pLogFont != NULL); + *(LOGFONT*)this = *pLogFont; + } + + CLogFont& operator =(const CLogFont& src) + { + Copy(&src); + return *this; + } + + CLogFont& operator =(const LOGFONT& src) + { + Copy(&src); + return *this; + } + + CLogFont& operator =(HFONT hFont) + { + ATLASSERT(::GetObjectType(hFont) == OBJ_FONT); + ::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*)this); + return *this; + } + + bool operator ==(const LOGFONT& logfont) const + { + return((logfont.lfHeight == lfHeight) && + (logfont.lfWidth == lfWidth) && + (logfont.lfEscapement == lfEscapement) && + (logfont.lfOrientation == lfOrientation) && + (logfont.lfWeight == lfWeight) && + (logfont.lfItalic == lfItalic) && + (logfont.lfUnderline == lfUnderline) && + (logfont.lfStrikeOut == lfStrikeOut) && + (logfont.lfCharSet == lfCharSet) && + (logfont.lfOutPrecision == lfOutPrecision) && + (logfont.lfClipPrecision == lfClipPrecision) && + (logfont.lfQuality == lfQuality) && + (logfont.lfPitchAndFamily == lfPitchAndFamily) && + (lstrcmp(logfont.lfFaceName, lfFaceName) == 0)); + } +}; + + +template <bool t_bManaged> +class CFontT +{ +public: +// Data members + HFONT m_hFont; + +// Constructor/destructor/operators + CFontT(HFONT hFont = NULL) : m_hFont(hFont) + { } + + ~CFontT() + { + if(t_bManaged && (m_hFont != NULL)) + DeleteObject(); + } + + CFontT<t_bManaged>& operator =(HFONT hFont) + { + Attach(hFont); + return *this; + } + + void Attach(HFONT hFont) + { + if(t_bManaged && (m_hFont != NULL) && (m_hFont != hFont)) + ::DeleteObject(m_hFont); + m_hFont = hFont; + } + + HFONT Detach() + { + HFONT hFont = m_hFont; + m_hFont = NULL; + return hFont; + } + + operator HFONT() const { return m_hFont; } + + bool IsNull() const { return (m_hFont == NULL); } + +// Create methods + HFONT CreateFontIndirect(const LOGFONT* lpLogFont) + { + ATLASSERT(m_hFont == NULL); + m_hFont = ::CreateFontIndirect(lpLogFont); + return m_hFont; + } + + HFONT CreateFontIndirectEx(CONST ENUMLOGFONTEXDV* penumlfex) + { + ATLASSERT(m_hFont == NULL); + m_hFont = ::CreateFontIndirectEx(penumlfex); + return m_hFont; + } + + HFONT CreateFont(int nHeight, int nWidth, int nEscapement, + int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, + BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, + BYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily, + LPCTSTR lpszFacename) + { + ATLASSERT(m_hFont == NULL); + m_hFont = ::CreateFont(nHeight, nWidth, nEscapement, + nOrientation, nWeight, bItalic, bUnderline, cStrikeOut, + nCharSet, nOutPrecision, nClipPrecision, nQuality, + nPitchAndFamily, lpszFacename); + return m_hFont; + } + + HFONT CreatePointFont(int nPointSize, LPCTSTR lpszFaceName, HDC hDC = NULL, bool bBold = false, bool bItalic = false) + { + LOGFONT logFont = {}; + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfHeight = nPointSize; + ATL::Checked::tcsncpy_s(logFont.lfFaceName, _countof(logFont.lfFaceName), lpszFaceName, _TRUNCATE); + + if(bBold) + logFont.lfWeight = FW_BOLD; + if(bItalic) + logFont.lfItalic = (BYTE)TRUE; + + return CreatePointFontIndirect(&logFont, hDC); + } + + HFONT CreatePointFontIndirect(const LOGFONT* lpLogFont, HDC hDC = NULL) + { + HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); + + // convert nPointSize to logical units based on hDC + LOGFONT logFont = *lpLogFont; + POINT pt = { 0, 0 }; + pt.y = ::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), logFont.lfHeight, 720); // 72 points/inch, 10 decipoints/point + ::DPtoLP(hDC1, &pt, 1); + POINT ptOrg = { 0, 0 }; + ::DPtoLP(hDC1, &ptOrg, 1); + logFont.lfHeight = -abs(pt.y - ptOrg.y); + + if(hDC == NULL) + ::ReleaseDC(NULL, hDC1); + + return CreateFontIndirect(&logFont); + } + + BOOL DeleteObject() + { + ATLASSERT(m_hFont != NULL); + BOOL bRet = ::DeleteObject(m_hFont); + if(bRet) + m_hFont = NULL; + return bRet; + } + +// Attributes + int GetLogFont(LOGFONT* pLogFont) const + { + ATLASSERT(m_hFont != NULL); + return ::GetObject(m_hFont, sizeof(LOGFONT), pLogFont); + } + + bool GetLogFont(LOGFONT& LogFont) const + { + ATLASSERT(m_hFont != NULL); + return (::GetObject(m_hFont, sizeof(LOGFONT), &LogFont) == sizeof(LOGFONT)); + } +}; + +typedef CFontT<false> CFontHandle; +typedef CFontT<true> CFont; + + +/////////////////////////////////////////////////////////////////////////////// +// CBitmap + +template <bool t_bManaged> +class CBitmapT +{ +public: +// Data members + HBITMAP m_hBitmap; + +// Constructor/destructor/operators + CBitmapT(HBITMAP hBitmap = NULL) : m_hBitmap(hBitmap) + { } + + ~CBitmapT() + { + if(t_bManaged && (m_hBitmap != NULL)) + DeleteObject(); + } + + CBitmapT<t_bManaged>& operator =(HBITMAP hBitmap) + { + Attach(hBitmap); + return *this; + } + + void Attach(HBITMAP hBitmap) + { + if(t_bManaged && (m_hBitmap != NULL) && (m_hBitmap != hBitmap)) + ::DeleteObject(m_hBitmap); + m_hBitmap = hBitmap; + } + + HBITMAP Detach() + { + HBITMAP hBitmap = m_hBitmap; + m_hBitmap = NULL; + return hBitmap; + } + + operator HBITMAP() const { return m_hBitmap; } + + bool IsNull() const { return (m_hBitmap == NULL); } + +// Create and load methods + HBITMAP LoadBitmap(ATL::_U_STRINGorID bitmap) + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr); + return m_hBitmap; + } + + HBITMAP LoadOEMBitmap(UINT nIDBitmap) // for OBM_/OCR_/OIC_ + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::LoadBitmap(NULL, MAKEINTRESOURCE(nIDBitmap)); + return m_hBitmap; + } + + HBITMAP LoadMappedBitmap(UINT nIDBitmap, UINT nFlags = 0, LPCOLORMAP lpColorMap = NULL, int nMapSize = 0) + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::CreateMappedBitmap(ModuleHelper::GetResourceInstance(), nIDBitmap, (WORD)nFlags, lpColorMap, nMapSize); + return m_hBitmap; + } + + HBITMAP CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitsPerPixel, const void* lpBits) + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::CreateBitmap(nWidth, nHeight, nPlanes, nBitsPerPixel, lpBits); + return m_hBitmap; + } + + HBITMAP CreateBitmapIndirect(LPBITMAP lpBitmap) + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::CreateBitmapIndirect(lpBitmap); + return m_hBitmap; + } + + HBITMAP CreateCompatibleBitmap(HDC hDC, int nWidth, int nHeight) + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::CreateCompatibleBitmap(hDC, nWidth, nHeight); + return m_hBitmap; + } + + HBITMAP CreateDiscardableBitmap(HDC hDC, int nWidth, int nHeight) + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::CreateDiscardableBitmap(hDC, nWidth, nHeight); + return m_hBitmap; + } + + BOOL DeleteObject() + { + ATLASSERT(m_hBitmap != NULL); + BOOL bRet = ::DeleteObject(m_hBitmap); + if(bRet) + m_hBitmap = NULL; + return bRet; + } + +// Attributes + int GetBitmap(BITMAP* pBitMap) const + { + ATLASSERT(m_hBitmap != NULL); + return ::GetObject(m_hBitmap, sizeof(BITMAP), pBitMap); + } + + bool GetBitmap(BITMAP& bm) const + { + ATLASSERT(m_hBitmap != NULL); + return (::GetObject(m_hBitmap, sizeof(BITMAP), &bm) == sizeof(BITMAP)); + } + + bool GetSize(SIZE& size) const + { + ATLASSERT(m_hBitmap != NULL); + BITMAP bm = {}; + if(!GetBitmap(&bm)) + return false; + size.cx = bm.bmWidth; + size.cy = bm.bmHeight; + return true; + } + + DWORD GetBitmapBits(DWORD dwCount, LPVOID lpBits) const + { + ATLASSERT(m_hBitmap != NULL); + return ::GetBitmapBits(m_hBitmap, dwCount, lpBits); + } + + DWORD SetBitmapBits(DWORD dwCount, const void* lpBits) + { + ATLASSERT(m_hBitmap != NULL); + return ::SetBitmapBits(m_hBitmap, dwCount, lpBits); + } + + BOOL GetBitmapDimension(LPSIZE lpSize) const + { + ATLASSERT(m_hBitmap != NULL); + return ::GetBitmapDimensionEx(m_hBitmap, lpSize); + } + + BOOL SetBitmapDimension(int nWidth, int nHeight, LPSIZE lpSize = NULL) + { + ATLASSERT(m_hBitmap != NULL); + return ::SetBitmapDimensionEx(m_hBitmap, nWidth, nHeight, lpSize); + } + +// DIB support + HBITMAP CreateDIBitmap(HDC hDC, CONST BITMAPINFOHEADER* lpbmih, DWORD dwInit, CONST VOID* lpbInit, CONST BITMAPINFO* lpbmi, UINT uColorUse) + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::CreateDIBitmap(hDC, lpbmih, dwInit, lpbInit, lpbmi, uColorUse); + return m_hBitmap; + } + + HBITMAP CreateDIBSection(HDC hDC, CONST BITMAPINFO* lpbmi, UINT uColorUse, VOID** ppvBits, HANDLE hSection, DWORD dwOffset) + { + ATLASSERT(m_hBitmap == NULL); + m_hBitmap = ::CreateDIBSection(hDC, lpbmi, uColorUse, ppvBits, hSection, dwOffset); + return m_hBitmap; + } + + int GetDIBits(HDC hDC, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT uColorUse) const + { + ATLASSERT(m_hBitmap != NULL); + return ::GetDIBits(hDC, m_hBitmap, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse); + } + + int SetDIBits(HDC hDC, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse) + { + ATLASSERT(m_hBitmap != NULL); + return ::SetDIBits(hDC, m_hBitmap, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse); + } +}; + +typedef CBitmapT<false> CBitmapHandle; +typedef CBitmapT<true> CBitmap; + + +/////////////////////////////////////////////////////////////////////////////// +// CPalette + +template <bool t_bManaged> +class CPaletteT +{ +public: +// Data members + HPALETTE m_hPalette; + +// Constructor/destructor/operators + CPaletteT(HPALETTE hPalette = NULL) : m_hPalette(hPalette) + { } + + ~CPaletteT() + { + if(t_bManaged && (m_hPalette != NULL)) + DeleteObject(); + } + + CPaletteT<t_bManaged>& operator =(HPALETTE hPalette) + { + Attach(hPalette); + return *this; + } + + void Attach(HPALETTE hPalette) + { + if(t_bManaged && (m_hPalette != NULL) && (m_hPalette != hPalette)) + ::DeleteObject(m_hPalette); + m_hPalette = hPalette; + } + + HPALETTE Detach() + { + HPALETTE hPalette = m_hPalette; + m_hPalette = NULL; + return hPalette; + } + + operator HPALETTE() const { return m_hPalette; } + + bool IsNull() const { return (m_hPalette == NULL); } + +// Create methods + HPALETTE CreatePalette(LPLOGPALETTE lpLogPalette) + { + ATLASSERT(m_hPalette == NULL); + m_hPalette = ::CreatePalette(lpLogPalette); + return m_hPalette; + } + + HPALETTE CreateHalftonePalette(HDC hDC) + { + ATLASSERT(m_hPalette == NULL); + ATLASSERT(hDC != NULL); + m_hPalette = ::CreateHalftonePalette(hDC); + return m_hPalette; + } + + BOOL DeleteObject() + { + ATLASSERT(m_hPalette != NULL); + BOOL bRet = ::DeleteObject(m_hPalette); + if(bRet) + m_hPalette = NULL; + return bRet; + } + +// Attributes + int GetEntryCount() const + { + ATLASSERT(m_hPalette != NULL); + WORD nEntries = 0; + ::GetObject(m_hPalette, sizeof(WORD), &nEntries); + return (int)nEntries; + } + + UINT GetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) const + { + ATLASSERT(m_hPalette != NULL); + return ::GetPaletteEntries(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors); + } + + UINT SetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) + { + ATLASSERT(m_hPalette != NULL); + return ::SetPaletteEntries(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors); + } + +// Operations + void AnimatePalette(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) + { + ATLASSERT(m_hPalette != NULL); + ::AnimatePalette(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors); + } + + BOOL ResizePalette(UINT nNumEntries) + { + ATLASSERT(m_hPalette != NULL); + return ::ResizePalette(m_hPalette, nNumEntries); + } + + UINT GetNearestPaletteIndex(COLORREF crColor) const + { + ATLASSERT(m_hPalette != NULL); + return ::GetNearestPaletteIndex(m_hPalette, crColor); + } +}; + +typedef CPaletteT<false> CPaletteHandle; +typedef CPaletteT<true> CPalette; + + +/////////////////////////////////////////////////////////////////////////////// +// CRgn + +template <bool t_bManaged> +class CRgnT +{ +public: +// Data members + HRGN m_hRgn; + +// Constructor/destructor/operators + CRgnT(HRGN hRgn = NULL) : m_hRgn(hRgn) + { } + + ~CRgnT() + { + if(t_bManaged && (m_hRgn != NULL)) + DeleteObject(); + } + + CRgnT<t_bManaged>& operator =(HRGN hRgn) + { + Attach(hRgn); + return *this; + } + + void Attach(HRGN hRgn) + { + if(t_bManaged && (m_hRgn != NULL) && (m_hRgn != hRgn)) + ::DeleteObject(m_hRgn); + m_hRgn = hRgn; + } + + HRGN Detach() + { + HRGN hRgn = m_hRgn; + m_hRgn = NULL; + return hRgn; + } + + operator HRGN() const { return m_hRgn; } + + bool IsNull() const { return (m_hRgn == NULL); } + +// Create methods + HRGN CreateRectRgn(int x1, int y1, int x2, int y2) + { + ATLASSERT(m_hRgn == NULL); + m_hRgn = ::CreateRectRgn(x1, y1, x2, y2); + return m_hRgn; + } + + HRGN CreateRectRgnIndirect(LPCRECT lpRect) + { + ATLASSERT(m_hRgn == NULL); + m_hRgn = ::CreateRectRgnIndirect(lpRect); + return m_hRgn; + } + + HRGN CreateEllipticRgn(int x1, int y1, int x2, int y2) + { + ATLASSERT(m_hRgn == NULL); + m_hRgn = ::CreateEllipticRgn(x1, y1, x2, y2); + return m_hRgn; + } + + HRGN CreateEllipticRgnIndirect(LPCRECT lpRect) + { + ATLASSERT(m_hRgn == NULL); + m_hRgn = ::CreateEllipticRgnIndirect(lpRect); + return m_hRgn; + } + + HRGN CreatePolygonRgn(const POINT* lpPoints, int nCount, int nMode) + { + ATLASSERT(m_hRgn == NULL); + m_hRgn = ::CreatePolygonRgn(lpPoints, nCount, nMode); + return m_hRgn; + } + + HRGN CreatePolyPolygonRgn(const POINT* lpPoints, const INT* lpPolyCounts, int nCount, int nPolyFillMode) + { + ATLASSERT(m_hRgn == NULL); + m_hRgn = ::CreatePolyPolygonRgn(lpPoints, lpPolyCounts, nCount, nPolyFillMode); + return m_hRgn; + } + + HRGN CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3) + { + ATLASSERT(m_hRgn == NULL); + m_hRgn = ::CreateRoundRectRgn(x1, y1, x2, y2, x3, y3); + return m_hRgn; + } + + HRGN CreateFromPath(HDC hDC) + { + ATLASSERT(m_hRgn == NULL); + ATLASSERT(hDC != NULL); + m_hRgn = ::PathToRegion(hDC); + return m_hRgn; + } + + HRGN CreateFromData(const XFORM* lpXForm, int nCount, const RGNDATA* pRgnData) + { + ATLASSERT(m_hRgn == NULL); + m_hRgn = ::ExtCreateRegion(lpXForm, nCount, pRgnData); + return m_hRgn; + } + + BOOL DeleteObject() + { + ATLASSERT(m_hRgn != NULL); + BOOL bRet = ::DeleteObject(m_hRgn); + if(bRet) + m_hRgn = NULL; + return bRet; + } + +// Operations + void SetRectRgn(int x1, int y1, int x2, int y2) + { + ATLASSERT(m_hRgn != NULL); + ::SetRectRgn(m_hRgn, x1, y1, x2, y2); + } + + void SetRectRgn(LPCRECT lpRect) + { + ATLASSERT(m_hRgn != NULL); + ::SetRectRgn(m_hRgn, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); + } + + int CombineRgn(HRGN hRgnSrc1, HRGN hRgnSrc2, int nCombineMode) + { + ATLASSERT(m_hRgn != NULL); + return ::CombineRgn(m_hRgn, hRgnSrc1, hRgnSrc2, nCombineMode); + } + + int CombineRgn(HRGN hRgnSrc, int nCombineMode) + { + ATLASSERT(m_hRgn != NULL); + return ::CombineRgn(m_hRgn, m_hRgn, hRgnSrc, nCombineMode); + } + + int CopyRgn(HRGN hRgnSrc) + { + ATLASSERT(m_hRgn != NULL); + return ::CombineRgn(m_hRgn, hRgnSrc, NULL, RGN_COPY); + } + + BOOL EqualRgn(HRGN hRgn) const + { + ATLASSERT(m_hRgn != NULL); + return ::EqualRgn(m_hRgn, hRgn); + } + + int OffsetRgn(int x, int y) + { + ATLASSERT(m_hRgn != NULL); + return ::OffsetRgn(m_hRgn, x, y); + } + + int OffsetRgn(POINT point) + { + ATLASSERT(m_hRgn != NULL); + return ::OffsetRgn(m_hRgn, point.x, point.y); + } + + int GetRgnBox(LPRECT lpRect) const + { + ATLASSERT(m_hRgn != NULL); + return ::GetRgnBox(m_hRgn, lpRect); + } + + BOOL PtInRegion(int x, int y) const + { + ATLASSERT(m_hRgn != NULL); + return ::PtInRegion(m_hRgn, x, y); + } + + BOOL PtInRegion(POINT point) const + { + ATLASSERT(m_hRgn != NULL); + return ::PtInRegion(m_hRgn, point.x, point.y); + } + + BOOL RectInRegion(LPCRECT lpRect) const + { + ATLASSERT(m_hRgn != NULL); + return ::RectInRegion(m_hRgn, lpRect); + } + + int GetRegionData(LPRGNDATA lpRgnData, int nDataSize) const + { + ATLASSERT(m_hRgn != NULL); + return (int)::GetRegionData(m_hRgn, nDataSize, lpRgnData); + } +}; + +typedef CRgnT<false> CRgnHandle; +typedef CRgnT<true> CRgn; + + +/////////////////////////////////////////////////////////////////////////////// +// CDC - The device context class + +template <bool t_bManaged> +class CDCT; +typedef CDCT<false> CDCHandle; +typedef CDCT<true> CDC; + +template <bool t_bManaged> +class CDCT +{ +public: +// Data members + HDC m_hDC; + +// Constructor/destructor/operators + CDCT(HDC hDC = NULL) : m_hDC(hDC) + { + } + + ~CDCT() + { + if(t_bManaged && (m_hDC != NULL)) + ::DeleteDC(Detach()); + } + + CDCT<t_bManaged>& operator =(HDC hDC) + { + Attach(hDC); + return *this; + } + + void Attach(HDC hDC) + { + if(t_bManaged && (m_hDC != NULL) && (m_hDC != hDC)) + ::DeleteDC(m_hDC); + m_hDC = hDC; + } + + HDC Detach() + { + HDC hDC = m_hDC; + m_hDC = NULL; + return hDC; + } + + operator HDC() const { return m_hDC; } + + bool IsNull() const { return (m_hDC == NULL); } + +// Operations + HWND WindowFromDC() const + { + ATLASSERT(m_hDC != NULL); + return ::WindowFromDC(m_hDC); + } + + CPenHandle GetCurrentPen() const + { + ATLASSERT(m_hDC != NULL); + return CPenHandle((HPEN)::GetCurrentObject(m_hDC, OBJ_PEN)); + } + + CBrushHandle GetCurrentBrush() const + { + ATLASSERT(m_hDC != NULL); + return CBrushHandle((HBRUSH)::GetCurrentObject(m_hDC, OBJ_BRUSH)); + } + + CPaletteHandle GetCurrentPalette() const + { + ATLASSERT(m_hDC != NULL); + return CPaletteHandle((HPALETTE)::GetCurrentObject(m_hDC, OBJ_PAL)); + } + + CFontHandle GetCurrentFont() const + { + ATLASSERT(m_hDC != NULL); + return CFontHandle((HFONT)::GetCurrentObject(m_hDC, OBJ_FONT)); + } + + CBitmapHandle GetCurrentBitmap() const + { + ATLASSERT(m_hDC != NULL); + return CBitmapHandle((HBITMAP)::GetCurrentObject(m_hDC, OBJ_BITMAP)); + } + + HDC CreateDC(LPCTSTR lpszDriverName, LPCTSTR lpszDeviceName, LPCTSTR lpszOutput, const DEVMODE* lpInitData) + { + ATLASSERT(m_hDC == NULL); + m_hDC = ::CreateDC(lpszDriverName, lpszDeviceName, lpszOutput, lpInitData); + return m_hDC; + } + + HDC CreateCompatibleDC(HDC hDC = NULL) + { + ATLASSERT(m_hDC == NULL); + m_hDC = ::CreateCompatibleDC(hDC); + return m_hDC; + } + + BOOL DeleteDC() + { + if(m_hDC == NULL) + return FALSE; + BOOL bRet = ::DeleteDC(m_hDC); + if(bRet) + m_hDC = NULL; + return bRet; + } + +// Device-Context Functions + int SaveDC() + { + ATLASSERT(m_hDC != NULL); + return ::SaveDC(m_hDC); + } + + BOOL RestoreDC(int nSavedDC) + { + ATLASSERT(m_hDC != NULL); + return ::RestoreDC(m_hDC, nSavedDC); + } + + int GetDeviceCaps(int nIndex) const + { + ATLASSERT(m_hDC != NULL); + return ::GetDeviceCaps(m_hDC, nIndex); + } + + UINT SetBoundsRect(LPCRECT lpRectBounds, UINT flags) + { + ATLASSERT(m_hDC != NULL); + return ::SetBoundsRect(m_hDC, lpRectBounds, flags); + } + + UINT GetBoundsRect(LPRECT lpRectBounds, UINT flags) const + { + ATLASSERT(m_hDC != NULL); + return ::GetBoundsRect(m_hDC, lpRectBounds, flags); + } + + BOOL ResetDC(const DEVMODE* lpDevMode) + { + ATLASSERT(m_hDC != NULL); + return ::ResetDC(m_hDC, lpDevMode) != NULL; + } + +// Drawing-Tool Functions + BOOL GetBrushOrg(LPPOINT lpPoint) const + { + ATLASSERT(m_hDC != NULL); + return ::GetBrushOrgEx(m_hDC, lpPoint); + } + + BOOL SetBrushOrg(int x, int y, LPPOINT lpPoint = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::SetBrushOrgEx(m_hDC, x, y, lpPoint); + } + + BOOL SetBrushOrg(POINT point, LPPOINT lpPointRet = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::SetBrushOrgEx(m_hDC, point.x, point.y, lpPointRet); + } + + int EnumObjects(int nObjectType, int (CALLBACK* lpfn)(LPVOID, LPARAM), LPARAM lpData) + { + ATLASSERT(m_hDC != NULL); +#ifdef STRICT + return ::EnumObjects(m_hDC, nObjectType, (GOBJENUMPROC)lpfn, lpData); +#else + return ::EnumObjects(m_hDC, nObjectType, (GOBJENUMPROC)lpfn, (LPVOID)lpData); +#endif + } + +// Type-safe selection helpers + HPEN SelectPen(HPEN hPen) + { + ATLASSERT(m_hDC != NULL); + ATLASSERT((hPen == NULL) || (::GetObjectType(hPen) == OBJ_PEN) || (::GetObjectType(hPen) == OBJ_EXTPEN)); + return (HPEN)::SelectObject(m_hDC, hPen); + } + + HBRUSH SelectBrush(HBRUSH hBrush) + { + ATLASSERT(m_hDC != NULL); + ATLASSERT((hBrush == NULL) || (::GetObjectType(hBrush) == OBJ_BRUSH)); + return (HBRUSH)::SelectObject(m_hDC, hBrush); + } + + HFONT SelectFont(HFONT hFont) + { + ATLASSERT(m_hDC != NULL); + ATLASSERT((hFont == NULL) || (::GetObjectType(hFont) == OBJ_FONT)); + return (HFONT)::SelectObject(m_hDC, hFont); + } + + HBITMAP SelectBitmap(HBITMAP hBitmap) + { + ATLASSERT(m_hDC != NULL); + ATLASSERT((hBitmap == NULL) || (::GetObjectType(hBitmap) == OBJ_BITMAP)); + return (HBITMAP)::SelectObject(m_hDC, hBitmap); + } + + int SelectRgn(HRGN hRgn) // special return for regions + { + ATLASSERT(m_hDC != NULL); + ATLASSERT((hRgn == NULL) || (::GetObjectType(hRgn) == OBJ_REGION)); + return PtrToInt(::SelectObject(m_hDC, hRgn)); + } + +// Type-safe selection helpers for stock objects + HPEN SelectStockPen(int nPen) + { + ATLASSERT(m_hDC != NULL); + ATLASSERT((nPen == WHITE_PEN) || (nPen == BLACK_PEN) || (nPen == NULL_PEN) || (nPen == DC_PEN)); + return SelectPen((HPEN)::GetStockObject(nPen)); + } + + HBRUSH SelectStockBrush(int nBrush) + { + ATLASSERT(((nBrush >= WHITE_BRUSH) && (nBrush <= HOLLOW_BRUSH)) || (nBrush == DC_BRUSH)); + return SelectBrush((HBRUSH)::GetStockObject(nBrush)); + } + + HFONT SelectStockFont(int nFont) + { + ATLASSERT(((nFont >= OEM_FIXED_FONT) && (nFont <= SYSTEM_FIXED_FONT)) || (nFont == DEFAULT_GUI_FONT)); + return SelectFont((HFONT)::GetStockObject(nFont)); + } + + HPALETTE SelectStockPalette(int nPalette, BOOL bForceBackground) + { + ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported + return SelectPalette((HPALETTE)::GetStockObject(nPalette), bForceBackground); + } + +// Color and Color Palette Functions + COLORREF GetNearestColor(COLORREF crColor) const + { + ATLASSERT(m_hDC != NULL); + return ::GetNearestColor(m_hDC, crColor); + } + + HPALETTE SelectPalette(HPALETTE hPalette, BOOL bForceBackground) + { + ATLASSERT(m_hDC != NULL); + + return ::SelectPalette(m_hDC, hPalette, bForceBackground); + } + + UINT RealizePalette() + { + ATLASSERT(m_hDC != NULL); + return ::RealizePalette(m_hDC); + } + + void UpdateColors() + { + ATLASSERT(m_hDC != NULL); + ::UpdateColors(m_hDC); + } + +// Drawing-Attribute Functions + COLORREF GetBkColor() const + { + ATLASSERT(m_hDC != NULL); + return ::GetBkColor(m_hDC); + } + + int GetBkMode() const + { + ATLASSERT(m_hDC != NULL); + return ::GetBkMode(m_hDC); + } + + int GetPolyFillMode() const + { + ATLASSERT(m_hDC != NULL); + return ::GetPolyFillMode(m_hDC); + } + + int GetROP2() const + { + ATLASSERT(m_hDC != NULL); + return ::GetROP2(m_hDC); + } + + int GetStretchBltMode() const + { + ATLASSERT(m_hDC != NULL); + return ::GetStretchBltMode(m_hDC); + } + + COLORREF GetTextColor() const + { + ATLASSERT(m_hDC != NULL); + return ::GetTextColor(m_hDC); + } + + COLORREF SetBkColor(COLORREF crColor) + { + ATLASSERT(m_hDC != NULL); + return ::SetBkColor(m_hDC, crColor); + } + + int SetBkMode(int nBkMode) + { + ATLASSERT(m_hDC != NULL); + return ::SetBkMode(m_hDC, nBkMode); + } + + int SetPolyFillMode(int nPolyFillMode) + { + ATLASSERT(m_hDC != NULL); + return ::SetPolyFillMode(m_hDC, nPolyFillMode); + } + + int SetROP2(int nDrawMode) + { + ATLASSERT(m_hDC != NULL); + return ::SetROP2(m_hDC, nDrawMode); + } + + int SetStretchBltMode(int nStretchMode) + { + ATLASSERT(m_hDC != NULL); + return ::SetStretchBltMode(m_hDC, nStretchMode); + } + + COLORREF SetTextColor(COLORREF crColor) + { + ATLASSERT(m_hDC != NULL); + return ::SetTextColor(m_hDC, crColor); + } + + BOOL GetColorAdjustment(LPCOLORADJUSTMENT lpColorAdjust) const + { + ATLASSERT(m_hDC != NULL); + return ::GetColorAdjustment(m_hDC, lpColorAdjust); + } + + BOOL SetColorAdjustment(const COLORADJUSTMENT* lpColorAdjust) + { + ATLASSERT(m_hDC != NULL); + return ::SetColorAdjustment(m_hDC, lpColorAdjust); + } + +// Mapping Functions + int GetMapMode() const + { + ATLASSERT(m_hDC != NULL); + return ::GetMapMode(m_hDC); + } + + BOOL GetViewportOrg(LPPOINT lpPoint) const + { + ATLASSERT(m_hDC != NULL); + return ::GetViewportOrgEx(m_hDC, lpPoint); + } + + int SetMapMode(int nMapMode) + { + ATLASSERT(m_hDC != NULL); + return ::SetMapMode(m_hDC, nMapMode); + } + + // Viewport Origin + BOOL SetViewportOrg(int x, int y, LPPOINT lpPoint = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::SetViewportOrgEx(m_hDC, x, y, lpPoint); + } + + BOOL SetViewportOrg(POINT point, LPPOINT lpPointRet = NULL) + { + ATLASSERT(m_hDC != NULL); + return SetViewportOrg(point.x, point.y, lpPointRet); + } + + BOOL OffsetViewportOrg(int nWidth, int nHeight, LPPOINT lpPoint = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::OffsetViewportOrgEx(m_hDC, nWidth, nHeight, lpPoint); + } + + // Viewport Extent + BOOL GetViewportExt(LPSIZE lpSize) const + { + ATLASSERT(m_hDC != NULL); + return ::GetViewportExtEx(m_hDC, lpSize); + } + + BOOL SetViewportExt(int x, int y, LPSIZE lpSize = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::SetViewportExtEx(m_hDC, x, y, lpSize); + } + + BOOL SetViewportExt(SIZE size, LPSIZE lpSizeRet = NULL) + { + ATLASSERT(m_hDC != NULL); + return SetViewportExt(size.cx, size.cy, lpSizeRet); + } + + BOOL ScaleViewportExt(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::ScaleViewportExtEx(m_hDC, xNum, xDenom, yNum, yDenom, lpSize); + } + + // Window Origin + BOOL GetWindowOrg(LPPOINT lpPoint) const + { + ATLASSERT(m_hDC != NULL); + return ::GetWindowOrgEx(m_hDC, lpPoint); + } + + BOOL SetWindowOrg(int x, int y, LPPOINT lpPoint = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::SetWindowOrgEx(m_hDC, x, y, lpPoint); + } + + BOOL SetWindowOrg(POINT point, LPPOINT lpPointRet = NULL) + { + ATLASSERT(m_hDC != NULL); + return SetWindowOrg(point.x, point.y, lpPointRet); + } + + BOOL OffsetWindowOrg(int nWidth, int nHeight, LPPOINT lpPoint = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::OffsetWindowOrgEx(m_hDC, nWidth, nHeight, lpPoint); + } + + // Window extent + BOOL GetWindowExt(LPSIZE lpSize) const + { + ATLASSERT(m_hDC != NULL); + return ::GetWindowExtEx(m_hDC, lpSize); + } + + BOOL SetWindowExt(int x, int y, LPSIZE lpSize = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::SetWindowExtEx(m_hDC, x, y, lpSize); + } + + BOOL SetWindowExt(SIZE size, LPSIZE lpSizeRet = NULL) + { + ATLASSERT(m_hDC != NULL); + return SetWindowExt(size.cx, size.cy, lpSizeRet); + } + + BOOL ScaleWindowExt(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::ScaleWindowExtEx(m_hDC, xNum, xDenom, yNum, yDenom, lpSize); + } + +// Coordinate Functions + BOOL DPtoLP(LPPOINT lpPoints, int nCount = 1) const + { + ATLASSERT(m_hDC != NULL); + return ::DPtoLP(m_hDC, lpPoints, nCount); + } + + BOOL DPtoLP(LPRECT lpRect) const + { + ATLASSERT(m_hDC != NULL); + return ::DPtoLP(m_hDC, (LPPOINT)lpRect, 2); + } + + BOOL DPtoLP(LPSIZE lpSize) const + { + SIZE sizeWinExt = {}; + if(!GetWindowExt(&sizeWinExt)) + return FALSE; + SIZE sizeVpExt = {}; + if(!GetViewportExt(&sizeVpExt)) + return FALSE; + lpSize->cx = ::MulDiv(lpSize->cx, abs(sizeWinExt.cx), abs(sizeVpExt.cx)); + lpSize->cy = ::MulDiv(lpSize->cy, abs(sizeWinExt.cy), abs(sizeVpExt.cy)); + return TRUE; + } + + BOOL LPtoDP(LPPOINT lpPoints, int nCount = 1) const + { + ATLASSERT(m_hDC != NULL); + return ::LPtoDP(m_hDC, lpPoints, nCount); + } + + BOOL LPtoDP(LPRECT lpRect) const + { + ATLASSERT(m_hDC != NULL); + return ::LPtoDP(m_hDC, (LPPOINT)lpRect, 2); + } + + BOOL LPtoDP(LPSIZE lpSize) const + { + SIZE sizeWinExt = {}; + if(!GetWindowExt(&sizeWinExt)) + return FALSE; + SIZE sizeVpExt = {}; + if(!GetViewportExt(&sizeVpExt)) + return FALSE; + lpSize->cx = ::MulDiv(lpSize->cx, abs(sizeVpExt.cx), abs(sizeWinExt.cx)); + lpSize->cy = ::MulDiv(lpSize->cy, abs(sizeVpExt.cy), abs(sizeWinExt.cy)); + return TRUE; + } + +// Special Coordinate Functions (useful for dealing with metafiles and OLE) + #define HIMETRIC_INCH 2540 // HIMETRIC units per inch + + void DPtoHIMETRIC(LPSIZE lpSize) + { + ATLASSERT(m_hDC != NULL); + int nMapMode = GetMapMode(); + if((nMapMode < MM_ISOTROPIC) && (nMapMode != MM_TEXT)) + { + // when using a constrained map mode, map against physical inch + SetMapMode(MM_HIMETRIC); + DPtoLP(lpSize); + SetMapMode(nMapMode); + } + else + { + // map against logical inch for non-constrained mapping modes + int cxPerInch = GetDeviceCaps(LOGPIXELSX); + int cyPerInch = GetDeviceCaps(LOGPIXELSY); + ATLASSERT((cxPerInch != 0) && (cyPerInch != 0)); + lpSize->cx = ::MulDiv(lpSize->cx, HIMETRIC_INCH, cxPerInch); + lpSize->cy = ::MulDiv(lpSize->cy, HIMETRIC_INCH, cyPerInch); + } + } + + void HIMETRICtoDP(LPSIZE lpSize) + { + ATLASSERT(m_hDC != NULL); + int nMapMode = GetMapMode(); + if((nMapMode < MM_ISOTROPIC) && (nMapMode != MM_TEXT)) + { + // when using a constrained map mode, map against physical inch + SetMapMode(MM_HIMETRIC); + LPtoDP(lpSize); + SetMapMode(nMapMode); + } + else + { + // map against logical inch for non-constrained mapping modes + int cxPerInch = GetDeviceCaps(LOGPIXELSX); + int cyPerInch = GetDeviceCaps(LOGPIXELSY); + ATLASSERT((cxPerInch != 0) && (cyPerInch != 0)); + lpSize->cx = ::MulDiv(lpSize->cx, cxPerInch, HIMETRIC_INCH); + lpSize->cy = ::MulDiv(lpSize->cy, cyPerInch, HIMETRIC_INCH); + } + } + + void LPtoHIMETRIC(LPSIZE lpSize) + { + LPtoDP(lpSize); + DPtoHIMETRIC(lpSize); + } + + void HIMETRICtoLP(LPSIZE lpSize) + { + HIMETRICtoDP(lpSize); + DPtoLP(lpSize); + } + +// Region Functions + BOOL FillRgn(HRGN hRgn, HBRUSH hBrush) + { + ATLASSERT(m_hDC != NULL); + return ::FillRgn(m_hDC, hRgn, hBrush); + } + + BOOL FrameRgn(HRGN hRgn, HBRUSH hBrush, int nWidth, int nHeight) + { + ATLASSERT(m_hDC != NULL); + return ::FrameRgn(m_hDC, hRgn, hBrush, nWidth, nHeight); + } + + BOOL InvertRgn(HRGN hRgn) + { + ATLASSERT(m_hDC != NULL); + return ::InvertRgn(m_hDC, hRgn); + } + + BOOL PaintRgn(HRGN hRgn) + { + ATLASSERT(m_hDC != NULL); + return ::PaintRgn(m_hDC, hRgn); + } + +// Clipping Functions + int GetClipBox(LPRECT lpRect) const + { + ATLASSERT(m_hDC != NULL); + return ::GetClipBox(m_hDC, lpRect); + } + + int GetClipRgn(CRgn& region) const + { + ATLASSERT(m_hDC != NULL); + if(region.IsNull()) + region.CreateRectRgn(0, 0, 0, 0); + + int nRet = ::GetClipRgn(m_hDC, region); + if(nRet != 1) + region.DeleteObject(); + + return nRet; + } + + BOOL PtVisible(int x, int y) const + { + ATLASSERT(m_hDC != NULL); + return ::PtVisible(m_hDC, x, y); + } + + BOOL PtVisible(POINT point) const + { + ATLASSERT(m_hDC != NULL); + return ::PtVisible(m_hDC, point.x, point.y); + } + + BOOL RectVisible(LPCRECT lpRect) const + { + ATLASSERT(m_hDC != NULL); + return ::RectVisible(m_hDC, lpRect); + } + + int SelectClipRgn(HRGN hRgn) + { + ATLASSERT(m_hDC != NULL); + return ::SelectClipRgn(m_hDC, (HRGN)hRgn); + } + + int ExcludeClipRect(int x1, int y1, int x2, int y2) + { + ATLASSERT(m_hDC != NULL); + return ::ExcludeClipRect(m_hDC, x1, y1, x2, y2); + } + + int ExcludeClipRect(LPCRECT lpRect) + { + ATLASSERT(m_hDC != NULL); + return ::ExcludeClipRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); + } + + int ExcludeUpdateRgn(HWND hWnd) + { + ATLASSERT(m_hDC != NULL); + return ::ExcludeUpdateRgn(m_hDC, hWnd); + } + + int IntersectClipRect(int x1, int y1, int x2, int y2) + { + ATLASSERT(m_hDC != NULL); + return ::IntersectClipRect(m_hDC, x1, y1, x2, y2); + } + + int IntersectClipRect(LPCRECT lpRect) + { + ATLASSERT(m_hDC != NULL); + return ::IntersectClipRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); + } + + int OffsetClipRgn(int x, int y) + { + ATLASSERT(m_hDC != NULL); + return ::OffsetClipRgn(m_hDC, x, y); + } + + int OffsetClipRgn(SIZE size) + { + ATLASSERT(m_hDC != NULL); + return ::OffsetClipRgn(m_hDC, size.cx, size.cy); + } + + int SelectClipRgn(HRGN hRgn, int nMode) + { + ATLASSERT(m_hDC != NULL); + return ::ExtSelectClipRgn(m_hDC, hRgn, nMode); + } + +// Line-Output Functions + BOOL GetCurrentPosition(LPPOINT lpPoint) const + { + ATLASSERT(m_hDC != NULL); + return ::GetCurrentPositionEx(m_hDC, lpPoint); + } + + BOOL MoveTo(int x, int y, LPPOINT lpPoint = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::MoveToEx(m_hDC, x, y, lpPoint); + } + + BOOL MoveTo(POINT point, LPPOINT lpPointRet = NULL) + { + ATLASSERT(m_hDC != NULL); + return MoveTo(point.x, point.y, lpPointRet); + } + + BOOL LineTo(int x, int y) + { + ATLASSERT(m_hDC != NULL); + return ::LineTo(m_hDC, x, y); + } + + BOOL LineTo(POINT point) + { + ATLASSERT(m_hDC != NULL); + return LineTo(point.x, point.y); + } + + BOOL Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) + { + ATLASSERT(m_hDC != NULL); + return ::Arc(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4); + } + + BOOL Arc(LPCRECT lpRect, POINT ptStart, POINT ptEnd) + { + ATLASSERT(m_hDC != NULL); + return ::Arc(m_hDC, lpRect->left, lpRect->top, + lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, + ptEnd.x, ptEnd.y); + } + + BOOL Polyline(const POINT* lpPoints, int nCount) + { + ATLASSERT(m_hDC != NULL); + return ::Polyline(m_hDC, lpPoints, nCount); + } + + BOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle) + { + ATLASSERT(m_hDC != NULL); + return ::AngleArc(m_hDC, x, y, nRadius, fStartAngle, fSweepAngle); + } + + BOOL ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) + { + ATLASSERT(m_hDC != NULL); + return ::ArcTo(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4); + } + + BOOL ArcTo(LPCRECT lpRect, POINT ptStart, POINT ptEnd) + { + ATLASSERT(m_hDC != NULL); + return ArcTo(lpRect->left, lpRect->top, lpRect->right, + lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); + } + + int GetArcDirection() const + { + ATLASSERT(m_hDC != NULL); + return ::GetArcDirection(m_hDC); + } + + int SetArcDirection(int nArcDirection) + { + ATLASSERT(m_hDC != NULL); + return ::SetArcDirection(m_hDC, nArcDirection); + } + + BOOL PolyDraw(const POINT* lpPoints, const BYTE* lpTypes, int nCount) + { + ATLASSERT(m_hDC != NULL); + return ::PolyDraw(m_hDC, lpPoints, lpTypes, nCount); + } + + BOOL PolylineTo(const POINT* lpPoints, int nCount) + { + ATLASSERT(m_hDC != NULL); + return ::PolylineTo(m_hDC, lpPoints, nCount); + } + + BOOL PolyPolyline(const POINT* lpPoints, + const DWORD* lpPolyPoints, int nCount) + { + ATLASSERT(m_hDC != NULL); + return ::PolyPolyline(m_hDC, lpPoints, lpPolyPoints, nCount); + } + + BOOL PolyBezier(const POINT* lpPoints, int nCount) + { + ATLASSERT(m_hDC != NULL); + return ::PolyBezier(m_hDC, lpPoints, nCount); + } + + BOOL PolyBezierTo(const POINT* lpPoints, int nCount) + { + ATLASSERT(m_hDC != NULL); + return ::PolyBezierTo(m_hDC, lpPoints, nCount); + } + +// Simple Drawing Functions + BOOL FillRect(LPCRECT lpRect, HBRUSH hBrush) + { + ATLASSERT(m_hDC != NULL); + return ::FillRect(m_hDC, lpRect, hBrush); + } + + BOOL FillRect(LPCRECT lpRect, int nColorIndex) + { + ATLASSERT(m_hDC != NULL); + return ::FillRect(m_hDC, lpRect, (HBRUSH)LongToPtr(nColorIndex + 1)); + } + + BOOL FrameRect(LPCRECT lpRect, HBRUSH hBrush) + { + ATLASSERT(m_hDC != NULL); + return ::FrameRect(m_hDC, lpRect, hBrush); + } + + BOOL InvertRect(LPCRECT lpRect) + { + ATLASSERT(m_hDC != NULL); + return ::InvertRect(m_hDC, lpRect); + } + + BOOL DrawIcon(int x, int y, HICON hIcon) + { + ATLASSERT(m_hDC != NULL); + return ::DrawIcon(m_hDC, x, y, hIcon); + } + + BOOL DrawIcon(POINT point, HICON hIcon) + { + ATLASSERT(m_hDC != NULL); + return ::DrawIcon(m_hDC, point.x, point.y, hIcon); + } + + BOOL DrawIconEx(int x, int y, HICON hIcon, int cxWidth, int cyWidth, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL) + { + ATLASSERT(m_hDC != NULL); + return ::DrawIconEx(m_hDC, x, y, hIcon, cxWidth, cyWidth, uStepIfAniCur, hbrFlickerFreeDraw, uFlags); + } + + BOOL DrawIconEx(POINT point, HICON hIcon, SIZE size, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL) + { + ATLASSERT(m_hDC != NULL); + return ::DrawIconEx(m_hDC, point.x, point.y, hIcon, size.cx, size.cy, uStepIfAniCur, hbrFlickerFreeDraw, uFlags); + } + + BOOL DrawState(POINT pt, SIZE size, HBITMAP hBitmap, UINT nFlags, HBRUSH hBrush = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::DrawState(m_hDC, hBrush, NULL, (LPARAM)hBitmap, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_BITMAP); + } + + BOOL DrawState(POINT pt, SIZE size, HICON hIcon, UINT nFlags, HBRUSH hBrush = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::DrawState(m_hDC, hBrush, NULL, (LPARAM)hIcon, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_ICON); + } + + BOOL DrawState(POINT pt, SIZE size, LPCTSTR lpszText, UINT nFlags, BOOL bPrefixText = TRUE, int nTextLen = 0, HBRUSH hBrush = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::DrawState(m_hDC, hBrush, NULL, (LPARAM)lpszText, (WPARAM)nTextLen, pt.x, pt.y, size.cx, size.cy, nFlags | (bPrefixText ? DST_PREFIXTEXT : DST_TEXT)); + } + + BOOL DrawState(POINT pt, SIZE size, DRAWSTATEPROC lpDrawProc, LPARAM lData, UINT nFlags, HBRUSH hBrush = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::DrawState(m_hDC, hBrush, lpDrawProc, lData, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_COMPLEX); + } + +// Ellipse and Polygon Functions + BOOL Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) + { + ATLASSERT(m_hDC != NULL); + return ::Chord(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4); + } + + BOOL Chord(LPCRECT lpRect, POINT ptStart, POINT ptEnd) + { + ATLASSERT(m_hDC != NULL); + return ::Chord(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); + } + + void DrawFocusRect(LPCRECT lpRect) + { + ATLASSERT(m_hDC != NULL); + ::DrawFocusRect(m_hDC, lpRect); + } + + BOOL Ellipse(int x1, int y1, int x2, int y2) + { + ATLASSERT(m_hDC != NULL); + return ::Ellipse(m_hDC, x1, y1, x2, y2); + } + + BOOL Ellipse(LPCRECT lpRect) + { + ATLASSERT(m_hDC != NULL); + return ::Ellipse(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); + } + + BOOL Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) + { + ATLASSERT(m_hDC != NULL); + return ::Pie(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4); + } + + BOOL Pie(LPCRECT lpRect, POINT ptStart, POINT ptEnd) + { + ATLASSERT(m_hDC != NULL); + return ::Pie(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); + } + + BOOL Polygon(const POINT* lpPoints, int nCount) + { + ATLASSERT(m_hDC != NULL); + return ::Polygon(m_hDC, lpPoints, nCount); + } + + BOOL PolyPolygon(const POINT* lpPoints, const INT* lpPolyCounts, int nCount) + { + ATLASSERT(m_hDC != NULL); + return ::PolyPolygon(m_hDC, lpPoints, lpPolyCounts, nCount); + } + + BOOL Rectangle(int x1, int y1, int x2, int y2) + { + ATLASSERT(m_hDC != NULL); + return ::Rectangle(m_hDC, x1, y1, x2, y2); + } + + BOOL Rectangle(LPCRECT lpRect) + { + ATLASSERT(m_hDC != NULL); + return ::Rectangle(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); + } + + BOOL RoundRect(int x1, int y1, int x2, int y2, int x3, int y3) + { + ATLASSERT(m_hDC != NULL); + return ::RoundRect(m_hDC, x1, y1, x2, y2, x3, y3); + } + + BOOL RoundRect(LPCRECT lpRect, POINT point) + { + ATLASSERT(m_hDC != NULL); + return ::RoundRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, point.x, point.y); + } + +// Bitmap Functions + BOOL PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop) + { + ATLASSERT(m_hDC != NULL); + return ::PatBlt(m_hDC, x, y, nWidth, nHeight, dwRop); + } + + BOOL BitBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, + int xSrc, int ySrc, DWORD dwRop) + { + ATLASSERT(m_hDC != NULL); + return ::BitBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, dwRop); + } + + BOOL StretchBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop) + { + ATLASSERT(m_hDC != NULL); + return ::StretchBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, dwRop); + } + + COLORREF GetPixel(int x, int y) const + { + ATLASSERT(m_hDC != NULL); + return ::GetPixel(m_hDC, x, y); + } + + COLORREF GetPixel(POINT point) const + { + ATLASSERT(m_hDC != NULL); + return ::GetPixel(m_hDC, point.x, point.y); + } + + COLORREF SetPixel(int x, int y, COLORREF crColor) + { + ATLASSERT(m_hDC != NULL); + return ::SetPixel(m_hDC, x, y, crColor); + } + + COLORREF SetPixel(POINT point, COLORREF crColor) + { + ATLASSERT(m_hDC != NULL); + return ::SetPixel(m_hDC, point.x, point.y, crColor); + } + + BOOL FloodFill(int x, int y, COLORREF crColor) + { + ATLASSERT(m_hDC != NULL); + return ::FloodFill(m_hDC, x, y, crColor); + } + + BOOL ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType) + { + ATLASSERT(m_hDC != NULL); + return ::ExtFloodFill(m_hDC, x, y, crColor, nFillType); + } + + BOOL MaskBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, HBITMAP hMaskBitmap, int xMask, int yMask, DWORD dwRop) + { + ATLASSERT(m_hDC != NULL); + return ::MaskBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, hMaskBitmap, xMask, yMask, dwRop); + } + + BOOL PlgBlt(LPPOINT lpPoint, HDC hSrcDC, int xSrc, int ySrc, int nWidth, int nHeight, HBITMAP hMaskBitmap, int xMask, int yMask) + { + ATLASSERT(m_hDC != NULL); + return ::PlgBlt(m_hDC, lpPoint, hSrcDC, xSrc, ySrc, nWidth, nHeight, hMaskBitmap, xMask, yMask); + } + + BOOL SetPixelV(int x, int y, COLORREF crColor) + { + ATLASSERT(m_hDC != NULL); + return ::SetPixelV(m_hDC, x, y, crColor); + } + + BOOL SetPixelV(POINT point, COLORREF crColor) + { + ATLASSERT(m_hDC != NULL); + return ::SetPixelV(m_hDC, point.x, point.y, crColor); + } + + BOOL TransparentBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, UINT crTransparent) + { + ATLASSERT(m_hDC != NULL); + return ::TransparentBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, crTransparent); + } + + BOOL GradientFill(const PTRIVERTEX pVertices, DWORD nVertices, void* pMeshElements, DWORD nMeshElements, DWORD dwMode) + { + ATLASSERT(m_hDC != NULL); + return ::GradientFill(m_hDC, pVertices, nVertices, pMeshElements, nMeshElements, dwMode); + } + + BOOL GradientFillRect(RECT& rect, COLORREF clr1, COLORREF clr2, bool bHorizontal) + { + ATLASSERT(m_hDC != NULL); + + TRIVERTEX arrTvx[2] = { { 0 }, { 0 } }; + + arrTvx[0].x = rect.left; + arrTvx[0].y = rect.top; + arrTvx[0].Red = MAKEWORD(0, GetRValue(clr1)); + arrTvx[0].Green = MAKEWORD(0, GetGValue(clr1)); + arrTvx[0].Blue = MAKEWORD(0, GetBValue(clr1)); + arrTvx[0].Alpha = 0; + + arrTvx[1].x = rect.right; + arrTvx[1].y = rect.bottom; + arrTvx[1].Red = MAKEWORD(0, GetRValue(clr2)); + arrTvx[1].Green = MAKEWORD(0, GetGValue(clr2)); + arrTvx[1].Blue = MAKEWORD(0, GetBValue(clr2)); + arrTvx[1].Alpha = 0; + + GRADIENT_RECT gr = { 0, 1 }; + + return ::GradientFill(m_hDC, arrTvx, 2, &gr, 1, bHorizontal ? GRADIENT_FILL_RECT_H : GRADIENT_FILL_RECT_V); + } + + BOOL AlphaBlend(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, BLENDFUNCTION bf) + { + ATLASSERT(m_hDC != NULL); + return ::AlphaBlend(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, bf); + } + +// Extra bitmap functions + // Helper function for painting a disabled toolbar or menu bitmap + // This function can take either an HBITMAP (for SS) or a DC with + // the bitmap already painted (for cmdbar) + BOOL DitherBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, HBITMAP hBitmap, int xSrc, int ySrc, + HBRUSH hBrushBackground = ::GetSysColorBrush(COLOR_3DFACE), + HBRUSH hBrush3DEffect = ::GetSysColorBrush(COLOR_3DHILIGHT), + HBRUSH hBrushDisabledImage = ::GetSysColorBrush(COLOR_3DSHADOW)) + { + ATLASSERT((m_hDC != NULL) || (hBitmap != NULL)); + ATLASSERT((nWidth > 0) && (nHeight > 0)); + + // Create a generic DC for all BitBlts + CDCT<false> dc = (hSrcDC != NULL) ? hSrcDC : ::CreateCompatibleDC(m_hDC); + ATLASSERT(dc.m_hDC != NULL); + if(dc.m_hDC == NULL) + return FALSE; + + // Create a DC for the monochrome DIB section + CDCT<true> dcBW = ::CreateCompatibleDC(m_hDC); + ATLASSERT(dcBW.m_hDC != NULL); + if(dcBW.m_hDC == NULL) + { + if(hSrcDC == NULL) + dc.DeleteDC(); + return FALSE; + } + + // Create the monochrome DIB section with a black and white palette + struct RGBBWBITMAPINFO + { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[2]; + }; + + RGBBWBITMAPINFO rgbBWBitmapInfo = + { + { sizeof(BITMAPINFOHEADER), nWidth, nHeight, 1, 1, BI_RGB, 0, 0, 0, 0, 0 }, + { { 0x00, 0x00, 0x00, 0x00 }, { 0xFF, 0xFF, 0xFF, 0x00 } } + }; + + VOID* pbitsBW; + CBitmap bmpBW = ::CreateDIBSection(dcBW, (LPBITMAPINFO)&rgbBWBitmapInfo, DIB_RGB_COLORS, &pbitsBW, NULL, 0); + ATLASSERT(bmpBW.m_hBitmap != NULL); + if(bmpBW.m_hBitmap == NULL) + { + if(hSrcDC == NULL) + dc.DeleteDC(); + return FALSE; + } + + // Attach the monochrome DIB section and the bitmap to the DCs + HBITMAP hbmOldBW = dcBW.SelectBitmap(bmpBW); + HBITMAP hbmOldDC = NULL; + if(hBitmap != NULL) + hbmOldDC = dc.SelectBitmap(hBitmap); + + // Block: Dark gray removal: we want (128, 128, 128) pixels to become black and not white + { + CDCT<true> dcTemp1 = ::CreateCompatibleDC(m_hDC); + CDCT<true> dcTemp2 = ::CreateCompatibleDC(m_hDC); + CBitmap bmpTemp1; + bmpTemp1.CreateCompatibleBitmap(dc, nWidth, nHeight); + CBitmap bmpTemp2; + bmpTemp2.CreateBitmap(nWidth, nHeight, 1, 1, NULL); + HBITMAP hOldBmp1 = dcTemp1.SelectBitmap(bmpTemp1); + HBITMAP hOldBmp2 = dcTemp2.SelectBitmap(bmpTemp2); + // Let's copy our image, it will be altered + dcTemp1.BitBlt(0, 0, nWidth, nHeight, dc, xSrc, ySrc, SRCCOPY); + + // All dark gray pixels will become white, the others black + dcTemp1.SetBkColor(RGB(128, 128, 128)); + dcTemp2.BitBlt(0, 0, nWidth, nHeight, dcTemp1, 0, 0, SRCCOPY); + // Do an XOR to set to black these white pixels + dcTemp1.BitBlt(0, 0, nWidth, nHeight, dcTemp2, 0, 0, SRCINVERT); + + // BitBlt the bitmap into the monochrome DIB section + // The DIB section will do a true monochrome conversion + // The magenta background being closer to white will become white + dcBW.BitBlt(0, 0, nWidth, nHeight, dcTemp1, 0, 0, SRCCOPY); + + // Cleanup + dcTemp1.SelectBitmap(hOldBmp1); + dcTemp2.SelectBitmap(hOldBmp2); + } + + // Paint the destination rectangle using hBrushBackground + if(hBrushBackground != NULL) + { + RECT rc = { x, y, x + nWidth, y + nHeight }; + FillRect(&rc, hBrushBackground); + } + + // BitBlt the black bits in the monochrome bitmap into hBrush3DEffect color in the destination DC + // The magic ROP comes from the Charles Petzold's book + HBRUSH hOldBrush = SelectBrush(hBrush3DEffect); + BitBlt(x + 1, y + 1, nWidth, nHeight, dcBW, 0, 0, 0xB8074A); + + // BitBlt the black bits in the monochrome bitmap into hBrushDisabledImage color in the destination DC + SelectBrush(hBrushDisabledImage); + BitBlt(x, y, nWidth, nHeight, dcBW, 0, 0, 0xB8074A); + + SelectBrush(hOldBrush); + dcBW.SelectBitmap(hbmOldBW); + dc.SelectBitmap(hbmOldDC); + + if(hSrcDC == NULL) + dc.DeleteDC(); + + return TRUE; + } + +// Text Functions + BOOL TextOut(int x, int y, LPCTSTR lpszString, int nCount = -1) + { + ATLASSERT(m_hDC != NULL); + if(nCount == -1) + nCount = lstrlen(lpszString); + return ::TextOut(m_hDC, x, y, lpszString, nCount); + } + + BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, LPCTSTR lpszString, int nCount = -1, LPINT lpDxWidths = NULL) + { + ATLASSERT(m_hDC != NULL); + if(nCount == -1) + nCount = lstrlen(lpszString); + ATLASSERT((nCount >= 0) && (nCount <= 8192)); + return ::ExtTextOut(m_hDC, x, y, nOptions, lpRect, lpszString, (UINT)nCount, lpDxWidths); + } + + SIZE TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount = -1, int nTabPositions = 0, LPINT lpnTabStopPositions = NULL, int nTabOrigin = 0) + { + ATLASSERT(m_hDC != NULL); + if(nCount == -1) + nCount = lstrlen(lpszString); + LONG lRes = ::TabbedTextOut(m_hDC, x, y, lpszString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin); + SIZE size = { GET_X_LPARAM(lRes), GET_Y_LPARAM(lRes) }; + return size; + } + + int DrawText(LPCTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat) + { + ATLASSERT(m_hDC != NULL); + ATLASSERT((uFormat & DT_MODIFYSTRING) == 0); + return ::DrawText(m_hDC, lpstrText, cchText, lpRect, uFormat); + } + + int DrawText(LPTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat) + { + ATLASSERT(m_hDC != NULL); + return ::DrawText(m_hDC, lpstrText, cchText, lpRect, uFormat); + } + + int DrawTextEx(LPTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat, LPDRAWTEXTPARAMS lpDTParams = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::DrawTextEx(m_hDC, lpstrText, cchText, lpRect, uFormat, lpDTParams); + } + + // Note - ::DrawShadowText() is present only if comctl32.dll version 6 is loaded + int DrawShadowText(LPCWSTR lpstrText, int cchText, LPRECT lpRect, DWORD dwFlags, COLORREF clrText, COLORREF clrShadow, int xOffset, int yOffset) + { + ATLASSERT(m_hDC != NULL); + ATLASSERT(lpRect != NULL); + return ::DrawShadowText(m_hDC, lpstrText, cchText, lpRect, dwFlags, clrText, clrShadow, xOffset, yOffset); + } + + BOOL GetTextExtent(LPCTSTR lpszString, int nCount, LPSIZE lpSize) const + { + ATLASSERT(m_hDC != NULL); + if(nCount == -1) + nCount = lstrlen(lpszString); + return ::GetTextExtentPoint32(m_hDC, lpszString, nCount, lpSize); + } + + BOOL GetTextExtentExPoint(LPCTSTR lpszString, int cchString, LPSIZE lpSize, int nMaxExtent, LPINT lpnFit = NULL, LPINT alpDx = NULL) + { + ATLASSERT(m_hDC != NULL); + return ::GetTextExtentExPoint(m_hDC, lpszString, cchString, nMaxExtent, lpnFit, alpDx, lpSize); + } + + DWORD GetTabbedTextExtent(LPCTSTR lpszString, int nCount = -1, int nTabPositions = 0, LPINT lpnTabStopPositions = NULL) const + { + ATLASSERT(m_hDC != NULL); + if(nCount == -1) + nCount = lstrlen(lpszString); + return ::GetTabbedTextExtent(m_hDC, lpszString, nCount, nTabPositions, lpnTabStopPositions); + } + + BOOL GrayString(HBRUSH hBrush, BOOL (CALLBACK* lpfnOutput)(HDC, LPARAM, int), LPARAM lpData, int nCount, int x, int y, int nWidth, int nHeight) + { + ATLASSERT(m_hDC != NULL); + return ::GrayString(m_hDC, hBrush, (GRAYSTRINGPROC)lpfnOutput, lpData, nCount, x, y, nWidth, nHeight); + } + + UINT GetTextAlign() const + { + ATLASSERT(m_hDC != NULL); + return ::GetTextAlign(m_hDC); + } + + UINT SetTextAlign(UINT nFlags) + { + ATLASSERT(m_hDC != NULL); + return ::SetTextAlign(m_hDC, nFlags); + } + + int GetTextFace(LPTSTR lpszFacename, int nCount) const + { + ATLASSERT(m_hDC != NULL); + return ::GetTextFace(m_hDC, nCount, lpszFacename); + } + + int GetTextFaceLen() const + { + ATLASSERT(m_hDC != NULL); + return ::GetTextFace(m_hDC, 0, NULL); + } + +#ifdef _OLEAUTO_H_ + BOOL GetTextFace(BSTR& bstrFace) const + { + USES_CONVERSION; + ATLASSERT(m_hDC != NULL); + ATLASSERT(bstrFace == NULL); + + int nLen = GetTextFaceLen(); + if(nLen == 0) + return FALSE; + + ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff; + LPTSTR lpszText = buff.Allocate(nLen); + if(lpszText == NULL) + return FALSE; + + if(!GetTextFace(lpszText, nLen)) + return FALSE; + + bstrFace = ::SysAllocString(T2OLE(lpszText)); + return (bstrFace != NULL) ? TRUE : FALSE; + } +#endif + +#ifdef __ATLSTR_H__ + int GetTextFace(ATL::CString& strFace) const + { + ATLASSERT(m_hDC != NULL); + + int nLen = GetTextFaceLen(); + if(nLen == 0) + return 0; + + LPTSTR lpstr = strFace.GetBufferSetLength(nLen); + if(lpstr == NULL) + return 0; + int nRet = GetTextFace(lpstr, nLen); + strFace.ReleaseBuffer(); + return nRet; + } +#endif // __ATLSTR_H__ + + BOOL GetTextMetrics(LPTEXTMETRIC lpMetrics) const + { + ATLASSERT(m_hDC != NULL); + return ::GetTextMetrics(m_hDC, lpMetrics); + } + + int SetTextJustification(int nBreakExtra, int nBreakCount) + { + ATLASSERT(m_hDC != NULL); + return ::SetTextJustification(m_hDC, nBreakExtra, nBreakCount); + } + + int GetTextCharacterExtra() const + { + ATLASSERT(m_hDC != NULL); + return ::GetTextCharacterExtra(m_hDC); + } + + int SetTextCharacterExtra(int nCharExtra) + { + ATLASSERT(m_hDC != NULL); + return ::SetTextCharacterExtra(m_hDC, nCharExtra); + } + +// Advanced Drawing + BOOL DrawEdge(LPRECT lpRect, UINT nEdge, UINT nFlags) + { + ATLASSERT(m_hDC != NULL); + return ::DrawEdge(m_hDC, lpRect, nEdge, nFlags); + } + + BOOL DrawFrameControl(LPRECT lpRect, UINT nType, UINT nState) + { + ATLASSERT(m_hDC != NULL); + return ::DrawFrameControl(m_hDC, lpRect, nType, nState); + } + +// Scrolling Functions + BOOL ScrollDC(int dx, int dy, LPCRECT lpRectScroll, LPCRECT lpRectClip, HRGN hRgnUpdate, LPRECT lpRectUpdate) + { + ATLASSERT(m_hDC != NULL); + return ::ScrollDC(m_hDC, dx, dy, lpRectScroll, lpRectClip, hRgnUpdate, lpRectUpdate); + } + +// Font Functions + BOOL GetCharWidth(UINT nFirstChar, UINT nLastChar, LPINT lpBuffer) const + { + ATLASSERT(m_hDC != NULL); + return ::GetCharWidth(m_hDC, nFirstChar, nLastChar, lpBuffer); + } + + // GetCharWidth32 is not supported under Win9x + BOOL GetCharWidth32(UINT nFirstChar, UINT nLastChar, LPINT lpBuffer) const + { + ATLASSERT(m_hDC != NULL); + return ::GetCharWidth32(m_hDC, nFirstChar, nLastChar, lpBuffer); + } + + DWORD SetMapperFlags(DWORD dwFlag) + { + ATLASSERT(m_hDC != NULL); + return ::SetMapperFlags(m_hDC, dwFlag); + } + + BOOL GetAspectRatioFilter(LPSIZE lpSize) const + { + ATLASSERT(m_hDC != NULL); + return ::GetAspectRatioFilterEx(m_hDC, lpSize); + } + + BOOL GetCharABCWidths(UINT nFirstChar, UINT nLastChar, LPABC lpabc) const + { + ATLASSERT(m_hDC != NULL); + return ::GetCharABCWidths(m_hDC, nFirstChar, nLastChar, lpabc); + } + + DWORD GetFontData(DWORD dwTable, DWORD dwOffset, LPVOID lpData, DWORD cbData) const + { + ATLASSERT(m_hDC != NULL); + return ::GetFontData(m_hDC, dwTable, dwOffset, lpData, cbData); + } + + int GetKerningPairs(int nPairs, LPKERNINGPAIR lpkrnpair) const + { + ATLASSERT(m_hDC != NULL); + return ::GetKerningPairs(m_hDC, nPairs, lpkrnpair); + } + + UINT GetOutlineTextMetrics(UINT cbData, LPOUTLINETEXTMETRIC lpotm) const + { + ATLASSERT(m_hDC != NULL); + return ::GetOutlineTextMetrics(m_hDC, cbData, lpotm); + } + + DWORD GetGlyphOutline(UINT nChar, UINT nFormat, LPGLYPHMETRICS lpgm, DWORD cbBuffer, LPVOID lpBuffer, const MAT2* lpmat2) const + { + ATLASSERT(m_hDC != NULL); + return ::GetGlyphOutline(m_hDC, nChar, nFormat, lpgm, cbBuffer, lpBuffer, lpmat2); + } + + BOOL GetCharABCWidths(UINT nFirstChar, UINT nLastChar, LPABCFLOAT lpABCF) const + { + ATLASSERT(m_hDC != NULL); + return ::GetCharABCWidthsFloat(m_hDC, nFirstChar, nLastChar, lpABCF); + } + + BOOL GetCharWidth(UINT nFirstChar, UINT nLastChar, float* lpFloatBuffer) const + { + ATLASSERT(m_hDC != NULL); + return ::GetCharWidthFloat(m_hDC, nFirstChar, nLastChar, lpFloatBuffer); + } + +// Printer/Device Escape Functions + int Escape(int nEscape, int nCount, LPCSTR lpszInData, LPVOID lpOutData) + { + ATLASSERT(m_hDC != NULL); + return ::Escape(m_hDC, nEscape, nCount, lpszInData, lpOutData); + } + + int Escape(int nEscape, int nInputSize, LPCSTR lpszInputData, + int nOutputSize, LPSTR lpszOutputData) + { + ATLASSERT(m_hDC != NULL); + return ::ExtEscape(m_hDC, nEscape, nInputSize, lpszInputData, nOutputSize, lpszOutputData); + } + + int DrawEscape(int nEscape, int nInputSize, LPCSTR lpszInputData) + { + ATLASSERT(m_hDC != NULL); + return ::DrawEscape(m_hDC, nEscape, nInputSize, lpszInputData); + } + + // Escape helpers + int StartDoc(LPCTSTR lpszDocName) // old Win3.0 version + { + DOCINFO di = {}; + di.cbSize = sizeof(DOCINFO); + di.lpszDocName = lpszDocName; + return StartDoc(&di); + } + + int StartDoc(LPDOCINFO lpDocInfo) + { + ATLASSERT(m_hDC != NULL); + return ::StartDoc(m_hDC, lpDocInfo); + } + + int StartPage() + { + ATLASSERT(m_hDC != NULL); + return ::StartPage(m_hDC); + } + + int EndPage() + { + ATLASSERT(m_hDC != NULL); + return ::EndPage(m_hDC); + } + + int SetAbortProc(BOOL (CALLBACK* lpfn)(HDC, int)) + { + ATLASSERT(m_hDC != NULL); + return ::SetAbortProc(m_hDC, (ABORTPROC)lpfn); + } + + int AbortDoc() + { + ATLASSERT(m_hDC != NULL); + return ::AbortDoc(m_hDC); + } + + int EndDoc() + { + ATLASSERT(m_hDC != NULL); + return ::EndDoc(m_hDC); + } + +// MetaFile Functions + BOOL PlayMetaFile(HMETAFILE hMF) + { + ATLASSERT(m_hDC != NULL); + if(::GetDeviceCaps(m_hDC, TECHNOLOGY) == DT_METAFILE) + { + // playing metafile in metafile, just use core windows API + return ::PlayMetaFile(m_hDC, hMF); + } + + // for special playback, lParam == pDC + return ::EnumMetaFile(m_hDC, hMF, EnumMetaFileProc, (LPARAM)this); + } + + BOOL PlayMetaFile(HENHMETAFILE hEnhMetaFile, LPCRECT lpBounds) + { + ATLASSERT(m_hDC != NULL); + return ::PlayEnhMetaFile(m_hDC, hEnhMetaFile, lpBounds); + } + + BOOL AddMetaFileComment(UINT nDataSize, const BYTE* pCommentData) // can be used for enhanced metafiles only + { + ATLASSERT(m_hDC != NULL); + return ::GdiComment(m_hDC, nDataSize, pCommentData); + } + + // Special handling for metafile playback + static int CALLBACK EnumMetaFileProc(HDC hDC, HANDLETABLE* pHandleTable, METARECORD* pMetaRec, int nHandles, LPARAM lParam) + { + CDCT<false>* pDC = (CDCT<false>*)lParam; + + switch (pMetaRec->rdFunction) + { + case META_SETMAPMODE: + pDC->SetMapMode((int)(short)pMetaRec->rdParm[0]); + break; + case META_SETWINDOWEXT: + pDC->SetWindowExt((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); + break; + case META_SETWINDOWORG: + pDC->SetWindowOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); + break; + case META_SETVIEWPORTEXT: + pDC->SetViewportExt((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); + break; + case META_SETVIEWPORTORG: + pDC->SetViewportOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); + break; + case META_SCALEWINDOWEXT: + pDC->ScaleWindowExt((int)(short)pMetaRec->rdParm[3], (int)(short)pMetaRec->rdParm[2], + (int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); + break; + case META_SCALEVIEWPORTEXT: + pDC->ScaleViewportExt((int)(short)pMetaRec->rdParm[3], (int)(short)pMetaRec->rdParm[2], + (int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); + break; + case META_OFFSETVIEWPORTORG: + pDC->OffsetViewportOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); + break; + case META_SAVEDC: + pDC->SaveDC(); + break; + case META_RESTOREDC: + pDC->RestoreDC((int)(short)pMetaRec->rdParm[0]); + break; + case META_SETBKCOLOR: + pDC->SetBkColor(*(UNALIGNED COLORREF*)&pMetaRec->rdParm[0]); + break; + case META_SETTEXTCOLOR: + pDC->SetTextColor(*(UNALIGNED COLORREF*)&pMetaRec->rdParm[0]); + break; + + // need to watch out for SelectObject(HFONT), for custom font mapping + case META_SELECTOBJECT: + { + HGDIOBJ hObject = pHandleTable->objectHandle[pMetaRec->rdParm[0]]; + UINT nObjType = ::GetObjectType(hObject); + if(nObjType == 0) + { + // object type is unknown, determine if it is a font + HFONT hStockFont = (HFONT)::GetStockObject(SYSTEM_FONT); + HFONT hFontOld = (HFONT)::SelectObject(pDC->m_hDC, hStockFont); + HGDIOBJ hObjOld = ::SelectObject(pDC->m_hDC, hObject); + if(hObjOld == hStockFont) + { + // got the stock object back, so must be selecting a font + pDC->SelectFont((HFONT)hObject); + break; // don't play the default record + } + else + { + // didn't get the stock object back, so restore everything + ::SelectObject(pDC->m_hDC, hFontOld); + ::SelectObject(pDC->m_hDC, hObjOld); + } + // and fall through to PlayMetaFileRecord... + } + else if(nObjType == OBJ_FONT) + { + // play back as CDCHandle::SelectFont(HFONT) + pDC->SelectFont((HFONT)hObject); + break; // don't play the default record + } + } + // fall through... + + default: + ::PlayMetaFileRecord(hDC, pHandleTable, pMetaRec, nHandles); + break; + } + + return 1; + } + +// Path Functions + BOOL AbortPath() + { + ATLASSERT(m_hDC != NULL); + return ::AbortPath(m_hDC); + } + + BOOL BeginPath() + { + ATLASSERT(m_hDC != NULL); + return ::BeginPath(m_hDC); + } + + BOOL CloseFigure() + { + ATLASSERT(m_hDC != NULL); + return ::CloseFigure(m_hDC); + } + + BOOL EndPath() + { + ATLASSERT(m_hDC != NULL); + return ::EndPath(m_hDC); + } + + BOOL FillPath() + { + ATLASSERT(m_hDC != NULL); + return ::FillPath(m_hDC); + } + + BOOL FlattenPath() + { + ATLASSERT(m_hDC != NULL); + return ::FlattenPath(m_hDC); + } + + BOOL StrokeAndFillPath() + { + ATLASSERT(m_hDC != NULL); + return ::StrokeAndFillPath(m_hDC); + } + + BOOL StrokePath() + { + ATLASSERT(m_hDC != NULL); + return ::StrokePath(m_hDC); + } + + BOOL WidenPath() + { + ATLASSERT(m_hDC != NULL); + return ::WidenPath(m_hDC); + } + + BOOL GetMiterLimit(PFLOAT pfMiterLimit) const + { + ATLASSERT(m_hDC != NULL); + return ::GetMiterLimit(m_hDC, pfMiterLimit); + } + + BOOL SetMiterLimit(float fMiterLimit) + { + ATLASSERT(m_hDC != NULL); + return ::SetMiterLimit(m_hDC, fMiterLimit, NULL); + } + + int GetPath(LPPOINT lpPoints, LPBYTE lpTypes, int nCount) const + { + ATLASSERT(m_hDC != NULL); + return ::GetPath(m_hDC, lpPoints, lpTypes, nCount); + } + + BOOL SelectClipPath(int nMode) + { + ATLASSERT(m_hDC != NULL); + return ::SelectClipPath(m_hDC, nMode); + } + +// Misc Helper Functions + static CBrushHandle PASCAL GetHalftoneBrush() + { + HBRUSH halftoneBrush = NULL; + WORD grayPattern[8] = {}; + for(int i = 0; i < 8; i++) + grayPattern[i] = (WORD)(0x5555 << (i & 1)); + HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern); + if(grayBitmap != NULL) + { + halftoneBrush = ::CreatePatternBrush(grayBitmap); + DeleteObject(grayBitmap); + } + return CBrushHandle(halftoneBrush); + } + + void DrawDragRect(LPCRECT lpRect, SIZE size, LPCRECT lpRectLast, SIZE sizeLast, HBRUSH hBrush = NULL, HBRUSH hBrushLast = NULL) + { + // first, determine the update region and select it + CRgn rgnOutside; + rgnOutside.CreateRectRgnIndirect(lpRect); + RECT rect = *lpRect; + ::InflateRect(&rect, -size.cx, -size.cy); + ::IntersectRect(&rect, &rect, lpRect); + CRgn rgnInside; + rgnInside.CreateRectRgnIndirect(&rect); + CRgn rgnNew; + rgnNew.CreateRectRgn(0, 0, 0, 0); + rgnNew.CombineRgn(rgnOutside, rgnInside, RGN_XOR); + + HBRUSH hBrushOld = NULL; + CBrush brushHalftone; + if(hBrush == NULL) + brushHalftone = hBrush = CDCHandle::GetHalftoneBrush(); + if(hBrushLast == NULL) + hBrushLast = hBrush; + + CRgn rgnLast; + CRgn rgnUpdate; + if(lpRectLast != NULL) + { + // find difference between new region and old region + rgnLast.CreateRectRgn(0, 0, 0, 0); + rgnOutside.SetRectRgn(lpRectLast->left, lpRectLast->top, lpRectLast->right, lpRectLast->bottom); + rect = *lpRectLast; + ::InflateRect(&rect, -sizeLast.cx, -sizeLast.cy); + ::IntersectRect(&rect, &rect, lpRectLast); + rgnInside.SetRectRgn(rect.left, rect.top, rect.right, rect.bottom); + rgnLast.CombineRgn(rgnOutside, rgnInside, RGN_XOR); + + // only diff them if brushes are the same + if(hBrush == hBrushLast) + { + rgnUpdate.CreateRectRgn(0, 0, 0, 0); + rgnUpdate.CombineRgn(rgnLast, rgnNew, RGN_XOR); + } + } + if((hBrush != hBrushLast) && (lpRectLast != NULL)) + { + // brushes are different -- erase old region first + SelectClipRgn(rgnLast); + GetClipBox(&rect); + hBrushOld = SelectBrush(hBrushLast); + PatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT); + SelectBrush(hBrushOld); + hBrushOld = NULL; + } + + // draw into the update/new region + SelectClipRgn(rgnUpdate.IsNull() ? rgnNew : rgnUpdate); + GetClipBox(&rect); + hBrushOld = SelectBrush(hBrush); + PatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT); + + // cleanup DC + if(hBrushOld != NULL) + SelectBrush(hBrushOld); + SelectClipRgn(NULL); + } + + void FillSolidRect(LPCRECT lpRect, COLORREF clr) + { + ATLASSERT(m_hDC != NULL); + + COLORREF clrOld = ::SetBkColor(m_hDC, clr); + ATLASSERT(clrOld != CLR_INVALID); + if(clrOld != CLR_INVALID) + { + ::ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, lpRect, NULL, 0, NULL); + ::SetBkColor(m_hDC, clrOld); + } + } + + void FillSolidRect(int x, int y, int cx, int cy, COLORREF clr) + { + ATLASSERT(m_hDC != NULL); + + RECT rect = { x, y, x + cx, y + cy }; + FillSolidRect(&rect, clr); + } + + void Draw3dRect(LPCRECT lpRect, COLORREF clrTopLeft, COLORREF clrBottomRight) + { + Draw3dRect(lpRect->left, lpRect->top, lpRect->right - lpRect->left, + lpRect->bottom - lpRect->top, clrTopLeft, clrBottomRight); + } + + void Draw3dRect(int x, int y, int cx, int cy, COLORREF clrTopLeft, COLORREF clrBottomRight) + { + FillSolidRect(x, y, cx - 1, 1, clrTopLeft); + FillSolidRect(x, y, 1, cy - 1, clrTopLeft); + FillSolidRect(x + cx, y, -1, cy, clrBottomRight); + FillSolidRect(x, y + cy, cx, -1, clrBottomRight); + } + +// DIB support + int SetDIBitsToDevice(int x, int y, DWORD dwWidth, DWORD dwHeight, int xSrc, int ySrc, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse) + { + ATLASSERT(m_hDC != NULL); + return ::SetDIBitsToDevice(m_hDC, x, y, dwWidth, dwHeight, xSrc, ySrc, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse); + } + + int StretchDIBits(int x, int y, int nWidth, int nHeight, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse, DWORD dwRop) + { + ATLASSERT(m_hDC != NULL); + return ::StretchDIBits(m_hDC, x, y, nWidth, nHeight, xSrc, ySrc, nSrcWidth, nSrcHeight, lpvBits, lpbmi, uColorUse, dwRop); + } + + UINT GetDIBColorTable(UINT uStartIndex, UINT cEntries, RGBQUAD* pColors) const + { + ATLASSERT(m_hDC != NULL); + return ::GetDIBColorTable(m_hDC, uStartIndex, cEntries, pColors); + } + + UINT SetDIBColorTable(UINT uStartIndex, UINT cEntries, CONST RGBQUAD* pColors) + { + ATLASSERT(m_hDC != NULL); + return ::SetDIBColorTable(m_hDC, uStartIndex, cEntries, pColors); + } + +// OpenGL support +#if !defined(_ATL_NO_OPENGL) + int ChoosePixelFormat(CONST PIXELFORMATDESCRIPTOR* ppfd) + { + ATLASSERT(m_hDC != NULL); + return ::ChoosePixelFormat(m_hDC, ppfd); + } + + int DescribePixelFormat(int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR ppfd) + { + ATLASSERT(m_hDC != NULL); + return ::DescribePixelFormat(m_hDC, iPixelFormat, nBytes, ppfd); + } + + int GetPixelFormat() const + { + ATLASSERT(m_hDC != NULL); + return ::GetPixelFormat(m_hDC); + } + + BOOL SetPixelFormat(int iPixelFormat, CONST PIXELFORMATDESCRIPTOR* ppfd) + { + ATLASSERT(m_hDC != NULL); + return ::SetPixelFormat(m_hDC, iPixelFormat, ppfd); + } + + BOOL SwapBuffers() + { + ATLASSERT(m_hDC != NULL); + return ::SwapBuffers(m_hDC); + } + + HGLRC wglCreateContext() + { + ATLASSERT(m_hDC != NULL); + return ::wglCreateContext(m_hDC); + } + + HGLRC wglCreateLayerContext(int iLayerPlane) + { + ATLASSERT(m_hDC != NULL); + return ::wglCreateLayerContext(m_hDC, iLayerPlane); + } + + BOOL wglMakeCurrent(HGLRC hglrc) + { + ATLASSERT(m_hDC != NULL); + return ::wglMakeCurrent(m_hDC, hglrc); + } + + BOOL wglUseFontBitmaps(DWORD dwFirst, DWORD dwCount, DWORD listBase) + { + ATLASSERT(m_hDC != NULL); + return ::wglUseFontBitmaps(m_hDC, dwFirst, dwCount, listBase); + } + + BOOL wglUseFontOutlines(DWORD dwFirst, DWORD dwCount, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, LPGLYPHMETRICSFLOAT lpgmf) + { + ATLASSERT(m_hDC != NULL); + return ::wglUseFontOutlines(m_hDC, dwFirst, dwCount, listBase, deviation, extrusion, format, lpgmf); + } + + BOOL wglDescribeLayerPlane(int iPixelFormat, int iLayerPlane, UINT nBytes, LPLAYERPLANEDESCRIPTOR plpd) + { + ATLASSERT(m_hDC != NULL); + return ::wglDescribeLayerPlane(m_hDC, iPixelFormat, iLayerPlane, nBytes, plpd); + } + + int wglSetLayerPaletteEntries(int iLayerPlane, int iStart, int cEntries, CONST COLORREF* pclr) + { + ATLASSERT(m_hDC != NULL); + return ::wglSetLayerPaletteEntries(m_hDC, iLayerPlane, iStart, cEntries, pclr); + } + + int wglGetLayerPaletteEntries(int iLayerPlane, int iStart, int cEntries, COLORREF* pclr) + { + ATLASSERT(m_hDC != NULL); + return ::wglGetLayerPaletteEntries(m_hDC, iLayerPlane, iStart, cEntries, pclr); + } + + BOOL wglRealizeLayerPalette(int iLayerPlane, BOOL bRealize) + { + ATLASSERT(m_hDC != NULL); + return ::wglRealizeLayerPalette(m_hDC, iLayerPlane, bRealize); + } + + BOOL wglSwapLayerBuffers(UINT uPlanes) + { + ATLASSERT(m_hDC != NULL); + return ::wglSwapLayerBuffers(m_hDC, uPlanes); + } +#endif // !defined(_ATL_NO_OPENGL) + + COLORREF GetDCPenColor() const + { + ATLASSERT(m_hDC != NULL); + return ::GetDCPenColor(m_hDC); + } + + COLORREF SetDCPenColor(COLORREF clr) + { + ATLASSERT(m_hDC != NULL); + return ::SetDCPenColor(m_hDC, clr); + } + + COLORREF GetDCBrushColor() const + { + ATLASSERT(m_hDC != NULL); + return ::GetDCBrushColor(m_hDC); + } + + COLORREF SetDCBrushColor(COLORREF clr) + { + ATLASSERT(m_hDC != NULL); + return ::SetDCBrushColor(m_hDC, clr); + } + + DWORD GetFontUnicodeRanges(LPGLYPHSET lpgs) const + { + ATLASSERT(m_hDC != NULL); + return ::GetFontUnicodeRanges(m_hDC, lpgs); + } + + DWORD GetGlyphIndices(LPCTSTR lpstr, int cch, LPWORD pgi, DWORD dwFlags) const + { + ATLASSERT(m_hDC != NULL); + return ::GetGlyphIndices(m_hDC, lpstr, cch, pgi, dwFlags); + } + + BOOL GetTextExtentPointI(LPWORD pgiIn, int cgi, LPSIZE lpSize) const + { + ATLASSERT(m_hDC != NULL); + return ::GetTextExtentPointI(m_hDC, pgiIn, cgi, lpSize); + } + + BOOL GetTextExtentExPointI(LPWORD pgiIn, int cgi, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize) const + { + ATLASSERT(m_hDC != NULL); + return ::GetTextExtentExPointI(m_hDC, pgiIn, cgi, nMaxExtent, lpnFit, alpDx, lpSize); + } + + BOOL GetCharWidthI(UINT giFirst, UINT cgi, LPWORD pgi, LPINT lpBuffer) const + { + ATLASSERT(m_hDC != NULL); + return ::GetCharWidthI(m_hDC, giFirst, cgi, pgi, lpBuffer); + } + + BOOL GetCharABCWidthsI(UINT giFirst, UINT cgi, LPWORD pgi, LPABC lpabc) const + { + ATLASSERT(m_hDC != NULL); + return ::GetCharABCWidthsI(m_hDC, giFirst, cgi, pgi, lpabc); + } + + BOOL ColorCorrectPalette(HPALETTE hPalette, DWORD dwFirstEntry, DWORD dwNumOfEntries) + { + ATLASSERT(m_hDC != NULL); + return ::ColorCorrectPalette(m_hDC, hPalette, dwFirstEntry, dwNumOfEntries); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CDC Helpers + +class CPaintDC : public CDC +{ +public: +// Data members + HWND m_hWnd; + PAINTSTRUCT m_ps; + +// Constructor/destructor + CPaintDC(HWND hWnd) + { + ATLASSERT(::IsWindow(hWnd)); + m_hWnd = hWnd; + m_hDC = ::BeginPaint(hWnd, &m_ps); + } + + ~CPaintDC() + { + ATLASSERT(m_hDC != NULL); + ATLASSERT(::IsWindow(m_hWnd)); + ::EndPaint(m_hWnd, &m_ps); + Detach(); + } +}; + +class CClientDC : public CDC +{ +public: +// Data members + HWND m_hWnd; + +// Constructor/destructor + CClientDC(HWND hWnd) + { + ATLASSERT((hWnd == NULL) || ::IsWindow(hWnd)); + m_hWnd = hWnd; + m_hDC = ::GetDC(hWnd); + } + + ~CClientDC() + { + ATLASSERT(m_hDC != NULL); + ::ReleaseDC(m_hWnd, Detach()); + } +}; + +class CWindowDC : public CDC +{ +public: +// Data members + HWND m_hWnd; + +// Constructor/destructor + CWindowDC(HWND hWnd) + { + ATLASSERT((hWnd == NULL) || ::IsWindow(hWnd)); + m_hWnd = hWnd; + m_hDC = ::GetWindowDC(hWnd); + } + + ~CWindowDC() + { + ATLASSERT(m_hDC != NULL); + ::ReleaseDC(m_hWnd, Detach()); + } +}; + +class CMemoryDC : public CDC +{ +public: +// Data members + HDC m_hDCOriginal; + RECT m_rcPaint; + CBitmap m_bmp; + HBITMAP m_hBmpOld; + +// Constructor/destructor + CMemoryDC(HDC hDC, const RECT& rcPaint) : m_hDCOriginal(hDC), m_hBmpOld(NULL) + { + m_rcPaint = rcPaint; + CreateCompatibleDC(m_hDCOriginal); + ATLASSERT(m_hDC != NULL); + m_bmp.CreateCompatibleBitmap(m_hDCOriginal, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top); + ATLASSERT(m_bmp.m_hBitmap != NULL); + m_hBmpOld = SelectBitmap(m_bmp); + SetViewportOrg(-m_rcPaint.left, -m_rcPaint.top); + } + + ~CMemoryDC() + { + ::BitBlt(m_hDCOriginal, m_rcPaint.left, m_rcPaint.top, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top, m_hDC, m_rcPaint.left, m_rcPaint.top, SRCCOPY); + SelectBitmap(m_hBmpOld); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Enhanced metafile support + +class CEnhMetaFileInfo +{ +public: +// Data members + HENHMETAFILE m_hEMF; + BYTE* m_pBits; + TCHAR* m_pDesc; + ENHMETAHEADER m_header; + PIXELFORMATDESCRIPTOR m_pfd; + +// Constructor/destructor + CEnhMetaFileInfo(HENHMETAFILE hEMF) : m_hEMF(hEMF), m_pBits(NULL), m_pDesc(NULL) + { + memset(&m_header, 0, sizeof(m_header)); + memset(&m_pfd, 0, sizeof(m_pfd)); + } + + ~CEnhMetaFileInfo() + { + delete [] m_pBits; + delete [] m_pDesc; + } + +// Operations + BYTE* GetEnhMetaFileBits() + { + ATLASSERT(m_hEMF != NULL); + UINT nBytes = ::GetEnhMetaFileBits(m_hEMF, 0, NULL); + delete [] m_pBits; + m_pBits = NULL; + ATLTRY(m_pBits = new BYTE[nBytes]); + if (m_pBits != NULL) + ::GetEnhMetaFileBits(m_hEMF, nBytes, m_pBits); + return m_pBits; + } + + LPTSTR GetEnhMetaFileDescription() + { + ATLASSERT(m_hEMF != NULL); + UINT nLen = ::GetEnhMetaFileDescription(m_hEMF, 0, NULL); + delete [] m_pDesc; + m_pDesc = NULL; + ATLTRY(m_pDesc = new TCHAR[nLen]); + if (m_pDesc != NULL) + nLen = ::GetEnhMetaFileDescription(m_hEMF, nLen, m_pDesc); + return m_pDesc; + } + + ENHMETAHEADER* GetEnhMetaFileHeader() + { + ATLASSERT(m_hEMF != NULL); + memset(&m_header, 0, sizeof(m_header)); + m_header.iType = EMR_HEADER; + m_header.nSize = sizeof(ENHMETAHEADER); + UINT n = ::GetEnhMetaFileHeader(m_hEMF, sizeof(ENHMETAHEADER), &m_header); + return (n != 0) ? &m_header : NULL; + } + + PIXELFORMATDESCRIPTOR* GetEnhMetaFilePixelFormat() + { + ATLASSERT(m_hEMF != NULL); + memset(&m_pfd, 0, sizeof(m_pfd)); + UINT n = ::GetEnhMetaFilePixelFormat(m_hEMF, sizeof(m_pfd), &m_pfd); + return (n != 0) ? &m_pfd : NULL; + } +}; + + +template <bool t_bManaged> +class CEnhMetaFileT +{ +public: +// Data members + HENHMETAFILE m_hEMF; + +// Constructor/destructor + CEnhMetaFileT(HENHMETAFILE hEMF = NULL) : m_hEMF(hEMF) + { + } + + ~CEnhMetaFileT() + { + if(t_bManaged && (m_hEMF != NULL)) + DeleteObject(); + } + +// Operations + CEnhMetaFileT<t_bManaged>& operator =(HENHMETAFILE hEMF) + { + Attach(hEMF); + return *this; + } + + void Attach(HENHMETAFILE hEMF) + { + if(t_bManaged && (m_hEMF != NULL) && (m_hEMF != hEMF)) + DeleteObject(); + m_hEMF = hEMF; + } + + HENHMETAFILE Detach() + { + HENHMETAFILE hEMF = m_hEMF; + m_hEMF = NULL; + return hEMF; + } + + operator HENHMETAFILE() const { return m_hEMF; } + + bool IsNull() const { return (m_hEMF == NULL); } + + BOOL DeleteObject() + { + ATLASSERT(m_hEMF != NULL); + BOOL bRet = ::DeleteEnhMetaFile(m_hEMF); + m_hEMF = NULL; + return bRet; + } + + UINT GetEnhMetaFileBits(UINT cbBuffer, LPBYTE lpbBuffer) const + { + ATLASSERT(m_hEMF != NULL); + return ::GetEnhMetaFileBits(m_hEMF, cbBuffer, lpbBuffer); + } + + UINT GetEnhMetaFileDescription(UINT cchBuffer, LPTSTR lpszDescription) const + { + ATLASSERT(m_hEMF != NULL); + return ::GetEnhMetaFileDescription(m_hEMF, cchBuffer, lpszDescription); + } + + UINT GetEnhMetaFileHeader(LPENHMETAHEADER lpemh) const + { + ATLASSERT(m_hEMF != NULL); + lpemh->iType = EMR_HEADER; + lpemh->nSize = sizeof(ENHMETAHEADER); + return ::GetEnhMetaFileHeader(m_hEMF, sizeof(ENHMETAHEADER), lpemh); + } + + UINT GetEnhMetaFilePaletteEntries(UINT cEntries, LPPALETTEENTRY lppe) const + { + ATLASSERT(m_hEMF != NULL); + return ::GetEnhMetaFilePaletteEntries(m_hEMF, cEntries, lppe); + } + + UINT GetEnhMetaFilePixelFormat(DWORD cbBuffer, PIXELFORMATDESCRIPTOR* ppfd) const + { + ATLASSERT(m_hEMF != NULL); + return ::GetEnhMetaFilePixelFormat(m_hEMF, cbBuffer, ppfd); + } +}; + +typedef CEnhMetaFileT<false> CEnhMetaFileHandle; +typedef CEnhMetaFileT<true> CEnhMetaFile; + + +class CEnhMetaFileDC : public CDC +{ +public: +// Constructor/destructor + CEnhMetaFileDC() + { + } + + CEnhMetaFileDC(HDC hdc, LPCRECT lpRect) + { + Create(hdc, NULL, lpRect, NULL); + ATLASSERT(m_hDC != NULL); + } + + CEnhMetaFileDC(HDC hdcRef, LPCTSTR lpFilename, LPCRECT lpRect, LPCTSTR lpDescription) + { + Create(hdcRef, lpFilename, lpRect, lpDescription); + ATLASSERT(m_hDC != NULL); + } + + ~CEnhMetaFileDC() + { + HENHMETAFILE hEMF = Close(); + if (hEMF != NULL) + ::DeleteEnhMetaFile(hEMF); + } + +// Operations + void Create(HDC hdcRef, LPCTSTR lpFilename, LPCRECT lpRect, LPCTSTR lpDescription) + { + ATLASSERT(m_hDC == NULL); + m_hDC = ::CreateEnhMetaFile(hdcRef, lpFilename, lpRect, lpDescription); + } + + HENHMETAFILE Close() + { + HENHMETAFILE hEMF = NULL; + if (m_hDC != NULL) + { + hEMF = ::CloseEnhMetaFile(m_hDC); + m_hDC = NULL; + } + return hEMF; + } +}; + +} // namespace WTL + +#endif // __ATLGDI_H__ diff --git a/Examples/WhisperDesktop/Utils/WTL/atlres.h b/Examples/WhisperDesktop/Utils/WTL/atlres.h new file mode 100644 index 0000000..aed58f1 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/WTL/atlres.h @@ -0,0 +1,259 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLRES_H__ +#define __ATLRES_H__ + +#pragma once + + +#ifdef RC_INVOKED +#ifndef _INC_WINDOWS + + #define _INC_WINDOWS + + #define VS_VERSION_INFO 1 + + #ifdef APSTUDIO_INVOKED + #define APSTUDIO_HIDDEN_SYMBOLS // Ignore following symbols + #endif // APSTUDIO_INVOKED + + #ifndef WINVER + #define WINVER 0x0500 + #endif // !WINVER + + #include <winresrc.h> + + // operation messages sent to DLGINIT + #define LB_ADDSTRING (WM_USER+1) + #define CB_ADDSTRING (WM_USER+3) + + #ifdef APSTUDIO_INVOKED + #undef APSTUDIO_HIDDEN_SYMBOLS + #endif // APSTUDIO_INVOKED + + #ifdef IDC_STATIC + #undef IDC_STATIC + #endif // IDC_STATIC + #define IDC_STATIC (-1) + +#endif // !_INC_WINDOWS +#endif // RC_INVOKED + +#ifdef APSTUDIO_INVOKED + #define APSTUDIO_HIDDEN_SYMBOLS +#endif // APSTUDIO_INVOKED + +/////////////////////////////////////////////////////////////////////////////// +// ATL resource types + +#ifndef RC_INVOKED + #define RT_DLGINIT MAKEINTRESOURCE(240) + #define RT_TOOLBAR MAKEINTRESOURCE(241) +#endif // RC_INVOKED + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef APSTUDIO_INVOKED + #undef APSTUDIO_HIDDEN_SYMBOLS +#endif // APSTUDIO_INVOKED + +/////////////////////////////////////////////////////////////////////////////// +// Standard window components + +#define ID_SEPARATOR 0 // special separator value +#define ID_DEFAULT_PANE 0 // default status bar pane + +#ifndef RC_INVOKED // code only +// standard control bars (IDW = window ID) + #define ATL_IDW_TOOLBAR 0xE800 // main Toolbar for window + #define ATL_IDW_STATUS_BAR 0xE801 // Status bar window + #define ATL_IDW_COMMAND_BAR 0xE802 // Command bar window + +// parts of a frame window + #define ATL_IDW_CLIENT 0xE900 + #define ATL_IDW_PANE_FIRST 0xE900 // first pane (256 max) + #define ATL_IDW_PANE_LAST 0xE9FF + #define ATL_IDW_HSCROLL_FIRST 0xEA00 // first Horz scrollbar (16 max) + #define ATL_IDW_VSCROLL_FIRST 0xEA10 // first Vert scrollbar (16 max) + + #define ATL_IDW_SIZE_BOX 0xEA20 // size box for splitters + #define ATL_IDW_PANE_SAVE 0xEA21 // to shift ATL_IDW_PANE_FIRST + +// bands for a rebar + #define ATL_IDW_BAND_FIRST 0xEB00 + #define ATL_IDW_BAND_LAST 0xEBFF +#endif // !RC_INVOKED + +/////////////////////////////////////////////////////////////////////////////// +// Standard Commands + +// File commands +#define ID_FILE_NEW 0xE100 +#define ID_FILE_OPEN 0xE101 +#define ID_FILE_CLOSE 0xE102 +#define ID_FILE_SAVE 0xE103 +#define ID_FILE_SAVE_AS 0xE104 +#define ID_FILE_PAGE_SETUP 0xE105 +#define ID_FILE_PRINT_SETUP 0xE106 +#define ID_FILE_PRINT 0xE107 +#define ID_FILE_PRINT_DIRECT 0xE108 +#define ID_FILE_PRINT_PREVIEW 0xE109 +#define ID_FILE_UPDATE 0xE10A +#define ID_FILE_SAVE_COPY_AS 0xE10B +#define ID_FILE_SEND_MAIL 0xE10C + +#define ID_FILE_MRU_FIRST 0xE110 +#define ID_FILE_MRU_FILE1 0xE110 // range - 16 max +#define ID_FILE_MRU_FILE2 0xE111 +#define ID_FILE_MRU_FILE3 0xE112 +#define ID_FILE_MRU_FILE4 0xE113 +#define ID_FILE_MRU_FILE5 0xE114 +#define ID_FILE_MRU_FILE6 0xE115 +#define ID_FILE_MRU_FILE7 0xE116 +#define ID_FILE_MRU_FILE8 0xE117 +#define ID_FILE_MRU_FILE9 0xE118 +#define ID_FILE_MRU_FILE10 0xE119 +#define ID_FILE_MRU_FILE11 0xE11A +#define ID_FILE_MRU_FILE12 0xE11B +#define ID_FILE_MRU_FILE13 0xE11C +#define ID_FILE_MRU_FILE14 0xE11D +#define ID_FILE_MRU_FILE15 0xE11E +#define ID_FILE_MRU_FILE16 0xE11F +#define ID_FILE_MRU_LAST 0xE11F + +// Edit commands +#define ID_EDIT_CLEAR 0xE120 +#define ID_EDIT_CLEAR_ALL 0xE121 +#define ID_EDIT_COPY 0xE122 +#define ID_EDIT_CUT 0xE123 +#define ID_EDIT_FIND 0xE124 +#define ID_EDIT_PASTE 0xE125 +#define ID_EDIT_PASTE_LINK 0xE126 +#define ID_EDIT_PASTE_SPECIAL 0xE127 +#define ID_EDIT_REPEAT 0xE128 +#define ID_EDIT_REPLACE 0xE129 +#define ID_EDIT_SELECT_ALL 0xE12A +#define ID_EDIT_UNDO 0xE12B +#define ID_EDIT_REDO 0xE12C +#define ID_EDIT_DELETE ID_EDIT_CLEAR +#define ID_EDIT_FIND_NEXT ID_EDIT_REPEAT +#define ID_EDIT_FIND_PREVIOUS 0xE12D + +// Window commands +#define ID_WINDOW_NEW 0xE130 +#define ID_WINDOW_ARRANGE 0xE131 +#define ID_WINDOW_CASCADE 0xE132 +#define ID_WINDOW_TILE_HORZ 0xE133 +#define ID_WINDOW_TILE_VERT 0xE134 +#define ID_WINDOW_SPLIT 0xE135 +#ifndef RC_INVOKED // code only + #define ATL_IDM_WINDOW_FIRST 0xE130 + #define ATL_IDM_WINDOW_LAST 0xE13F + #define ATL_IDM_FIRST_MDICHILD 0xFF00 // window list starts here + #define ATL_IDM_LAST_MDICHILD 0xFFFD +#endif // !RC_INVOKED +// TabView +#define ID_WINDOW_TABFIRST 0xFF00 // = ATL_IDM_FIRST_MDICHILD +#define ID_WINDOW_TABLAST 0xFFFD +#define ID_WINDOW_SHOWTABLIST 0xFFFE + +// Help and App commands +#define ID_APP_ABOUT 0xE140 +#define ID_APP_EXIT 0xE141 +#define ID_HELP_INDEX 0xE142 +#define ID_HELP_FINDER 0xE143 +#define ID_HELP_USING 0xE144 +#define ID_CONTEXT_HELP 0xE145 // shift-F1 +// special commands for processing help +#define ID_HELP 0xE146 // first attempt for F1 +#define ID_DEFAULT_HELP 0xE147 // last attempt + +// Misc +#define ID_NEXT_PANE 0xE150 +#define ID_PREV_PANE 0xE151 +#define ID_PANE_CLOSE 0xE152 +#define ID_PANE_NEXT ID_NEXT_PANE +#define ID_PANE_PREVIOUS ID_PREV_PANE + +// Format +#define ID_FORMAT_FONT 0xE160 + +// Scroll +#define ID_SCROLL_UP 0xE170 +#define ID_SCROLL_DOWN 0xE171 +#define ID_SCROLL_PAGE_UP 0xE172 +#define ID_SCROLL_PAGE_DOWN 0xE173 +#define ID_SCROLL_TOP 0xE174 +#define ID_SCROLL_BOTTOM 0xE175 +#define ID_SCROLL_LEFT 0xE176 +#define ID_SCROLL_RIGHT 0xE177 +#define ID_SCROLL_PAGE_LEFT 0xE178 +#define ID_SCROLL_PAGE_RIGHT 0xE179 +#define ID_SCROLL_ALL_LEFT 0xE17A +#define ID_SCROLL_ALL_RIGHT 0xE17B + +// OLE commands +#define ID_OLE_INSERT_NEW 0xE200 +#define ID_OLE_EDIT_LINKS 0xE201 +#define ID_OLE_EDIT_CONVERT 0xE202 +#define ID_OLE_EDIT_CHANGE_ICON 0xE203 +#define ID_OLE_EDIT_PROPERTIES 0xE204 +#define ID_OLE_VERB_FIRST 0xE210 // range - 16 max +#ifndef RC_INVOKED // code only + #define ID_OLE_VERB_LAST 0xE21F +#endif // !RC_INVOKED + +// View commands (same number used as IDW used for toolbar and status bar) +#define ID_VIEW_TOOLBAR 0xE800 +#define ID_VIEW_STATUS_BAR 0xE801 +#define ID_VIEW_REFRESH 0xE803 +#define ID_VIEW_RIBBON 0xE804 + +/////////////////////////////////////////////////////////////////////////////// +// Standard control IDs + +#ifdef IDC_STATIC + #undef IDC_STATIC +#endif // IDC_STATIC +#define IDC_STATIC (-1) // all static controls + +/////////////////////////////////////////////////////////////////////////////// +// Standard string error/warnings + +// idle status bar message +#define ATL_IDS_IDLEMESSAGE 0xE001 + +#ifndef RC_INVOKED // code only + #define ATL_IDS_SCFIRST 0xEF00 +#endif // !RC_INVOKED + +#define ATL_IDS_SCSIZE 0xEF00 +#define ATL_IDS_SCMOVE 0xEF01 +#define ATL_IDS_SCMINIMIZE 0xEF02 +#define ATL_IDS_SCMAXIMIZE 0xEF03 +#define ATL_IDS_SCNEXTWINDOW 0xEF04 +#define ATL_IDS_SCPREVWINDOW 0xEF05 +#define ATL_IDS_SCCLOSE 0xEF06 +#define ATL_IDS_SCRESTORE 0xEF12 +#define ATL_IDS_SCTASKLIST 0xEF13 + +#define ATL_IDS_MDICHILD 0xEF1F +#define ATL_IDS_MRU_FILE 0xEFDA + +/////////////////////////////////////////////////////////////////////////////// +// Misc. control IDs + +// Property Sheet control id's (determined with Spy++) +#define ID_APPLY_NOW 0x3021 +#define ID_WIZBACK 0x3023 +#define ID_WIZNEXT 0x3024 +#define ID_WIZFINISH 0x3025 +#define ATL_IDC_TAB_CONTROL 0x3020 + +#endif // __ATLRES_H__ diff --git a/Examples/WhisperDesktop/Utils/WTL/atluser.h b/Examples/WhisperDesktop/Utils/WTL/atluser.h new file mode 100644 index 0000000..9f8e0f4 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/WTL/atluser.h @@ -0,0 +1,1231 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLUSER_H__ +#define __ATLUSER_H__ + +#pragma once + +#ifndef __ATLAPP_H__ + #error atluser.h requires atlapp.h to be included first +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Classes in this file: +// +// CMenuItemInfo +// CMenuT<t_bManaged> +// CAcceleratorT<t_bManaged> +// CIconT<t_bManaged> +// CCursorT<t_bManaged> +// CResource +// +// Global functions: +// AtlMessageBox() +// +// AtlLoadAccelerators() +// AtlLoadMenu() +// AtlLoadBitmap() +// AtlLoadSysBitmap() +// AtlLoadCursor() +// AtlLoadSysCursor() +// AtlLoadIcon() +// AtlLoadSysIcon() +// AtlLoadBitmapImage() +// AtlLoadCursorImage() +// AtlLoadIconImage() +// AtlLoadSysBitmapImage() +// AtlLoadSysCursorImage() +// AtlLoadSysIconImage() +// AtlLoadString() + + +namespace WTL +{ + +/////////////////////////////////////////////////////////////////////////////// +// AtlMessageBox - accepts both memory and resource based strings + +inline int AtlMessageBox(HWND hWndOwner, ATL::_U_STRINGorID message, ATL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uType = MB_OK | MB_ICONINFORMATION) +{ + ATLASSERT((hWndOwner == NULL) || ::IsWindow(hWndOwner)); + + LPTSTR lpstrMessage = NULL; + if(IS_INTRESOURCE(message.m_lpstr)) + { + for(int nLen = 256; ; nLen *= 2) + { + ATLTRY(lpstrMessage = new TCHAR[nLen]); + if(lpstrMessage == NULL) + { + ATLASSERT(FALSE); + return 0; + } + int nRes = ::LoadString(ModuleHelper::GetResourceInstance(), LOWORD(message.m_lpstr), lpstrMessage, nLen); + if(nRes < nLen - 1) + break; + delete [] lpstrMessage; + lpstrMessage = NULL; + } + + message.m_lpstr = lpstrMessage; + } + + LPTSTR lpstrTitle = NULL; + if(IS_INTRESOURCE(title.m_lpstr) && (LOWORD(title.m_lpstr) != 0)) + { + for(int nLen = 256; ; nLen *= 2) + { + ATLTRY(lpstrTitle = new TCHAR[nLen]); + if(lpstrTitle == NULL) + { + ATLASSERT(FALSE); + return 0; + } + int nRes = ::LoadString(ModuleHelper::GetResourceInstance(), LOWORD(title.m_lpstr), lpstrTitle, nLen); + if(nRes < nLen - 1) + break; + delete [] lpstrTitle; + lpstrTitle = NULL; + } + + title.m_lpstr = lpstrTitle; + } + + int nRet = ::MessageBox(hWndOwner, message.m_lpstr, title.m_lpstr, uType); + + delete [] lpstrMessage; + delete [] lpstrTitle; + + return nRet; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CMenu + +class CMenuItemInfo : public MENUITEMINFO +{ +public: + CMenuItemInfo() + { + memset(this, 0, sizeof(MENUITEMINFO)); + cbSize = sizeof(MENUITEMINFO); + } +}; + + +// forward declarations +template <bool t_bManaged> class CMenuT; +typedef CMenuT<false> CMenuHandle; +typedef CMenuT<true> CMenu; + + +template <bool t_bManaged> +class CMenuT +{ +public: +// Data members + HMENU m_hMenu; + +// Constructor/destructor/operators + CMenuT(HMENU hMenu = NULL) : m_hMenu(hMenu) + { } + + ~CMenuT() + { + if(t_bManaged && (m_hMenu != NULL)) + DestroyMenu(); + } + + CMenuT<t_bManaged>& operator =(HMENU hMenu) + { + Attach(hMenu); + return *this; + } + + void Attach(HMENU hMenuNew) + { + ATLASSERT(::IsMenu(hMenuNew)); + if(t_bManaged && (m_hMenu != NULL) && (m_hMenu != hMenuNew)) + ::DestroyMenu(m_hMenu); + m_hMenu = hMenuNew; + } + + HMENU Detach() + { + HMENU hMenu = m_hMenu; + m_hMenu = NULL; + return hMenu; + } + + operator HMENU() const { return m_hMenu; } + + bool IsNull() const { return (m_hMenu == NULL); } + + BOOL IsMenu() const + { + return ::IsMenu(m_hMenu); + } + +// Create/destroy methods + BOOL CreateMenu() + { + ATLASSERT(m_hMenu == NULL); + m_hMenu = ::CreateMenu(); + return (m_hMenu != NULL) ? TRUE : FALSE; + } + + BOOL CreatePopupMenu() + { + ATLASSERT(m_hMenu == NULL); + m_hMenu = ::CreatePopupMenu(); + return (m_hMenu != NULL) ? TRUE : FALSE; + } + + BOOL LoadMenu(ATL::_U_STRINGorID menu) + { + ATLASSERT(m_hMenu == NULL); + m_hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr); + return (m_hMenu != NULL) ? TRUE : FALSE; + } + + BOOL LoadMenuIndirect(const void* lpMenuTemplate) + { + ATLASSERT(m_hMenu == NULL); + m_hMenu = ::LoadMenuIndirect(lpMenuTemplate); + return (m_hMenu != NULL) ? TRUE : FALSE; + } + + BOOL DestroyMenu() + { + if (m_hMenu == NULL) + return FALSE; + BOOL bRet = ::DestroyMenu(m_hMenu); + if(bRet) + m_hMenu = NULL; + return bRet; + } + +// Menu Operations + BOOL DeleteMenu(UINT nPosition, UINT nFlags) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::DeleteMenu(m_hMenu, nPosition, nFlags); + } + + BOOL TrackPopupMenu(UINT nFlags, int x, int y, HWND hWnd, LPCRECT lpRect = NULL) + { + ATLASSERT(::IsMenu(m_hMenu)); + x = _FixTrackMenuPopupX(x, y); + return ::TrackPopupMenu(m_hMenu, nFlags, x, y, 0, hWnd, lpRect); + } + + BOOL TrackPopupMenuEx(UINT uFlags, int x, int y, HWND hWnd, LPTPMPARAMS lptpm = NULL) + { + ATLASSERT(::IsMenu(m_hMenu)); + x = _FixTrackMenuPopupX(x, y); + return ::TrackPopupMenuEx(m_hMenu, uFlags, x, y, hWnd, lptpm); + } + + // helper that fixes popup menu X position when it's off-screen + static int _FixTrackMenuPopupX(int x, int y) + { + POINT pt = { x, y }; + HMONITOR hMonitor = ::MonitorFromPoint(pt, MONITOR_DEFAULTTONULL); + if(hMonitor == NULL) + { + HMONITOR hMonitorNear = ::MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + if(hMonitorNear != NULL) + { + MONITORINFO mi = { sizeof(MONITORINFO) }; + if(::GetMonitorInfo(hMonitorNear, &mi) != FALSE) + { + if(x < mi.rcWork.left) + x = mi.rcWork.left; + else if(x > mi.rcWork.right) + x = mi.rcWork.right; + } + } + } + + return x; + } + + BOOL GetMenuInfo(LPMENUINFO lpMenuInfo) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuInfo(m_hMenu, lpMenuInfo); + } + + BOOL SetMenuInfo(LPCMENUINFO lpMenuInfo) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::SetMenuInfo(m_hMenu, lpMenuInfo); + } + +// Menu Item Operations + BOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::AppendMenu(m_hMenu, nFlags, nIDNewItem, lpszNewItem); + } + + BOOL AppendMenu(UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem) + { + ATLASSERT(::IsMenu(m_hMenu)); + ATLASSERT(::IsMenu(hSubMenu)); + return ::AppendMenu(m_hMenu, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem); + } + + BOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::AppendMenu(m_hMenu, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp); + } + + BOOL AppendMenu(UINT nFlags, HMENU hSubMenu, HBITMAP hBmp) + { + ATLASSERT(::IsMenu(m_hMenu)); + ATLASSERT(::IsMenu(hSubMenu)); + return ::AppendMenu(m_hMenu, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp); + } + + UINT CheckMenuItem(UINT nIDCheckItem, UINT nCheck) + { + ATLASSERT(::IsMenu(m_hMenu)); + return (UINT)::CheckMenuItem(m_hMenu, nIDCheckItem, nCheck); + } + + UINT EnableMenuItem(UINT nIDEnableItem, UINT nEnable) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::EnableMenuItem(m_hMenu, nIDEnableItem, nEnable); + } + + BOOL HiliteMenuItem(HWND hWnd, UINT uIDHiliteItem, UINT uHilite) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::HiliteMenuItem(hWnd, m_hMenu, uIDHiliteItem, uHilite); + } + + int GetMenuItemCount() const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuItemCount(m_hMenu); + } + + UINT GetMenuItemID(int nPos) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuItemID(m_hMenu, nPos); + } + + UINT GetMenuState(UINT nID, UINT nFlags) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuState(m_hMenu, nID, nFlags); + } + + int GetMenuString(UINT nIDItem, LPTSTR lpString, int nMaxCount, UINT nFlags) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuString(m_hMenu, nIDItem, lpString, nMaxCount, nFlags); + } + + int GetMenuStringLen(UINT nIDItem, UINT nFlags) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuString(m_hMenu, nIDItem, NULL, 0, nFlags); + } + + BOOL GetMenuString(UINT nIDItem, BSTR& bstrText, UINT nFlags) const + { + USES_CONVERSION; + ATLASSERT(::IsMenu(m_hMenu)); + ATLASSERT(bstrText == NULL); + + int nLen = GetMenuStringLen(nIDItem, nFlags); + if(nLen == 0) + { + bstrText = ::SysAllocString(OLESTR("")); + return (bstrText != NULL) ? TRUE : FALSE; + } + + nLen++; // increment to include terminating NULL char + ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff; + LPTSTR lpszText = buff.Allocate(nLen); + if(lpszText == NULL) + return FALSE; + + if(!GetMenuString(nIDItem, lpszText, nLen, nFlags)) + return FALSE; + + bstrText = ::SysAllocString(T2OLE(lpszText)); + return (bstrText != NULL) ? TRUE : FALSE; + } + +#ifdef __ATLSTR_H__ + int GetMenuString(UINT nIDItem, ATL::CString& strText, UINT nFlags) const + { + ATLASSERT(::IsMenu(m_hMenu)); + + int nLen = GetMenuStringLen(nIDItem, nFlags); + if(nLen == 0) + return 0; + + nLen++; // increment to include terminating NULL char + LPTSTR lpstr = strText.GetBufferSetLength(nLen); + if(lpstr == NULL) + return 0; + int nRet = GetMenuString(nIDItem, lpstr, nLen, nFlags); + strText.ReleaseBuffer(); + return nRet; + } +#endif // __ATLSTR_H__ + + CMenuHandle GetSubMenu(int nPos) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return CMenuHandle(::GetSubMenu(m_hMenu, nPos)); + } + + BOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::InsertMenu(m_hMenu, nPosition, nFlags, nIDNewItem, lpszNewItem); + } + + BOOL InsertMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem) + { + ATLASSERT(::IsMenu(m_hMenu)); + ATLASSERT(::IsMenu(hSubMenu)); + return ::InsertMenu(m_hMenu, nPosition, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem); + } + + BOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::InsertMenu(m_hMenu, nPosition, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp); + } + + BOOL InsertMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, HBITMAP hBmp) + { + ATLASSERT(::IsMenu(m_hMenu)); + ATLASSERT(::IsMenu(hSubMenu)); + return ::InsertMenu(m_hMenu, nPosition, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp); + } + + BOOL ModifyMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::ModifyMenu(m_hMenu, nPosition, nFlags, nIDNewItem, lpszNewItem); + } + + BOOL ModifyMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem) + { + ATLASSERT(::IsMenu(m_hMenu)); + ATLASSERT(::IsMenu(hSubMenu)); + return ::ModifyMenu(m_hMenu, nPosition, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem); + } + + BOOL ModifyMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::ModifyMenu(m_hMenu, nPosition, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp); + } + + BOOL ModifyMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, HBITMAP hBmp) + { + ATLASSERT(::IsMenu(m_hMenu)); + ATLASSERT(::IsMenu(hSubMenu)); + return ::ModifyMenu(m_hMenu, nPosition, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp); + } + + BOOL RemoveMenu(UINT nPosition, UINT nFlags) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::RemoveMenu(m_hMenu, nPosition, nFlags); + } + + BOOL SetMenuItemBitmaps(UINT nPosition, UINT nFlags, HBITMAP hBmpUnchecked, HBITMAP hBmpChecked) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::SetMenuItemBitmaps(m_hMenu, nPosition, nFlags, hBmpUnchecked, hBmpChecked); + } + + BOOL CheckMenuRadioItem(UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::CheckMenuRadioItem(m_hMenu, nIDFirst, nIDLast, nIDItem, nFlags); + } + + BOOL GetMenuItemInfo(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return (BOOL)::GetMenuItemInfo(m_hMenu, uItem, bByPosition, lpmii); + } + + BOOL SetMenuItemInfo(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii) + { + ATLASSERT(::IsMenu(m_hMenu)); + return (BOOL)::SetMenuItemInfo(m_hMenu, uItem, bByPosition, lpmii); + } + + BOOL InsertMenuItem(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii) + { + ATLASSERT(::IsMenu(m_hMenu)); + return (BOOL)::InsertMenuItem(m_hMenu, uItem, bByPosition, lpmii); + } + + UINT GetMenuDefaultItem(BOOL bByPosition = FALSE, UINT uFlags = 0U) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuDefaultItem(m_hMenu, (UINT)bByPosition, uFlags); + } + + BOOL SetMenuDefaultItem(UINT uItem = (UINT)-1, BOOL bByPosition = FALSE) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::SetMenuDefaultItem(m_hMenu, uItem, (UINT)bByPosition); + } + + BOOL GetMenuItemRect(HWND hWnd, UINT uItem, LPRECT lprcItem) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuItemRect(hWnd, m_hMenu, uItem, lprcItem); + } + + int MenuItemFromPoint(HWND hWnd, POINT point) const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::MenuItemFromPoint(hWnd, m_hMenu, point); + } + +// Context Help Functions + BOOL SetMenuContextHelpId(DWORD dwContextHelpId) + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::SetMenuContextHelpId(m_hMenu, dwContextHelpId); + } + + DWORD GetMenuContextHelpId() const + { + ATLASSERT(::IsMenu(m_hMenu)); + return ::GetMenuContextHelpId(m_hMenu); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CAccelerator + +template <bool t_bManaged> +class CAcceleratorT +{ +public: + HACCEL m_hAccel; + +// Constructor/destructor/operators + CAcceleratorT(HACCEL hAccel = NULL) : m_hAccel(hAccel) + { } + + ~CAcceleratorT() + { + if(t_bManaged && (m_hAccel != NULL)) + ::DestroyAcceleratorTable(m_hAccel); + } + + CAcceleratorT<t_bManaged>& operator =(HACCEL hAccel) + { + Attach(hAccel); + return *this; + } + + void Attach(HACCEL hAccel) + { + if(t_bManaged && (m_hAccel != NULL)) + ::DestroyAcceleratorTable(m_hAccel); + m_hAccel = hAccel; + } + + HACCEL Detach() + { + HACCEL hAccel = m_hAccel; + m_hAccel = NULL; + return hAccel; + } + + operator HACCEL() const { return m_hAccel; } + + bool IsNull() const { return m_hAccel == NULL; } + +// Create/destroy methods + HACCEL LoadAccelerators(ATL::_U_STRINGorID accel) + { + ATLASSERT(m_hAccel == NULL); + m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), accel.m_lpstr); + return m_hAccel; + } + + HACCEL CreateAcceleratorTable(LPACCEL pAccel, int cEntries) + { + ATLASSERT(m_hAccel == NULL); + ATLASSERT(pAccel != NULL); + m_hAccel = ::CreateAcceleratorTable(pAccel, cEntries); + return m_hAccel; + } + + void DestroyObject() + { + if(m_hAccel != NULL) + { + ::DestroyAcceleratorTable(m_hAccel); + m_hAccel = NULL; + } + } + +// Operations + int CopyAcceleratorTable(LPACCEL lpAccelDst, int cEntries) + { + ATLASSERT(m_hAccel != NULL); + ATLASSERT(lpAccelDst != NULL); + return ::CopyAcceleratorTable(m_hAccel, lpAccelDst, cEntries); + } + + int GetEntriesCount() const + { + ATLASSERT(m_hAccel != NULL); + return ::CopyAcceleratorTable(m_hAccel, NULL, 0); + } + + BOOL TranslateAccelerator(HWND hWnd, LPMSG pMsg) + { + ATLASSERT(m_hAccel != NULL); + ATLASSERT(::IsWindow(hWnd)); + ATLASSERT(pMsg != NULL); + return ::TranslateAccelerator(hWnd, m_hAccel, pMsg); + } +}; + +typedef CAcceleratorT<false> CAcceleratorHandle; +typedef CAcceleratorT<true> CAccelerator; + + +/////////////////////////////////////////////////////////////////////////////// +// CIcon + +template <bool t_bManaged> +class CIconT +{ +public: + HICON m_hIcon; + +// Constructor/destructor/operators + CIconT(HICON hIcon = NULL) : m_hIcon(hIcon) + { } + + ~CIconT() + { + if(t_bManaged && (m_hIcon != NULL)) + ::DestroyIcon(m_hIcon); + } + + CIconT<t_bManaged>& operator =(HICON hIcon) + { + Attach(hIcon); + return *this; + } + + void Attach(HICON hIcon) + { + if(t_bManaged && (m_hIcon != NULL)) + ::DestroyIcon(m_hIcon); + m_hIcon = hIcon; + } + + HICON Detach() + { + HICON hIcon = m_hIcon; + m_hIcon = NULL; + return hIcon; + } + + operator HICON() const { return m_hIcon; } + + bool IsNull() const { return m_hIcon == NULL; } + +// Create/destroy methods + HICON LoadIcon(ATL::_U_STRINGorID icon) + { + ATLASSERT(m_hIcon == NULL); + m_hIcon = ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr); + return m_hIcon; + } + + HICON LoadIcon(ATL::_U_STRINGorID icon, int cxDesired, int cyDesired, UINT fuLoad = 0) + { + ATLASSERT(m_hIcon == NULL); + m_hIcon = (HICON) ::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad); + return m_hIcon; + } + + HICON LoadOEMIcon(LPCTSTR lpstrIconName) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(IsOEMIcon(lpstrIconName)); + m_hIcon = ::LoadIcon(NULL, lpstrIconName); + return m_hIcon; + } + + HICON CreateIcon(int nWidth, int nHeight, BYTE cPlanes, BYTE cBitsPixel, CONST BYTE* lpbANDbits, CONST BYTE *lpbXORbits) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(lpbANDbits != NULL); + ATLASSERT(lpbXORbits != NULL); + m_hIcon = ::CreateIcon(ModuleHelper::GetResourceInstance(), nWidth, nHeight, cPlanes, cBitsPixel, lpbANDbits, lpbXORbits); + return m_hIcon; + } + + HICON CreateIconFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(pBits != NULL); + m_hIcon = ::CreateIconFromResource(pBits, dwResSize, TRUE, dwVersion); + return m_hIcon; + } + + HICON CreateIconFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(pbBits != NULL); + ATLASSERT(cbBits > 0); + m_hIcon = ::CreateIconFromResourceEx(pbBits, cbBits, TRUE, dwVersion, cxDesired, cyDesired, uFlags); + return m_hIcon; + } + + HICON CreateIconIndirect(PICONINFO pIconInfo) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(pIconInfo != NULL); + m_hIcon = ::CreateIconIndirect(pIconInfo); + return m_hIcon; + } + + HICON ExtractIcon(LPCTSTR lpszExeFileName, UINT nIconIndex) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(lpszExeFileName != NULL); + m_hIcon = ::ExtractIcon(ModuleHelper::GetModuleInstance(), lpszExeFileName, nIconIndex); + return m_hIcon; + } + + HICON ExtractAssociatedIcon(HINSTANCE hInst, LPTSTR lpIconPath, LPWORD lpiIcon) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(lpIconPath != NULL); + ATLASSERT(lpiIcon != NULL); + m_hIcon = ::ExtractAssociatedIcon(hInst, lpIconPath, lpiIcon); + return m_hIcon; + } + + BOOL DestroyIcon() + { + ATLASSERT(m_hIcon != NULL); + BOOL bRet = ::DestroyIcon(m_hIcon); + if(bRet != FALSE) + m_hIcon = NULL; + return bRet; + } + +// Operations + HICON CopyIcon() + { + ATLASSERT(m_hIcon != NULL); + return ::CopyIcon(m_hIcon); + } + + HICON DuplicateIcon() + { + ATLASSERT(m_hIcon != NULL); + return ::DuplicateIcon(NULL, m_hIcon); + } + + BOOL DrawIcon(HDC hDC, int x, int y) + { + ATLASSERT(m_hIcon != NULL); + return ::DrawIcon(hDC, x, y, m_hIcon); + } + + BOOL DrawIcon(HDC hDC, POINT pt) + { + ATLASSERT(m_hIcon != NULL); + return ::DrawIcon(hDC, pt.x, pt.y, m_hIcon); + } + + BOOL DrawIconEx(HDC hDC, int x, int y, int cxWidth, int cyWidth, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL) + { + ATLASSERT(m_hIcon != NULL); + return ::DrawIconEx(hDC, x, y, m_hIcon, cxWidth, cyWidth, uStepIfAniCur, hbrFlickerFreeDraw, uFlags); + } + + BOOL DrawIconEx(HDC hDC, POINT pt, SIZE size, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL) + { + ATLASSERT(m_hIcon != NULL); + return ::DrawIconEx(hDC, pt.x, pt.y, m_hIcon, size.cx, size.cy, uStepIfAniCur, hbrFlickerFreeDraw, uFlags); + } + + BOOL GetIconInfo(PICONINFO pIconInfo) const + { + ATLASSERT(m_hIcon != NULL); + ATLASSERT(pIconInfo != NULL); + return ::GetIconInfo(m_hIcon, pIconInfo); + } + +#if (_WIN32_WINNT >= 0x0600) + BOOL GetIconInfoEx(PICONINFOEX pIconInfo) const + { + ATLASSERT(m_hIcon != NULL); + ATLASSERT(pIconInfo != NULL); + return ::GetIconInfoEx(m_hIcon, pIconInfo); + } +#endif // (_WIN32_WINNT >= 0x0600) + +#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) + HRESULT LoadIconMetric(ATL::_U_STRINGorID icon, int lims) + { + ATLASSERT(m_hIcon == NULL); + USES_CONVERSION; + return ::LoadIconMetric(ModuleHelper::GetResourceInstance(), T2CW(icon.m_lpstr), lims, &m_hIcon); + } + + HRESULT LoadIconWithScaleDown(ATL::_U_STRINGorID icon, int cx, int cy) + { + ATLASSERT(m_hIcon == NULL); + USES_CONVERSION; + return ::LoadIconWithScaleDown(ModuleHelper::GetResourceInstance(), T2CW(icon.m_lpstr), cx, cy, &m_hIcon); + } + + HRESULT LoadOEMIconMetric(LPCTSTR lpstrIconName, int lims) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(IsOEMIcon(lpstrIconName)); + return ::LoadIconMetric(NULL, (LPCWSTR)lpstrIconName, lims, &m_hIcon); + } + + HRESULT LoadOEMIconWithScaleDown(LPCTSTR lpstrIconName, int cx, int cy) + { + ATLASSERT(m_hIcon == NULL); + ATLASSERT(IsOEMIcon(lpstrIconName)); + USES_CONVERSION; + return ::LoadIconWithScaleDown(NULL, (LPCWSTR)lpstrIconName, cx, cy, &m_hIcon); + } +#endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) + + // Helper + static bool IsOEMIcon(LPCTSTR lpstrIconName) + { +#if (WINVER >= 0x0600) + return ((lpstrIconName == IDI_APPLICATION) || (lpstrIconName == IDI_ASTERISK) || (lpstrIconName == IDI_EXCLAMATION) || + (lpstrIconName == IDI_HAND) || (lpstrIconName == IDI_QUESTION) || (lpstrIconName == IDI_WINLOGO) || + (lpstrIconName == IDI_SHIELD)); +#else // !(WINVER >= 0x0600) + return ((lpstrIconName == IDI_APPLICATION) || (lpstrIconName == IDI_ASTERISK) || (lpstrIconName == IDI_EXCLAMATION) || + (lpstrIconName == IDI_HAND) || (lpstrIconName == IDI_QUESTION) || (lpstrIconName == IDI_WINLOGO)); +#endif // !(WINVER >= 0x0600) + } +}; + +typedef CIconT<false> CIconHandle; +typedef CIconT<true> CIcon; + + +/////////////////////////////////////////////////////////////////////////////// +// CCursor + +// protect template member from a winuser.h macro +#ifdef CopyCursor + #undef CopyCursor +#endif + +template <bool t_bManaged> +class CCursorT +{ +public: + HCURSOR m_hCursor; + +// Constructor/destructor/operators + CCursorT(HCURSOR hCursor = NULL) : m_hCursor(hCursor) + { } + + ~CCursorT() + { + if(t_bManaged && (m_hCursor != NULL)) + DestroyCursor(); + } + + CCursorT<t_bManaged>& operator =(HCURSOR hCursor) + { + Attach(hCursor); + return *this; + } + + void Attach(HCURSOR hCursor) + { + if(t_bManaged && (m_hCursor != NULL)) + DestroyCursor(); + m_hCursor = hCursor; + } + + HCURSOR Detach() + { + HCURSOR hCursor = m_hCursor; + m_hCursor = NULL; + return hCursor; + } + + operator HCURSOR() const { return m_hCursor; } + + bool IsNull() const { return m_hCursor == NULL; } + +// Create/destroy methods + HCURSOR LoadCursor(ATL::_U_STRINGorID cursor) + { + ATLASSERT(m_hCursor == NULL); + m_hCursor = ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr); + return m_hCursor; + } + + HCURSOR LoadSysCursor(LPCTSTR lpstrCursorName) + { + ATLASSERT(m_hCursor == NULL); + ATLASSERT((lpstrCursorName == IDC_ARROW) || (lpstrCursorName == IDC_IBEAM) || (lpstrCursorName == IDC_WAIT) || + (lpstrCursorName == IDC_CROSS) || (lpstrCursorName == IDC_UPARROW) || (lpstrCursorName == IDC_SIZE) || + (lpstrCursorName == IDC_ICON) || (lpstrCursorName == IDC_SIZENWSE) || (lpstrCursorName == IDC_SIZENESW) || + (lpstrCursorName == IDC_SIZEWE) || (lpstrCursorName == IDC_SIZENS) || (lpstrCursorName == IDC_SIZEALL) || + (lpstrCursorName == IDC_NO) || (lpstrCursorName == IDC_APPSTARTING) || (lpstrCursorName == IDC_HELP) || + (lpstrCursorName == IDC_HAND)); + m_hCursor = ::LoadCursor(NULL, lpstrCursorName); + return m_hCursor; + } + + // deprecated + HCURSOR LoadOEMCursor(LPCTSTR lpstrCursorName) + { + return LoadSysCursor(lpstrCursorName); + } + + HCURSOR LoadCursor(ATL::_U_STRINGorID cursor, int cxDesired, int cyDesired, UINT fuLoad = 0) + { + ATLASSERT(m_hCursor == NULL); + m_hCursor = (HCURSOR) ::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad); + return m_hCursor; + } + + HCURSOR LoadCursorFromFile(LPCTSTR pstrFilename) + { + ATLASSERT(m_hCursor == NULL); + ATLASSERT(pstrFilename != NULL); + m_hCursor = ::LoadCursorFromFile(pstrFilename); + return m_hCursor; + } + + HCURSOR CreateCursor(int xHotSpot, int yHotSpot, int nWidth, int nHeight, CONST VOID *pvANDPlane, CONST VOID *pvXORPlane) + { + ATLASSERT(m_hCursor == NULL); + m_hCursor = ::CreateCursor(ModuleHelper::GetResourceInstance(), xHotSpot, yHotSpot, nWidth, nHeight, pvANDPlane, pvXORPlane); + return m_hCursor; + } + + HCURSOR CreateCursorFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000) + { + ATLASSERT(m_hCursor == NULL); + ATLASSERT(pBits != NULL); + m_hCursor = (HCURSOR)::CreateIconFromResource(pBits, dwResSize, FALSE, dwVersion); + return m_hCursor; + } + + HCURSOR CreateCursorFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR) + { + ATLASSERT(m_hCursor == NULL); + ATLASSERT(pbBits != NULL); + ATLASSERT(cbBits > 0); + m_hCursor = (HCURSOR)::CreateIconFromResourceEx(pbBits, cbBits, FALSE, dwVersion, cxDesired, cyDesired, uFlags); + return m_hCursor; + } + + BOOL DestroyCursor() + { + ATLASSERT(m_hCursor != NULL); + BOOL bRet = ::DestroyCursor(m_hCursor); + if(bRet != FALSE) + m_hCursor = NULL; + return bRet; + } + +// Operations + HCURSOR CopyCursor() + { + ATLASSERT(m_hCursor != NULL); + return (HCURSOR)::CopyIcon((HICON)m_hCursor); + } + + BOOL GetCursorInfo(LPCURSORINFO pCursorInfo) + { + ATLASSERT(m_hCursor != NULL); + ATLASSERT(pCursorInfo != NULL); + return ::GetCursorInfo(pCursorInfo); + } +}; + +typedef CCursorT<false> CCursorHandle; +typedef CCursorT<true> CCursor; + + +/////////////////////////////////////////////////////////////////////////////// +// CResource - Wraps a generic Windows resource. +// Use it with custom resource types other than the +// standard RT_CURSOR, RT_BITMAP, etc. + +class CResource +{ +public: + HGLOBAL m_hGlobal; + HRSRC m_hResource; + +// Constructor/destructor + CResource() : m_hGlobal(NULL), m_hResource(NULL) + { } + + ~CResource() + { + Release(); + } + +// Load methods + bool Load(ATL::_U_STRINGorID Type, ATL::_U_STRINGorID ID) + { + ATLASSERT(m_hResource == NULL); + ATLASSERT(m_hGlobal == NULL); + + m_hResource = ::FindResource(ModuleHelper::GetResourceInstance(), ID.m_lpstr, Type.m_lpstr); + if(m_hResource == NULL) + return false; + + m_hGlobal = ::LoadResource(ModuleHelper::GetResourceInstance(), m_hResource); + if(m_hGlobal == NULL) + { + m_hResource = NULL; + return false; + } + + return true; + } + + bool LoadEx(ATL::_U_STRINGorID ID, ATL::_U_STRINGorID Type, WORD wLanguage) + { + ATLASSERT(m_hResource == NULL); + ATLASSERT(m_hGlobal == NULL); + + m_hResource = ::FindResourceEx(ModuleHelper::GetResourceInstance(), Type.m_lpstr, ID.m_lpstr, wLanguage); + if(m_hResource == NULL) + return false; + + m_hGlobal = ::LoadResource(ModuleHelper::GetResourceInstance(), m_hResource); + if(m_hGlobal == NULL) + { + m_hResource = NULL; + return false; + } + + return true; + } + +// Misc. operations + DWORD GetSize() const + { + ATLASSERT(m_hResource != NULL); + return ::SizeofResource(ModuleHelper::GetResourceInstance(), m_hResource); + } + + LPVOID Lock() + { + ATLASSERT(m_hResource != NULL); + ATLASSERT(m_hGlobal != NULL); + LPVOID pVoid = ::LockResource(m_hGlobal); + ATLASSERT(pVoid != NULL); + return pVoid; + } + + void Release() + { + if(m_hGlobal != NULL) + { + FreeResource(m_hGlobal); + m_hGlobal = NULL; + m_hResource = NULL; + } + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Toolbar resource descriptor + +struct _AtlToolBarData +{ + WORD wVersion; + WORD wWidth; + WORD wHeight; + WORD wItemCount; + + WORD* items() + { return (WORD*)(this+1); } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Global functions for loading resources + +inline HACCEL AtlLoadAccelerators(ATL::_U_STRINGorID table) +{ + return ::LoadAccelerators(ModuleHelper::GetResourceInstance(), table.m_lpstr); +} + +inline HMENU AtlLoadMenu(ATL::_U_STRINGorID menu) +{ + return ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr); +} + +inline HBITMAP AtlLoadBitmap(ATL::_U_STRINGorID bitmap) +{ + return ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr); +} + +#ifdef OEMRESOURCE +inline HBITMAP AtlLoadSysBitmap(ATL::_U_STRINGorID bitmap) +{ +#ifdef _DEBUG + WORD wID = LOWORD(bitmap.m_lpstr); + ATLASSERT((wID >= 32734) && (wID <= 32767)); +#endif // _DEBUG + return ::LoadBitmap(NULL, bitmap.m_lpstr); +} +#endif // OEMRESOURCE + +inline HCURSOR AtlLoadCursor(ATL::_U_STRINGorID cursor) +{ + return ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr); +} + +inline HCURSOR AtlLoadSysCursor(LPCTSTR lpCursorName) +{ + ATLASSERT((lpCursorName == IDC_ARROW) || (lpCursorName == IDC_IBEAM) || (lpCursorName == IDC_WAIT) || + (lpCursorName == IDC_CROSS) || (lpCursorName == IDC_UPARROW) || (lpCursorName == IDC_SIZE) || + (lpCursorName == IDC_ICON) || (lpCursorName == IDC_SIZENWSE) || (lpCursorName == IDC_SIZENESW) || + (lpCursorName == IDC_SIZEWE) || (lpCursorName == IDC_SIZENS) || (lpCursorName == IDC_SIZEALL) || + (lpCursorName == IDC_NO) || (lpCursorName == IDC_APPSTARTING) || (lpCursorName == IDC_HELP) || + (lpCursorName == IDC_HAND)); + return ::LoadCursor(NULL, lpCursorName); +} + +inline HICON AtlLoadIcon(ATL::_U_STRINGorID icon) +{ + return ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr); +} + +inline HICON AtlLoadSysIcon(LPCTSTR lpIconName) +{ +#if (WINVER >= 0x0600) + ATLASSERT((lpIconName == IDI_APPLICATION) || (lpIconName == IDI_ASTERISK) || (lpIconName == IDI_EXCLAMATION) || + (lpIconName == IDI_HAND) || (lpIconName == IDI_QUESTION) || (lpIconName == IDI_WINLOGO) || + (lpIconName == IDI_SHIELD)); +#else // !(WINVER >= 0x0600) + ATLASSERT((lpIconName == IDI_APPLICATION) || (lpIconName == IDI_ASTERISK) || (lpIconName == IDI_EXCLAMATION) || + (lpIconName == IDI_HAND) || (lpIconName == IDI_QUESTION) || (lpIconName == IDI_WINLOGO)); +#endif // !(WINVER >= 0x0600) + return ::LoadIcon(NULL, lpIconName); +} + +inline HBITMAP AtlLoadBitmapImage(ATL::_U_STRINGorID bitmap, UINT fuLoad = LR_DEFAULTCOLOR) +{ + return (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, IMAGE_BITMAP, 0, 0, fuLoad); +} + +inline HCURSOR AtlLoadCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) +{ + return (HCURSOR)::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad); +} + +inline HICON AtlLoadIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) +{ + return (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad); +} + +#ifdef OEMRESOURCE +inline HBITMAP AtlLoadSysBitmapImage(WORD wBitmapID, UINT fuLoad = LR_DEFAULTCOLOR) +{ + ATLASSERT((wBitmapID >= 32734) && (wBitmapID <= 32767)); + ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file + return (HBITMAP)::LoadImage(NULL, MAKEINTRESOURCE(wBitmapID), IMAGE_BITMAP, 0, 0, fuLoad); +} +#endif // OEMRESOURCE + +inline HCURSOR AtlLoadSysCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) +{ +#ifdef _DEBUG + WORD wID = LOWORD(cursor.m_lpstr); + ATLASSERT(((wID >= 32512) && (wID <= 32516)) || ((wID >= 32640) && (wID <= 32648)) || (wID == 32650) || (wID == 32651)); + ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file +#endif // _DEBUG + return (HCURSOR)::LoadImage(NULL, cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad); +} + +inline HICON AtlLoadSysIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) +{ +#ifdef _DEBUG + WORD wID = LOWORD(icon.m_lpstr); + ATLASSERT((wID >= 32512) && (wID <= 32517)); + ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file +#endif // _DEBUG + return (HICON)::LoadImage(NULL, icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad); +} + +inline bool AtlLoadString(UINT uID, BSTR& bstrText) +{ + USES_CONVERSION; + ATLASSERT(bstrText == NULL); + + LPTSTR lpstrText = NULL; + int nRes = 0; + for(int nLen = 256; ; nLen *= 2) + { + ATLTRY(lpstrText = new TCHAR[nLen]); + if(lpstrText == NULL) + break; + nRes = ::LoadString(ModuleHelper::GetResourceInstance(), uID, lpstrText, nLen); + if(nRes < nLen - 1) + break; + delete [] lpstrText; + lpstrText = NULL; + } + + if(lpstrText != NULL) + { + if(nRes != 0) + bstrText = ::SysAllocString(T2OLE(lpstrText)); + delete [] lpstrText; + } + + return (bstrText != NULL) ? true : false; +} + +} // namespace WTL + +#endif // __ATLUSER_H__ diff --git a/Examples/WhisperDesktop/Utils/WTL/atlwinx.h b/Examples/WhisperDesktop/Utils/WTL/atlwinx.h new file mode 100644 index 0000000..b89c513 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/WTL/atlwinx.h @@ -0,0 +1,623 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLWINX_H__ +#define __ATLWINX_H__ + +#pragma once + +#ifndef __ATLAPP_H__ + #error atlwinx.h requires atlapp.h to be included first +#endif + +#include <atlwin.h> + + +/////////////////////////////////////////////////////////////////////////////// +// Classes in this file: +// +// CWindowEx + + +///////////////////////////////////////////////////////////////////////////// +// Additional macros needed for template classes + +#ifndef DECLARE_WND_CLASS_EX2 + #define DECLARE_WND_CLASS_EX2(WndClassName, EnclosingClass, style, bkgnd) \ + static ATL::CWndClassInfo& GetWndClassInfo() \ + { \ + static ATL::CWndClassInfo wc = \ + { \ + { sizeof(WNDCLASSEX), style, EnclosingClass::StartWindowProc, \ + 0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \ + NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \ + }; \ + return wc; \ + } +#endif // DECLARE_WND_CLASS_EX2 + +#ifndef DECLARE_WND_SUPERCLASS2 + #define DECLARE_WND_SUPERCLASS2(WndClassName, EnclosingClass, OrigWndClassName) \ + static ATL::CWndClassInfo& GetWndClassInfo() \ + { \ + static ATL::CWndClassInfo wc = \ + { \ + { sizeof(WNDCLASSEX), 0, EnclosingClass::StartWindowProc, \ + 0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \ + OrigWndClassName, NULL, NULL, TRUE, 0, _T("") \ + }; \ + return wc; \ + } +#endif // DECLARE_WND_SUPERCLASS2 + + +/////////////////////////////////////////////////////////////////////////////// +// Command Chaining Macros + +#define CHAIN_COMMANDS(theChainClass) \ + if(uMsg == WM_COMMAND) \ + CHAIN_MSG_MAP(theChainClass) + +#define CHAIN_COMMANDS_ALT(theChainClass, msgMapID) \ + if(uMsg == WM_COMMAND) \ + CHAIN_MSG_MAP_ALT(theChainClass, msgMapID) + +#define CHAIN_COMMANDS_MEMBER(theChainMember) \ + if(uMsg == WM_COMMAND) \ + CHAIN_MSG_MAP_MEMBER(theChainMember) + +#define CHAIN_COMMANDS_ALT_MEMBER(theChainMember, msgMapID) \ + if(uMsg == WM_COMMAND) \ + CHAIN_MSG_MAP_ALT_MEMBER(theChainMember, msgMapID) + + +/////////////////////////////////////////////////////////////////////////////// +// Macros for parent message map to selectively reflect control messages + +// NOTE: ReflectNotifications is a member of ATL's CWindowImplRoot +// (and overridden in 2 cases - CContainedWindowT and CAxHostWindow) +// Since we can't modify ATL, we'll provide the needed additions +// in a separate function (that is not a member of CWindowImplRoot) + +namespace WTL +{ + +inline LRESULT WtlReflectNotificationsFiltered(HWND hWndParent, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled, + UINT uMsgFilter = WM_NULL, UINT_PTR idFromFilter = 0, HWND hWndChildFilter = NULL) +{ + if((uMsgFilter != WM_NULL) && (uMsgFilter != uMsg)) + { + // The notification message doesn't match the filter. + bHandled = FALSE; + return 1; + } + + HWND hWndChild = NULL; + UINT_PTR idFrom = 0; + + switch(uMsg) + { + case WM_COMMAND: + if(lParam != NULL) // not from a menu + { + hWndChild = (HWND)lParam; + idFrom = (UINT_PTR)LOWORD(wParam); + } + break; + case WM_NOTIFY: + hWndChild = ((LPNMHDR)lParam)->hwndFrom; + idFrom = ((LPNMHDR)lParam)->idFrom; + break; + case WM_PARENTNOTIFY: + switch(LOWORD(wParam)) + { + case WM_CREATE: + case WM_DESTROY: + hWndChild = (HWND)lParam; + idFrom = (UINT_PTR)HIWORD(wParam); + break; + default: + hWndChild = ::GetDlgItem(hWndParent, HIWORD(wParam)); + idFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild); + break; + } + break; + case WM_DRAWITEM: + if(wParam) // not from a menu + { + hWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem; + idFrom = (UINT_PTR)wParam; + } + break; + case WM_MEASUREITEM: + if(wParam) // not from a menu + { + hWndChild = ::GetDlgItem(hWndParent, ((LPMEASUREITEMSTRUCT)lParam)->CtlID); + idFrom = (UINT_PTR)wParam; + } + break; + case WM_COMPAREITEM: + if(wParam) // not from a menu + { + hWndChild = ((LPCOMPAREITEMSTRUCT)lParam)->hwndItem; + idFrom = (UINT_PTR)wParam; + } + break; + case WM_DELETEITEM: + if(wParam) // not from a menu + { + hWndChild = ((LPDELETEITEMSTRUCT)lParam)->hwndItem; + idFrom = (UINT_PTR)wParam; + } + break; + case WM_VKEYTOITEM: + case WM_CHARTOITEM: + case WM_HSCROLL: + case WM_VSCROLL: + case WM_CTLCOLORBTN: + case WM_CTLCOLORDLG: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORMSGBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + hWndChild = (HWND)lParam; + idFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild); + break; + default: + break; + } + + if((hWndChild == NULL) || + ((hWndChildFilter != NULL) && (hWndChildFilter != hWndChild))) + { + // Either hWndChild isn't valid, or + // hWndChild doesn't match the filter. + bHandled = FALSE; + return 1; + } + + if((idFromFilter != 0) && (idFromFilter != idFrom)) + { + // The dialog control id doesn't match the filter. + bHandled = FALSE; + return 1; + } + + ATLASSERT(::IsWindow(hWndChild)); + LRESULT lResult = ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam); + if((lResult == 0) && (uMsg >= WM_CTLCOLORMSGBOX) && (uMsg <= WM_CTLCOLORSTATIC)) + { + // Try to prevent problems with WM_CTLCOLOR* messages when + // the message wasn't really handled + bHandled = FALSE; + } + + return lResult; +} + +} // namespace WTL + +// Try to prevent problems with WM_CTLCOLOR* messages when +// the message wasn't really handled +#define REFLECT_NOTIFICATIONS_EX() \ +{ \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if((lResult == 0) && (uMsg >= WM_CTLCOLORMSGBOX) && (uMsg <= WM_CTLCOLORSTATIC)) \ + bHandled = FALSE; \ + if(bHandled) \ + return TRUE; \ +} + +#define REFLECT_NOTIFICATIONS_MSG_FILTERED(uMsgFilter) \ + { \ + bHandled = TRUE; \ + lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, 0, NULL); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFICATIONS_ID_FILTERED(idFromFilter) \ + { \ + bHandled = TRUE; \ + lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, WM_NULL, idFromFilter, NULL); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFICATIONS_HWND_FILTERED(hWndChildFilter) \ + { \ + bHandled = TRUE; \ + lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, WM_NULL, 0, hWndChildFilter); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFICATIONS_MSG_ID_FILTERED(uMsgFilter, idFromFilter) \ + { \ + bHandled = TRUE; \ + lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, idFromFilter, NULL); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFICATIONS_MSG_HWND_FILTERED(uMsgFilter, hWndChildFilter) \ + { \ + bHandled = TRUE; \ + lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, 0, hWndChildFilter); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_COMMAND(id, code) \ + if((uMsg == WM_COMMAND) && (id == LOWORD(wParam)) && (code == HIWORD(wParam))) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_COMMAND_ID(id) \ + if((uMsg == WM_COMMAND) && (id == LOWORD(wParam))) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_COMMAND_CODE(code) \ + if((uMsg == WM_COMMAND) && (code == HIWORD(wParam))) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_COMMAND_RANGE(idFirst, idLast) \ + if((uMsg == WM_COMMAND) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_COMMAND_RANGE_CODE(idFirst, idLast, code) \ + if((uMsg == WM_COMMAND) && (code == HIWORD(wParam)) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFY(id, cd) \ + if((uMsg == WM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom) && (cd == ((LPNMHDR)lParam)->code)) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFY_ID(id) \ + if((uMsg == WM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom)) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFY_CODE(cd) \ + if((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code)) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFY_RANGE(idFirst, idLast) \ + if((uMsg == WM_NOTIFY) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + +#define REFLECT_NOTIFY_RANGE_CODE(idFirst, idLast, cd) \ + if((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \ + { \ + bHandled = TRUE; \ + lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \ + if(bHandled) \ + return TRUE; \ + } + + +/////////////////////////////////////////////////////////////////////////////// +// GetClassLong/SetClassLong redefinition to avoid problems with class members + +#ifdef SetClassLongPtrA + #undef SetClassLongPtrA + inline LONG_PTR SetClassLongPtrA(HWND hWnd, int nIndex, LONG_PTR dwNewLong) + { + return ::SetClassLongA(hWnd, nIndex, LONG(dwNewLong)); + } +#endif + +#ifdef SetClassLongPtrW + #undef SetClassLongPtrW + inline LONG_PTR SetClassLongPtrW(HWND hWnd, int nIndex, LONG_PTR dwNewLong) + { + return ::SetClassLongW(hWnd, nIndex, LONG(dwNewLong)); + } +#endif + +#ifdef GetClassLongPtrA + #undef GetClassLongPtrA + inline LONG_PTR GetClassLongPtrA(HWND hWnd, int nIndex) + { + return ::GetClassLongA(hWnd, nIndex); + } +#endif + +#ifdef GetClassLongPtrW + #undef GetClassLongPtrW + inline LONG_PTR GetClassLongPtrW(HWND hWnd, int nIndex) + { + return ::GetClassLongW(hWnd, nIndex); + } +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// CWindowEx - extension of ATL::CWindow + +namespace WTL +{ + +class CWindowEx : public ATL::CWindow +{ +public: + CWindowEx(HWND hWnd = NULL) : ATL::CWindow(hWnd) + { } + + CWindowEx& operator =(HWND hWnd) + { + m_hWnd = hWnd; + return *this; + } + + operator HWND() const + { + return m_hWnd; + } + +// Methods + BOOL PrintWindow(HDC hDC, UINT uFlags = 0) + { + ATLASSERT(::IsWindow(m_hWnd)); + return ::PrintWindow(m_hWnd, hDC, uFlags); + } + + BOOL DragDetect(POINT pt) + { + ATLASSERT(::IsWindow(m_hWnd)); + return ::DragDetect(m_hWnd, pt); + } + + BOOL DragDetect() + { + ATLASSERT(::IsWindow(m_hWnd)); + + POINT pt = {}; + ::GetCursorPos(&pt); + return ::DragDetect(m_hWnd, pt); + } + + CWindowEx GetAncestor(UINT uFlags) const + { + ATLASSERT(::IsWindow(m_hWnd)); + return CWindowEx(::GetAncestor(m_hWnd, uFlags)); + } + + // Note: Does not work properly on Vista Aero and above + BOOL AnimateWindow(DWORD dwFlags, DWORD dwTime = 200) + { + ATLASSERT(::IsWindow(m_hWnd)); + return ::AnimateWindow(m_hWnd, dwTime, dwFlags); + } + + BOOL FlashWindowEx(DWORD dwFlags, UINT uCount, DWORD dwTimeout = 0) + { + ATLASSERT(::IsWindow(m_hWnd)); + + FLASHWINFO fi = { sizeof(FLASHWINFO) }; + fi.hwnd = m_hWnd; + fi.dwFlags = dwFlags; + fi.uCount = uCount; + fi.dwTimeout = dwTimeout; + return ::FlashWindowEx(&fi); + } + + BOOL StopFlashWindowEx() + { + ATLASSERT(::IsWindow(m_hWnd)); + + FLASHWINFO fi = { sizeof(FLASHWINFO) }; + fi.hwnd = m_hWnd; + fi.dwFlags = FLASHW_STOP; + return ::FlashWindowEx(&fi); + } + +// Class long properties + DWORD GetClassLong(int nIndex) const + { + ATLASSERT(::IsWindow(m_hWnd)); + return ::GetClassLong(m_hWnd, nIndex); + } + + DWORD SetClassLong(int nIndex, LONG dwNewLong) + { + ATLASSERT(::IsWindow(m_hWnd)); + return ::SetClassLong(m_hWnd, nIndex, dwNewLong); + } + + ULONG_PTR GetClassLongPtr(int nIndex) const + { + ATLASSERT(::IsWindow(m_hWnd)); + return ::GetClassLongPtr(m_hWnd, nIndex); + } + + ULONG_PTR SetClassLongPtr(int nIndex, LONG_PTR dwNewLong) + { + ATLASSERT(::IsWindow(m_hWnd)); + return ::SetClassLongPtr(m_hWnd, nIndex, dwNewLong); + } + +// Layered windows + BOOL SetLayeredWindowAttributes(COLORREF crlKey, BYTE byteAlpha, DWORD dwFlags) + { + ATLASSERT(::IsWindow(m_hWnd)); + ATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0); + + return ::SetLayeredWindowAttributes(m_hWnd, crlKey, byteAlpha, dwFlags); + } + + BOOL UpdateLayeredWindow(HDC hdcDst, LPPOINT pptDst, LPSIZE psize, HDC hdcSrc, LPPOINT pptSrc, COLORREF crlKey, BLENDFUNCTION* pblend, DWORD dwFlags) + { + ATLASSERT(::IsWindow(m_hWnd)); + ATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0); + + return ::UpdateLayeredWindow(m_hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crlKey, pblend, dwFlags); + } + + BOOL UpdateLayeredWindow(LPPOINT pptDst = NULL) + { + ATLASSERT(::IsWindow(m_hWnd)); + ATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0); + + return ::UpdateLayeredWindow(m_hWnd, NULL, pptDst, NULL, NULL, NULL, CLR_NONE, NULL, 0); + } + + BOOL GetLayeredWindowAttributes(COLORREF* pcrlKey, BYTE* pbyteAlpha, DWORD* pdwFlags) const + { + ATLASSERT(::IsWindow(m_hWnd)); + ATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0); + + return ::GetLayeredWindowAttributes(m_hWnd, pcrlKey, pbyteAlpha, pdwFlags); + } + +// Mouse tracking + BOOL StartTrackMouseLeave() + { + ATLASSERT(::IsWindow(m_hWnd)); + + TRACKMOUSEEVENT tme = {}; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = m_hWnd; + return ::TrackMouseEvent(&tme); + } + + BOOL StartTrackMouse(DWORD dwFlags, DWORD dwHoverTime = HOVER_DEFAULT) + { + ATLASSERT(::IsWindow(m_hWnd)); + + TRACKMOUSEEVENT tme = {}; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = dwFlags; + tme.hwndTrack = m_hWnd; + tme.dwHoverTime = dwHoverTime; + return ::TrackMouseEvent(&tme); + } + + BOOL CancelTrackMouse(DWORD dwType) + { + ATLASSERT(::IsWindow(m_hWnd)); + + TRACKMOUSEEVENT tme = {}; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_CANCEL | dwType; + tme.hwndTrack = m_hWnd; + return ::TrackMouseEvent(&tme); + } + +// CString support +#ifdef __ATLSTR_H__ + int GetWindowText(ATL::CString& strText) const + { + int nLength = GetWindowTextLength(); + LPTSTR pszText = strText.GetBuffer(nLength + 1); + nLength = ::GetWindowText(m_hWnd, pszText, nLength + 1); + strText.ReleaseBuffer(nLength); + + return nLength; + } + + UINT GetDlgItemText(int nID, ATL::CString& strText) const + { + ATLASSERT(::IsWindow(m_hWnd)); + + HWND hItem = GetDlgItem(nID); + if(hItem != NULL) + { + int nLength = ::GetWindowTextLength(hItem); + LPTSTR pszText = strText.GetBuffer(nLength + 1); + nLength = ::GetWindowText(hItem, pszText, nLength + 1); + strText.ReleaseBuffer(nLength); + + return nLength; + } + else + { + strText.Empty(); + + return 0; + } + } +#endif // __ATLSTR_H__ + +// Dialog window only + UINT DlgGetDefID() const + { + ATLASSERT(::IsWindow(m_hWnd)); + + LRESULT lRet = ::SendMessage(m_hWnd, DM_GETDEFID, 0, 0L); + UINT uID = 0U; + if(HIWORD(lRet) == DC_HASDEFID) + uID = LOWORD(lRet); + + return uID; + } + + void DlgSetDefID(UINT uID) + { + ATLASSERT(::IsWindow(m_hWnd)); + + ::SendMessage(m_hWnd, DM_SETDEFID, uID, 0L); + } + + void DlgReposition() + { + ATLASSERT(::IsWindow(m_hWnd)); + + ::SendMessage(m_hWnd, DM_REPOSITION, 0, 0L); + } +}; + +} // namespace WTL + +#endif // __ATLWINX_H__ diff --git a/Examples/WhisperDesktop/Utils/logger.cpp b/Examples/WhisperDesktop/Utils/logger.cpp new file mode 100644 index 0000000..5c7c257 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/logger.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "logger.h" +#include "miscUtils.h" + +namespace +{ + using namespace Whisper; + + // Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9] + // Lowest is red, middle is yellow, highest is green. + static const std::array<const char*, 10> k_colors = + { + "\033[38;5;196m", "\033[38;5;202m", "\033[38;5;208m", "\033[38;5;214m", "\033[38;5;220m", + "\033[38;5;226m", "\033[38;5;190m", "\033[38;5;154m", "\033[38;5;118m", "\033[38;5;82m", + }; + + static int colorIndex( const sToken& tok ) + { + const float p = tok.probability; + const float p3 = p * p * p; + int col = (int)( p3 * float( k_colors.size() ) ); + col = std::max( 0, std::min( (int)k_colors.size() - 1, col ) ); + return col; + } +} + +void printTimeStamp( CStringA& rdi, Whisper::sTimeSpan ts ) +{ + sTimeSpanFields fields = ts; + uint32_t msec = fields.ticks / 10'000; + uint32_t hr = fields.days * 24 + fields.hours; + uint32_t min = fields.minutes; + uint32_t sec = fields.seconds; + rdi.AppendFormat( "%02d:%02d:%02d.%03d", hr, min, sec, msec ); +} + +HRESULT logNewSegments( const iTranscribeResult* results, size_t newSegments, bool printSpecial ) +{ + sTranscribeLength length; + CHECK( results->getSize( length ) ); + + const size_t len = length.countSegments; + size_t i = len - newSegments; + + const sSegment* const segments = results->getSegments(); + const sToken* const tokens = results->getTokens(); + + CStringA str; + for( ; i < len; i++ ) + { + const sSegment& seg = segments[ i ]; + str = "["; + printTimeStamp( str, seg.time.begin ); + str += " --> "; + printTimeStamp( str, seg.time.end ); + str += "] "; + + for( uint32_t j = 0; j < seg.countTokens; j++ ) + { + const sToken& tok = tokens[ seg.firstToken + j ]; + if( !printSpecial && ( tok.flags & eTokenFlags::Special ) ) + continue; + str += k_colors[ colorIndex( tok ) ]; + str += tok.text; + str += "\033[0m"; + } + logInfo( u8"%s", cstr( str ) ); + } + + return S_OK; +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/logger.h b/Examples/WhisperDesktop/Utils/logger.h new file mode 100644 index 0000000..07ec012 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/logger.h @@ -0,0 +1,36 @@ +#pragma once +#include <whisperWindows.h> +#include <cstdarg> + +void logMessage( Whisper::eLogLevel lvl, const char8_t* pszFormat, va_list args ); + +#define LOG_MESSAGE_IMPL( lvl ) \ + std::va_list args; \ + va_start( args, pszFormat ); \ + logMessage( lvl, pszFormat, args ); \ + va_end( args ) + +inline void logError( const char8_t* pszFormat, ... ) +{ + LOG_MESSAGE_IMPL( Whisper::eLogLevel::Error ); +} +inline void logWarning( const char8_t* pszFormat, ... ) +{ + LOG_MESSAGE_IMPL( Whisper::eLogLevel::Warning ); +} +inline void logInfo( const char8_t* pszFormat, ... ) +{ + LOG_MESSAGE_IMPL( Whisper::eLogLevel::Info ); +} +inline void logDebug( const char8_t* pszFormat, ... ) +{ + LOG_MESSAGE_IMPL( Whisper::eLogLevel::Debug ); +} + +#undef LOG_MESSAGE_IMPL + +HRESULT logNewSegments( const Whisper::iTranscribeResult* results, size_t newSegments, bool printSpecial = false ); + +void clearLastError(); +bool getLastError( CString& rdi ); +void printTimeStamp( CStringA& rdi, Whisper::sTimeSpan ts );
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/miscUtils.cpp b/Examples/WhisperDesktop/Utils/miscUtils.cpp new file mode 100644 index 0000000..485cf00 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/miscUtils.cpp @@ -0,0 +1,254 @@ +#include "stdafx.h" +#include "miscUtils.h" + +namespace +{ + wchar_t* formatMessage( HRESULT hr ) + { + wchar_t* err; + if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + hr, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + (LPTSTR)&err, + 0, + NULL ) ) + return err; + return nullptr; + } +} + +CString formatErrorMessage( HRESULT hr ) +{ + CString message; + const wchar_t* err = formatMessage( hr ); + if( nullptr != err ) + { + message = err; + LocalFree( (HLOCAL)err ); + message.TrimRight(); + } + else + message.Format( L"Error code %i (0x%08X)", hr, hr ); + + return message; +} + +void reportFatalError( const char* what, HRESULT hr ) +{ + CString message; + message.Format( L"%S\n%S\n", "Unable to start the application.", what ); + message += formatErrorMessage( hr ); + ::MessageBox( nullptr, message, L"Whisper Desktop Startup", MB_OK | MB_ICONERROR ); +} + +namespace +{ + using Whisper::eModelImplementation; + + struct sImplString + { + eModelImplementation val; + LPCTSTR str; + }; + static const std::array<sImplString, 3> s_implStrings = + { + sImplString{ eModelImplementation::GPU, L"GPU" }, + sImplString{ eModelImplementation::Hybrid, L"Hybrid" }, + sImplString{ eModelImplementation::Reference, L"Reference" }, + }; +} + +HRESULT implParse( const CString& s, eModelImplementation& rdi ) +{ + for( const auto& is : s_implStrings ) + { + if( 0 != s.CompareNoCase( is.str ) ) + continue; + rdi = is.val;; + return S_OK; + } + return E_INVALIDARG; +} + +LPCTSTR implString( eModelImplementation i ) +{ + for( const auto& is : s_implStrings ) + if( is.val == i ) + return is.str; + return nullptr; +} + +void implPopulateCombobox( CComboBox& cb, Whisper::eModelImplementation impl ) +{ + int curSel = 0; + int idx = 0; + for( const auto& is : s_implStrings ) + { + cb.AddString( is.str ); + if( is.val == impl ) + curSel = idx; + idx++; + } + cb.SetCurSel( curSel ); +} + +Whisper::eModelImplementation implGetValue( CComboBox& cb ) +{ + int curSel = cb.GetCurSel(); + if( curSel < 0 ) + return (Whisper::eModelImplementation)0; + return s_implStrings[ curSel ].val; +} + +ThreadPoolWork::~ThreadPoolWork() +{ + if( nullptr != work ) + { + CloseThreadpoolWork( work ); + work = nullptr; + } +} + +void __stdcall ThreadPoolWork::callback( PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work ) +{ + iThreadPoolCallback* cb = (iThreadPoolCallback*)Context; + cb->poolCallback(); +} + +HRESULT ThreadPoolWork::create( iThreadPoolCallback* cb ) +{ + if( nullptr == cb ) + return E_POINTER; + if( nullptr != work ) + return HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED ); + + work = CreateThreadpoolWork( &callback, cb, nullptr ); + if( nullptr != work ) + return S_OK; + + return HRESULT_FROM_WIN32( GetLastError() ); +} + +HRESULT ThreadPoolWork::post() +{ + if( nullptr == work ) + return OLE_E_BLANK; + SubmitThreadpoolWork( work ); + return S_OK; +} + +void makeUtf16( CString& rdi, const char* utf8 ) +{ + const size_t length = strlen( utf8 ); + int count = MultiByteToWideChar( CP_UTF8, 0, utf8, (int)length, nullptr, 0 ); + wchar_t* p = rdi.GetBufferSetLength( count ); + MultiByteToWideChar( CP_UTF8, 0, utf8, (int)length, p, count ); + rdi.ReleaseBuffer(); +} + +void makeUtf8( CStringA& rdi, const CString& utf16 ) +{ + int count = WideCharToMultiByte( CP_UTF8, 0, utf16, utf16.GetLength(), nullptr, 0, nullptr, nullptr ); + char* s = rdi.GetBufferSetLength( count + 1 ); + count = WideCharToMultiByte( CP_UTF8, 0, utf16, utf16.GetLength(), s, count, nullptr, nullptr ); + rdi.ReleaseBufferSetLength( count ); +} + +constexpr int ofnBufferLength = 2048; + +bool getOpenFileName( HWND owner, LPCTSTR title, LPCTSTR filter, CString& path ) +{ + wchar_t buffer[ ofnBufferLength ]; + buffer[ 0 ] = 0; + OPENFILENAME ofn; + memset( &ofn, 0, sizeof( ofn ) ); + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.hwndOwner = owner; + ofn.lpstrFilter = filter; + ofn.lpstrTitle = title; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + ofn.lpstrFile = buffer; + ofn.nMaxFile = ofnBufferLength - 1; + + CString dir; + if( path.GetLength() > 0 && path.GetLength() < ofnBufferLength ) + wcsncpy_s( buffer, path, path.GetLength() ); + + if( !GetOpenFileName( &ofn ) ) + { + path = L""; + return false; + } + else + { + path = ofn.lpstrFile; + return true; + } +} + +bool getSaveFileName( HWND owner, LPCTSTR title, LPCTSTR filter, CString& path, DWORD* filterIndex ) +{ + wchar_t buffer[ ofnBufferLength ]; + buffer[ 0 ] = 0; + + OPENFILENAME ofn; + memset( &ofn, 0, sizeof( ofn ) ); + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.hwndOwner = owner; + ofn.lpstrFilter = filter; + ofn.lpstrTitle = title; + ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST; + ofn.lpstrFile = buffer; + ofn.nMaxFile = ofnBufferLength - 1; + if( nullptr != filterIndex ) + ofn.nFilterIndex = *filterIndex + 1; + + if( path.GetLength() > 0 && path.GetLength() < ofnBufferLength ) + wcsncpy_s( buffer, path, path.GetLength() ); + + if( !GetSaveFileName( &ofn ) ) + return false; + + path = ofn.lpstrFile; + + if( nullptr != filterIndex ) + *filterIndex = ofn.nFilterIndex - 1; + + return true; +} + +void reportError( HWND owner, LPCTSTR text, LPCTSTR title, HRESULT hr ) +{ + if( nullptr == title ) + title = L"Operation Failed"; + + CString message = text; + message.TrimRight(); + if( FAILED( hr ) ) + { + message += L"\n"; + message += formatErrorMessage( hr ); + } + + ::MessageBox( owner, message, title, MB_OK | MB_ICONWARNING ); +} + +HRESULT writeUtf8Bom( CAtlFile& file ) +{ + const std::array<uint8_t, 3> bom = { 0xEF, 0xBB, 0xBF }; + return file.Write( bom.data(), 3 ); +} + +bool isInvalidTranslate( HWND owner, uint32_t lang, bool translate ) +{ + if( !translate ) + return false; + constexpr uint32_t english = 0x6E65; + if( lang != english ) + return false; + + LPCTSTR message = L"The translate feature translates speech to English.\nIt’s not available when the audio language is already English."; + MessageBox( owner, message, L"Incompatible parameters", MB_OK | MB_ICONINFORMATION ); + return true; +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/Utils/miscUtils.h b/Examples/WhisperDesktop/Utils/miscUtils.h new file mode 100644 index 0000000..8cf8151 --- /dev/null +++ b/Examples/WhisperDesktop/Utils/miscUtils.h @@ -0,0 +1,72 @@ +#pragma once +#include <iContext.h> +#include "logger.h" + +CString formatErrorMessage( HRESULT hr ); + +void reportFatalError( const char* what, HRESULT hr ); + +#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; } + +HRESULT implParse( const CString& s, Whisper::eModelImplementation& rdi ); + +LPCTSTR implString( Whisper::eModelImplementation i ); + +void implPopulateCombobox( CComboBox& cb, Whisper::eModelImplementation impl ); + +Whisper::eModelImplementation implGetValue( CComboBox& cb ); + +__interface iThreadPoolCallback +{ + void __stdcall poolCallback() noexcept; +}; + +class ThreadPoolWork +{ + PTP_WORK work = nullptr; + static void __stdcall callback( PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work ); + +public: + + ~ThreadPoolWork(); + HRESULT create( iThreadPoolCallback* cb ); + HRESULT post(); +}; + +void makeUtf16( CString& rdi, const char* utf8 ); +void makeUtf8( CStringA& rdi, const CString& utf16 ); + +bool getOpenFileName( HWND owner, LPCTSTR title, LPCTSTR filter, CString& path ); + +bool getSaveFileName( HWND owner, LPCTSTR title, LPCTSTR filter, CString& path, DWORD* filterIndex = nullptr ); + +#define ON_BUTTON_CLICK( id, func ) \ + if( uMsg == WM_COMMAND && \ + id == LOWORD( wParam ) ) \ + { \ + bHandled = TRUE; \ + func(); \ + lResult = 0; \ + return TRUE; \ + } + +void reportError( HWND owner, LPCTSTR text, LPCTSTR title, HRESULT hr = S_FALSE ); + +inline const wchar_t* cstr( const CString& s ) { return s; } +inline const char* cstr( const CStringA& s ) { return s; } + +inline HRESULT getLastHr() +{ + return HRESULT_FROM_WIN32( GetLastError() ); +} + +HRESULT writeUtf8Bom( CAtlFile& file ); + +// Flip order of bytes from RGB to BGR, or vice versa +inline uint32_t flipRgb( uint32_t val ) +{ + val = _byteswap_ulong( val ); + return val >> 8; +} + +bool isInvalidTranslate( HWND owner, uint32_t lang, bool translate );
\ No newline at end of file diff --git a/Examples/WhisperDesktop/WhisperDesktop.cpp b/Examples/WhisperDesktop/WhisperDesktop.cpp new file mode 100644 index 0000000..ddef5b6 --- /dev/null +++ b/Examples/WhisperDesktop/WhisperDesktop.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "AppState.h" +#include "Utils/miscUtils.h" +#include "LoadModelDlg.h" +#include "TranscribeDlg.h" +#include "CaptureDlg.h" + +HRESULT dialogLoadModel( AppState& appState ) +{ + LoadModelDlg loadDialog{ appState }; + HRESULT hr = loadDialog.show(); + if( FAILED( hr ) ) + { + reportFatalError( "Error loading the model", hr ); + return hr; + } + appState.automaticallyLoadModel = false; + return hr; +} + +HRESULT dialogTranscribe( AppState& appState ) +{ + TranscribeDlg dialog{ appState }; + return dialog.show(); +} + +HRESULT dialogCapture( AppState& appState ) +{ + CaptureDlg dialog{ appState }; + return dialog.show(); +} + +using pfnDialog = HRESULT( * )( AppState& appState ); +static const std::array<pfnDialog, 4> s_dialogs = +{ + nullptr, // S_OK + &dialogLoadModel, // SCREEN_MODEL + &dialogTranscribe, // SCREEN_TRANSCRIBE + &dialogCapture, // SCREEN_CAPTURE +}; + +int __stdcall wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) +{ + AppState appState; + HRESULT hr = appState.startup(); + if( FAILED( hr ) ) + return hr; + + appState.findModelSource(); + + hr = SCREEN_MODEL; + while( true ) + { + pfnDialog pfn = s_dialogs[ hr ]; + if( nullptr == pfn ) + return S_OK; + hr = pfn( appState ); + if( FAILED( hr ) ) + return hr; + if( hr == SCREEN_MODEL ) + appState.model = nullptr; + } +}
\ No newline at end of file diff --git a/Examples/WhisperDesktop/WhisperDesktop.manifest b/Examples/WhisperDesktop/WhisperDesktop.manifest new file mode 100644 index 0000000..af2b796 --- /dev/null +++ b/Examples/WhisperDesktop/WhisperDesktop.manifest @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> + <assemblyIdentity version="1.0.0.0" processorArchitecture="amd64" name="CompanyName.ProductName.YourApplication" type="win32" /> + <description>Your application description here.</description> + <dependency> + <dependentAssembly> + <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*" /> + </dependentAssembly> + </dependency> + <asmv3:application> + <asmv3:windowsSettings> + <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> + </asmv3:windowsSettings> + </asmv3:application> +</assembly>
\ No newline at end of file diff --git a/Examples/WhisperDesktop/WhisperDesktop.rc b/Examples/WhisperDesktop/WhisperDesktop.rc Binary files differnew file mode 100644 index 0000000..67461d7 --- /dev/null +++ b/Examples/WhisperDesktop/WhisperDesktop.rc diff --git a/Examples/WhisperDesktop/WhisperDesktop.vcxproj b/Examples/WhisperDesktop/WhisperDesktop.vcxproj new file mode 100644 index 0000000..4956410 --- /dev/null +++ b/Examples/WhisperDesktop/WhisperDesktop.vcxproj @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{cd9e49f0-75a3-4f91-ac71-336109ee39c6}</ProjectGuid> + <RootNamespace>WhisperDesktop</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <IncludePath>$(ProjectDir);$(SolutionDir)Whisper\API\;$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <IncludePath>$(ProjectDir);$(SolutionDir)Whisper\API\;$(IncludePath)</IncludePath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <PrecompiledHeader>Use</PrecompiledHeader> + <LanguageStandard>stdcpp20</LanguageStandard> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ManifestFile>WhisperDesktop.manifest</ManifestFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <PrecompiledHeader>Use</PrecompiledHeader> + <LanguageStandard>stdcpp20</LanguageStandard> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ManifestFile>WhisperDesktop.manifest</ManifestFile> + <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="CircleIndicator.h" /> + <ClInclude Include="AppState.h" /> + <ClInclude Include="CaptureDlg.h" /> + <ClInclude Include="Utils\TranslateCheckbox.h" /> + <ClInclude Include="Utils\DebugConsole.h" /> + <ClInclude Include="framework.h" /> + <ClInclude Include="Utils\logger.h" /> + <ClInclude Include="Utils\PendingState.h" /> + <ClInclude Include="Utils\LanguageDropdown.h" /> + <ClInclude Include="LoadModelDlg.h" /> + <ClInclude Include="TranscribeDlg.h" /> + <ClInclude Include="Utils\miscUtils.h" /> + <ClInclude Include="stdafx.h" /> + <ClInclude Include="Resource.h" /> + <ClInclude Include="targetver.h" /> + <ClInclude Include="Utils\WTL\atlapp.h" /> + <ClInclude Include="Utils\WTL\atlcrack.h" /> + <ClInclude Include="Utils\WTL\atlctrls.h" /> + <ClInclude Include="Utils\WTL\atlddx.h" /> + <ClInclude Include="Utils\WTL\atlgdi.h" /> + <ClInclude Include="Utils\WTL\atlres.h" /> + <ClInclude Include="Utils\WTL\atluser.h" /> + <ClInclude Include="Utils\WTL\atlwinx.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="CircleIndicator.cpp" /> + <ClCompile Include="AppState.cpp" /> + <ClCompile Include="CaptureDlg.cpp" /> + <ClCompile Include="Utils\TranslateCheckbox.cpp" /> + <ClCompile Include="Utils\DebugConsole.cpp" /> + <ClCompile Include="Utils\logger.cpp" /> + <ClCompile Include="Utils\PendingState.cpp" /> + <ClCompile Include="Utils\LanguageDropdown.cpp" /> + <ClCompile Include="LoadModelDlg.cpp" /> + <ClCompile Include="TranscribeDlg.cpp" /> + <ClCompile Include="Utils\miscUtils.cpp" /> + <ClCompile Include="stdafx.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="WhisperDesktop.cpp" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="WhisperDesktop.rc" /> + </ItemGroup> + <ItemGroup> + <Image Include="sunflower.ico" /> + </ItemGroup> + <ItemGroup> + <Manifest Include="WhisperDesktop.manifest" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\Whisper\Whisper.vcxproj"> + <Project>{701df8c8-e4a5-43ec-9c6b-747bbf4d8e71}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Examples/WhisperDesktop/WhisperDesktop.vcxproj.filters b/Examples/WhisperDesktop/WhisperDesktop.vcxproj.filters new file mode 100644 index 0000000..7a36c86 --- /dev/null +++ b/Examples/WhisperDesktop/WhisperDesktop.vcxproj.filters @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="framework.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="targetver.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="stdafx.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="LoadModelDlg.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="AppState.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\miscUtils.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\WTL\atlapp.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\WTL\atlddx.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\WTL\atlctrls.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\WTL\atlgdi.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\WTL\atlres.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\WTL\atluser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\WTL\atlwinx.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TranscribeDlg.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\LanguageDropdown.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\PendingState.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\DebugConsole.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\logger.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="CaptureDlg.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="CircleIndicator.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\WTL\atlcrack.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Utils\TranslateCheckbox.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="WhisperDesktop.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="stdafx.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="LoadModelDlg.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="AppState.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Utils\miscUtils.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TranscribeDlg.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Utils\LanguageDropdown.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Utils\PendingState.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Utils\DebugConsole.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Utils\logger.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CaptureDlg.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CircleIndicator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Utils\TranslateCheckbox.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="WhisperDesktop.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <Image Include="sunflower.ico"> + <Filter>Resource Files</Filter> + </Image> + </ItemGroup> + <ItemGroup> + <Manifest Include="WhisperDesktop.manifest" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Examples/WhisperDesktop/framework.h b/Examples/WhisperDesktop/framework.h new file mode 100644 index 0000000..b52a644 --- /dev/null +++ b/Examples/WhisperDesktop/framework.h @@ -0,0 +1,22 @@ +#pragma once +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define NOMINMAX +// Windows Header Files +#include "targetver.h" +#include <windows.h> +// ATL header files +#include <atlstr.h> +#include <atlfile.h> +#include <atlbase.h> +#include <atlwin.h> + +// C RunTime Header Files +#include <stdlib.h> +#include <malloc.h> +#include <memory.h> +#include <tchar.h> +#include <assert.h> +// C++ headers +#include <vector> +#include <array> +#include <algorithm>
\ No newline at end of file diff --git a/Examples/WhisperDesktop/stdafx.cpp b/Examples/WhisperDesktop/stdafx.cpp new file mode 100644 index 0000000..1577c4e --- /dev/null +++ b/Examples/WhisperDesktop/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h"
\ No newline at end of file diff --git a/Examples/WhisperDesktop/stdafx.h b/Examples/WhisperDesktop/stdafx.h new file mode 100644 index 0000000..6f46ad0 --- /dev/null +++ b/Examples/WhisperDesktop/stdafx.h @@ -0,0 +1,8 @@ +#pragma once +#include "framework.h" + +#include <whisperWindows.h> + +#include "resource.h" +#include "Utils/WTL/atlapp.h" +#include "Utils/WTL/atlctrls.h"
\ No newline at end of file diff --git a/Examples/WhisperDesktop/sunflower.ico b/Examples/WhisperDesktop/sunflower.ico Binary files differnew file mode 100644 index 0000000..b6404e1 --- /dev/null +++ b/Examples/WhisperDesktop/sunflower.ico diff --git a/Examples/WhisperDesktop/targetver.h b/Examples/WhisperDesktop/targetver.h new file mode 100644 index 0000000..17d4075 --- /dev/null +++ b/Examples/WhisperDesktop/targetver.h @@ -0,0 +1,6 @@ +#pragma once +// Setup Windows SDK to only enable features available since Windows 8.0 +#include <WinSDKVer.h> +#define _WIN32_WINNT _WIN32_WINNT_WIN8 +#define NTDDI_VERSION NTDDI_WIN8 +#include <SDKDDKVer.h> diff --git a/Examples/main/main.cpp b/Examples/main/main.cpp new file mode 100644 index 0000000..c9eacf2 --- /dev/null +++ b/Examples/main/main.cpp @@ -0,0 +1,315 @@ +#include "params.h" +#include "../../Whisper/API/iContext.cl.h" +#include "../../Whisper/API/iMediaFoundation.cl.h" +#include "../../ComLightLib/comLightClient.h" +#include "miscUtils.h" +#include <array> +#include <atomic> +using namespace Whisper; + +#define STREAM_AUDIO 1 + +static HRESULT loadWhisperModel( const wchar_t* path, iModel** pp ) +{ + using namespace Whisper; + constexpr eModelImplementation impl = eModelImplementation::GPU; + // constexpr eModelImplementation impl = eModelImplementation::Reference; + return Whisper::loadModel( path, impl, nullptr, pp ); +} + +namespace +{ + struct sPrintUserData + { + const whisper_params* params; + // const std::vector<std::vector<float>>* pcmf32s; + }; + + // Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9] + // Lowest is red, middle is yellow, highest is green. + static const std::array<const char*, 10> k_colors = + { + "\033[38;5;196m", "\033[38;5;202m", "\033[38;5;208m", "\033[38;5;214m", "\033[38;5;220m", + "\033[38;5;226m", "\033[38;5;190m", "\033[38;5;154m", "\033[38;5;118m", "\033[38;5;82m", + }; + + std::string to_timestamp( sTimeSpan ts, bool comma = false ) + { + sTimeSpanFields fields = ts; + uint32_t msec = fields.ticks / 10'000; + uint32_t hr = fields.days * 24 + fields.hours; + uint32_t min = fields.minutes; + uint32_t sec = fields.seconds; + + char buf[ 32 ]; + snprintf( buf, sizeof( buf ), "%02d:%02d:%02d%s%03d", hr, min, sec, comma ? "," : ".", msec ); + return std::string( buf ); + } + + static int colorIndex( const sToken& tok ) + { + const float p = tok.probability; + const float p3 = p * p * p; + int col = (int)( p3 * float( k_colors.size() ) ); + col = std::max( 0, std::min( (int)k_colors.size() - 1, col ) ); + return col; + } + + HRESULT __cdecl newSegmentCallback( iContext* context, uint32_t n_new, void* user_data ) noexcept + { + ComLight::CComPtr<iTranscribeResult> results; + CHECK( context->getResults( eResultFlags::Timestamps | eResultFlags::Tokens, &results ) ); + + sTranscribeLength length; + CHECK( results->getSize( length ) ); + + const whisper_params& params = *( (sPrintUserData*)user_data )->params; + // const std::vector<std::vector<float>>& pcmf32s = *( (sPrintUserData*)user_data )->pcmf32s; + + // print the last n_new segments + const uint32_t s0 = length.countSegments - n_new; + if( s0 == 0 ) + printf( "\n" ); + + const sSegment* const segments = results->getSegments(); + const sToken* const tokens = results->getTokens(); + + for( uint32_t i = s0; i < length.countSegments; i++ ) + { + const sSegment& seg = segments[ i ]; + + if( params.no_timestamps ) + { + if( params.print_colors ) + { + for( uint32_t j = 0; j < seg.countTokens; j++ ) + { + const sToken& tok = tokens[ seg.firstToken + j ]; + if( !params.print_special && ( tok.flags & eTokenFlags::Special ) ) + continue; + wprintf( L"%S%s%S", k_colors[ colorIndex( tok ) ], utf16( tok.text ).c_str(), "\033[0m" ); + } + } + else + wprintf( L"%s", utf16( seg.text ).c_str() ); + fflush( stdout ); + continue; + } + + std::string speaker = ""; +#if 0 + if( params.diarize && pcmf32s.size() == 2 ) + { + const size_t n_samples = pcmf32s[ 0 ].size(); + const int64_t is0 = SourceAudio::sampleFromTimestamp( seg.time.begin, n_samples ); + const int64_t is1 = SourceAudio::sampleFromTimestamp( seg.time.end, n_samples ); + + double energy0 = 0.0f; + double energy1 = 0.0f; + + for( int64_t j = is0; j < is1; j++ ) + { + energy0 += fabs( pcmf32s[ 0 ][ j ] ); + energy1 += fabs( pcmf32s[ 1 ][ j ] ); + } + + if( energy0 > 1.1 * energy1 ) + speaker = "(speaker 0)"; + else if( energy1 > 1.1 * energy0 ) + speaker = "(speaker 1)"; + else + speaker = "(speaker ?)"; + + //printf("is0 = %lld, is1 = %lld, energy0 = %f, energy1 = %f, %s\n", is0, is1, energy0, energy1, speaker.c_str()); + } +#endif + + if( params.print_colors ) + { + printf( "[%s --> %s] ", to_timestamp( seg.time.begin ).c_str(), to_timestamp( seg.time.end ).c_str() ); + + for( uint32_t j = 0; j < seg.countTokens; j++ ) + { + const sToken& tok = tokens[ seg.firstToken + j ]; + if( !params.print_special && ( tok.flags & eTokenFlags::Special ) ) + continue; + wprintf( L"%S%S%s%S", speaker.c_str(), k_colors[ colorIndex( tok ) ], utf16( tok.text ).c_str(), "\033[0m" ); + } + printf( "\n" ); + } + else + wprintf( L"[%S --> %S] %S%s\n", to_timestamp( seg.time.begin ).c_str(), to_timestamp( seg.time.end ).c_str(), speaker.c_str(), utf16( seg.text ).c_str() ); + } + return S_OK; + } + + HRESULT __cdecl beginSegmentCallback( iContext* context, void* user_data ) noexcept + { + std::atomic_bool* flag = (std::atomic_bool*)user_data; + bool aborted = flag->load(); + return aborted ? S_FALSE : S_OK; + } + + HRESULT setupConsoleColors() + { + HANDLE h = GetStdHandle( STD_OUTPUT_HANDLE ); + if( h == INVALID_HANDLE_VALUE ) + return HRESULT_FROM_WIN32( GetLastError() ); + + DWORD mode = 0; + if( !GetConsoleMode( h, &mode ) ) + return HRESULT_FROM_WIN32( GetLastError() ); + if( 0 != ( mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING ) ) + return S_FALSE; + + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if( !SetConsoleMode( h, mode ) ) + return HRESULT_FROM_WIN32( GetLastError() ); + return S_OK; + } +} + +int wmain( int argc, wchar_t* argv[] ) +{ + // Whisper::dbgCompareTraces( LR"(C:\Temp\2remove\Whisper\ref.bin)", LR"(C:\Temp\2remove\Whisper\gpu.bin )" ); return 0; + + // Tell logger to use the standard output stream for the messages + { + Whisper::sLoggerSetup logSetup; + logSetup.flags = eLoggerFlags::UseStandardError; + logSetup.level = eLogLevel::Debug; + Whisper::setupLogger( logSetup ); + } + + whisper_params params; + if( !params.parse( argc, argv ) ) + return 1; + + if( params.print_colors ) + { + if( FAILED( setupConsoleColors() ) ) + params.print_colors = false; + } + + if( params.fname_inp.empty() ) + { + fprintf( stderr, "error: no input files specified\n" ); + whisper_print_usage( argc, argv, params ); + return 2; + } + + if( Whisper::findLanguageKeyA( params.language.c_str() ) == UINT_MAX ) + { + fprintf( stderr, "error: unknown language '%s'\n", params.language.c_str() ); + whisper_print_usage( argc, argv, params ); + return 3; + } + + ComLight::CComPtr<iModel> model; + HRESULT hr = loadWhisperModel( params.model.c_str(), &model ); + if( FAILED( hr ) ) + { + printError( "failed to load the model", hr ); + return 4; + } + + ComLight::CComPtr<iContext> context; + hr = model->createContext( &context ); + if( FAILED( hr ) ) + { + printError( "failed to initialize whisper context", hr ); + return 5; + } + + ComLight::CComPtr<iMediaFoundation> mf; + hr = initMediaFoundation( &mf ); + if( FAILED( hr ) ) + { + printError( "failed to initialize Media Foundation runtime", hr ); + return 5; + } + + for( const std::wstring& fname : params.fname_inp ) + { + // print some info about the processing + { + if( model->isMultilingual() == S_FALSE ) + { + if( params.language != "en" || params.translate ) + { + params.language = "en"; + params.translate = false; + fprintf( stderr, "%s: WARNING: model is not multilingual, ignoring language and translation options\n", __func__ ); + } + } + + /* + fwprintf( stderr, L"%S: processing '%s' (%zu samples, %.1f sec), %d threads, %d processors, lang = %S, task = %S, timestamps = %d ...\n", + __func__, fname.c_str(), audio.pcmf32.size(), audio.seconds(), + params.n_threads, params.n_processors, + params.language.c_str(), + params.translate ? "translate" : "transcribe", + params.no_timestamps ? 0 : 1 ); + */ + } + + // run the inference + Whisper::sFullParams wparams; + context->fullDefaultParams( eSamplingStrategy::Greedy, &wparams ); + + wparams.resetFlag( eFullParamsFlags::PrintRealtime | eFullParamsFlags::PrintProgress ); + wparams.setFlag( eFullParamsFlags::PrintTimestamps, !params.no_timestamps ); + wparams.setFlag( eFullParamsFlags::PrintSpecial, params.print_special ); + wparams.setFlag( eFullParamsFlags::Translate, params.translate ); + wparams.language = Whisper::makeLanguageKey( params.language.c_str() ); + wparams.cpuThreads = params.n_threads; + if( params.max_context != UINT_MAX ) + wparams.n_max_text_ctx = params.max_context; + wparams.offset_ms = params.offset_t_ms; + wparams.duration_ms = params.duration_ms; + + wparams.setFlag( eFullParamsFlags::TokenTimestamps, params.output_wts || params.max_len > 0 ); + wparams.thold_pt = params.word_thold; + wparams.max_len = params.output_wts && params.max_len == 0 ? 60 : params.max_len; + + wparams.setFlag( eFullParamsFlags::SpeedupAudio, params.speed_up ); + // sPrintUserData user_data = { ¶ms, &audio.pcmf32s }; + sPrintUserData user_data = { ¶ms }; + + // this callback is called on each new segment + if( !wparams.flag( eFullParamsFlags::PrintRealtime ) ) + { + wparams.new_segment_callback = &newSegmentCallback; + wparams.new_segment_callback_user_data = &user_data; + } + + // example for abort mechanism + // in this example, we do not abort the processing, but we could if the flag is set to true + // the callback is called before every encoder run - if it returns false, the processing is aborted + std::atomic_bool is_aborted = false; + { + wparams.encoder_begin_callback = &beginSegmentCallback; + wparams.encoder_begin_callback_user_data = &is_aborted; + } + +#if STREAM_AUDIO + ComLight::CComPtr<iAudioReader> reader; + CHECK( mf->openAudioFile( fname.c_str(), params.diarize, &reader ) ); + sProgressSink progressSink{ nullptr, nullptr }; + hr = context->runStreamed( wparams, progressSink, reader ); +#else + ComLight::CComPtr<iAudioBuffer> buffer; + CHECK( mf->loadAudioFile( fname.c_str(), params.diarize, &buffer ) ); + hr = context->runFull( wparams, buffer ); +#endif + if( FAILED( hr ) ) + { + fwprintf( stderr, L"%s: failed to process audio\n", argv[ 0 ] ); + return 10; + } + } + + context->timingsPrint(); + context = nullptr; + return 0; +}
\ No newline at end of file diff --git a/Examples/main/main.vcxproj b/Examples/main/main.vcxproj new file mode 100644 index 0000000..4945b88 --- /dev/null +++ b/Examples/main/main.vcxproj @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{4cca7042-eb15-4f7a-b77b-5cafd2df47b2}</ProjectGuid> + <RootNamespace>main</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NOMINMAX;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <LanguageStandard>stdcpp20</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NOMINMAX;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <LanguageStandard>stdcpp20</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + <ClCompile Include="miscUtils.cpp" /> + <ClCompile Include="params.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="miscUtils.h" /> + <ClInclude Include="params.h" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\Whisper\Whisper.vcxproj"> + <Project>{701df8c8-e4a5-43ec-9c6b-747bbf4d8e71}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Examples/main/main.vcxproj.filters b/Examples/main/main.vcxproj.filters new file mode 100644 index 0000000..94cd8a1 --- /dev/null +++ b/Examples/main/main.vcxproj.filters @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="main.cpp" /> + <ClCompile Include="params.cpp" /> + <ClCompile Include="miscUtils.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="params.h" /> + <ClInclude Include="miscUtils.h" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Examples/main/miscUtils.cpp b/Examples/main/miscUtils.cpp new file mode 100644 index 0000000..3ebda20 --- /dev/null +++ b/Examples/main/miscUtils.cpp @@ -0,0 +1,48 @@ +#include "miscUtils.h" +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +std::string utf8( const std::wstring& utf16 ) +{ + int count = WideCharToMultiByte( CP_UTF8, 0, utf16.c_str(), (int)utf16.length(), nullptr, 0, nullptr, nullptr ); + std::string str( count, 0 ); + WideCharToMultiByte( CP_UTF8, 0, utf16.c_str(), -1, &str[ 0 ], count, nullptr, nullptr ); + return str; +} + +std::wstring utf16( const std::string& u8 ) +{ + int count = MultiByteToWideChar( CP_UTF8, 0, u8.c_str(), (int)u8.length(), nullptr, 0 ); + std::wstring str( count, 0 ); + MultiByteToWideChar( CP_UTF8, 0, u8.c_str(), (int)u8.length(), &str[ 0 ], count ); + return str; +} + +namespace +{ + wchar_t* formatMessage( HRESULT hr ) + { + wchar_t* err; + if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + hr, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + (LPTSTR)&err, + 0, + NULL ) ) + return err; + return nullptr; + } +} + +void printError( const char* what, HRESULT hr ) +{ + const wchar_t* err = formatMessage( hr ); + if( nullptr != err ) + { + fwprintf( stderr, L"%S: %s\n", what, err ); + LocalFree( (HLOCAL)err ); + } + else + fprintf( stderr, "%s: error code %i (0x%08X)\n", what, hr, hr ); +}
\ No newline at end of file diff --git a/Examples/main/miscUtils.h b/Examples/main/miscUtils.h new file mode 100644 index 0000000..52770a6 --- /dev/null +++ b/Examples/main/miscUtils.h @@ -0,0 +1,9 @@ +#pragma once +#include <string> + +std::string utf8( const std::wstring& utf16 ); + +std::wstring utf16( const std::string& u8 ); + +using HRESULT = long; +void printError( const char* what, HRESULT hr );
\ No newline at end of file diff --git a/Examples/main/params.cpp b/Examples/main/params.cpp new file mode 100644 index 0000000..ff1cfdd --- /dev/null +++ b/Examples/main/params.cpp @@ -0,0 +1,101 @@ +#include "params.h" +#include <algorithm> +#include <thread> +#include "miscUtils.h" + +whisper_params::whisper_params() +{ +#ifdef _DEBUG + n_threads = 2; +#else + n_threads = std::min( 4u, std::thread::hardware_concurrency() ); +#endif +} + +namespace +{ + const char* cstr( bool b ) + { + return b ? "true" : "false"; + } +} + +void whisper_print_usage( int argc, wchar_t** argv, const whisper_params& params ) +{ + fprintf( stderr, "\n" ); + fprintf( stderr, "usage: %S [options] file0.wav file1.wav ...\n", argv[ 0 ] ); + fprintf( stderr, "\n" ); + fprintf( stderr, "options:\n" ); + fprintf( stderr, " -h, --help [default] show this help message and exit\n" ); + fprintf( stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads ); + fprintf( stderr, " -p N, --processors N [%-7d] number of processors to use during computation\n", params.n_processors ); + fprintf( stderr, " -ot N, --offset-t N [%-7d] time offset in milliseconds\n", params.offset_t_ms ); + fprintf( stderr, " -on N, --offset-n N [%-7d] segment index offset\n", params.offset_n ); + fprintf( stderr, " -d N, --duration N [%-7d] duration of audio to process in milliseconds\n", params.duration_ms ); + fprintf( stderr, " -mc N, --max-context N [%-7d] maximum number of text context tokens to store\n", params.max_context ); + fprintf( stderr, " -ml N, --max-len N [%-7d] maximum segment length in characters\n", params.max_len ); + fprintf( stderr, " -wt N, --word-thold N [%-7.2f] word timestamp probability threshold\n", params.word_thold ); + fprintf( stderr, " -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n", cstr( params.speed_up ) ); + fprintf( stderr, " -tr, --translate [%-7s] translate from source language to english\n", cstr( params.translate ) ); + fprintf( stderr, " -di, --diarize [%-7s] stereo audio diarization\n", cstr( params.diarize ) ); + fprintf( stderr, " -otxt, --output-txt [%-7s] output result in a text file\n", cstr( params.output_txt ) ); + fprintf( stderr, " -ovtt, --output-vtt [%-7s] output result in a vtt file\n", cstr( params.output_vtt ) ); + fprintf( stderr, " -osrt, --output-srt [%-7s] output result in a srt file\n", cstr( params.output_srt ) ); + fprintf( stderr, " -owts, --output-words [%-7s] output script for generating karaoke video\n", cstr( params.output_wts ) ); + fprintf( stderr, " -ps, --print-special [%-7s] print special tokens\n", cstr( params.print_special ) ); + fprintf( stderr, " -nc, --no-colors [%-7s] do not print colors\n", cstr( !params.print_colors ) ); + fprintf( stderr, " -nt, --no-timestamps [%-7s] do not print timestamps\n", cstr( params.no_timestamps ) ); + fprintf( stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str() ); + fprintf( stderr, " -m FNAME, --model FNAME [%-7S] model path\n", params.model.c_str() ); + fprintf( stderr, " -f FNAME, --file FNAME [%-7s] path of the input audio file\n", "" ); + fprintf( stderr, "\n" ); +} + +bool whisper_params::parse( int argc, wchar_t* argv[] ) +{ + for( int i = 1; i < argc; i++ ) + { + std::wstring arg = argv[ i ]; + + if( arg[ 0 ] != '-' ) + { + fname_inp.push_back( arg ); + continue; + } + + if( arg == L"-h" || arg == L"--help" ) + { + whisper_print_usage( argc, argv, *this ); + return false; + } + + else if( arg == L"-t" || arg == L"--threads" ) { n_threads = std::stoul( argv[ ++i ] ); } + else if( arg == L"-p" || arg == L"--processors" ) { n_processors = std::stoul( argv[ ++i ] ); } + else if( arg == L"-ot" || arg == L"--offset-t" ) { offset_t_ms = std::stoul( argv[ ++i ] ); } + else if( arg == L"-on" || arg == L"--offset-n" ) { offset_n = std::stoul( argv[ ++i ] ); } + else if( arg == L"-d" || arg == L"--duration" ) { duration_ms = std::stoul( argv[ ++i ] ); } + else if( arg == L"-mc" || arg == L"--max-context" ) { max_context = std::stoul( argv[ ++i ] ); } + else if( arg == L"-ml" || arg == L"--max-len" ) { max_len = std::stoul( argv[ ++i ] ); } + else if( arg == L"-wt" || arg == L"--word-thold" ) { word_thold = std::stof( argv[ ++i ] ); } + else if( arg == L"-su" || arg == L"--speed-up" ) { speed_up = true; } + else if( arg == L"-tr" || arg == L"--translate" ) { translate = true; } + else if( arg == L"-di" || arg == L"--diarize" ) { diarize = true; } + else if( arg == L"-otxt" || arg == L"--output-txt" ) { output_txt = true; } + else if( arg == L"-ovtt" || arg == L"--output-vtt" ) { output_vtt = true; } + else if( arg == L"-osrt" || arg == L"--output-srt" ) { output_srt = true; } + else if( arg == L"-owts" || arg == L"--output-words" ) { output_wts = true; } + else if( arg == L"-ps" || arg == L"--print-special" ) { print_special = true; } + else if( arg == L"-nc" || arg == L"--no-colors" ) { print_colors = false; } + else if( arg == L"-nt" || arg == L"--no-timestamps" ) { no_timestamps = true; } + else if( arg == L"-l" || arg == L"--language" ) { language = utf8( argv[ ++i ] ); } + else if( arg == L"-m" || arg == L"--model" ) { model = argv[ ++i ]; } + else if( arg == L"-f" || arg == L"--file" ) { fname_inp.push_back( argv[ ++i ] ); } + else + { + fprintf( stderr, "error: unknown argument: %S\n", arg.c_str() ); + whisper_print_usage( argc, argv, *this ); + return false; + } + } + return true; +}
\ No newline at end of file diff --git a/Examples/main/params.h b/Examples/main/params.h new file mode 100644 index 0000000..9eb2b04 --- /dev/null +++ b/Examples/main/params.h @@ -0,0 +1,38 @@ +#pragma once +#include <vector> +#include <string> + +// command-line parameters +struct whisper_params +{ + uint32_t n_threads; + uint32_t n_processors = 1; + uint32_t offset_t_ms = 0; + uint32_t offset_n = 0; + uint32_t duration_ms = 0; + uint32_t max_context = UINT_MAX; + uint32_t max_len = 0; + + float word_thold = 0.01f; + + bool speed_up = false; + bool translate = false; + bool diarize = false; + bool output_txt = false; + bool output_vtt = false; + bool output_srt = false; + bool output_wts = false; + bool print_special = false; + bool print_colors = true; + bool no_timestamps = false; + + std::string language = "en"; + std::wstring model = L"models/ggml-base.en.bin"; + std::vector<std::wstring> fname_inp; + + whisper_params(); + + bool parse( int argc, wchar_t* argv[] ); +}; + +void whisper_print_usage( int argc, wchar_t** argv, const whisper_params& params );
\ No newline at end of file |
