株式会社クリアコード > ククログ

ククログ


Developer Migration 2013: 「開発者は仕事でリーダブルなコードを書けるのか?」 #devmi

2013年3月2日にDeveloper Migration 2013が開催されました。

Developer Migrationは今回が第1回目の開催です。エンジニアは変化にどう対応していけばよいか、に焦点を当てたカンファレンスです。4つのセッションとパネルディスカッションという内容で、セッションの内訳は開発系2つとインフラ系2つという幅広い話題を扱いました。

さて、そんなDeveloper Migration 2013で「開発者は仕事でリーダブルなコードを書けるのか?」というセッションをしてきました。

今回はSIerで働いている開発者向けに話しました。といっても、SIerとして働いた経験はないのでDevLOVE2012のときに聞いたことへの回答や、クリアコードでよいと思って実践しているやり方を紹介しました。クリアコードのやり方はやりすぎだと思われたかもしれません。例えば、1時間に1回以上コミットしているのは参加者のうち1割くらいでした*1

今回はパネルディスカッションもあり参加者とやりとりをする機会が多かったのですが、思い返してみれば「コードを書くといいです」ばかり言っていたような気がします。他の人にコメントしたときにネガティブに(否定するように)伝わってしまったりしないか、というような質問には「よりよいコードをコミットして伝えるといいのではないでしょうか」というように答えた気がします。よいコードを書くやり方を上司に提案してもなかなか受け入れられない、というような質問には「上司も一緒にコードを書いていますか?書いているんですね。書いているのなら、最近、私のコードよくなっていませんか?実は、こういうやり方をしているんです。チームでも導入してみませんか?というのはどうでしょうか」と答えたような気がします。コードを書く人たちならコードで伝えられることがいろいろあるんじゃないかと思っているのかもしれません。

あなたもコードで伝えてみませんか。

*1 1時間に1回以上のコミットができるかどうかはバージョン管理システムの種類には依存しません。Gitなどの分散バージョン管理システムでもできますし、Subversionなどの中央集権的なバージョン管理システムでもできます。実際やっています。

2013-03-04

開発したいFirefox for AndroidおよびFirefox用アドオン

こんにちは。クリアコードでMozilla FirefoxおよびThunderbirdのサポート業務を主に担当している結城です。

先日このブログで紹介したインターンシップ制度では、クリアコードのメンバーが開発したいと考えているフリーソフトウェアの中から「これは」というものをインターンが選択します。本エントリでは、開発したいと考えているフリーソフトウェアとして、Firefox for AndroidおよびFirefox用アドオンをいくつか挙げます。

Firefox for Android用アドオンとしては以下を開発したいと考えています。

  • Firefox for Android用アドオン「テキストリンク」
  • Firefox for Android用アドオン「ロケーションバーから新しいタブを開く」
  • Firefox for Android用アドオン「XUL/Migemo」

Firefox用アドオンとしては以下を開発(更新)したいと考えています。

  • Firefox用アドオン「XUL/Migemo」

以下に、各アドオンの開発内容の詳細を述べます。

Firefox for Android用アドオン「テキストリンク」の開発

概要

私が以前から個人的に開発しているFirefox用のアドオンの1つに、「テキストリンク」があります。このアドオンは、Webページ中に書かれたリンクになっていないURI文字列を自動的に検出して、通常のリンクのように開けるようにします。

URI文字列をリンク化する他の類似のアドオン(Linkification)とは異なり、このアドオンは、基本的にWebページの内容を改変しないという特徴があります。これは、アドオンがコンテンツに変更を加えてしまう事は望ましくないという、私の個人的なポリシーに基づいています。

私自身、個人的にAndroid端末を利用し始めてみて、この機能があった方が便利だ!という思いを改めて感じています。しかしながら、現在のテキストリンクはデスクトップ版のFirefox用に特化して開発されているため、そのままではFirefox for Androidでは動作しません。そこで、インターンシップの期間を用いてこのアドオンのFirefox for Android対応版を開発したいと考えています。

開発の流れ(現時点での想定)
  • 現在のテキストリンクのコアモジュールの設計を見直し、再起動がいらない種類のアドオンにもモジュールを再利用できるようにする。
  • コアモジュールを再利用して、Firefox for Android対応版テキストリンクを開発する。
  • もし可能であれば、デスクトップ版Firefox用のテキストリンクについても再起動がいらないようにする。
開発に必要な要素技術や知識の分野
  • JavaScript一般の知識。
  • W3C DOMの知識。
  • Firefox/Firefox for Android固有のライブラリやモジュール、アドオン開発の作法。
  • 自動テストの知識。
挑戦ポイント

Firefox for Android用アドオンの開発は、古典的な形式のデスクトップ版Firefox用アドオンの開発とは異なる知識が要求されます。デスクトップ版Firefoxでの経験を踏まえて開発されたFirefox for Androidでは、アドオンを開発しやすいようなAPIが最初から整えられており、これまでFirefox用アドオンを開発してきた経験が無い人でも比較的取っつきやすいのではないかと思います。

また、テキストリンクにはコアモジュールの自動テストが用意されているため、変更が元で既存の機能が動かなくなったといった後退バグ(リグレッション)の検出が容易です。自動テストを伴う開発の例としても参考になるでしょう。

なお、デスクトップ版FirefoxとFirefox for Androidでは操作性がかなり異なっており、現状のテキストリンクをそのまま移植する事がユーザの利便に寄与するとは断言できません。実際に、Firefox for Androidではページ内のダブルタップが「ダブルタップされた要素へのズームイン」という操作に割り当てられているため、2つの動作をどのように並立させるかが問題となります。場合によっては、前述のようなポリシーを脇に置き、ページ内のURIをワンタップでリンクとして開くような設計に変更する必要があるかもしれません。

Firefox for Android用アドオン「ロケーションバーから新しいタブを開く」の開発

概要

私が以前から個人的に開発しているFirefox用のアドオンの1つに、「ロケーションバーから新しいタブを開く」があります。これは、FirefoxのロケーションバーにURIを入力した際に、現在のタブの内容を置き換える(現在のタブで読み込む)代わりに、必ず新しいタブでURIを開くようにします。

私自身、個人的にAndroid端末を利用し始めてみて、この機能があった方が便利だ!という思いを改めて感じています。しかしながら、現在の「ロケーションバーから新しいタブを開く」はデスクトップ版のFirefox用に特化して開発されているため、そのままではFirefox for Androidでは動作しません。そこで、インターンシップの期間を用いてこのアドオンのFirefox for Android対応版を開発したいと考えています。

開発の流れ(現時点での想定)
  • そのようなアドオンを実現する事が技術的に可能なのかどうかを調査する。
  • 実際のアドオンを開発する。
開発に必要な要素技術や知識の分野
  • JavaScript一般の知識。
  • Firefox/Firefox for Android固有のライブラリやモジュール、アドオン開発の作法。
  • Firefox for Androidのソースコードの調査方法。
挑戦ポイント

最大の懸念点は、本当にそのような事をするアドオンを開発できるのかどうか、技術的可否の裏付けが取れていないという点です。

デスクトップ版FirefoxはUIの隅々までカスタマイズできる仕様となっているため、このアドオンのように「FirefoxのUIの挙動を変更する」アドオンも容易に開発できます。しかしながら、Firefox for AndroidはロケーションバーなどのUI部分はJavaで開発されており、アドオンから制御できる部分は、メニューの内容などごく一部だけに限定されています。そのため、このようなアドオンはFirefox for Androidでは実現できないという可能性もあります。

ですので、プロジェクトの最初のステップとして、まず技術的な実現可否の調査からとりかかる必要があります。これは、クリアコードのFLOSSサポート業務でもよく見られるワークフローです。

