BeginPath Textout EndPath draws inverted text - c++

here is the code that I have in OnPaint event of my form:
int elementCount;
String tStr = L"15:00";
::BeginPath(Canvas->Handle);
::TextOut(Canvas->Handle, 5, 5, tStr.c_str(), tStr.Length());
::EndPath(Canvas->Handle);
elementCount = ::GetPath(Canvas->Handle, NULL, NULL, 0);
Canvas->Brush->Color = clBlue;
Canvas->Pen->Color = clYellow;
Canvas->Pen->Width = 4;
if(0 < elementCount)
{
boost::scoped_array<TPoint> mPoints(new TPoint[elementCount]);
boost::scoped_array<BYTE> mTypes(new BYTE[elementCount]);
::GetPath(Canvas->Handle, mPoints.get(), mTypes.get(), elementCount);
::FillPath(Canvas->Handle);
::PolyDraw(Canvas->Handle, mPoints.get(), mTypes.get(), elementCount);
}
else
::StrokeAndFillPath(Canvas->Handle);
but here is what I get on the form:
as you can see the text comes out inverted (the text has to be blue and background gray but it is the other way around and the yellow line is around the background instead of text). Does anyone know how I can fix this?
I am using C++ Builder 10 Seattle but if anyone knows that Delphi or pure C++ trick, I can work with that as well.
Thank you

This is explained in TextOut's documentation:
When the TextOut function is placed inside a path bracket, the
system generates a path for the TrueType text that includes each
character plus its character box. The region generated is the
character box minus the text, rather than the text itself. You can
obtain the region enclosed by the outline of the TrueType text by
setting the background mode to transparent before placing the
TextOut function in the path bracket. Following is sample code that demonstrates this procedure.
The below is a Delphi adaption of the mentioned sample code and your snippet, draws yellow outlined blue text:
procedure TForm1.FormPaint(Sender: TObject);
var
elementCount: Integer;
mPoints: array of TPoint;
mTypes: array of Byte;
const
tStr = '15:00';
begin
BeginPath(Canvas.Handle);
Canvas.Brush.Style := bsClear;
TextOut(Canvas.Handle, 5, 5, PChar(tStr), Length(tStr));
EndPath(Canvas.Handle);
Canvas.Brush.Color := clBlue;
Canvas.Pen.Color := clYellow;
Canvas.Pen.Width := 4;
elementCount := GetPath(Canvas.Handle, Pointer(nil)^, Pointer(nil)^, 0);
if elementCount > 0 then begin
SetLength(mPoints, elementCount);
SetLength(mTypes, elementCount);
GetPath(Canvas.Handle, mPoints[0], mTypes[0], elementCount);
Canvas.Brush.Style := bsSolid;
SelectClipPath(Canvas.Handle, RGN_AND);
Canvas.FillRect(ClientRect);
SelectClipRgn(Canvas.Handle, 0);
PolyDraw(Canvas.Handle, mPoints[0], mTypes[0], elementCount);
end else
StrokeAndFillPath(Canvas.Handle);
end;

Related

FireMonkey drawing with tPath does not fill the shape

