Home > Tags > cocoa

cocoa

SafariでMigemoを使えるようにしようとした

SIMBLプラグインをつくる習作として、Safariのページ内検索にMigemoをつかえるようにできないか試行錯誤してみました。 分かったことは、SIMBLプラグインとしてつくるのは難しそう、ということでした。

作戦

Migemoの出力は正規表現です。

例: takai → (タカイ|タカイ|隆一|多階層|他界|高[市石鼾泉井]|たかい|takai|takai)

対して、Safariのページ内検索は1つのキーワードに対する全文検索です。 つまり、探すべきキーワードが1つから複数に変わるということです。これは大きな差。

ならば、元のコードでキーワードを渡して検索している関数を、キーワードの個数ぶんだけ繰り返し呼べば所望の機能が実現できそうだ、と考えました。

どこに手を入れるか

Safariの文字列検索を追う で書いていたように、WebView::searchFor にブレークポイントをはり、デバッガでステップ実行して挙動を調べました。

コードを書き変える

まず、元の searachFor メソッドを _safari_searchFor メソッドに変名してとっておき、 この_safari_searchFor メソッドをMigemoの正規表現から得られる文字列の数だけ呼び出す、という戦略を考えます。 うまくいけば、SIMBLプラグインとして実装する際に Method Swizzling として実現できますね。

ためしに、与えられたキーワードと google の2つで検索するようにしてみました。

Index: mac/WebView/WebViewPrivate.h
===================================================================
--- mac/WebView/WebViewPrivate.h        (revision 32849)
+++ mac/WebView/WebViewPrivate.h        (working copy)
@@ -106,6 +106,9 @@
  */
 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection;
 
+- (BOOL)_safari_searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection;
+
+
 - (void)setMainFrameDocumentReady:(BOOL)mainFrameDocumentReady;
 - (void)setTabKeyCyclesThroughElements:(BOOL)cyclesElements;
Index: mac/WebView/WebView.mm
===================================================================
--- mac/WebView/WebView.mm      (revision 32849)
+++ mac/WebView/WebView.mm      (working copy)
@@ -3002,6 +3002,14 @@
 
 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
 {
+  BOOL ret1 = [self _safari_searchFor:@"google" direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:startInSelection];
+  BOOL ret2 = [self _safari_searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:startInSelection];
+
+  return ret1 and ret2;
+}
+
+- (BOOL)_safari_searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
+{
  if (_private->closed)
    return NO;
 
@@ -3247,24 +3255,27 @@
 
 - (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit
 {
-    WebFrame *frame = [self mainFrame];+    NSString *strs[2] = {@"google", string};
     unsigned matchCount = 0;
-    do {
-        id <WebDocumentView> view = [[frame frameView] documentView];
-        if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)]) {
-            [(NSView <WebMultipleTextMatches>*)view  setMarkedTextMatchesAreHighlighted:highlight];
-            ASSERT(limit == 0 || matchCount < limit);
-            matchCount += [(NSView <WebMultipleTextMatches>*)view markAllMatchesForText:string caseSensitive:caseFlag limit:limit == 0 ? 0 : limit - matchCount];
 
-            // Stop looking if we've reached the limit. A limit of 0 means no limit.
-            if (limit > 0 && matchCount >= limit)
-                break;
-        }
-        
-        frame = incrementFrame(frame, YES, NO);
-    } while (frame);
-
+    for (int i(0); i<2; i++) {
+        WebFrame *frame = [self mainFrame];
+        do {
+            id <WebDocumentView> view = [[frame frameView] documentView];
+            if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)]) {
+                [(NSView <WebMultipleTextMatches>*)view  setMarkedTextMatchesAreHighlighted:highlight];
+                ASSERT(limit == 0 || matchCount < limit);
+                matchCount += [(NSView <WebMultipleTextMatches>*)view markAllMatchesForText:strs[i] caseSensitive:caseFlag limit:limit == 0 ? 0 : limit - matchCount];
+                
+                // Stop looking if we've reached the limit. A limit of 0 means no limit.
+                if (limit > 0 && matchCount >= limit)
+                    break;
+            }
+            
+            frame = incrementFrame(frame, YES, NO);
+        } while (frame);
+    }
     return matchCount;
 }

じっさいに、マッチしたキーワードをハイライト表示させているのは markAllMatchesForText というメソッドのようだったので、 こちらにも複数キーワード対応するためのハックを仕込んでおきます。

実行結果

result

複数キーワードがハイライト表示されました。やりましたね :) ただし、「次の候補へ」のようにして、次のマッチ文字列に飛ぼうとしても、同じキーワード間でしかジャンプできませんでした。 ><

原因と対策

そもそも、キーワードにマッチした元のテキストやマッチした結果はどこに保存されているのか。 これを調べていくと WebCore の中に入っていくことになります。 WebCoreはKHTML由来のコードで、C++で書かれているライブラリです。 となると、Objective-C みたいに実行時に柔軟なメソッド差し替えはできなくなってしまいます。 今回はSIMBLプラグインの習作が目的だったので、深追いはここまで。

本気でやるならば、C/Migemoと鬼車をWebCoreにリンクして、マッチしたテキストの範囲などを複数キーワードに対応させればよいでしょう。

まとめ

あと少しだったんだけどなあ…

手駒

  • Migemoの実装には、C/Migemoをつかいます。
  • Migemoの出力は正規表現なので、鬼車のCocoa版であるOgreKitをつかいます。
  • Safariをハックするには、WebKitをデバッグビルドするのが便利です。
  • Cocoaアプリを拡張するには、SIMBLプラグインをつくるのが定石です。

夏ライオンにメッセージフィルタリングをつけるハック

@akrさんがつくっている、夏ライオンというTwitterクライアントに、表示されるメッセージを特定の条件でフィルタリングするハックをしてみました。

何ができるか

特定のユーザのメッセージだけを表示したり、

ある言葉が含まれるメッセージだけを表示したりできます。

キモ

  • どこから手をつけるか
  • NSPredicate をつかう

長くなるので、続きは以下で。

Continue reading

CocoaMilk - Lists & Tasks

CocoaMilk : Remember the Milk を Cocoa から の続きをつくっています。

Cocoaのフレームワークに悪戦苦闘しつつ、Remember the MilkのListとTaskを一覧表示できることができるようになりました。

CocoaMilk - Lists and Tasks

SmartListはどぎつい色でハイライトしたりしています。AddressBook.appのようなインターフェイスが理想です。でもまだそこまでの道のりは長そう…

Cocoaのフレームワークを習得するのはなかなか骨が折れます。まだまだ僕はオブ脳になっていないのでしょう。


オブジェクト脳のつくり方―Java・UML・EJBをマスターするための究極の基礎講座
牛尾 剛 長瀬 嘉秀
翔泳社
売り上げランキング: 103236
おすすめ度の平均: 3.5
4 OJTのテキストみたいな本です
3 オブジェクト指向の方法論のみならず実践もまじえてある♪
3 私にとっては微妙

CocoaMilk : Remember the Milk を Cocoa から

Remember the Milk のデータを、Mac OSXの素晴らしいインターフェイスを使って操作したいと思いました。

ということで、まずCocoaでテーブル表示させるアプリを書いてみました。

CocoaMilk::SimpleLists

REST APIのrtm.lists.getListを使って、Objective-C++で書いています。やっぱり慣れ親しんでいるC++がグッド。

この調子で進めていって、そのうちかっこいいアプリにしたいなというところです。

Home > Tags > cocoa

Feeds

Return to page top