2024/09/29

TRichEdit.SelAttributes.Name の欠落

 さて「TRichEdit.SelAttributes.Name の欠落

という事柄に何日も悩まされていたわけですが、どうやら一段落ついたようなので覚え書き。


Delphi2009 前後の RichEdit で「UD デジタル 教科書体 N-B」など少し長い名前の日本語名を持つ文字列をドラッグ&ドロップ或いはクリップボードから貼り付けた場合にフォント名の最後の方が欠落してしまうという問題。

Delphi XE でも発生して、Delphi 11 以降では、TRichEditコンポーネントが、RichEdit 4.1 に変わっているので問題ないらしい。

Delphi5 では問題ないので最初は Delphi が内部で UniCode に対応した時のせいかと思ったんだけど、オリジナルの NanaTree でもおかしくなるので(多分 Delphi6 製)これは結構深い問題かなと、ChatGPT らと話し合いながら色々と探索。


■UniCode 方面から攻めたので最初は「型」と「エンコード」を重点に調べる。

昔から RichEdit くんは、ASCII でずっと頑張っていたから……

VCL ソースを D5 と比較しながら、ちょっと修正してはコンパイル→ここでもない→一旦閉じて→またいじって……

■自分でドラッグ&ドロップやってみれば同じようなことを GREP して探せるのでは?

■どうやってドラッグ中のデータを参照するの? > ChatGPT

ほーん

なる

■どうせなら RichEdit 自身のドラッグ&ドロップ無効にして自分で処理してしまえ!

■出来たんじゃね?

■ペーストも処理しなくちゃだわ

■出来たんじゃね?


満足して微調整(移動する場合元の文字列を削除したりするのもやらなくちゃいけないから SelStart より前にドロップしたとか色々ややこしい)



【実際には TJvRichEdit 使っているので】

■画像とかファイルとかの外部からのドラッグ&ドロップが出来なくなってるじゃん!

これは今までやってきたことすべてリセットやん!


■TJvRichEdit には OnQueryAcceptData イベントがあってここで唯一「IDataObject」を参照することが出来る。

ここに今までやってきたことを全部詰め込めばええやんか


多分これでファイナルアンサー

サンプル♪ヘ(^^ヘ)(ノ^^)ノヘ(^^ヘ)(ノ^^)ノ♪
フォームに TRichEdit 貼り付けて Form.OnCreate で適当に文字列入れてフォント変更。
TRichEdit の OnSelectionChang でフォント名を参照出来るようにする。
uses に(無かったら)ComCtrls, RichEdit, ActiveX の3つを追加。

以下ソース
  
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, JvExStdCtrls, JvRichEdit, ComCtrls, RichEdit, ActiveX;

