タグ別アーカイブ: TWebBrowser

TWebBrowser でクリックした位置の文字インデックスを取得する方法

StyleNoteではTWebBrowser上でクリックすると、該当するHTMLソースにキャレットを移動させることができます。プレビューを見ながら、編集箇所に簡単に移動できる機能ですが、他のフリーのHTMLエディタではあまり見かけない機能です。

実装されない理由は、たぶん、IE の Scripting Object Interfaces(MSHTML)に、ソースコードベースで【要素】の文字インデックスを取得するインターフェイスが用意されていないからだと思います(私が探しきれていないだけかもしれませんが)。

MSHTMLでは各【要素】をIHTMLELementオブジェクトで特定することはできますが、そのIHTMLElementがソースコード上でどの位置にあるか(文字インデックス)を特定する方法はありません。

StyleNoteでの実装方法はこんな感じ↓です。

 1) TWebBrowser上でクリックされた要素を取得する
 2) その要素の IHTMLUniqueName::UniqueNumber を取得する
 3) StyleNote側で別途、HTMLソースを解析し、その要素の一覧を取得する
 4) 要素一覧から、IHTMLUniqueName::UniqueNumber と合致する要素を見つける

簡単に説明すると、TWebBrowser側でクリックされた要素(1)の先頭からのインデックス(文字インデックスではない)を取得(2)した上で、StyleNote側で独自に抽出したHTMLソースの要素一覧(3)から、TWebBrowserで取得したインデックスに位置する要素を見つけて(4)、キャレットを移動させています。

ここで肝になるのは、(2)の IHTMLUniqueName::UniqueNumber です。MSHTMLでは要素の先頭からの文字インデックスは取得できませんが、要素の先頭からの順番であれば取得できます。例えば、<HTML><HEAD><BODY><IMG> というHTMLソースの場合、<IMG>タグの要素インデックスは先頭から数えて4番目になります。<BODY>タグは3番目です。このように IHTMLUniqueName::UniqueNumberを利用することで、MSHTMLで要素を識別する固有番号を取得できます。

この固有番号を取得した上で、StyleNote側では別途、HTMLソースの要素一覧を抽出します(編集中のHTMLファイルからタグのみを抽出し、且つ、各要素の文字インデックスを保持しておきます)。ここまで来たら、後は(2)で取得したインデックスにあたる要素を特定して、その要素の文字インデックスにキャレットを移動できるようになります。

▼TWebBrowserでクリックした要素(IHTMLElement)を引数にして、UniqueNumberを返す関数
[CPP]
//—————————————————————————
//要素インデックス(UniqueNumber)の取得
//—————————————————————————
long TClassName::getIndexFromElement(IHTMLElement *Element)
{
long originalID(-1);

//イベント要素の固有ID取得
IHTMLUniqueName *uniqueName;
Element->QueryInterface(::IID_IHTMLUniqueName, (void **)&uniqueName);
if(uniqueName!= NULL){

//固有ID取得
uniqueName->get_uniqueNumber(&originalID);
uniqueName->Release();

}

return originalID;
}
[/CPP]

TWebBrowser でクリックせずにホイールスクロールを有効にする

StyleNoteのブラウザー部分は、TWebBrowserコンポーネントを利用しています。

TWebBrowserは、その他のコンポーネントと同じように、マウスホイールでコンテンツをスクロールできますが、TWebBrowserオブジェクトの生成後は一度「マウスクリック」でTWebBrowserにフォーカス(?)を当ててからでないと、なぜかスクロールできないようです。

そこで調べてみると、TWebBrowserコンポーネント上で、DISPID_HTMLELEMENTEVENTS2_ONMOUSEOVER イベントが発生した際に、IOleObject::DoVerb に OLEIVERB_UIACTIVATE を指定してすることで回避できるようです。

[cpp]
DelphiInterface OleObject = IE->Application;
if(OleObject){
DelphiInterface ClientSite;
OleObject->GetClientSite(&ClientSite);
RECT r = IE->ClientRect;
OleObject->DoVerb(OLEIVERB_UIACTIVATE, NULL, ClientSite, 0, IE->Handle, &r);
}
[/cpp]