Firefox for Android用アドオン「XUL/Migemo」の開発

概要

私が以前から個人的にメンテナンスしているFirefox用のアドオンの1つに、「XUL/Migemo」があります。これは、Firefoxのページ内検索などにおいて、ローマ字入力するだけで日本語の文章を検索できるようにするアドオンです。

Firefoxのページ内検索はインクリメンタルサーチと呼ばれる形式をとっており、キー入力が進むごとに、その内容がすぐ検索結果に反映されるようになっています。しかしながら、この種のインクリメンタルサーチと日本語入力(およびその他のIMEを必要とする言語)は相性が良くなく、変換・確定を待ってからでなければ検索できないという問題があります。XUL/Migemoは、内蔵の辞書を用いてこの問題を克服するアドオンです。ローマ字入力が1文字進むごとに、そこから推測された日本語の候補をインクリメンタルサーチすることができます。

Android端末では一般的に、デスクトップ環境以上に文字入力が大きな負担となります。日本語入力においては正しい変換候補を選ぶという手間が必要になりますが、画面サイズが小さいAndroid端末では、文字の入力も候補の選択もどちらも大変なストレスとなりがちです。そこで、インターンシップの期間を用いてこのアドオンのFirefox for Android対応版を開発したいと考えています。

開発の流れ(現時点での想定)
  • そのようなアドオンを実現する事が技術的に可能なのかどうかを調査する。
  • 現在のXUL/Migemoのコアモジュールの設計を見直し、再起動がいらない種類のアドオンにもモジュールを再利用できるようにする。
  • コアモジュールを再利用して、Firefox for Android対応版XUL/Migemoを開発する。
  • デスクトップ版Firefox用のXUL/Migemoにも開発の成果を反映する。
開発に必要な要素技術や知識の分野
  • JavaScript一般の知識。
  • Firefox/Firefox for Android固有のライブラリやモジュール、アドオン開発の作法。
  • Firefox for Androidのソースコードの調査方法。
  • 自動テストの知識。
挑戦ポイント

最大の懸念点は、「ロケーションバーから新しいタブを開く」と同様に、技術的可否の裏付けが取れていないという点です。ですので、プロジェクトの最初のステップとして、まず技術的な実現可否の調査からとりかかる必要があります。

また、現在のXUL/Migemoのコアモジュールは、歴史的な経緯から設計が複雑化しすぎており、再起動のいらないアドオンにすぐに組み込める状態にはなっていません。よって、ユーザが触れる事になるUIの開発に入る前に、コアモジュールのリファクタリングや再設計を行わなくてはなりません。XUL/Migemoのコアモジュールには自動テストが用意されていますが、モジュールの設計の変更に伴って、テスト自体のメンテナンスも並行して進める必要があります。

Firefox用アドオン「XUL/Migemo」の更新

概要

XUL/Migemo」には、Firefox for Androidに対応していないという以前に、最新のFirefoxで動作しなくなっているという問題があります。

XUL/MigemoはFirefoxの検索機能の挙動をシームレスに変更する事を目指して開発してきたため、Firefox自体のUIの設計変更の影響を受けやすい作りになっています。そのため、メンテナンスに十分な時間を割けなくなって以降、最新のFirefoxに追従できていない部分が非常に多くなっています。また、スマートロケーションバーの履歴・ブックマーク・タブを横断した検索機能との統合については、履歴データベースにアクセスする方法が今後大きく変わる予定であるという事がアドオン開発者向けにアナウンスされており、この点の追従にも大きな工数を要すると予想されます。

そのため、インターンシップの期間を用いてこのアドオンを更新したいと考えています。

開発の流れ(現時点での想定)
  • 現在のコアモジュールの設計を見直し、設計を近代化する。
  • FirefoxのUIの挙動を変更する箇所を修正する。
  • スマートロケーションバーの検索機能の実装を更新する。
開発に必要な要素技術や知識の分野
  • JavaScript一般の知識。
  • Firefox固有のライブラリやモジュール、アドオン開発の作法。
  • Firefoxのソースコードの調査方法。
  • 自動テストの知識。
挑戦ポイント

XUL/Migemoは古典的な形式のアドオンであるため、開発にあたってはFirefox内部の作りを詳しく調べる必要があります。これは、既存のFLOSSのソースコードの障害調査や改修でもよくある工程です。

また、現在のXUL/Migemoのコアモジュールは、歴史的な経緯から設計が複雑化しすぎている部分があるため、モジュールのリファクタリングや再設計も行っておきたい所です。XUL/Migemoのコアモジュールには自動テストが用意されていますが、モジュールの設計の変更に伴って、テスト自体のメンテナンスも並行して進める必要があります。

まとめ

インターンシップで開発したいFirefox for AndroidおよびFirefox用アドオンを4つ挙げました。この中に「挑戦してみたい!」と思えるアドオンがあれば、インターン募集ページから是非ともご応募下さい。

クリアコードは、日常的に利用するソフトウェアのちょっとした不満について、自力で工夫して解決してみたいと考える開発者を求めています。

2013-03-06

ぐんまRuby会議01: 「プログラマー」 #guruby

2013年3月9日にぐんまRuby会議01が開催されました。

ぐんまRuby会議はゆったりしているのに時間の計り方はマメな味わい深い会議でした。進行チームがゆったりとした話し方で繰り出すユーモアもまたとても味わい深く、そうか、これがぐんまなのかと妙に納得したものです。

さて、そんなぐんまRuby会議01で「プログラマー」というタイトルで招待講演してきました。

このタイトルにしたときはたくさん話すことがあって時間内に収めるのが大変だろうと思っていました。しかし、いざ準備してみると逆に時間が余ってしまいます。どうやら「同じことでつまづく人を少なくしたい」というのが自分の中でベースとなっている考え方のようです。それさえ言ってしまえば他のことは特筆すべきことではないと思えてしまったのが不思議です。

今回の私の成果は、招待講演よりも、むしろ、全体の話題の方向性をより具体化したことでした*1。ぐんまRuby会議01は他の人の世界観に触れるという何でもありとも言えるコンセプトでした。そこに、「こういう立場から見た」世界観、というより具体化した方向性を与えました。方向性をタイトルで示すようにもっていったのが私です。「Mad Web Programmer」、「プログラマー」、「サラリーマン」、「フリーランス」、「群馬県民」、「アーティスト」など。いろんな立場がありましたね。

これまで参加したどのRuby会議とも違う、なんというか、いい感じのRuby会議でした。

*1 そう思いたい。少なくともこれだけは成果があったと思いたい。

タグ: Ruby
2013-03-11

Mac OS XのCocoa版GTK+で日本語入力を行うためのgtkimmodule(GtkIMCocoa)の開発

Mac OS X*1には、Cocoaと呼ばれるネイティブアプリケーション構築のためのAPIがあります。

通常のアプリケーションはこのCocoaを使って実装されていますが、その他にUnix系OSではメジャーなGUIツールキットであるGTK+を使うこともできます。GTK+がMac OS X向けに移植されているので、GTK+で実装されたアプリケーションをMac OS Xでも使うことができるのです。

ただし、現在のMac OS X版GTK+には、日本語入力の点で課題があります。

そこで、Mac OS X版GTK+で日本語入力を行う上での問題点と、その解決のために新たに開発中のGTK+ immodule(GtkIMCocoa)について紹介します。

GTK+における日本語入力の仕組み

GTK+におけるテキスト入力の機能は、immodule(input method module。インプットメソッドモジュール。)と呼ばれるプラグインとして実装するようになっています。