type
  TForm1 = class(TForm)
    JvRichEdit1: TJvRichEdit;
    procedure JvRichEdit1QueryAcceptData(Sender: TObject;
      const ADataObject: IDataObject; var AFormat: Word;
      ClipboardOperationKind: Cardinal; Really: Boolean; IconMetaPict: Cardinal;
      var Handled: Boolean);
    procedure FormCreate(Sender: TObject);
    procedure JvRichEdit1SelectionChange(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function InsertRTF(Target:TRichEdit;RTF:string):Boolean;
var
  SL:TStringList;
  ES:TEditStream;
  Rich:TRichEdit;
  MS:TMemoryStream;

  // コールバック関数
  function myCallback(dwCookie:DWORD; pbBuff:PByte;
    cb:Longint; var pcb:Longint):DWORD; stdcall;
  var
    MS:TMemoryStream;
  begin
    Result:=0;
    MS:=TMemoryStream(dwCookie);
    pcb:=MS.Read(pbBuff^, cb);
  end;

begin
  SL:=TStringList.Create;
  try
    SL.Add(RTF);

    Rich:=TRichEdit.Create(nil);
    try
      Rich.Visible:=False;
      Rich.Parent:=Application.MainForm;
      Rich.PlainText:=False;//.StreamFormat:=sfRichText;

      MS:=TMemoryStream.Create;
      try
        MS.Position:=0;
        SL.SaveToStream(MS,TEncoding.ASCII);
        MS.Position:=0;
        ES.dwCookie:=DWORD(MS);
        ES.dwError:=0;
        ES.pfnCallback:=@myCallback;

        Target.Perform(EM_STREAMIN, SF_RTF or SFF_SELECTION, LParam(@ES));

      finally
        MS.Free;
      end;
    finally
      Rich.Free;
    end;
  finally
    SL.Free;
  end;
  Result:=True;
end;

procedure TForm1.FormCreate(Sender: TObject);
const
  TestText='ABCDE';
  FontName='UDデジタル教科書体 N-B';
begin
  Font.Size:=12;//←我、老眼なので
//ここだけわからん何故かデフォのTahomaだとD&Dのあと日本語が文字化けする
  Font.Name:='MS ゴシック';

  JvRichEdit1.Lines.Text:=TestText;
  JvRichEdit1.SelStart:=0;
  JvRichEdit1.SelLength:=1;
  JvRichEdit1.SelAttributes.Name:=FontName;
  JvRichEdit1.SelAttributes.Color:=clRed;
  JvRichEdit1.SelAttributes.Style:=[fsBold];
end;

procedure TForm1.JvRichEdit1SelectionChange(Sender: TObject);
begin
  Caption:=JvRichEdit1.SelAttributes.Name;
end;

procedure TForm1.JvRichEdit1QueryAcceptData(Sender: TObject;
  const ADataObject: IDataObject; var AFormat: Word;
  ClipboardOperationKind: Cardinal; Really: Boolean; IconMetaPict: Cardinal;
  var Handled: Boolean);
var
  FormatEtc: TFormatEtc;
  RtfText: string;
  Medium: TStgMedium;
  GlobalMem: HGlobal;
  pText: PAnsiChar;
  CF_RTF: UINT;
begin
  if Really then
  begin
// IDataObjectからデータを取得
    CF_RTF:=RegisterClipboardFormat('Rich Text Format');
    FormatEtc.cfFormat := CF_RTF;  // CF_RTFを登録
    FormatEtc.ptd := nil;
    FormatEtc.dwAspect := DVASPECT_CONTENT;
    FormatEtc.lindex := -1;
    FormatEtc.tymed := TYMED_HGLOBAL;
//リッチエディタ上でのD&D
    if ADataObject.GetData(FormatEtc, Medium) = S_OK then
    begin
      GlobalMem := Medium.hGlobal;
      pText := GlobalLock(GlobalMem);
      SetString(RtfText, pText, GlobalSize(GlobalMem));
      GlobalUnlock(GlobalMem);
      ReleaseStgMedium(Medium);
      Handled:=True;//これがきも!
      InsertRTF(TRichEdit(JvRichEdit1),RtfText);
    end;
  end;
end;

end.

そして出来上がったのがこちらです。

ドラッグ&ドロップで移動しても他のソフトから貼り付けても「UD デジタル 教科書体 N-B」のフォント名は壊れていない
(ちなみに Ctrl+ でコピードラッグです)

ここまでやって凄く不思議な現象が。
今、上で追加したソースの InsertRTF と QueryAcceptData の部分を削除して実行したらフォント名の欠落は起きないんだよなー。
まあ最新の開発環境を使えってはなしなんだけどそれはそれで最初のリンク先読んで貰ったらわかるけど
RichEdit 4.x がウンチなんよね

2020/02/14

覚え書き

多分今まで出ていたアプリケーションエラー全ての根源がわかった
オリジナルからあったサブマリンバグ

オリジナル当初からノード破棄したときのイベントで
・ノードごとに持っていたデータを破棄して(このデータに対してはちゃんとFreeAndNilっていう破棄したあと「nil(ヌル)」を代入してる)
・ノードのタイトルに「」を代入
・ノード移動履歴から削除
という三つの処理をしてたんだけどノード自体に「nil(ヌル)」を入れていなかった

オリジナルもおいらもノードを参照するときは必ず
Assigned 関数というのを使ってそのノードがちゃんと使えるのか確認してるんだけど
その関数は「変数または手続き型変数が nil (値が割り当てられていない) かをテストします」というもので
オリジナルではあんまり込み入ったことをしていなかったから表面化していなかっただけ
ただ一つだけこれが原因だろうなというのが UniCode ファイルを読み込んで
---------------------------
警告
---------------------------
xx.nnaをnanaファイルとして読み込めませんでした。
テキストファイルとして読み込んでみますか?
---------------------------
はい(Y)   いいえ(N) 
---------------------------

いいえを選ぶと Access violation になってタスクキルしか出来なくなる



オリジナルソース
//----------------------------------------
// ノード破棄
//----------------------------------------


procedure TTreeForm.TreeViewFreeNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
begin
  FreeAndNil(NodeData(Node).Strings);
  NodeData(Node).Caption := '';
  MainForm.TreeViewFreeNode(Sender);
//↓に一行入れるだけで多い日も安心
  Node:=nil;
end;

2020/02/08

四捨五入

普段は何も考えてなかったけどやっぱり四捨五入はちゃんとしないとね

↓の「Roundoff 関数」は Delphi5 のヘルプ「Round 関数」のところに載ってる
通常の四捨五入を使う場合には以下の関数を使用してください』というやつ

100/6=16.6666666...
Trunc だと切り捨てられちゃう





function Roundoff(X: Extended):integer; begin if x >= 0 then Result := Trunc(x + 0.5) else Result := Trunc(x - 0.5); end; procedure TForm1.Button1Click(Sender: TObject); var i:integer; begin for i:=1 to 10 do begin Memo1.Lines.Add(IntToStr(Roundoff(100/i))+' '+IntToStr(Trunc(100/i))); end; end; 

2019/11/17

ぬるぽ

これはわかりやすい画像

if Assigned(トイレットペーパー) then//トイレットペーパーにアクセスできたら
ShowMessage(IntToStr(Length(トイレットペーパー)));//長さを表示

↓この一行がないと
if Assigned(トイレットペーパー) then
右に見に行ったときに「トイレットペーパー?そんなのねーじゃん!」ってエラーになる
左側なら「ゼロだよ~♪」って返ってくる

2014/07/01

でるけどは残念ながら休刊となりました

>6ヶ月間発行がなく、残念ながら休刊となりました
>
>・メルマガID   :0000016258
>・メルマガタイトル:Delphi 買ったけど。。
>・最新発行日   :2013/12/01
>-------------------------------------------------
>
>上記メールマガジンにつきましては、
>長期に渡り発行されていないため、まことに残念ではありますが
>休刊措置を行わせていただきました。

XEシリーズになってから一つもさわっていないので
もう新しい話題とかついていけません
こちらには何か思いついたこととかあればまた書くこともあるかも知れません

それでは(^.^)/~~

2013/01/05

XE3 での TImage / TImageControl / TImageViewer

メモ

[要注意コンポーネント (2) - DEKO のアヤシいお部屋]
http://ht-deko.minim.ne.jp/ft1301.html#130105_01