I want to use a TPathData to draw shapes and fill them with an arbitrary color. I'm using the following code, at Button1Click, wich I extracted from a sample at Embarcadero documentation:
procedure TformPathDrawing.Button1Click(Sender: TObject);
var path: TPathData;
begin
Image1.Bitmap.Canvas.Fill.Color := TAlphaColorRec.Blue;
path := TPathData.Create;
path.Data := 'M 01,00 L 02,01 L 01,02 L 00,01 L 01,00';
Image1.Bitmap.Clear ($FFFFFF);
Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.FillPath (path, 200);
Image1.Bitmap.Canvas.EndScene;
end;
When this code is executed, as expected, a romboid is rendered, but it is not filled up with the color set in the first command. Anyone knows what is wrong? Thanks.
Here is right code. You can find another details in FMX.Objects.pas procedure TCustomPath.UpdateCurrent and other; or just Debug TPath visual component.
{aPath - is vector path of image;
aStretch - how to draw vector data - true - stretch draw, false = fit to bitmap
aBitmap - must be created. In bitmap you should specify:
Width, Height, Bitmap.Canvas.Fill.Color, Bitmap.Canvas.Stroke.Color
and, if you need, another drawing stuff like Gradient, stroke thikness,
background texture) }
procedure TForm1.DrawVectorPath(const aPath: string; aBitmap: TBitmap; aStretch: boolean);
var
vPath: TPathData;
R: TRectF;
begin
Assert(aBitmap <> nil);
vPath := TPathData.Create;
try
vPath.Data := aPath;
aBitmap.Clear($FFFFFF);
if aStretch then
begin
R := vPath.GetBounds;
vPath.Translate(-R.Left, -R.Top);
vPath.Scale(aBitmap.Width / R.Width, aBitmap.Height / R.Height);
end
else // Fit image
begin
R := TRect.Create(0, 0, aBitmap.Width, aBitmap.Height);
vPath.FitToRect(R);
end;
aBitmap.Canvas.BeginScene;
aBitmap.Canvas.FillPath(vPath, 1);
aBitmap.Canvas.DrawPath(vPath, 1);
aBitmap.Canvas.EndScene;
finally
vPath.Free;
end;
end;
Because comments can't have code (at least not formatted). Here is my code, that works.
procedure TForm7.RadioButton6Click(Sender: TObject);
var path: TPathData;
begin
Image1.Bitmap.Canvas.Fill.Color := TAlphaColorRec.Blue;
Image1.Bitmap.Canvas.Stroke.Color := TAlphaColorRec.black;
path := TPathData.Create;
try
path.Data := 'M 01,00 L 20,01 L 10,20 L 00,10 L 01,00';
Image1.Bitmap.Clear ($FFFFFF);
Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.FillPath (path, 200);
Image1.Bitmap.Canvas.EndScene;
finally
path.Free;
end;
end;
Note that the stroke color is set as well. That's the only difference I can make out here.
Follows code that works (in my case):
procedure TformPathDrawing.Button1Click(Sender: TObject);
begin
Image1.Bitmap.Canvas.Fill.Color := TAlphaColorRec.Blue;
path.Clear;
path.Data := 'M 01,00 L 02,01 L 01,02 L 00,01 L 01,00';
Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.FillPath (path, 1);
Image1.Bitmap.Canvas.EndScene;
end;
In my code, variable path is created outside the painting code. Since path.Data; is additive, it is mandatory, before adding the current path.Data;, to place a path.Clear; statement, to clean out whatever remains into the point array. Hope this help other people.

How to load from memory stream to firemonkey (FMX) memo component in C++ Builder Berlin?

