2015年6月13日土曜日

GTKでファイルを表示するTreeを作ってみた

練習がてらに調べてつなぎあわせて作ったコードを公開してみる。
LinuxPCでhomeディレクトリを表示し、クリックするたびにその中身を読み込んでツリーを作ってゆく。

表示するウィジェットの設定はめんどいのでgladeを利用している。

ドラッグアンドドロップやダブルクリックした際のイベントは設定していないので、
ただディレクトリを表示するだけだが、色々と苦労した・・・

以下のmain.cと、glade_test_tree.gladeをメモ帳かなんかでテキトーに作成し、
ファイルのあるディレクトリに入ってコンパイルすれば行けるはず。
なお、画像ファイルとしてfolder.icoとdocument.icoもテキトーに用意して同じディレクトリに入れておく。

アイコン探すならここが便利かも。
iconizer.net

コンパイル次のコマンド
gcc -o test main.c `pkg-config --cflags --libs gtk+-3.0 gmodule-2.0`


main.cの中身。

#include <gtk/gtk.h>
#include<glade/glade.h>//gladeXMLから名前を引用できるようになったりする。
#include <glib.h>
#include <glib/gprintf.h>
#include <stdio.h>

GtkBuilder *builder; //

enum//カラムに設定する項目を列挙。N_COLUMNSは順番で2が割り当てられる。
{
    COLUMN_ICON, //0
    COLUMN_LABEL, //1
    COLUMN_IS_DIR, //2
    COLUMN_FULL_PATH, //3
    N_COLUMNS//4
};

typedef struct _TreeData TreeData;

struct _TreeData {
    gchar *label;
    gboolean isdir;
    gchar *fullpath;
};

static gint sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) {//ソート用の関数。ちょっとよくわからん。
    gboolean is_dir_a, is_dir_b;
    gchar *name_a, *name_b;
    int result;

    gtk_tree_model_get(model, a,
            COLUMN_IS_DIR, &is_dir_a,
            COLUMN_LABEL, &name_a, -1);
    gtk_tree_model_get(model, b,
            COLUMN_IS_DIR, &is_dir_b,
            COLUMN_LABEL, &name_b, -1);
    if (!is_dir_a && is_dir_b) {
        result = 1;
    } else if (is_dir_a && !is_dir_b) {
        result = -1;
    } else {
        result = g_utf8_collate(name_a, name_b);
    }
    return result;
}

static void add_data(gchar *myDir, GtkTreeIter *parent) {
    GDir *dire;
    gchar *path;
    GtkTreeStore *store;
    store = (GtkTreeStore*) gtk_builder_get_object(builder, "treestore1");
    //gtk_tree_store_clear(store);
    GdkPixbuf *folder_pixbuf = gdk_pixbuf_new_from_file("folder.ico", NULL);
    GdkPixbuf *file_pixbuf = gdk_pixbuf_new_from_file("document.ico", NULL);

    //標準ソート関数の設定 よくわからん
    gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), sort_func, NULL, NULL);
    //ソートに使用する項目の設定 よくわからん
    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);


    GtkTreeIter iter; //構造体
    dire = g_dir_open(myDir, 0, NULL); //ディレクトリを開く
    if (!dire) return;
    if (dire) {
        const gchar *name;
        gboolean is_dir;
        while (name = g_dir_read_name(dire)) {
            if (name[0] != '.') {//位置文字目がドットの場合を除く(隠しファイルの除外
                path = g_build_filename(myDir, name, NULL); //ファイル名用のメモリ領域を確保した上で、ファイル名にパスを追加する
                is_dir = g_file_test(path, G_FILE_TEST_IS_DIR); //ディレクトリかどうかを調べている。
                gtk_tree_store_append(store, &iter, parent); //一番上のトップレベルなので、親を表す引数(3番目)はNULL
                gtk_tree_store_set(store, &iter,
                        COLUMN_ICON, (is_dir) ? folder_pixbuf : file_pixbuf,
                        COLUMN_LABEL, g_filename_to_utf8(name, -1, NULL, NULL, NULL),
                        COLUMN_IS_DIR, is_dir,
                        COLUMN_FULL_PATH, path,
                        -1); //トップレベルのアイコンとラベルの追加
                g_free(path);
            }
        }
    }
    g_dir_close(dire);
    g_object_unref(folder_pixbuf);
    g_object_unref(file_pixbuf);
}