プラグイン構造でインプットメソッドのモジュールを個別に追加できる仕組みにすることで、多種多様なインプットメソッドを動的に切り替えて利用できるようになっています。実際に、GTK+アプリケーションのテキスト入力画面で右クリックすると、[入力メソッド]メニューからインプットメソッドを簡単に切り替えることができます。

なお、通常immoduleはGTK+とインプットメソッドの仲介をするものなので、事前にインプットメソッドのインストールが必要です。例えば、im-ximであればXIMサーバーと呼ばれるソフトウェアをあらかじめインストールする必要があります。

以下の図はアプリケーションとインプットメソッドの関係を表しています。

アプリケーションとインプットメソッド

Mac OS XにおけるGTK+の日本語入力事情

Mac OS X上で動くGTK+の日本語入力の現状について、まずはMac OS X特有の事情から説明します。

Mac OS XではGTK+のバイナリーが2種類あります。

  • X Window Systemに依存したGTK+
  • Cocoaにのみ依存したGTK+

X Window System版GTK+の場合、アプリケーションでの日本語入力は、前述のようにXIMと呼ばれるインプットメソッドをGTK+から利用することで実現しています。

一方、Cocoa版GTK+では対応するimmoduleがないため、日本語入力を行うことができません。

そのため、日本語入力をまともに使うには、X Window System版GTK+を使うしか選択肢はありませんでした。

しかし、この制約もMac OS X Mountain Lionがリリースされて以降、さらに厳しいものになりました。

なぜなら、Mountain Lionでは、そもそもAppleによるX Window Systemの実装であるX11.appがインストールされないようになってしまったためです。それだけではなく、X11.appがインストールされていた過去のバージョンを使用していても、Mountain LionにアップグレードするとX11.appが削除されてしまいます*2。もともとX Window SystemだけではなくXIMサーバーもインストールする必要があったことを考えると、簡単に日本語入力の環境を整えられるとはとても言えない状況です。

このような状況下で今後Cocoa版GTK+を使ったアプリケーションが増えてきたとき、日本語入力が使えないというのは致命的です。

しかし、Cocoa版GTK+でも日本語入力ができるようになれば、非標準となってしまったX Window Systemのパッケージをわざわざインストールする手間もなくなりますし、完成度が向上してくれば、これまでMac OS X版を提供していなかったGTK+アプリケーションでも、パッケージが提供されるようになるかもしれません*3

Mac OS X版GTK+ immoduleの開発状況

前述のように、現在のGTK+のソースコードには、Mac OS X用のimmoduleは含まれていません。一方、Sylpheedの開発者である山本博之氏による試験的な実装は存在していました(以下、im-quartz)。

山本氏自身が上記記事内で指摘されておられるように、この実装は設計上、大きな問題を抱えています。そのため、このままではGTK+本体にマージされるものにはならないだろう、という懸念がありました。

最大の懸念点は、テキスト処理がimmodule内で完結していないという点です。GTK+の設計思想では、テキスト入力の処理はimmodule内で完結しているべきなのですが、im-quartzでは、テキスト入力処理の大半が描画レイヤーであるGDKの側で実装されています。そのため、せっかくのモジュール構造であるのにも関わらず、GDKに対するパッチも必要となってしまいます。ただし、これはあえてそういう設計にしているというわけではなく、Cocoaのテキスト入力APIがビューのAPIと癒着していることから、そうせざるを得なかった、というのが真相のようです。

im-quartzに対する改善アプローチ

Mac OS X上でGTK+の設計思想に沿った形のimmoduleを実装するにはどうすればよいのかを検討しました。その検討過程には紆余曲折があったのですが、最終的に以下のような設計にするとうまくいくのではないか、という結論に至りました。

  • immoduleは、テキスト入力専用に独自のNSView(CocoaネイティブのViewオブジェクト)をもつ。
  • immoduleは、受け取ったキーイベントを上記の独自NSViewにフォーワードし、独自NSViewでのテキスト処理結果を受け取ってGTK+のウィジェットに返す。
  • 独自NSViewはユーザーの目に触れる形で表示されることはなく、裏方で上記処理を行う。

本アプローチによる成果

上記の改善方針に基づいて、新しいimmodule「GtkIMCocoa」の開発を開始しました。ソースコードはGitHubにて公開しています。

基本的な機能は既に完了しており、この改善アプローチでもテキスト入力処理が正しく機能することを確認しています。

Mac OS X版GTK+で日本語を入力している様子

また、当初の目論見どおりテキスト入力処理をimmodule内で完結させることができるようになったため、GTK+本体に対するパッチが不要となりました。

このことにより、以下のようなメリットが得られます。

  • 異なる複数のレイヤーに分断されていたテキスト入力処理の実装が統合され、コードの見通しがよくなった。
  • テキスト入力を必要とするビューにのみテキスト入力処理が実装されるようになった。
  • 単独でのパッケージ配布が可能となり、既にビルド済みのGTK+に対して多言語入力機能を追加することも可能となった。

現時点のGtkIMCocoaは、バージョン0.0.0として以下で公開しています。

最終的にはGTK+本体に取り込まれるのが理想ですが、それまでの間は上記サイトにて定期的にパッケージを公開していく予定です。

GtkIMCocoaの導入方法について

GtkIMCocoaのインストール方法について解説します。

GTK+ 3のインストール

まず、使用するGTK+について説明します。

現在のGtkIMCocoaは、GTK+ 3に対してのみ動作を確認しています。GTK+のサイトではMac OS X版のバイナリも配布されていますが、im-quartz(あるいは後述する改良版)を試したい場合はGTK+側にも手を入れる必要があるので、ここではjhbuildを使用してソースコードからGTK+ 3をビルドする方法を紹介します。

ここで対象とするMac OS Xのバージョンは、Mountain Lionのみです。他のバージョンのMac OS Xで使用したい場合は、Building GTK-OSXを参照してください。

1. 開発環境の準備
  • Xcodeをインストールします。Mountain Lionの場合は、App Storeから無料でインストールすることができます。
  • Xcodeのメニューから「Xcode」→「Preferences...」で設定画面を開き、「Downloads」→「Components」画面で「Command Line Tools」をインストールします。
  • MacPortsを使用している場合は、PATHからMacPortsのパスを外してください。MacPortsの一部のコマンドを使用すると、ビルドに失敗することがあるようです。
2. gtk-osx-build-setup.shの入手および実行

jhbuildをMac OS X用にセットアップするためのスクリプトgtk-osx-build-setup.shを入手します。

$ curl -s -O https://git.gnome.org/browse/gtk-osx/plain/gtk-osx-build-setup.sh

このスクリプトをエディタで開き、下記の行

BASEURL="http://git.gnome.org/browse/gtk-osx/plain/"

"http""https"に変更します。

BASEURL="https://git.gnome.org/browse/gtk-osx/plain/"

変更を保存し、スクリプトを実行します。

$ sh gtk-osx-build-setup.sh
3. jhbuild shell起動

適切な環境変数をセットするために、jhbuildのシェルを起動します。

$ ~/.local/bin/jhbuild shell
4. GTK+ 3のビルド

以下のコマンドでGTK+ 3をビルドできます。

$ ~/.local/bin/jhbuild bootstrap
$ ~/.local/bin/jhbuild build meta-gtk-osx-bootstrap
$ ~/.local/bin/jhbuild build meta-gtk-osx-gtk3

デフォルトの設定のまま実行した場合、GTK+は~/gtk/inst以下にインストールされます。

5. 動作確認

gtk3-demoを起動して、GTK+ 3が正しくインストールされたことを確認します。

$ gtk3-demo
GtkIMCocoaのインストール

次にGtkIMCocoaのインストール方法を説明します。

