Sylpheedのプラグインの作り方 - 2013-03-26 - ククログ

ククログ

株式会社クリアコード > ククログ > Sylpheedのプラグインの作り方

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のインクルードが必要です。

#include <glib.h>
#include <gtk/gtk.h>

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

プラグインのロード

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

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

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

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

プラグインのアンロード

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

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

void plugin_unload(void)
{
}

プラグインの情報取得

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

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

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

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

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の実装は以下の通りです。

gint plugin_interface_version(void)
{
  return SYL_PLUGIN_INTERFACE_VERSION;
}

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

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

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

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

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

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の実装は以下の通りです。

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の実装は以下の通りです。

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に関する注意点があります。

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

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

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

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

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

#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の場合です。他の環境では適宜読み替えてください。