大規模ソフトウェアを手探るその4 〜ツールチップの実装〜

こんにちは。

今回でこの課題も最後で、最後にツールバーのアイコンの上にマウスをおくとそのツールの説明が表示されるツールチップ機能の実装について書きます。

もともとGpaintにはこの機能はなく、しかもツールのアイコンもお世辞にもわかりやすいものであるとは言えず、ぜひともこの機能は欲しいと思っていました。

今まで通り実装の流れを書いていきます。

 

実はそもそもこういう機能は欲しいなとは思っていたものの機能の名前がわからず調べるのも一苦労でした(最初はオンマウスとかでググっていました)。調べていくうちにツールチップという機能であるとわかり、しかもネットに同じような実装をしている例がいろいろあったので試してみましたがエラーがたくさんでて上手くいきません。そこで原点に立ち返ってソースコードを眺めることにすると、ui.cで下のようにツールチップを設定している箇所がありました(よく考えるとウィンドウ上部にある保存ボタンや印刷ボタンにはもともと説明が表示されていたのであるのは当たり前でした)。

gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (print_button), tooltips, _("Print"), NULL);

 

あとはこれを流用すればよいと思いやってみましたが、下のようなエラーがでて上手くいきません。

(gpaint-2:10597): GLib-GObject-WARNING **: invalid cast from 'GtkToggleButton' to 'GtkToolItem'

(gpaint-2:10597): Gtk-CRITICAL **: IA__gtk_tool_item_set_tooltip: assertion 'GTK_IS_TOOL_ITEM (tool_item)' failed

どうやら上部の保存や印刷などのボタンと左側のツールバーのボタンは種類が違うらしく(前者がGtkToolItemで後者がGtkToggleButton)、下のように階層も異なっているため単純には流用できなかったようです。

 GObject
   +----GtkObject
         +----GtkWidget
               +----GtkContainer
                     +----GtkBin
                           +----GtkButton
                                 +----GtkToggleButton
                                       +----GtkCheckButton
GObject
   +----GtkObject
         +----GtkWidget
               +----GtkContainer
                     +----GtkBin
                           +----GtkToolItem
                                 +----GtkToolButton
                                 +----GtkSeparatorToolItem
 
従ってGtkToggleButtonの元のGtkButtonのツールチップ実装について調べると、gtk_tooltips_set_tip関数を使うと良いということがわかったので下のように加えてみたところ無事にツールチップが実装されました。

gtk_tooltips_set_tip (tooltips, erase_button , _("erase"), NULL);

f:id:sumesimeji:20151030000609j:plain

変更を加えたのはui.cとtool_palette.cのみです。

 

 

以上4回に渡ってフリーソフトGpaintにあれこれ機能を追加してみました。すでに完成された大規模なプログラムに手を加えるのは初めての試みでしたが、以外と何とかなるなというのが終わってみての感想です。これを機に多少のことには物怖じしないでプログラミングができるようになったのではないかと思います。

最後まで読んでいただきありがとうございました。

大規模ソフトウェアを手探るその3 〜三角形描画ツールの実装〜

こんにちは。

次にやることとして、もともとある長方形描画ツールを上手く利用して三角形描画ツールを実装できないかと考えました。

 

まずは新しくツールを加えるにあたって左側のツールバー(下図)にそれ用のアイコンを追加させます。

f:id:sumesimeji:20151029105353p:plain

ui.cを眺めているとどうやらtoolbar4とtoolbar5がそれぞれツールバーの左側と右側に対応しているようで、

gtk_widget_set_size_request (toolbar4, 40, 284);

の40が横、284が縦の長さと考えるとつじつまが合います。

したがってそれぞれの縦の長さをアイコンの大きさと考えられる40だけ大きくして

gtk_widget_set_size_request (toolbar4, 40, 324);

とすると確かにツールバーが長くなりました(左下図:どうやら隠れていたアイコンも出てきたようです)。

f:id:sumesimeji:20151029111512p:plain  f:id:sumesimeji:20151029112401p:plain

あとは例えば楕円描画ツールのボタンの定義をそのままコピペすると、ちゃんとアイコンが追加されました(右上図)。

 

いよいよ三角形を描画する関数を書いていきます。長方形や直線などを描画する関数はshape.cに書かれていたので、そこを読んでいきます。

