右クリックメニューの実装(1) 表示編

はい。これを実装したのみなはもうちょい待ち。
メモだけ。

右クリックというイベントの取得

これは、nsIContextMenuListener2で取得します。
…いや、nsIContextMenuListenerでもいいのですが、2のほうが便利なので。
具体的に…はめんどくさいので、これを実装したChromeを作成すると思ってください。
たとえば、こんなかんじ

NS_IMETHODIMP nmnWebBrowserChrome::OnShowContextMenu(PRUint32 aContextFlags, nsIContextMenuInfo *aUtils)
{
    //ほげほげ
    return 0;
}

…なにがなんだかわかりませんが、まあ、これから。
#ちなみに、nsIDOMEventListenerで取得するのもアリだとは思います。が、僕にはわかりません。

aContextFlagsの中の人
CONTEXT_LINK <A>
CONTEXT_IMAGE <IMG>
CONTEXT_IMAGE CONTEXT_LINK <IMG> with <A> as an ancestor
CONTEXT_INPUT <INPUT>
CONTEXT_INPUT CONTEXT_IMAGE <INPUT> with type=image
CONTEXT_TEXT <TEXTAREA>
CONTEXT_DOCUMENT <HTML>
CONTEXT_BACKGROUND_IMAGE <HTML> with background image

そう、clickされたのの組み合わせなのです。

nsIContextMenuInfoの中の人

ざっと、こんな感じ。

  • GetMouseEvent(nsIDOMEvent * *aMouseEvent)
  • GetTargetNode(nsIDOMNode * *aTargetNode)
  • GetAssociatedLink(nsAString & aAssociatedLink)
  • GetImageContainer(imgIContainer * *aImageContainer)
  • GetImageSrc(nsIURI * *aImageSrc)
  • GetBackgroundImageContainer(imgIContainer * *aBackgroundImageContainer)
  • GetBackgroundImageSrc(nsIURI * *aBackgroundImageSrc)

…よくわかんないのもありますが、とりあえず、いろいろできそうです。

選択範囲がclickされたかどうかを調べる

そう、ここまでで気づいた方もいらっしゃると思うのですが、
選択範囲がclickされたかどうかはこれからはわからないのです。
どうするか。

ちょっと煩雑ですが、こんなふうにする…っぽいです。

  1. nsIContextMenuInfo::GetTargetNode(nsIDOMNode**)でclickされたDOMNodeを取得
  2. nsIDOMNode::GetOwnerDocument(nsIDOMDocument**)で、そのDOMNodeの親のDOMDocumentを取得
  3. そのnsIDOMDocumentを、nsIDOMDocumentViewにcast
  4. nsIDOMDocumentView::GetDefaultViewer(nsIDOMAbstractViewer**)で、そのDOMDocumentのViewerを取得
  5. nsIDOMAbstractViewerをnsIDOMWindowにcast
  6. nsIDOMWindow::GetSelection(nsISelection**)で、そのDOMWindow内の選択範囲を取得
  7. nsISelection::ContainsNode(nsIDOMNode*, PRBool, PRBool*)で、最初のDOMNodeが選択範囲にあるかどうかを調べる

…疲れましたね。
はい、26時の脳味噌にはとっても辛いです。


ここまでくれば自明だとは思いますが、一応、sampleを載せておきます。

//* 選択範囲を右clickしたかどうかを調べる
// * nsIContextMenuListener2::OnShowContextMenu()内
// * なので、nsIContextMenuInfo* aUtilsをもらっているとする
nsCOMPtr<nsIDOMNode> domNode;
aUtils->GetTargetNode(getter_AddRefs(domNode));
if (domNode) {
    nsCOMPtr<nsIDOMDocument> domDocument;
    domNode->GetOwnerDocument(getter_AddRefs(domDocument));
    nsCOMPtr<nsIDOMDocumentView> domDocumentView = do_QueryInterface(domDocument);
    if (domDocumentView) {
        nsCOMPtr<nsIDOMAbstractView> domAbstractView;
        domDocumentView->GetDefaultView(getter_AddRefs(domAbstractView));
        nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(domAbstractView);
        if (domWindow) {
            nsCOMPtr<nsISelection> selection;
            domWindow->GetSelection(getter_AddRefs(selection));
            if (selection) {
                PRBool contains;
                selection->ContainsNode(domNode, PR_TRUE, &contains);
                if (contains == PR_TRUE) {
                    //選択範囲です
                }
            }
        }
    }
}

と、ここまででなにがclickされたかはわかると思いますので…

あとは、win32APIなりC#なりGtkなりお好きな方法で、ポップアップメニューを表示します。
そのあと、選択範囲をほげったり、いろいろしたりするのは、また明日。

Reference

  • Bagel 0.xのsrc
  • K-Meleon1.02のsrc
  • DOMのdocumentについて
  • Mozillaのsrcたち
    • webbrwsr/nsIContextMenuListener2.h
    • nsIDOMNode.h
    • nsIDOMDocument.h
    • nsIDOMDocumentView.h
    • nsIDOMAbstractView.h
    • nsIDOMWindow.h
    • nsISelection.h