1. ソースコードの入手

GitHubからGtkIMCocoaのソースコードをcloneします。

$ git clone git://github.com/ashie/gtkimcocoa.git

あるいは、リリース版のGtkIMCocoaソースパッケージをダウンロードし、 以下のコマンドで任意のディレクトリにソースパッケージを展開します。

$ tar xvzf gtkimcocoa-0.0.0.tar.gz
2. GtkIMCocoaのビルド

jhbuildシェル上で下記コマンドを実行して、GtkIMCocoaをビルドします。

$ cd gtkimcocoa
$ ./autogen.sh
$ ./configure
$ make
$ make install
3. immodules.cacheの更新

以下のコマンドで、GTK+ 3のimmoduleの設定を更新します。

$ gtk-query-immodules-3.0 > ~/gtk/inst/lib/gtk-3.0/3.0.0/immodules.cache
4. 動作確認

gtk3-demoを起動し、「Text Widget」→「Hypertext」などを開いて日本語入力できることを確認します。

$ gtk3-demo

今後の課題

実は、GtkIMCocoaの開発開始と同時期に、uimプロジェクト等で知られるek.kato氏もim-quartzの改善パッチを作成されていたことが後から発覚しました。

GtkIMCocoaではうまく動作していない日本語入力システム(Google日本語入力)が、ek.kato版ではうまく動作しているなど、安定性の面ではこちらの方が進んでいるようです。一方で、基本設計の問題はオリジナルのim-quartzをそのまま受け継いでしまっているようです。このため、両実装の統合が必要になるでしょう。

また、日本語だけにとらわれず、あらゆるインプットメソッドとの組み合わせての検証を行い、完成度を上げていく必要があります。

まとめ

Mac OS XのCocoa版GTK+で日本語入力を行う上での問題点とその解決のために新たに開発したGTK+ immodule(GtkIMCocoa)の実装と今後について紹介しました。

GtkIMCocoaはまだ開発を開始したばかりのプロジェクトであり、まだまだ改善すべき余地が残されています。

そこで、Cocoa版GTK+で日本語入力をまともに使えるようにしたいという、一緒に開発してくれる人を募集しています。また、開発を手伝うというのが難しくても、GtkIMCocoaを実際に使ってみて問題があればバグレポートしてくれる人も募集しています。

なお、GtkIMCocoaの開発については先日このブログで紹介したインターンシップ制度の題材としても挙げていますので、挑戦されたい方は是非ともご応募ください。

*1  現在は「OS X」が正式名称のようですが、ここではX Window Systemとの混乱を避けるために「Mac OS X」と表記します。

*2 別途XQuartzのインストールが促されます。

*3 Unix系OSの老舗メールクライアントであるSylpheedのMac OS X版が登場することを期待して止みません。

2013-03-14

日本OSS奨励賞受賞!

先日、オープンソースカンファレンス2013 Tokyo/Springにて第8回 日本OSS貢献者賞および第4回 日本OSS奨励賞の授賞式が行われました。今回、クリアコードのメンバーおよび関係プロジェクトが日本OSS奨励賞を受賞いたしました。それぞれ推薦して下さった皆様、誠にありがとうございます。

個人での受賞

クリアコードのメンバーでは、Mozillaサポート業務を主に担当している結城が、個人で日本OSS奨励賞を受賞いたしました。個人としてのOSS分野での開発活動については、この1年でめざましい成果があったという訳ではありませんが、これまでのOSSの開発・公開の活動に加えて、Webサイト(ブログ)や雑誌記事執筆等を通じて技術情報を公開する活動についても評価していただけたとのことです。

若手への規範として今後も積極的な活動を続けていく事を奨励する、という意味を込めての受賞ということで、今まであまり実践できていなかった「アップストリームへの還元」にも力を入れていきたい所存です。

チームでの受賞

クリアコードが関わっているプロジェクトの1つであるgroongaも、groonga開発チームが日本OSS奨励賞を受賞いたしました。毎月コンスタントにリリースを行う「肉の日リリース」文化の実践と、ユーザーコミュニティや開発者コミュニティなどを牽引するコミュニティ活動を積極的に行っている点をご評価いただけたとのことです。

Sennaの後継ということで、日本国内では認知されつつあるものの、残念ながら英語圏へはなかなかリーチできていないという現状があり、今後は世界的な普及に繋げていきたいところです。また、分散化プロジェクトなどの新しい取り組みもあります。今後も定期的なリリースを続けながら、継続的な開発を実践し続けていければと、チーム一同思っております。

なお、groonga開発チームの一員として主にmroongaの開発に関わっておられる斯波健徳氏も、同時に個人で日本OSS奨励賞を受賞されています。

まとめ

クリアコードの関係者・関係プロジェクトの日本OSS奨励賞受賞について、簡単ながらご報告させていただきました。

なお、授賞式の様子については技術評論社さまのレポート記事が公開されています。そちらも是非とも併せてご覧下さい。

2013-03-21

Sylpheedのプラグインの作り方

はじめに

軽快で使いやすいオープンソースのメールソフトを標榜しているソフトウェアとしてSylpheedがあります。

Sylpheedは直感的に操作できるユーザーインタフェースをGTK+により提供していて、Windows、Linux、BSD、Mac OS X、その他Unix系OSなど、多数の環境で動作します。Sylpheedの特長について知りたい方はSylpheed - 軽快で使いやすいオープンソースのメールソフト - 特長を参照してください。

今回はそのSylpheedにバージョン3.0から正式に導入されたプラグインの仕組みを利用して機能拡張を行う方法を紹介します。対象とするSylpheedのバージョンは、現在リリースされている安定版である3.3.0 Windows版とします*1

Sylpheedのプラグインでできること

Sylpheedのプラグインの仕様はプラグイン仕様として文書化されています。

Sylpheedのプラグインは以下の2つのライブラリが提供するAPIを利用することでSylpheedと連携します。

  • LibSylphメールライブラリ(libsylph)
  • プラグインインターフェースライブラリ(libsylpheed-plugin)

具体的に利用できるAPIは、ソースアーカイブに含まれているlibsylph/libsylph-0.defsrc/libsylpheed-plugin-0.defで確認できます。エクスポートされているAPIが利用できます。

プラグインで利用できるAPI

プラグインで利用できるAPIを簡単に紹介します。

LibSylphメールライブラリ(libsylph)が提供しているAPI

libsylphが提供しているAPIをいくつか挙げます*2。ここで紹介しているAPIはごく一部です。提供されているAPIの一覧についてはlibsylph/libsylph-0.defを参照してください。

sock_*procmsg_*など、ネットワークやメールに関する機能を利用するためのAPIが数多く提供されていることがわかります。

xml_truncate_buf
xml_unescape_str
strcasestr_with_skip_quote
is_path_parent
extract_addresses
to_unumber
imap_msg_list_set_colorlabel_flags
filter_get_addressbook_func
filter_set_addressbook_func
procmsg_flaginfo_list_free
procmsg_concat_partial_messages
get_last_empty_line_size
append_file_part
procmime_scan_content_type_partial
folder_get_default_junk
folder_get_junk
folder_set_junk
procmime_get_part_fp_fp
xml_escape_str
session_connect_full
socks_info_new
socks_info_free
socks_connect
socks4_connect
socks5_connect
folder_remote_folder_destroy_all_sessions
filter_junk_rule_create
folder_remote_folder_active_session_exist
procmsg_add_messages_from_queue
to_human_readable_buf
folder_item_is_trash
play_sound
session_get_error
sock_new
sock_info_connect
sock_info_connect_async_thread
sock_info_connect_async_thread_wait
folder_set_ui_func2
folder_get_ui_func2
folder_call_ui_func2
export_msgs_to_mbox
...以下省略...
プラグインインターフェースライブラリ(libsylpheed-plugin)が提供しているAPI