もともとは長方形描画ツールを流用する予定でしたが、これはクリックして押したままドラッグ→離すの操作で長方形領域を作って描画するというもので、今回実装したいツールとは描画の仕方が異なる(クリック→離すで点をうっていき、二点を線で結んでいく)ので結局1から書いていきました。

#if TRIANGLE
/*
 * Draw a triangle.
 */
static void
draw_triangle(gpaint_shape *shape, gboolean filled)
{
	//初期化
	if( tri_init_flag == 1 ){
		tripoints->x = -1;
		tripoints->y = -1;
		(tripoints+1)->x = -1;
		(tripoints+1)->y = -1;
		(tripoints+2)->x = -1;
		(tripoints+2)->y = -1;
		tri_init_flag = 0;
	}	
	//初期化ここまで

    GdkDrawable *d = GPAINT_TOOL(shape)->drawing->window;
    GdkGC *gc = GPAINT_TOOL(shape)->drawing->gc;
	//最初の点が空だった場合
	if( tripoints->x == -1 && tripoints->y == -1 ){
    		tripoints->x = shape->anchor.x;
    		tripoints->y = shape->anchor.y;
		point_num = 1;
	}
	//最初の点は埋まっているけど2つ目の点は空な場合
	else if( (tripoints + 1)->x == -1 && (tripoints + 1)->y == -1 ){
		(tripoints + 1)->x = shape->anchor.x;
    		(tripoints + 1)->y = shape->anchor.y;
		point_num = 2;
	}
	else if( (tripoints + 2)->x == -1 && (tripoints + 2)->y == -1 ){
		(tripoints + 2)->x = shape->anchor.x;
    		(tripoints + 2)->y = shape->anchor.y;
		point_num = 3;
	}
	

    gdk_draw_polygon(d, gc, filled, tripoints, point_num);
    if ( point_num == 3 )
    {
	const GdkPoint tripoints_copy[3] = {
			{ tripoints->x, tripoints->y },
			{ (tripoints+1)->x, (tripoints+1)->y },
			{ (tripoints+2)->x, (tripoints+2)->y }
	};
	gdk_draw_polygon(GPAINT_TOOL(shape)->drawing->backing_pixmap, gc, filled, tripoints_copy, 3);
		tri_init_flag = 1;
		point_num = 0;
    }
    drawing_modified(GPAINT_TOOL(shape)->drawing);
}
#endif

 

いろいろエラーを直し、最終的に上の形になりました。GdkというGTKのグラフィックス関係を担当するライブラリに便利な関数があったのでそれを用いています。

下のようにちゃんと三角形が描画されています。

f:id:sumesimeji:20151029120101p:plain

 

今回変更を加えたファイルはui.c、shape.c、shape.h、callbacks.h、tool_palette.c、pixmaps.c、pixmaps.h(アイコン画像変更のため)です。

 

次回でいよいよ最後で、ツールバーのアイコンの上にマウスをおくとそのツールの説明が表示されるツールチップ機能の実装について書きます。

大規模ソフトウェアを手探るその2 〜カラーパレットに色を追加〜

こんにちは。

前回無事にGpaintをインストールできたので、いよいよ中身をいじっていきます。

 

まず最初の目標として、下図のカラーパレットに色を追加することにしました。

f:id:sumesimeji:20151024212628p:plain

 

srcフォルダの中にui.cとcolor_palette.cというファイルがあり、これらが怪しいと思いコードを眺めてみると見るからにカラーパレットを設定しているっぽい部分をui.cに見つけます。

f:id:sumesimeji:20151024224545p:plain

 

とりあえずこれの定義も含めそっくりそのまま数字を変えて追加してみましたが、変化はありません。行き詰っていたところTAの方に「逆に色の数を減らしてみたらいいんじゃないか」とアドバイスを頂いたのでやってみると、エラーがいくつか出ましたがそのおかげで何が足りていなかったのかがわかりました。

 

ちなみにこの時のエラーの1つに

(gpaint-2:20038): Gtk-CRITICAL **: gtk_widget_ref: assertion 'GTK_IS_WIDGET (widget)' failed

というものがありましたが、このようなassertion系のエラーはgdb(デバッガ)で

b main→b abort

ブレークポイントを設定してcontinueでabortされる直前まで飛び、whereでそこにいたるまでのプログラムの流れを見ることができます。upで上にたどれます。

 