I used to use below code to load plenty of text to VCL Memo.
LogMemo->Lines->BeginUpdate();
LogMemo->SelStart = LogMemo->GetTextLen();
LogMemo->SelLength = 0;
LogMemo->SelText = AnsiString((char *)LogMemoBufPtr->Memory, LogMemoBufPtr->Size);
LogMemo->SelStart = LogMemo->GetTextLen();
LogMemo->Perform(EM_SCROLLCARET, 0, 0);
LogMemo->Lines->EndUpdate();
But in FMX Memo component, I can't use "LogMemo->SelText = AnsiString(strFromMemStream)" anymore.
And I also can't use "GetTextLen" method to set selection start.
I tried to modify the code in below but it still didn't work. It always overwrite original content and can't append new text.
LogMemo->Lines->BeginUpdate();
LogMemo->GoToTextEnd();
LogMemo->SelStart = LogMemo->Text.Length();
LogMemo->SelLength = 0;
LogMemo->Text = AnsiString((char *)LogMemoBufPtr->Memory, LogMemoBufPtr->Size);
LogMemo->GoToTextEnd();
LogMemo->SelStart = LogMemo->Text.Length();
LogMemoBufPtr->Clear();
LogMemo->Lines->EndUpdate();
Is there any one know how to do it in FMX Memo component or just to display plenty of text smoothly?
Thanks!
Use the SetText function or Lines property.
In both cases I think you will have to convert the ASCII text to Unicode first.
Solution 1 - Based to the second source code provided, here is a solution to append a text in the TMemo object in FMX/FireMonkey Library.
Step 1 - Instead of trying to place the cursor and select the end of the text.
Before:
LogMemo->GoToTextEnd();
LogMemo->SelStart = LogMemo->Text.Length();
LogMemo->SelLength = 0;
Select all the text and store it in a temporary string.
After:
System::UnicodeString suTemp;
LogMemo->Lines->BeginUpdate();
LogMemo->GoToTextEnd();
LogMemo->SelStart = 0;
LogMemo->SelLength = LogMemo->Text.Length();
suTemp = LogMemo->SelText;
Step 2 - Then append the new text to the temporary string and update the Memo
suTemp += AnsiString((char *)LogMemoBufPtr->Memory, LogMemoBufPtr->Size);
LogMemo->Text = suTemp;
LogMemo->GoToTextEnd();
LogMemo->SelStart = LogMemo->Text.Length();
LogMemoBufPtr->Clear();
LogMemo->Lines->EndUpdate();
Solution 2 - Simple and faster solution when add the text at the end.
Store the current text into a temporary string and add the new text
then update the memo.
System::UnicodeString suTemp;
LogMemo->Lines->BeginUpdate();
suTemp = LogMemo->Text;
suTemp += AnsiString((char *)LogMemoBufPtr->Memory, LogMemoBufPtr->Size);
LogMemo->Text = suTemp;
LogMemo->GoToTextEnd();
LogMemoBufPtr->Clear();
LogMemo->Lines->EndUpdate();
TMemo in FireMonkey has a GoToTextEnd() method:
Moves the cursor to the end of the text in the memo control.
You can't use AnsiString in mobile platforms (without a compiler patch), nor should you anyway since TMemo holds Unicode text (same as VCL's TMemo in Delphi 2009 and later). If your TMemoryStream contains 8bit characters, you need to convert them to Unicode, such as with TEncoding, before appending them to the TMemo. The TEncoding::GetString() methods take a TBytes as input, so you could use TBytesStream instead of TMemoryStream. TBytesStream wraps a TBytes and has a public Bytes property.
Try something like this:
LogMemo->Lines->BeginUpdate();
try
{
LogMemo->GoToTextEnd();
LogMemo->SelLength = 0;
LogMemo->SelText = TEncoding::Default->GetString(LogMemoBufPtr->Bytes, 0, LogMemoBufPtr->Size);
/* or:
TEncoding *enc = TEncoding::GetEncoding(L"desired charset here");
try {
LogMemo->SelText = enc->GetString(LogMemoBufPtr->Bytes, 0, LogMemoBufPtr->Size);
}
__finally {
delete enc;
}
*/
LogMemo->GoToTextEnd();
LogMemo->SelLength = 0;
// not sure if this is the best replacement for EM_SCROLLCARET...
LogMemo->VScrollBar->Value = LogMemo->VScrollBar->Max;
}
__finally {
LogMemo->Lines->EndUpdate();
}
Update: I didn't realize that SelText is read-only in FireMonkey, unlike in VCL. In that case, you have no choice but to append to the Text property, which is not as efficient, especially for large text.
LogMemo->Lines->BeginUpdate();
try
{
LogMemo->Text = LogMemo->Text + TEncoding::Default->GetString(LogMemoBufPtr->Bytes, 0, LogMemoBufPtr->Size);
/* or:
TEncoding *enc = TEncoding::GetEncoding(L"desired charset here");
try {
LogMemo->Text = LogMemo->Text + enc->GetString(LogMemoBufPtr->Bytes, 0, LogMemoBufPtr->Size);
}
__finally {
delete enc;
}
*/
LogMemo->GoToTextEnd();
LogMemo->SelLength = 0;
// not sure if this is the best replacement for EM_SCROLLCARET...
LogMemo->VScrollBar->Value = LogMemo->VScrollBar->Max;
}
__finally {
LogMemo->Lines->EndUpdate();
}

SHGetImageList & Icons 64x64 [duplicate]