libsylpheed-pluginが提供しているAPIもいくつか挙げます*3。ここで紹介しているAPIはごく一部です。提供されているAPIの一覧についてはsrc/libsylpheed-plugin-0.defを参照してください。

こちらはプラグインからユーザインタフェースを制御するためのAPIが提供されていることがわかります。

syl_plugin_add_factory_item
syl_plugin_add_menuitem
syl_plugin_add_symbol
syl_plugin_alertpanel
syl_plugin_alertpanel_full
syl_plugin_alertpanel_message
syl_plugin_alertpanel_message_with_disable
syl_plugin_app_will_exit
syl_plugin_check_version
syl_plugin_compose_entry_append
syl_plugin_compose_entry_get_text
syl_plugin_compose_entry_set
syl_plugin_compose_lock
syl_plugin_compose_new
syl_plugin_compose_unlock
syl_plugin_folder_sel
syl_plugin_folder_sel_full
syl_plugin_folderview_add_sub_widget
syl_plugin_folderview_check_new
syl_plugin_folderview_check_new_all
syl_plugin_folderview_check_new_item
syl_plugin_folderview_check_new_selected
syl_plugin_folderview_get
syl_plugin_folderview_get_selected_item
syl_plugin_folderview_select
syl_plugin_folderview_select_next_unread
syl_plugin_folderview_unselect
syl_plugin_folderview_update_all_updated
syl_plugin_folderview_update_item
syl_plugin_folderview_update_item_foreach
syl_plugin_get_info
syl_plugin_get_module_list
syl_plugin_get_prog_version
syl_plugin_get_type
syl_plugin_inc_is_active
syl_plugin_inc_lock
...以下略...
プラグインで利用できるシグナル

プラグインで利用できるシグナルは以下の通りです。

プラグインでは、シグナルに対応するコールバックを登録することで、ユーザの操作に応じた処理を行うことができます。

シグナルシグナルの発行タイミング
folderview-menu-popupメールの振り分けフォルダツリー表示で右クリックしたときに発行されます。
summaryview-menu-popupサマリビュー(件名等の一覧表示)で右クリックしたときに発行されます。
compose-sendメール送信を実行したときに発行されます。
compose-createdメール作成画面を表示したときに発行されます。
compose-destroyメール作成画面を閉じたときに発行されます。
textview-menu-popupメッセージビュー(メール本文表示)で右クリックしたときに発行されます。
messageview-showメッセージビューにメールを表示したときに発行されます。
inc-mail-startメール受信開始時に発行されます。
inc-mail-finishedメール受信終了時に発行されます。
prefs-common-open全般の設定画面を表示したときに発行されます。
prefs-account-openアカウント編集画面を表示したときに発行されます。
prefs-filter-open振り分けの設定画面を表示したときに発行されます。
prefs-filter-edit-openフィルタルール編集画面を表示したときに発行されます。
prefs-template-openテンプレート設定画面を表示したときに発行されます。
plugin-manager-openプラグイン管理画面を表示したときに発行されます。

シグナル名に使われている名前と実際の画面の対応を図で示します。

Sylpheed画面構成確認結果

プラグインの紹介

Sylpheedで公式に提供されているプラグインは以下の2つです。

  • attachment_tool
  • test

ソースパッケージsylpheed-3.3.0.tar.gzもしくはsylpheed-3.3.0.tar.bz2plugin/attachment_toolディレクトリおよびplugin/testディレクトリにプラグインのソース一式が含まれています*4

公開中のSylpheedのプラグインについては、プラグインページに有志によるプラグインがいくつか紹介されています。

attachment_toolプラグイン

attachment_toolプラグインはSylpheedのプラグイン紹介ページにて紹介されています。添付ファイルを削除するためのプラグインです。

任意のメールを選択した状態で、メニューから「[ツール] - [添付ファイルを削除]」を選択することで添付ファイルを削除できます。対象がローカルフォルダである必要があるため、IMAPアカウントでは使えません。

MIMEの構造はそのまま残っているので、どんなファイルが添付されていたかという情報は失われません*5

testプラグイン

testプラグインはプラグインの基本的な構造を実装したものです。Sylpheedのプラグイン紹介ページでは言及されていません。しかし、プラグインを実装しようとするときに非常に参考になります。プラグインが実装すべき関数やシグナルがシンプルにまとまっているので、プラグインを作るときの雛形として使うことができます。

Windows版ではDLLとして提供されていないので、試してみるには自分でビルドする必要があります。

testプラグインの具体的な内容はソースパッケージのPLUGIN.ja.txtに記載されています。

  • プラグインのロード時に標準出力にtest plug-in loaded!という文字列を出力する
  • フォルダの一覧を取得し、標準出力に表示する
  • Sylpheedのバージョン文字列を取得し、標準出力に表示する
  • メインウィンドウを取得し、前面に出す
  • フォルダビューの下にサブウィジェットを追加する(Testというボタンが配置される)
  • 「ツール」メニューに「Plugin test」メニュー項目を追加する
  • 「Plugin test」メニューを選択すると、「Click this button」というボタンのみのウィンドウを表示し、クリックするとメッセージを出力する
  • アプリケーション初期化、終了、フォルダビューのコンテキストメニューポップアップ、メッセージ作成ウィンドウ作成、メッセージ作成ウィンドウ破棄のイベントを捕捉してメッセージを表示する
  • テキストビューのコンテキストメニュー表示イベントを捕捉してメニュー項目を追加する

セットアップ

WindowsでSylpheedのプラグインをビルドできるようにするための環境構築手順を紹介します。

環境構築手順についてはSylpheed Win32を元に記述しています。

  1. MinGWをインストールする
  2. msys-wgetをインストールする
  3. msys-unzipをインストールする
  4. mingw32-pexportsをインストールする
  5. GTK+ Windowsバイナリをインストールする
  6. GTK+のライブラリを更新する
  7. インポートライブラリを作成する
  8. ソースコードをダウンロード・展開する
  9. 環境変数を設定する
  10. testプラグインの動作確認をする

上記の手順を踏むと、公式で配布されているインストーラ版もしくはzipアーカイブ版で使えるプラグインをビルドするための環境構築ができます。

MinGWをインストールする

SylpheedのプラグインをビルドするためのツールはMinGWのものを利用します。ここではネットワーク経由でのインストールを行うことのできるインストーラーmingw-get-inst-20120426.exeを使ってインストールします。

インストールの途中でインストールするコンポーネントを選択する箇所があります。そこでは以下を選択します。

  • C Compiler
  • C++ Compiler
  • MSYS Basic System
  • MinGW Developer Toolkit

以降の説明ではc:\MinGWへインストールしたものとして説明します。

正しくインストールできていれば、c:\MinGW\msys\1.0\msys.batがインストールされています。msys.batを実行すると以下のようなウィンドウが表示されます。このウィンドウが表示されれば正しくインストールできています。

msys.bat動作確認結果

これ以降の説明では、msys.batを実行して起動したシェルでの操作はプロンプトに$をつけて説明します。

msys-wgetをインストールする

パッケージのダウンロードを行うのに必要なので、msys-wgetパッケージをインストールします。

$ mingw-get install msys-wget

正しくインストールできていれば、wget --versionを実行するとバージョンが表示されます。

$ wget --version
GNU Wget 1.12 built on msys.
...以下省略...
msys-unzipをインストールする

後でzipアーカイブの展開に使うのでunzipコマンドをインストールします。インストールには以下のコマンドを実行します。