gboolean cb_quit_dialog(GtkWidget *widget, gpointer user_data) {
    GtkWidget *dialog;
    gint result;
    //YESかNOを選択するダイアログを表示
    dialog = gtk_message_dialog_new(GTK_WINDOW(widget), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
            "終了します。\nよろしいですか?");
    result = gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
    //YESを選択した場合
    if (result == GTK_RESPONSE_YES) {
        //destroyシグナルが発生(指定したcb_destroy関数へ)
        gtk_main_quit();
        return FALSE;
    }//NOを選択した場合
    else {
        //何も発生しない
        return TRUE;
    }
}

void cb_tree_add(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) {
    GtkTreeStore *store;
    GtkTreeIter parent_iter;
    gchar *name;
    gchar *mypath;
    gboolean *mydir;
    gboolean has_child;
    store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
    //store = (GtkTreeStore*) gtk_builder_get_object(builder, "treestore1");
    //クリックされたアイテムに対するGtkTreeIterの値を取得
    gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &parent_iter, path); //pathに対応するiterの確認
    gtk_tree_model_get(GTK_TREE_MODEL(store), &parent_iter, COLUMN_LABEL, &name, COLUMN_FULL_PATH, &mypath, COLUMN_IS_DIR, &mydir, -1); //iterに格納されている値を取り出す。
    if (&mydir) {
        has_child = gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &parent_iter); //子があるかどうかのチェック
        if (!has_child) add_data(mypath, &parent_iter); //選択したものがディレクトリかつ、子が無いならツリーを追加
    }
    g_free(name);
    g_free(mypath);
    //g_free(mydir);
}


int main(int argc, char** argv) {
    GtkWidget *window;
    //GtkWidget *treeview;
    /* Gtkの初期化*/
    gtk_init(&argc, &argv);
    /* GtkBuilder作成 */
    builder = gtk_builder_new();
    /* gladeファイルの読み込み */
    gtk_builder_add_from_file(builder, "glade_test_tree.glade", NULL);
    //シグナル設定した関数を有効にする。
    gtk_builder_connect_signals(builder, NULL);
    add_data("/home", NULL);
    /* window1のオブジェクト取得 */
    window = (GtkWidget*) gtk_builder_get_object(builder, "window1");

    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}


glade_test_tree.gladeの中身。

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
  <requires lib="gtk+" version="3.10"/>
  <object class="GtkTreeStore" id="treestore1">
    <columns>
      <!-- column-name icon -->
      <column type="GdkPixbuf"/>
      <!-- column-name gchararray1 -->
      <column type="gchararray"/>
      <!-- column-name is_dir -->
      <column type="gboolean"/>
      <!-- column-name fullpass -->
      <column type="gchararray"/>
    </columns>
  </object>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <property name="default_width">200</property>
    <property name="default_height">500</property>
    <signal name="delete-event" handler="cb_quit_dialog" swapped="no"/>
    <child>
      <object class="GtkScrolledWindow" id="scrolledwindow1">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="shadow_type">in</property>
        <child>
          <object class="GtkTreeView" id="treeview1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="vscroll_policy">natural</property>
            <property name="model">treestore1</property>
            <property name="search_column">1</property>
            <property name="hover_expand">True</property>
            <property name="level_indentation">15</property>
            <property name="enable_tree_lines">True</property>
            <property name="activate_on_single_click">True</property>
            <signal name="drag-data-received" handler="cb_drag_data_received" swapped="no"/>
            <signal name="row-activated" handler="cb_tree_add" object="treestore1" swapped="no"/>
            <child internal-child="selection">
              <object class="GtkTreeSelection" id="treeview-selection"/>
            </child>
            <child>
              <object class="GtkTreeViewColumn" id="treeviewcolumn2">
                <property name="title" translatable="yes">ファイル</property>
                <property name="alignment">8.9406970715799616e-10</property>
                <property name="reorderable">True</property>
                <property name="sort_indicator">True</property>
                <property name="sort_column_id">1</property>
                <child>
                  <object class="GtkCellRendererPixbuf" id="cellrendererpixbuf2"/>
                  <attributes>
                    <attribute name="pixbuf">0</attribute>
                  </attributes>
                </child>
                <child>
                  <object class="GtkCellRendererText" id="cellrenderertext2"/>
                  <attributes>
                    <attribute name="text">1</attribute>
                  </attributes>
                </child>
              </object>
            </child>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>

0 件のコメント:

コメントを投稿