If 48x48 or 64x64 icons are present in the Vista Shell how can you get the handle to display one in a TImage using SHGetFileInfo?
I'd like to select an icon from a imagelist that represents a folder path and display a 48x48 or 64x64 icon in a Timage.
// load the large system image for the current path into Image1
SHGetFileInfo( PChar( CurrentPath ), FILE_ATTRIBUTE_NORMAL, SFI,
SizeOf( TSHFileInfo ), SHGFI_ICON or SHGFI_LARGEICON or SHGFI_SHELLICONSIZE or
SHGFI_SYSICONINDEX or SHGFI_TYPENAME or SHGFI_DISPLAYNAME );
AImageIndex := SFI.iIcon;
ImageList2.GetBitmap( AImageIndex, Image1.Picture.Bitmap );
Bill
You must use the SHGetImageList function, to get the image list with the larger icons.
Here you have an example in delphi
uses ShellApi, Commctrl, ShlObj;
const
SHIL_LARGE = $00; //The image size is normally 32x32 pixels. However, if the Use large icons option is selected from the Effects section of the Appearance tab in Display Properties, the image is 48x48 pixels.
SHIL_SMALL = $01; //These images are the Shell standard small icon size of 16x16, but the size can be customized by the user.
SHIL_EXTRALARGE= $02; //These images are the Shell standard extra-large icon size. This is typically 48x48, but the size can be customized by the user.
SHIL_SYSSMALL = $03; //These images are the size specified by GetSystemMetrics called with SM_CXSMICON and GetSystemMetrics called with SM_CYSMICON.
SHIL_JUMBO = $04; //Windows Vista and later. The image is normally 256x256 pixels.
IID_IImageList: TGUID= '{46EB5926-582E-4017-9FDF-E8998DAA0950}';
function GetImageListSH(SHIL_FLAG:Cardinal): HIMAGELIST;
type
_SHGetImageList = function (iImageList: integer; const riid: TGUID; var ppv: Pointer): hResult; stdcall;
var
Handle : THandle;
SHGetImageList: _SHGetImageList;
begin
Result:= 0;
Handle:= LoadLibrary('Shell32.dll');
if Handle<> S_OK then
try
SHGetImageList:= GetProcAddress(Handle, PChar(727));
if Assigned(SHGetImageList) and (Win32Platform = VER_PLATFORM_WIN32_NT) then
SHGetImageList(SHIL_FLAG, IID_IImageList, Pointer(Result));
finally
FreeLibrary(Handle);
end;
end;
Procedure GetIconFromFile(aFile:String; var aIcon : TIcon;SHIL_FLAG:Cardinal);
var
aImgList : HIMAGELIST;
SFI : TSHFileInfo;
Begin
//Get the index of the imagelist
SHGetFileInfo(PChar(aFile), FILE_ATTRIBUTE_NORMAL, SFI,
SizeOf( TSHFileInfo ), SHGFI_ICON or SHGFI_LARGEICON or SHGFI_SHELLICONSIZE or
SHGFI_SYSICONINDEX or SHGFI_TYPENAME or SHGFI_DISPLAYNAME );
if not Assigned(aIcon) then
aIcon:= TIcon.Create;
//get the imagelist
aImgList:= GetImageListSH(SHIL_FLAG);
//extract the icon handle
aIcon.Handle:= ImageList_GetIcon(aImgList, Pred(ImageList_GetImageCount(aImgList)), ILD_NORMAL);
End;
You can use these functions in this way
var
hicon :TIcon;
begin
hicon:= TIcon.Create;
try
GetIconFromFile('C:\Tools\reflector\readme.htm',hicon,SHIL_JUMBO);
Image1.Picture.Icon.Assign(hIcon); //assign to timage
finally
hIcon.Free;
end;
end;
Read here: (Code in C++)
Getting the 16×16 and 32×32 icons on
Windows is relatively easy and is
often as simple as one call to
ExtractIconEx.
However, getting the extra large
(48×48) and jumbo (256×256) icons
introduced respectively by XP and
Vista is slighly more complex. This is
normally done by:
Getting the file information, in particular the icon index, for the
given file using SHGetFileInfo
Retrieving the system image list where all the icons are stored
Casting the image list to an IImageList interface and getting the
icon from there
We found that the index of the file was not correct because the incorrect icon was shown during testing of the code posted by RRUZ. The GetIconFromFile method was setting the index baised on the image count. We changed GetIconFromFile to use the SFI index ( aIndex := SFI.iIcon ) and the correct icon was obtained. Apparently the shellimagelist is constantly changing so the index was incorrect.
Thanks to all to assisted. This seems like a very good piece of code now.
procedure GetIconFromFile( aFile: string; var aIcon: TIcon;SHIL_FLAG: Cardinal );
var
aImgList: HIMAGELIST;
SFI: TSHFileInfo;
aIndex: integer;
begin // Get the index of the imagelist
SHGetFileInfo( PChar( aFile ), FILE_ATTRIBUTE_NORMAL, SFI, SizeOf( TSHFileInfo ),
SHGFI_ICON or SHGFI_LARGEICON or SHGFI_SHELLICONSIZE or SHGFI_SYSICONINDEX or SHGFI_TYPENAME or SHGFI_DISPLAYNAME );
if not Assigned( aIcon ) then
aIcon := TIcon.Create;
// get the imagelist
aImgList := GetImageListSH( SHIL_FLAG );
// get index
//aIndex := Pred( ImageList_GetImageCount( aImgList ) );
aIndex := SFI.iIcon;
// extract the icon handle
aIcon.Handle := ImageList_GetIcon( aImgList, aIndex, ILD_NORMAL );
end;
kicon
i use the appropriate kicon method (LoadFromFile/LoadFromModule/LoadFromModuleByIndex) depending on the source file type.
if these methods fail, i use PrivateExtractIconsA:
function PrivateExtractIcons(lpszFile: PChar; nIconIndex, cxIcon, cyIcon: integer; phicon: PHandle; piconid: PDWORD; nIcons, flags: DWORD): DWORD; stdcall; external 'user32.dll' name 'PrivateExtractIconsA';
and pass the resulting handle to kicon's LoadFromHandle method.
once it's been loaded into kicon, iterate over the icondata[] array to pick out the size you want. kicon has methods to convert the returned image to PNG.