$ mingw-get install msys-unzip

正しくインストールできていれば、unzipを引数なしで実行するとヘルプが表示されます。

$ unzip
UnZip 6.00 of 20 April 2009, by Cygwin. Original by Info-ZIP.
...以下省略...
mingw32-pexportsをインストールする

後述するインポートライブラリの作成で使うので、mingw32-pexportsパッケージをインストールします。

$ mingw-get install mingw32-pexports

正しくインストールできていれば、pexportsを引数なしで実行するとヘルプが表示されます。

$ pexports
PExports 0.44 Copyright 1988, Anders Norlander
...以下省略...
GTK+ Windowsバイナリをインストールする

Sylpheedで使われているバージョンと一緒のGTK+のバイナリをインストールする必要があります。以降のシェルでの作業は$HOME/sylpheedディレクトリを作業ディレクトリとします。

MinGWのインストールで起動したシェルで以下を実行します。

$ cd $HOME
$ mkdir -p sylpheed
$ cd sylpheed
$ wget 'http://downloads.sourceforge.net/project/gladewin32/gtk%2B-win32-devel/2.10.11/gtk-dev-2.10.11-win32-1.exe'

ダウンロードしたgtk-dev-2.10.11-win32-1.exeを実行してc:/GTKへインストールします。

シェル上で以下のコマンドを実行します。GTK+のデモアプリケーションが起動したら正常にインストールできています。

$ /c/GTK/bin/gtk-demo.exe
GTK+のライブラリを更新する

次に、以下の手順でGTK+ライブラリを更新します。

