[Example] Typing opening and closing quotes
Posted: Mon Dec 23, 2013 5:56 pm
The example automatically changes typed " and ' characters to the appropriate opening or closing quote.
The result depends on the keyboard language:
“ ” (example: English)
„ “ (example: German)
„ ” (example: Polish)
« » (example: Russian)
» « (Danish)
” ” (Finnish, Swedish)
I based this work on the following tables:
http://en.wikipedia.org/wiki/Internatio ... tion_marks
http://technet.microsoft.com/en-us/libr ... 25682.aspx
If this procedure chooses an incorrect (or not traditional) quotes for your language, please report to richviewgmailcom or to this topic.
How it works.
In OnKeyPress event, we check for ' and " characters. If one of these characters is typed, we find an appropriate replacement using GetQuote().
Of course, we can assign this replacement character to Key parameter of OnKeyPress, but this is not a good solution, because undo will simple delete this character. Instead, we assign this character to a global variable ReplaceChar, and perform a replacement in OnChange event. In this way, the first undo will revert a quote replacement, the second undo will delete a typed character.
The code below is for TRichView version 17.2 and older. For newer versions of TRichView, see http://www.trichview.com/forums/viewtop ... 706#p34706
The result depends on the keyboard language:
“ ” (example: English)
„ “ (example: German)
„ ” (example: Polish)
« » (example: Russian)
» « (Danish)
” ” (Finnish, Swedish)
I based this work on the following tables:
http://en.wikipedia.org/wiki/Internatio ... tion_marks
http://technet.microsoft.com/en-us/libr ... 25682.aspx
If this procedure chooses an incorrect (or not traditional) quotes for your language, please report to richviewgmailcom or to this topic.
How it works.
In OnKeyPress event, we check for ' and " characters. If one of these characters is typed, we find an appropriate replacement using GetQuote().
Of course, we can assign this replacement character to Key parameter of OnKeyPress, but this is not a good solution, because undo will simple delete this character. Instead, we assign this character to a global variable ReplaceChar, and perform a replacement in OnChange event. In this way, the first undo will revert a quote replacement, the second undo will delete a typed character.
The code below is for TRichView version 17.2 and older. For newer versions of TRichView, see http://www.trichview.com/forums/viewtop ... 706#p34706
Code: Select all
uses RVUni;
{$IFnDEF UNICODE}
const LDQuo = #147;
const RDQuo = #148;
const LAQuo = #171;
const RAQuo = #187;
const LSQuo = #145;
const RSQuo = #146;
const BDQuo = #132;
{$ELSE}
const LDQuo = #$201C;
const RDQuo = #$201D;
const LAQuo = #$00AB;
const RAQuo = #$00BB;
const LSQuo = #$2018;
const RSQuo = #$2019;
const BDQuo = #$201E;
{$ENDIF}
function GetQuote(rve: TCustomRichViewEdit; Ch: Char): Char;
var ItemNo1, ItemNo2, Offs1, Offs2: Integer;
DelimBefore, DelimAfter: Boolean;
{..........................................}
function IsDelim(ItemNo, Index: Integer): Boolean;
var
s : String;
{$IFnDEF UNICODE}
CodePage: Cardinal;
{$ENDIF}
begin
s := rve.GetItemText(ItemNo);
if (Index>Length(s)) or (Index<0) then begin
Result := True;
exit;
end;
{$IFnDEF UNICODE}
CodePage := rve.RVData.GetItemCodePage(ItemNo);
{$ENDIF}
Result :=
{$IFDEF UNICODE}
rve.RVData.IsDelimiterW(s[Index])
{$ELSE}
rve.RVData.IsDelimiterA(s[Index], CodePage)
{$ENDIF}
end;
{..........................................}
function DoGetQuote(Opening: Boolean): Char;
var Lang: Cardinal;
begin
Lang := RVU_GetKeyboardLanguage;
case Ch of
'"':
case Lang of
// Albanian, Bulgarian, Czech, Estonian, Georgian,
// German,
// Icelandic, Lithuanian, Macedonian,
// Slovak, Slovene, Sorbian
$041c, $0402, $0405, $0425, $0437,
$0c07, $0407, $1407, $1007,
$040f, $0427, $042f,
$041b, $0424, $082e:
if Opening then
Result := BDQuo
else
Result := LDQuo;
// Croatian, Hungarian, Polish, Romanian,
// Serbian,
$041a, $101a, $040e, $0415, $0418,
$1c1a, $181a, $301a, $2c1a, $281a, $241a, $0c1a, $081a:
if Opening then
Result := BDQuo
else
Result := RDQuo;
// Armenian, Azerbaijani, Basque, Belarusian, Catalan,
// French,
// German-Swiss,
// Greek, Italian, Latvian, Norwegian
// Persian, Portuguese, Russian,
// Spanish,
// Ukrainian
$042b, $082c, $042c, $042d, $0423, $0403,
$080c, $0c0c, $040c, $140c, $180c, $100c,
$0807,
$0408, $0410, $0810, $0426, $0414, $0814,
$0429, $0416, $0816, $0419,
$2c0a, $200a, $400a, $340a, $240a, $140a, $1c0a, $300a, $440a, $100a,
$480a, $080a, $4c0a, $180a, $3c0a, $280a, $500a, $0c0a, $040a, $540a, $380a,
$0422:
if Opening then
Result := LAQuo
else
Result := RAQuo;
// Danish
$0406:
if Opening then
Result := RAQuo
else
Result := LAQuo;
// Finnish, Swedish
$040b, $081d, $041d:
Result := RDQuo;
else
if Opening then
Result := LDQuo
else
Result := RDQuo;
end;
'''':
case Lang of
// Finnish, Swedish
$040b, $081d, $041d:
Result := RSQuo;
else
if Opening then
Result := LSQuo
else
Result := RSQuo
end
else
Result := Ch;
end;
end;
{..........................................}
begin
// Returing the original character by default
Result := Ch;
rve := rve.TopLevelEditor;
if rve.SelectionExists then
exit;
// Finding the position before (ItemNo1, Offs1) and after (ItemNo2, Offs2) the insertion point
ItemNo1 := rve.CurItemNo;
Offs1 := rve.OffsetInCurItem;
if Offs1<=rve.GetOffsBeforeItem(ItemNo1) then begin
ItemNo2 := ItemNo1;
Offs2 := Offs1;
dec(ItemNo1);
if ItemNo1>=0 then
if not rve.IsFromNewLine(ItemNo1) then
Offs1 := rve.GetOffsAfterItem(ItemNo1)
else begin
ItemNo1 := -1;
Offs1 := -1;
end
else
Offs1 := -1;
end
else if Offs1>=rve.GetOffsAfterItem(ItemNo1) then begin
ItemNo2 := ItemNo1+1;
if ItemNo2<rve.ItemCount then
if not rve.IsFromNewLine(ItemNo2) then
Offs2 := rve.GetOffsBeforeItem(ItemNo2)
else begin
ItemNo2 := rve.ItemCount;
Offs2 := -1;
end
else
Offs2 := -1;
end
else begin
ItemNo2 := ItemNo1;
Offs2 := Offs1;
end;
// Is a delimiter (or nothing, or non-text) before the insertion point?
if (ItemNo1<0) or (rve.GetItemStyle(ItemNo1)<0) or
(rve.Style.TextStyles[rve.GetItemStyle(ItemNo1)].Charset=SYMBOL_CHARSET) then
DelimBefore := True
else
DelimBefore := IsDelim(ItemNo1, Offs1-1);
// Is a delimiter (or nothing, or non-text) after the insertion point?
if (ItemNo2>=rve.ItemCount) or (rve.GetItemStyle(ItemNo2)<0) or
(rve.Style.TextStyles[rve.GetItemStyle(ItemNo2)].Charset=SYMBOL_CHARSET) then
DelimAfter := True
else
DelimAfter := IsDelim(ItemNo2, Offs2);
// Choosing a quote
if DelimBefore and not DelimAfter then
Result := DoGetQuote(True)
else if not DelimBefore and DelimAfter then
Result := DoGetQuote(False);
end;
// this variable can be moved to a form
const ReplaceChar: Char = #0;
procedure TForm3.RichViewEdit1KeyPress(Sender: TObject; var Key: Char);
begin
ReplaceChar := #0;
if (Key='"') or (Key='''') then begin
ReplaceChar := GetQuote(RichViewEdit1, Key);
if ReplaceChar=Key then
ReplaceChar := #0;
end;
end;
procedure TForm3.RichViewEdit1Change(Sender: TObject);
var rve: TCustomRichViewEdit;
ItemNo, Offs: Integer;
Ch: Char;
begin
if ReplaceChar<>#0 then begin
Ch := ReplaceChar;
ReplaceChar := #0;
rve := RichViewEdit1.TopLevelEditor;
ItemNo := rve.CurItemNo;
Offs := rve.OffsetInCurItem;
rve.SetSelectionBounds(ItemNo, Offs-1, ItemNo, Offs);
rve.InsertText(Ch);
end;
end;