結局何が間違っていたのかというとframeとcolor_palette_entryの数字の対応が合っていなかったということで(上図でもframe4に対してcolor_palette_entry1が対応している)、ちゃんと数字を対応させてframe30-entry28、frame31-entry29で追加したところ、無事に右端に2つ(どちらも黒ですが)色が追加されました。

 

f:id:sumesimeji:20151025235715p:plain

f:id:sumesimeji:20151025235723p:plain

(↑frameとcolor_palette_entryの番号の割り振られ方。なんでこんな分かりにくくしたのか...)

 

f:id:sumesimeji:20151026000625p:plain

 

変えたところはというとui.cで上の画像の部分をその定義も含めて番号を変えてまるまるコピペしたのと、color_palette.cで色を定義している配列の最後に2つ分書き加えただけです。こんなもんで色を追加できるんだとちょっと驚きました。

 

無事に最初の目標は達成できたので次はちょっと難しそうなことをやってみます。

大規模ソフトウェアを手探るその1 〜インストールまで〜

こんにちは。

大学の課題でフリーのペイントソフトGpaint(GNU paint)にいろいろ手を加えてみたので、これから何回かに分けて紹介していこうと思います(環境はubuntu14.04)。

 

まずはGpaintについて。

Gpaint - GNU Project - Free Software Foundation (FSF)

f:id:sumesimeji:20151024130411p:plain

Gpaintはこんな感じの非常にシンプルなペイントソフトで、ソースコードは約15,000行、なんと全部C言語で書かれています。お世辞にも使い勝手が良いとは言えず、undo機能がない、ツールの説明がないなど、いろいろと改良の余地はありそうです。

 

とりあえずソースコードを自分でいじるためにビルドします。上のサイトからversion0.3.2のソースコードをダウンロードし、ファイルを解凍して

$ CFLAGS="-O0 -g" ./configure --prefix=/home/denjo/gpaint_install

$ make

$ make install

の順でインストールすれば良いのですが、コンパイルのところで多数のエラーが出てしまいます。そのうちの1つはこちら。

drawing.c: In function ‘drawing_prompt_to_save’:

drawing.c:432:69: error: ‘GTK_RESPONSE_DISCARD’ undeclared (first use in this function) gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_DISCARD,GTK_RESPONS

どういうことかわからなかったのでエラーメッセージをそのままGoogleで検索してみると、過去に全く同じところで躓いた人がいたようで、解決策がのっていました。どうやらGTKというGpaintが用いているライブラリのバージョンがGpaintが書かれた当時よりもあがってしまい、互換性のない変更が行われていたようです。エラーメッセージをそのままググるはこのあと度々お世話になりました。

 

こんな感じでエラーメッセージを見ながら1つ1つ対処していきますが、最後に以下のようなエラーが出ます。

$ gcc -O0 -g -o gpaint-2 about.o ..... ui.o -lgtk-x11-2.0 ..... -lglib-2.0 /usr/bin/ld: image.o: シンボル 'cos@@GLIBC_2.2.5' への未定義参照です

C言語でsinやsqrtなどの数学関数を使う際にはコンパイル時にオプション-lmをつける必要がありますが、今回はそれがないためこのようなエラーが出たと考えられます。実際手動で

$ gcc -O0 -g -o gpaint-2 about.o ..... ui.o -lgtk-x11-2.0 ..... -lglib-2.0 -lm

と最後に-lmを追加して実行すると上手く行きます。したがってmake時にオプション-lmが付くようになればよいということになるので、Makefileの中を見てみることにします。ここで先生にMakefileMakefile.inから生成されていると教えてもらったのでMakefile.inの中を見てみると

gpaint-2: $(gpaint_2_OBJECTS) $(gpaint_2_DEPENDENCIES) @rm -f gpaint-2 $(LINK) $(gpaint_2_LDFLAGS) $(gpaint_2_OBJECTS) $(gpaint_2_LDADD) $(LIBS)

という記述が見つかります。これはコンパイルメッセージはこのように構成されているということで、最後の$(LIBS)はリンク時に指定するライブラリを表します。 したがってここに-lmが入るようにすればよく、Makefileはconfigureによって、Makefile.inをテンプレートとして生成されているのでconfigure時に

$ LIBS=-lm CFLAGS="-O0 -g" ./configure --prefix=/home/denjo/gpaint_install

と追加することで無事にコンパイルが通りました。 

 

そんなこんなでなんとかインストールに成功します。

次回から実際に中身に手を加えていきます。