[BlueLeaf1336]> PROBLEMS> リンクタグ作成 HrefBuilder>

リンクタグ作成 HrefBuilder > 参考URLリストの作成

historyTOP

2005/05/22:作成

2005/05/22TOP

しょうもないツールを作るときでも、たいていインターネットで調べ物をします。このときに参考にさせてもらったURLを、次に使うことが全くないとも言い切れないので、可能な限り参考URLリストとして(勝手に)リンクしています。

ただ、コレが邪魔臭いわけです。本当に次回に役立てようと思うなら、URLとタイトルとそのページの概要と参考にした内容と、そのページのメインのページの概要をまとめておくべきであって、実際にはもっともっと邪魔くさいはずですが、とりあえずURLとタイトルだけでも...と考えただけでも邪魔臭い。

参考にしたページのリストをどうやって作るかにもよるとは思いますが、たいていの場合、Googleで検索して記述されているコードをコピーしてきて動くかどうかだけ確かめて、採用することにした場合は、そのページのURLだけコメントとしてソースコードに貼り付けています。で、恐れ多くもそのソースコードやプログラムを説明しようじゃないかと思い立った場合は、そのURLをもとにして、参考URLリストを作ることになります。その時点で死んじゃってるページなんてのもありますが...。

さて、そうすると、URLだけが入力として使える状態でそのページのタイトルを抜き出すことになります。で、これが邪魔臭い。というのも、WebブラウザにURLを入力してそのページのタイトルを抜き出そうとした場合、たいていはそのページのソースをエディタで開いて、TITLEタグを探してタイトルを得るという流れになるためです。

前置きが長くなりましたが、URLのリストを貼り付けて、それに対応するHTML用のリンクタグを生成するツールを以前に作ったことがあります。

これがかなり使いにくいんですが、多分自分で作ったプログラムの中で一番使用しているような気がします。でも、使いにくい。使いにくい原因は、URLのリストを貼り付けるために、まずそのURLのリストをエディタの新規ページか何かで作る必要がある点だと思います。もちろんそれからもいろいろと問題はあるんですが。

なので、今回、この腐った使い勝手を少しでもコマシにしようと思います。基本方針としては、クリップボードを監視して、URLっぽい文字列が貼り付けられたらバックグラウンドでタイトル文字列を取得して、どんどん貼り付けられたらどんどん取得して、最終的にこのプログラムをアクティブにした時点でネタは取得済み、あとはどうやって使うかだけ、な感じにしたいなぁと。

手始めに次の2点を実現してみます。

クリップボードを監視することTOP

実はやったことあります。TClipWatcherというクラスにしてあります。使い方はこんな感じです。

//-----------------------------------------------------------------------------
//  クリップボード読み取り
procedure TMainForm.GetClip();
begin
    //  テキストフォーマットなら
    if Clipboard.HasFormat(CF_TEXT) then
    begin
        Form1.ListBox1.Items.Add(Clipboard.AsText);
    end;
end;

//-----------------------------------------------------------------------------
//  フォーム生成時
procedure TMainForm.FormCreate(Sender: TObject);
begin
    // コピー検出クラス
    ClipWatcher := TClipWatcher.Create(GetClip);
end;

//-----------------------------------------------------------------------------
//  フォーム破棄時
procedure TMainForm.FormDestroy(Sender: TObject);
begin
    // コピー検出クラス
    ClipWatcher.Free;
end;

クリップボードを監視すること(本体)TOP

//=============================================================================
//  クリップボード監視クラス
//-----------------------------------------------------------------------------
//  http://www2.big.or.jp/~osamu/Delphi/tips.cgi?index=0210.txt
//  http://www2.big.or.jp/~osamu/Delphi/tips.cgi?index=0209.txt
//-----------------------------------------------------------------------------

unit iClip;

interface

uses
    Windows, Messages, SysUtils, Classes, ClipBrd;

type
    // 変更通知関数
    TClipUpdatedNotify = procedure() of object;
//    TClipUpdatedNotify = procedure();

    // 変更監視クラス
    TClipWatcher = class

    private
        IsFirst     : Boolean;

        WndHandle   : THandle;
        HClipNext   : HWND;
        ClipProc    : TClipUpdatedNotify;
        procedure   ClipboardSetHandle;
        procedure   ClipboardReleaseHandle;
        procedure   WndProc(var Msg: TMessage);
    public
        constructor Create(Proc: TClipUpdatedNotify);
        destructor  Destroy(); override;

    end;

implementation

//-----------------------------------------------------------------------------
//  TClipWatcher
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//  コンストラクタ
constructor TClipWatcher.Create(Proc: TClipUpdatedNotify);
begin
    inherited Create;

    // 初期化未完了
    IsFirst := True;

    // ウィンドウコントロールのふりをする
    WndHandle := AllocateHWnd(WndProc);
    // 次のクリップボードビューアのハンドル初期化
    HClipNext := INVALID_HANDLE_VALUE;
    // 通知関数受け取り
    ClipProc := Proc;
    // クリップボードビューアとして登録
    ClipboardSetHandle;

    // 初期化完了
    IsFirst := False;
end;

//-----------------------------------------------------------------------------
//  デストラクタ
destructor  TClipWatcher.Destroy();
begin
    // クリップボードビューアとして離脱
    ClipboardReleaseHandle;
    // ウィンドウのふり中止
    DeallocateHWnd(WndHandle);

    inherited Destroy;
end;