Converting LTR to RTL?

As you know many ui components and dev tools doesn't support rtl , we can call it flipping text , cause result is same example :
LTR
سلام salam متن راهنما word
RTL
word متن راهنما salam سلام
is there anyway to convert this LTR to RTL , i don't have any idea and language doesn't matter
Actually i am seeking for a solution to get this done in RAD Studio Firemonkey Application , as you may know firemonkey apps doesn't support rtl it's in roadmap of rad studio but not implemented yet
Under Windows, you can do that via the UniScribe API.
I've used this to convert Unicode text into set of glyphs, for our Open Source PDF writer.
You have source code sample in SynPdf.pas unit. See the TPdfWrite.AddUnicodeHexTextUniScribe method:
function TPdfWrite.AddUnicodeHexTextUniScribe(PW: PWideChar;
WinAnsiTTF: TPdfFontTrueType; NextLine: boolean; Canvas: TPdfCanvas): boolean;
var L, i,j: integer;
res: HRESULT;
max, count, numSp: integer;
Sp: PScriptPropertiesArray;
W: PWideChar;
items: array of TScriptItem;
level: array of byte;
VisualToLogical: array of integer;
psc: pointer; // opaque Uniscribe font metric cache
complex,R2L: boolean;
complexs: array of byte;
glyphs: array of TScriptVisAttr;
glyphsCount: integer;
OutGlyphs, LogClust: array of word;
procedure Append(i: Integer);
// local procedure used to add glyphs from items[i] to the PDF content stream
var L: integer;
W: PWideChar;
procedure DefaultAppend;
var tmpU: array of WideChar;
begin
SetLength(tmpU,L+1); // we need the text to be ending with #0
move(W^,tmpU[0],L*2);
AddUnicodeHexTextNoUniScribe(pointer(tmpU),WinAnsiTTF,false,Canvas);
end;
begin
L := items[i+1].iCharPos-items[i].iCharPos; // length of this shapeable item
if L=0 then
exit; // nothing to append
W := PW+items[i].iCharPos;
if not GetBit(complexs[0],i) then begin
// not complex items are rendered as fast as possible
DefaultAppend;
exit;
end;
res := ScriptShape(0,psc,W,L,max,#items[i].a,
pointer(OutGlyphs),pointer(LogClust),pointer(glyphs),glyphsCount);
case res of
E_OUTOFMEMORY: begin // max was not big enough (should never happen)
DefaultAppend;
exit;
end;
E_PENDING, USP_E_SCRIPT_NOT_IN_FONT: begin // need HDC and a selected font object
res := ScriptShape(Canvas.FDoc.GetDCWithFont(WinAnsiTTF),
psc,W,L,max,#items[i].a,
pointer(OutGlyphs),pointer(LogClust),pointer(glyphs),glyphsCount);
if res<>0 then begin // we won't change font if necessary, sorry
// we shall implement the complex technic as stated by
// http://msdn.microsoft.com/en-us/library/dd374105(v=VS.85).aspx
DefaultAppend;
exit;
end;
end;
0: ; // success -> will add glyphs just below
else exit;
end;
// add glyphs to the PDF content
// (NextLine has already been handled: not needed here)
AddGlyphs(pointer(OutGlyphs),glyphsCount,Canvas);
end;
begin
result := false; // on UniScribe error, handle as Unicode
// 1. Breaks a Unicode string into individually shapeable items
L := StrLenW(PW)+1; // include last #0
max := L+2; // should be big enough
SetLength(items,max);
count := 0;
if ScriptItemize(PW,L,max,nil,nil,pointer(items),count)<>0 then
exit; // error trying processing Glyph Shaping -> fast return
// 2. guess if requiring glyph shaping or layout
SetLength(complexs,(count shr 3)+1);
ScriptGetProperties(sP,numSp);
complex := false;
R2L := false;
for i := 0 to Count-2 do // don't need Count-1 = Terminator
if fComplex in sP^[items[i].a.eScript and (1 shl 10-1)]^.fFlags then begin
complex := true;
SetBit(complexs[0],i);
end else
if fRTL in items[i].a.fFlags then
R2L := true;
if not complex then begin
// no glyph shaping -> fast append as normal Unicode Text
if R2L then begin
// handle Right To Left but not Complex text
W := pointer(items); // there is enough temp space in items[]
W[L] := #0;
dec(L);
for i := 0 to L do
W[i] := PW[L-i];
AddUnicodeHexTextNoUniScribe(W,WinAnsiTTF,NextLine,Canvas);
result := true; // mark handled here
end;
exit;
end;
// 3. get Visual Order, i.e. how to render the content from left to right
SetLength(level,count);
for i := 0 to Count-1 do
level[i] := items[i].a.s.uBidiLevel;
SetLength(VisualToLogical,count);
if ScriptLayout(Count,pointer(level),pointer(VisualToLogical),nil)<>0 then
exit;
// 4. now we have enough information to start drawing
result := true;
if NextLine then
Canvas.MoveToNextLine; // manual NextLine handling
// 5. add glyphs for all shapeable items
max := (L*3)shr 1+32; // should be big enough - allocate only once
SetLength(glyphs,max);
SetLength(OutGlyphs,max);
SetLength(LogClust,max);
psc := nil; // cached for the same character style used
if Canvas.RightToLeftText then
// append from right to left visual order
for j := Count-2 downto 0 do // Count-2: ignore last ending item
Append(VisualToLogical[j]) else
// append from left to right visual order
for j := 0 to Count-2 do // Count-2: ignore last ending item
Append(VisualToLogical[j]);
end;
Of course, this is under Windows only. So it won't work on Mac OS X. You'll have to use another library under Mac OS X...
It's complicated. If you want to do it correctly, you must use the Bidi Library from the International Components for Unicode.
If you use MFC, here is how to set both righting direction and alignment. Assuming your CEdit control is named m_TextEdit:
void MyDialog::SetLangDirection(bool RTL)
{
DWORD w_dwStyle;
w_dwStyle = GetWindowLong(m_TextEdit.GetSafeHwnd(), GWL_EXSTYLE);
if (RTL)
{
w_dwStyle -= WS_EX_LEFT | WS_EX_LTRREADING;
w_dwStyle |= WS_EX_RIGHT | WS_EX_RTLREADING;
}
else
{
w_dwStyle -= WS_EX_RIGHT | WS_EX_RTLREADING;
w_dwStyle |= WS_EX_LEFT | WS_EX_LTRREADING;
}
SetWindowLong(m_TextEdit.GetSafeHwnd(), GWL_EXSTYLE, w_dwStyle);
}
See my tip.