$ cd $HOME/sylpheed
$ wget http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.10/gtk+-2.10.14.zip
$ unzip gtk+-2.10.14.zip
$ wget http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.10/gtk+-dev-2.10.14.zip
$ unzip gtk+-dev-2.10.14.zip
$ cp -r /c/GTK .
$ cp -rf gtk+-2.10.14/* GTK
$ cp -rf gtk+-dev-2.10.14/* GTK

c:/GTKにインストールしたバイナリを作業用ディレクトリにコピーしてから、ライブラリを更新します。プラグインのビルドにはコピーした$HOME/sylpheed/GTKを使うようにします*6

ライブラリがきちんと更新できたかどうかは、以下の手順で確認します。

$ $HOME/sylpheed/GTK/bin/pkg-config --modversion gtk+-2.0
2.10.14

上記のようにバージョンが2.10.14になっていれば正しく更新できています。

インポートライブラリを作成する

ここまでで、GTK+の環境が用意できました。プラグインのビルドには、冒頭で説明した、以下の2つのライブラリが必要です。

  • LibSylphメールライブラリ(libsylph)
  • プラグインインターフェースライブラリ(libsylpheed-plugin)

Sylpheedをソースからビルドしてもよいですが、MinGWだとビルドには時間もかかります。

そこで、Sylpheed公式サイトで配布されているzipアーカイブ版を利用してこれらライブラリのインポートライブラリを作成し、そのインポートライブラリをリンクすることでシンボルを解決します*7

$ cd $HOME/sylpheed
$ wget 'http://sourceforge.jp/frs/redir.php?m=jaist&f=/sylpheed/57342/sylpheed-3.3-win32.zip'
$ unzip sylpheed-3.3-win32.zip
$ cd Sylpheed-3.3
$ pexports libsylph-0-1.dll > libsylph-0-1.def
$ dlltool --dllname libsylph-0-1.dll --input-def libsylph-0-1.def --output-lib libsylph-0-1.a
$ pexports libsylpheed-plugin-0-1.dll > libsylpheed-plugin-0-1.def
$ dlltool --dllname libsylpheed-plugin-0-1.dll --input-def libsylpheed-plugin-0-1.def --output-lib libsylpheed-plugin-0-1.a

上記コマンドを実行して、インポートライブラリが2つ作成できていることを確認します。

  • libsylph-0-1.a
  • libsylpheed-plugin-0-1.a
ソースパッケージをダウンロード・展開する

プラグインのビルドに必要なヘッダが含まれているソースパッケージをダウンロード・展開します。

$ cd $HOME/sylpheed
$ wget 'http://sourceforge.jp/frs/redir.php?m=jaist&f=%2Fsylpheed%2F57343%2Fsylpheed-3.3.0.tar.bz2'
$ tar ixf sylpheed-3.3.0.tar.bz2
環境変数を設定する

GTK+関連で必要な環境変数の設定を行います。

$ export PATH=$PATH:$HOME/sylpheed/GTK/bin
$ export PKG_CONFIG_PATH=$HOME/sylpheed/GTK/lib/pkgconfig

pkg-configコマンドが実行できることを確認します*8

$ pkg-config --version
0.20

以上でプラグインのビルドに必要なすべての環境が整いました。

testプラグインの動作確認をする

次に、展開したソースコードのtestプラグインのディレクトリへと移動し、以下のようにしてプラグインをビルドします。

$ cd $HOME/sylpheed/sylpheed-3.3.0/plugin/test
$ gcc -shared -o test.dll test.c -I../../libsylph -I../../src $(pkg-config --cflags --libs gtk+-2.0) $HOME/sylpheed/Sylpheed-3.3/libsylph-0-1.a $HOME/sylpheed/Sylpheed-3.3/libsylpheed-plugin-0-1.a

test.dllがビルドできたら、以下のようにしてプラグインをインストールします*9

$ cp test.dll /c/Users/(ユーザー名)/AppData/Roaming/Sylpheed/plugins

Sylpheed.exeを実行し、アプリケーションを起動します。

メニューにある「[設定] - [プラグインの管理]」からtestプラグインがリストにあることを確認します。

プラグイン管理画面のtestプラグイン確認結果

以上で実際にSylpheed 3.3.0と一緒に動作するプラグインがビルドできることが確認できました。

サンプルプラグインを作る

それでは、実際にプラグインを作ります。題材は、「メール送信時に本文のプレビューと、送信可否の問い合わせを行うプラグイン」とします。

作成するプラグインの仕様は以下の通りとします。

  • メール作成ウィンドウを表示した状態で「送信」ボタンをクリックしたらダイアログを表示する
  • ダイアログでは送信メール本文のプレビューを表示する
  • ダイアログでOKをクリックするとメールが送信される
  • ダイアログでキャンセルをクリックするとメール送信がキャンセルされる
  • メール送信がキャンセルされると、メール作成画面に戻る

上記を実現するのに最適なシグナルが「プラグインから利用できるシグナル」で紹介したcompose-sendです。

compose-sendはメール作成ウィンドウで送信ボタンをクリックしたときに発行されるシグナルです。Sylpheed 3.2.0から使えるようになりました。

シグナルにひもづけたコールバック関数の戻り値によって、そのままメール送信を行うか、メール送信をキャンセルするかが決まります。適切なコールバックをサンプルプラグインで実装することで、今回のお題の仕様を満たすプラグインを作成することができます。

まずは作業用のディレクトリを用意します。

$ cd $HOME/sylpheed
$ mkdir -p sample
$ cd sample

次にインポートライブラリを作業用のディレクトリへとコピーしておきます*10

$ cp ../Sylpheed-3.3/libsylph-0-1.a .
$ cp ../Sylpheed-3.3/libsylpheed-plugin-0-1.a .

最後に空っぽのCのソースファイルを用意して作業を始めます。

$ touch preview_compose_text.c

以降、項目ごとに必要な修正内容を示します。

  1. 必要なヘッダのインクルード
  2. プラグインのロード
  3. プラグインのアンロード
  4. プラグインの情報取得
  5. プラグインのインターフェースバージョン
  6. compose-sendシグナルのコールバック
  7. プレビューダイアログの作成
必要なヘッダのインクルード

プラグインでは以下のようにして必要なヘッダをインクルードする必要があります。メール作成画面に関係した処理を実装するので、compose.hのインクルードが必要です。

1
2
3
4
5
6
#include <glib.h>
#include <gtk/gtk.h>

#include "sylmain.h"
#include "plugin.h"
#include "compose.h"
プラグインのロード

プラグインをロードしたときに呼ばれるplugin_loadを実装します。通常、Sylpheedを起動したときに呼ばれるので、プラグインの初期化ロジックをここで実装します。

compose-sendのコールバックを設定するのもこのタイミングで行います。

plugin_loadの実装は以下の通りです。

1
2
3
4
5
void plugin_load(void)
{
  syl_plugin_signal_connect("compose-send",
                            G_CALLBACK(compose_send_cb), NULL);
}
プラグインのアンロード

プラグインのアンロード時に呼ばれるplugin_unloadを実装します。通常、Sylpheedを終了したときに呼ばれるので、プラグインの後始末をここで行います。

今回のサンプルでは特に何もしません。

1
2
3
void plugin_unload(void)
{
}
プラグインの情報取得

プラグインの情報を返すためにplugin_infoを実装します。

ここで返したプラグインの情報は、Sylpheedのメニューの「[設定] - [プラグインの管理]」を選択すると表示されるプラグイン管理画面のリストの1つとして表示されます。後述するプラグインのインターフェースバージョンが一致していなかったりした場合には表示されないので、プラグインが正しく登録できたかを判断するのに使えます。

SylPluginInfoではプラグインの名称、プラグインのバージョン、プラグイン開発者、プラグインの詳細を定義します。

plugin_interface_versionの実装は以下の通りです。

1
2
3
4
5
6
7
8
9
10
11
static SylPluginInfo info = {
  "Preview message plugin",
  "1.0.0",
  "Yamada Taro",
  "Preview composed message plugin for Sylpheed"
};

SylPluginInfo *plugin_info(void)
{
  return &info;
}
プラグインのインターフェースバージョン

Sylpheedでは、互換性のあるプラグインかどうかを判定するのに、インターフェースバージョンと呼ばれる値を使っています。インターフェースバージョンの値に互換性がない場合には、無効なプラグインとなります。

どのインターフェースバージョンに適合するかという情報を返すためにはplugin_interface_versionを実装します。

SYL_PLUGIN_INTERFACE_VERSIONsrc/plugin.h0x0109と定義されています*11

plugin_interface_versionの実装は以下の通りです。

1
2
3
4
gint plugin_interface_version(void)
{
  return SYL_PLUGIN_INTERFACE_VERSION;
}

インターフェースバージョンの詳細については、プラグイン仕様を参照してください。

compose-sendシグナルのコールバック

メール作成画面で、送信ボタンをクリックしたときに送信可否を問い合わせるコールバックを用意します。

compose-sendシグナルのハンドラとしてcompose_send_cbを実装します。

実装すべきcompose_send_cbのプロトタイプ宣言は以下の通りです。

1
2
3
4
5
6
static gboolean compose_send_cb(GObject     *obj,
                                gpointer     compose,
                                gint         compose_mode,
                                gint         send_mode,
                                const gchar *msg_file,
                                GSList      *to_list)

引数のcomposeがメール作成関連のウィジェットを保持しています。メール本文のプレビューに必要なテキストを取得するには、composeのメンバーにアクセスします。

composetextをメンバーにGtkTextViewへのポインタを保持しているので、GtkTextBufferを経由してテキストを取得します。

このコールバックの返り値がTRUEだとメール送信がキャンセルされます。FALSEだと通常通りそのままメールを送信します。

compose_send_cbの処理の流れは以下の通りです。

  1. プレビューダイアログを作成(後述)
  2. ダイアログを表示してユーザの応答を待つ
  3. ユーザの応答によって送信可否のフラグを返す

compose_send_cbの実装は以下の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static gboolean compose_send_cb(GObject     *obj,
                                gpointer     data,
                                gint         compose_mode,
                                gint         send_mode,
                                const gchar *msg_file,
                                GSList      *to_list)
{
  GtkWidget *dialog;
  gboolean canceled = FALSE;
  Compose *compose = (Compose *)data;
  gint response;

  dialog = create_compose_preview_dialog(compose);

  response = gtk_dialog_run(GTK_DIALOG(dialog));

  switch (response) {
  case GTK_RESPONSE_YES:
    canceled = FALSE;
    break;
  case GTK_RESPONSE_NO:
  default:
    canceled = TRUE;
    break;
  }

  gtk_widget_destroy(dialog);

  return canceled;
}
プレビューダイアログの作成

今回のサンプルプラグインでは、メール送信をクリックしたときに表示するプレビューダイアログを以下のようにします。

  • メッセージ領域(確認のメッセージを表示)
  • プレビュー領域(メール本文を表示)
  • ボタン配置(はい・いいえのボタンを表示)

サンプルプラグインプレビューダイアログ構成

プレビューダイアログを作成する部分の処理の流れについては、以下の通りです。

  1. ダイアログウィンドウをボタンも含めて作成
  2. 確認メッセージを表示するウィジェットを配置
  3. メール本文プレビュー用のウィジェットを配置
  4. フレームの子要素としてプレビュー用のウィジェットを配置
  5. メッセージ、プレビューをダイアログに配置
  6. 作成したダイアログを返す

プレビューダイアログを作成するcreate_compose_preview_dialogの実装は以下の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
static GtkWidget *create_compose_preview_dialog(Compose *compose)
{
  GtkWidget *dialog;
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *message;
  GtkWidget *view;
  GtkWidget *vscroll;
  GtkWidget *frame;
  GtkTextBuffer *buffer;
  gint width, height;

  gtk_window_get_size(GTK_WINDOW(compose->window),
                      &width,
                      &height);

  dialog = gtk_dialog_new_with_buttons("Sending mail confirmation",
                                       GTK_WINDOW(compose->window),
                                       GTK_DIALOG_MODAL,
                                       GTK_STOCK_YES, GTK_RESPONSE_YES,
                                       GTK_STOCK_NO, GTK_RESPONSE_NO,
                                       NULL);

  gtk_widget_set_size_request(dialog, width * 0.8, height * 0.8);

  vbox = gtk_vbox_new(FALSE, WIDGET_SPACING);
  hbox = gtk_hbox_new(TRUE, WIDGET_SPACING);

  message = gtk_label_new("Do you really want to send mail?");

  frame = gtk_frame_new("Preview");

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
  view = gtk_text_view_new_with_buffer(buffer);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);

  vscroll = gtk_scrolled_window_new(NULL, NULL);
  gtk_container_add(GTK_CONTAINER(vscroll), view);
  gtk_container_add(GTK_CONTAINER(frame), vscroll);

  gtk_box_pack_start(GTK_BOX(vbox), message, FALSE, FALSE, WIDGET_SPACING);
  gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, WIDGET_SPACING);

  gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, WIDGET_SPACING);

  gtk_container_add(GTK_CONTAINER(GTK_WIDGET(GTK_DIALOG(dialog)->vbox)), hbox);

  gtk_widget_show_all(hbox);

  return dialog;
}

なお、上記サンプルプラグインの実装ですが、GtkDialogに関する注意点があります。

1
gtk_container_add(GTK_CONTAINER(GTK_WIDGET(GTK_DIALOG(dialog)->vbox)), hbox);

ダイアログの子要素としてウィジェットを配置するためにvbox要素に直接アクセスしていますが、最近のGTK+ではコンパイルエラーになります。

Windows版のSylpheedが使っているGTK+ 2.10.14では妥当な方法ですが、GTK+ 2.14以降ではウィジェットの配置先を取得するための専用の関数*12を使わなければならなくなったためです。

サンプルプラグインのソースコード

ここまでの修正をまとめたサンプルプラグインのソースコード全体を以下に示します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <glib.h>
#include <gtk/gtk.h>

#include "sylmain.h"
#include "plugin.h"
#include "compose.h"

#define WIDGET_SPACING 6

static SylPluginInfo info = {
  "Preview message plugin",
  "1.0.0",
  "Yamada Taro",
  "Preview composed message plugin for Sylpheed"
};

static gboolean compose_send_cb(GObject     *obj,
                                gpointer     compose,
                                gint         compose_mode,
                                gint         send_mode,
                                const gchar *msg_file,
                                GSList      *to_list);

void plugin_load(void)
{
  syl_plugin_signal_connect("compose-send",
                            G_CALLBACK(compose_send_cb), NULL);
}

void plugin_unload(void)
{
}

SylPluginInfo *plugin_info(void)
{
  return &info;
}

gint plugin_interface_version(void)
{
  return SYL_PLUGIN_INTERFACE_VERSION;
}

static GtkWidget *create_compose_preview_dialog(Compose *compose)
{
  GtkWidget *dialog;
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *message;
  GtkWidget *view;
  GtkWidget *vscroll;
  GtkWidget *frame;
  GtkTextBuffer *buffer;
  gint width, height;

  gtk_window_get_size(GTK_WINDOW(compose->window),
                      &width,
                      &height);

  dialog = gtk_dialog_new_with_buttons("Sending mail confirmation",
                                       GTK_WINDOW(compose->window),
                                       GTK_DIALOG_MODAL,
                                       GTK_STOCK_YES, GTK_RESPONSE_YES,
                                       GTK_STOCK_NO, GTK_RESPONSE_NO,
                                       NULL);

  gtk_widget_set_size_request(dialog, width * 0.8, height * 0.8);

  vbox = gtk_vbox_new(FALSE, WIDGET_SPACING);
  hbox = gtk_hbox_new(TRUE, WIDGET_SPACING);

  message = gtk_label_new("Do you really want to send mail?");

  frame = gtk_frame_new("Preview");

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
  view = gtk_text_view_new_with_buffer(buffer);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);

  vscroll = gtk_scrolled_window_new(NULL, NULL);
  gtk_container_add(GTK_CONTAINER(vscroll), view);
  gtk_container_add(GTK_CONTAINER(frame), vscroll);

  gtk_box_pack_start(GTK_BOX(vbox), message, FALSE, FALSE, WIDGET_SPACING);
  gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, WIDGET_SPACING);

  gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, WIDGET_SPACING);

  gtk_container_add(GTK_CONTAINER(GTK_WIDGET(GTK_DIALOG(dialog)->vbox)), hbox);

  gtk_widget_show_all(hbox);

  return dialog;
}

static gboolean compose_send_cb(GObject *obj,
                                gpointer data,
                                gint compose_mode,
                                gint send_mode,
                                const gchar *msg_file,
                                GSList *to_list)
{
  GtkWidget *dialog;
  gboolean canceled = FALSE;
  Compose *compose = (Compose *)data;
  gint response;

  dialog = create_compose_preview_dialog(compose);

  response = gtk_dialog_run(GTK_DIALOG(dialog));

  switch (response) {
  case GTK_RESPONSE_YES:
    canceled = FALSE;
    break;
  case GTK_RESPONSE_NO:
  default:
    canceled = TRUE;
    break;
  }

  gtk_widget_destroy(dialog);

  return canceled;
}
サンプルプラグインのビルド

サンプルプラグインのソースコードが完成したので、プラグインを以下の手順でビルドします。

$ gcc -shared -o preview_compose_text.dll preview_compose_text.c -I$HOME/sylpheed/sylpheed-3.3.0/src -I$HOME/sylpheed/sylpheed-3.3.0/libsylph $(pkg-config --cflags --libs gtk+-2.0) libsylph-0-1.a libsylpheed-plugin-0-1.a

ビルドに成功すると、preview_compose_text.dllが作成されます。

サンプルプラグインの動作確認

ビルドしたDLLをSylpheedのプラグインディレクトリへと以下のようにしてコピーします*13

$ cp preview_compose_text.dll /c/Users/$(ユーザー名)/AppData/Roaming/Sylpheed/plugins

Sylpheedを起動し、プラグインの管理画面からサンプルプラグインが登録されていることを確認します。

サンプルプラグイン管理画面確認結果

メールを作成し、送信ボタンをクリックします。すると、以下のような送信プレビュー画面が表示されます。プレビュー画面で「はい」をクリックするとそのままメールを送信できます。「いいえ」をクリックするとメールを送信せずメール作成画面に戻ります。

サンプルプラグインプレビュー確認結果

これで、作成したサンプルプラグインがきちんと動作することを確認できました。

まとめ

Windows版のSylpheed 3.3.0向けにプラグインを作る方法を紹介しました。

Sylpheedが提供しているAPIを利用したプラグインはまだあまり公開されていませんが、一度プラグインを作る環境さえ整えてしまえば、Windowsであっても(GTK+の知識さえあれば)独自に拡張していくのもそれほど敷居は高くないことがわかります。

今回はcompose-sendシグナルを使ったシンプルなプラグインの作り方についてでしたが、さらに発展させて、外部Webサービスと連携して文章チェックするところまで作り込むこともできるでしょう。

欲しい機能をいきなりSylpheed本体に実装してもらうというのは難しいかもしれません。そんなときはプラグインという仕組みを利用してアイデアを熟成させてみるのはいかがでしょうか。

Sylpheedで「こんなことができたら便利なのに」という思いから、プラグインを作って公開する人が増えるといいですね。

参考資料

サンプルプログラムで使用しているGTK+やGLibのドキュメントは以下の通りです。

サンプルプログラムで使用した主要ウィジェットのドキュメントは以下の通りです。

SylpheedのWindows版で使用しているGTK+のバージョンは2.10.14と古いため、Windows版のプラグインを新規で開発する場合にはドキュメントに記載されている関数の対応バージョンに注意が必要です。

古いバージョンでは使えない関数に数多く該当したり、ドキュメントでは非推奨となっているウィジェットをバージョンの都合で使わざるを得ないこともあります。特にWindowsだけでなく、Unix系OSでも使えるプラグインにするには、そのあたりに留意してください。

*1 あまり言及されていないのでWindows版特有の情報を重点的に紹介することにしましたが、その他OSでもプラグインの基本は同じです。

*2 個々のAPIの詳細は割愛します。

*3 個々のAPIの詳細は割愛します。

*4 インストーラ版およびzipアーカイブ版にはソースは含まれていません。

*5 アイコン等が変わらないので、添付ファイルが削除されていることは添付ファイルのサイズ等から判断します。

*6 GTK+のライブラリの更新をするので、c:/GTKとはディレクトリを分けています。

*7 すでにお使いのzipアーカイブ版もしくはインストール版のバイナリがあれば、改めてダウンロードしなくても既存のものを使うのでも構いません。その場合は適宜読み替えてください。

*8 pkg-configはGTK+に含まれています。

*9 ユーザー名は各自読み替えてください。

*10 既存のzipアーカイブ版もしくはインストーラ版でインポートライブラリを作成した場合は読み替えてください。

*11 Sylpheed 3.3.0の場合です。

*12 gtk_dialog_get_content_area()

*13 Windows 7の場合です。他の環境では適宜読み替えてください。

2013-03-26

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|