//-----------------------------------------------------------------------------
//  クリップボード監視のための初期化
procedure    TClipWatcher.ClipboardSetHandle;
begin
    // まだこのこの処理をやってないなら
    if HClipNext = INVALID_HANDLE_VALUE then
    begin
        // クリップボードビューアとして登録してみる
        HClipNext := SetClipboardViewer(WndHandle);
        // だめなら
        if HClipNext = INVALID_HANDLE_VALUE then
            // 例外
            if GetLastError <> 0 then
                raise Exception.Create(SysErrorMessage(GetLastError));
    end;
end;

//-----------------------------------------------------------------------------
//  クリップボード監視の破棄
procedure    TClipWatcher.ClipboardReleaseHandle;
begin
    // クリップボードビューアとして登録できているなら
    if HClipNext <> INVALID_HANDLE_VALUE then
    begin
        // 次の奴に「いち抜け」を教えて
        ChangeClipboardChain(WndHandle, HClipNext);
        // 次の奴のハンドル忘却
        HClipNext := INVALID_HANDLE_VALUE;
    end;
end;

//-----------------------------------------------------------------------------
//  メッセージ受け取り
procedure   TClipWatcher.WndProc(var Msg: TMessage);
var
    WmDrawClip: TWMDrawClipboard;
    WmChangeCB: TWMChangeCBChain;
begin
    case Msg.Msg of

    // クリップボード更新フック
    WM_DRAWCLIPBOARD:
        begin
            // 構造体のメンバがよくわからないので、キャストしてみる
            WmDrawClip := TWMDrawClipboard(Msg);

            //通知
            if not IsFirst then ClipProc();

            //次の人にも教えてやる
            if HClipNext <> INVALID_HANDLE_VALUE then
            begin
                SendMessage(HClipNext, WM_DRAWCLIPBOARD, 0, 0);
            end;
        end;

    // 次にまわす
    WM_CHANGECBCHAIN:
        begin
            // 構造体のメンバがよくわからないので、キャストしてみる
            WmChangeCB := TWMChangeCBChain(Msg);

            if WmChangeCB.Remove = HClipNext then
            begin
                HClipNext := WmChangeCB.Next;
            end;
            if HClipNext <> INVALID_HANDLE_VALUE then
            begin
                SendMessage(HClipNext, WM_CHANGECBCHAIN, WmChangeCB.Remove, WmChangeCB.Next);
            end;
        end;

    // それ以外のすべて
    else
        begin
            Msg.Result := DefWindowProc(WndHandle, Msg.Msg, Msg.wParam, Msg.lParam);
        end;
    end;
end;

end.

文字列がURLかどうかを判定することTOP

さて、こっちはやったことありません。先のクリップボードの監視の時に、単純にクリップボードの内容がテキスト形式なら処理する、としていましたが、この箇所を、クリップボードの内容がテキスト形式なら「URLっぽいなら」処理する、ように修正したいということです。

こんなページがあります。このページの中に、URI(URLと似たようなもの...だと思っていますが)の正規表現として、(異常に長い本当の規則どおりの正規表現は置いといて)簡易版の判定用の正規表現として、次のようなものが紹介されています。

s?https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+

そういうことらしいです。つまり、正規表現が使えれば簡単に判断できそうな感じです。また、以前にDelphiで正規表現をするための方法を探したときに、Delphiでマイクロソフト製の正規表現ActiveXコンポを使うというページを見つけました。が!! すでに閉鎖されていました。すごくわかりやすかったんですが...。Internet Archive: Wayback Machineを使っても辿れませんでした。

うーん。どうしよう。前回参考にして書いたソースコードがあるので、それから思い出しながらやればよいのかもしれませんが、ここは別の方法を考えることにします。

別の方法と書いたばかりですが、Delphiで正規表現を使うための別の方法ということです。つまり「文字列がURLかどうかを判定すること」という問題は「Delphiで正規表現を簡単に使うには」という問題に、それとなくずらしてしまうことにします。

Delphiで正規表現を簡単に使うにはTOP

日本語で書かれたページだけを対象に検索してみました。リンクに続く説明文は、リンク先のページからだいたいで抜き出したものです。この抜き出しも自動でやってくれるようなのがあればすばらしいんですが、そのぐらいは脳みそ使わないと腐ってしまいそうなので我慢します、じゃなくてそんなのは作れません。

先に見つけた、URIを一撃で表す正規表現がPerl用ですので、Perl互換がよいのではないか、でもDLLかぁというところで、bmonkey's Delphi pageの関数を使用させてもらうことにしようかと思ってダウンロードしてみると、難しそうです。

そうなると、やっぱりBlueLeaf1336-PROBLEMS-2004_0048 > MizuhoGetter > HTMLソースの解析(3)で書いたソースコードを参考にして、ActiveX使ってやることにしようかなぁ。でも使い方がなあ。

ここまで考えてきて、そういえば、今回は「URLかどうか」だけを調べるだけだということを思い出しました。前後にゴミデータがくっついてしまっている場合を考えると、正確には「文字列の中にURLっぽいものが含まれているかどうか」を調べるということになります。大丈夫かも。よって、ActiveXを使うことにします。あぁでも、普通にActiveXの取り込みを実行して作ったTLBファイルでは問題があるとかで、当時は閉鎖されていなかったページからダウンロードできたTLBファイルを使わないと駄目で、それ以前にどのActiveXなのかも特定できなくて、やっぱり、うーん。

EOFTOP