EM_SETSEL swaps parameters

I use EM_SETSEL message to select text in edit control. I need to select some text from the end to the middle, so that caret position is in the middle of the text.
MSDN documentation states the following:
The start value can be greater than the end value. The lower of the two values specifies the character position of the first character in the selection. The higher value specifies the position of the first character beyond the selection.
The start value is the anchor point of the selection, and the end value is the active end. If the user uses the SHIFT key to adjust the size of the selection, the active end can move but the anchor point remains the same.
But it seems that lesser value always becomes an anchor, e.g. I cannot achieve the desired behaviour.
Code sample (where "parent" is CWnd*):
TRACE("EM_SETSEL(%d, %d)\n", pos1, pos2);
parent->SendMessage(EM_SETSEL, pos1, pos2);
parent->SendMessage(EM_GETSEL, (WPARAM)&pos1, (LPARAM)&pos2);
TRACE("EM_GETSEL(%d, %d)\n", pos1, pos2);
produces the output:
EM_SETSEL(5, 1)
EM_GETSEL(1, 5)
Is there another way to get the desired selection?
Regarding EM_GETSEL/EM_SETSEL:
EM_GETSEL retrieves left/right positions
EM_SETSEL sets anchor/active positions
EM_SETSEL uses anchor/active positions, allowing you to easily place the caret at the left/right of the selection, so I'm not sure why a kludge was used in the other answer.
EM_GETSEL is the awkward window message, for which a kludge is necessary. This kludge temporarily changes the selection to 0 characters, in order to retrieve the active position, however, when I've used it I haven't seen any visible change.
To retrieve anchor/active positions:
use EM_GETSEL to retrieve the left/right positions
use EM_SETSEL to temporarily set the selection to 0 characters, leaving the caret at the active position
use EM_GETSEL to retrieve the active position
use EM_SETSEL to restore the original selection
Some example AutoHotkey code for setting the selection:
q:: ;Notepad - set active position (caret) at right
PostMessage, 0xB1, 5, 10, Edit1, A ;EM_SETSEL := 0xB1
return
w:: ;Notepad - set active position (caret) at left
PostMessage, 0xB1, 10, 5, Edit1, A ;EM_SETSEL := 0xB1
return
Some example AutoHotkey functions for getting/setting the selection:
JEE_EditGetRange(hCtl, ByRef vPos1, ByRef vPos2)
{
VarSetCapacity(vPos1, 4), VarSetCapacity(vPos2, 4)
SendMessage, 0xB0, % &vPos1, % &vPos2,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0 ;(left, right)
vPos1 := NumGet(&vPos1, 0, "UInt"), vPos2 := NumGet(&vPos2, 0, "UInt")
}
;==================================================
JEE_EditSetRange(hCtl, vPos1, vPos2, vDoScroll:=0)
{
SendMessage, 0xB1, % vPos1, % vPos2,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1 ;(anchor, active)
if vDoScroll
SendMessage, 0xB7, 0, 0,, % "ahk_id " hCtl ;EM_SCROLLCARET := 0xB7
}
;==================================================
;note: although this involves deselecting and selecting it seems to happen invisibly
JEE_EditGetRangeAnchorActive(hCtl, ByRef vPos1, ByRef vPos2)
{
;get selection
VarSetCapacity(vPos1, 4), VarSetCapacity(vPos2, 4)
SendMessage, 0xB0, % &vPos1, % &vPos2,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0
vPos1 := NumGet(&vPos1, 0, "UInt"), vPos2 := NumGet(&vPos2, 0, "UInt")
if (vPos1 = vPos2)
return
vPos1X := vPos1, vPos2X := vPos2
;set selection to 0 characters and get active position
SendMessage, 0xB1, -1, 0,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1
VarSetCapacity(vPos2, 4)
SendMessage, 0xB0, % &vPos2, 0,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0
vPos2 := NumGet(&vPos2, 0, "UInt")
;restore selection
vPos1 := (vPos2 = vPos2X) ? vPos1X : vPos2X
SendMessage, 0xB1, % vPos1, % vPos2,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1 ;(anchor, active)
}
LINKS:
The functions above that I originally posted at the AutoHotkey forums:
GUI COMMANDS: COMPLETE RETHINK - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=25893&p=138292#p138292
#vafylec's answer is good. I've just rewritten it here Delphi, which is a little easier to parse and translate to other languages than AHK.
Basically, EM_SETSEL allows you to set the anchor at either end of the range, but EM_GETSEL only ever returns the first and last character, losing that information. With a little kludge, you can work around this:
procedure GetEditSelection(Handle: THandle; var Anchor, Start, Finish: Integer);
begin
SendMessage(Handle, EM_GETSEL, NativeUInt(#Start), NativeUInt(#Finish));
SendMessage(Handle, EM_SETSEL, -1, 0);
SendMessage(Handle, EM_GETSEL, NativeUInt(#Anchor), 0);
if Anchor = Start then
SendMessage(Handle, EM_SETSEL, Finish, Start)
else
SendMessage(Handle, EM_SETSEL, Start, Finish);
end;