おしながき

ELFファイルフォーマット

  • .eh_frameセクションの構造と読み方

DWARFファイルフォーマット

NCURSESライブラリ

  • NCURSES Programing HOWTO ワタクシ的ほんやく
    1. Tools and Widget Libraries
    2. Just For Fun !!!
    3. References
  • その他、自分メモ
  • NCURSES雑多な自分メモ01


最近の更新 (Recent Changes)

2019-09-24
2013-10-10
2013-10-03
2013-10-01
2013-09-29

18.フォームライブラリ (Forms Library)

Webページにあるたくさんの種類のユーザからの入力を拾ってくるフォームを見ると、テキストモードの画面でもそのようなフォームの1つをどうやって作れるのか興味をもつだろう。
ncursesだけではあのような気のきいたフォームを使うのは少々難しい。
Formライブラリは、気楽にフォームを作り、維持するための基本的なフレームワークを提供している。
Formライブラリは、有効性の管理、フィールドの動的な展開などに沢山の機能(関数)を持っている。
以下で十分な流れを見て行こう。
フォームはフィールドの集まりであり、フィールドはラベル(固定されたテキスト)かデータを入力する場所のどちらかである。
また、フォームライブラリはフォームを複数のページに跨がせるための関数も提供する。


18.1. 基礎

フォームはメニューと同じ方法で作る。
最初に、フォームと関連するフィールドを new_field()関数で生成する。
フィールドには、見た目やフォーカスを失う前の有効化などのオプションをセットすることができる。
次に、フィールドをフォームに結びつける。
この後、フォームは画面に表示することができ、入力を受け付ける準備も整う。
menu_driver()関数の行と類似したものとして、フォームはform_driver()関数で操作する。
特定のフィールドにフォーカスを移動したり、カーソルをフィールドの終りまで動かしたり、などの要求をform_driver()関数に要求することができる。
利用者がフィールドに値を入力し値を確定させた後、フォームの表示を終えメモリの確保を開放することができる。

フォームライブラリを使うプログラムの制御方法の一般的な流れを以下に示す。

  • 1.ncursesの初期化
  • 2.new_field()関数を用いたフィールドの生成。
    フォームの高さ、幅、位置を決めることができる。
  • 3.結びつけるフィールドを指定する形での、new_form()関数によるフォームの生成
  • 4.フォームの確定、および画面のrefresh
  • 5.ループ処理によるユーザ要求の処理と必要に応じたform_driver()関数を持ちてのフォームの更新
  • 6.form_unpost()関数によりフォームの取消
  • 7.free_form()関数によるフォーム用メモリの開放
  • 8.free_field()関数によるフィールド用メモリの開放
  • 9.ncursesの終了処理

フォームライブラリの動作は、メニューライブラリの操作方法と良く似ていると気づいただろう。
以下の例はフォーム処理のさまざまな特徴を見ることができるだろう。
それでは、まず単純な例をみてみよう。


18.2. フォームライブラリのコンパイル

フォームライブラリの関数を使うためには、form.hをインクルードし、プログラムのリンク時に -lform -lncursesの順でオプション指定する必要がある。

    #include <form.h>
    .
    .
    .

    compile and link: gcc <program file> -lform -lncurses


/* Example 25. Forms Basics */

#include <form.h>

int main()
{       FIELD *field[3];
        FORM  *my_form;
        int ch;

        /* Initialize curses */
        initscr();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);

        /* Initialize the fields */
        field[0] = new_field(1, 10, 4, 18, 0, 0);
        field[1] = new_field(1, 10, 6, 18, 0, 0);
        field[2] = NULL;

        /* Set field options */
        set_field_back(field[0], A_UNDERLINE);  /* Print a line for the option  */
        field_opts_off(field[0], O_AUTOSKIP);   /* Don't go to next field when this */
                                                /* Field is filled up           */
        set_field_back(field[1], A_UNDERLINE);
        field_opts_off(field[1], O_AUTOSKIP);

        /* Create the form and post it */
        my_form = new_form(field);
        post_form(my_form);
        refresh();

        mvprintw(4, 10, "Value 1:");
        mvprintw(6, 10, "Value 2:");
        refresh();

        /* Loop through to get user requests */
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
                {       case KEY_DOWN:
                                /* Go to next field */
                                form_driver(my_form, REQ_NEXT_FIELD);
                                /* Go to the end of the present buffer */
                                /* Leaves nicely at the last character */
                                form_driver(my_form, REQ_END_LINE);
                                break;
                        case KEY_UP:
                                /* Go to previous field */
                                form_driver(my_form, REQ_PREV_FIELD);
                                form_driver(my_form, REQ_END_LINE);
                                break;
                        default:
                                /* If this is a normal character, it gets */
                                /* Printed                                */
                                form_driver(my_form, ch);
                                break;
                }
        }

        /* Un post form and free the memory */
        unpost_form(my_form);
        free_form(my_form);
        free_field(field[0]);
        free_field(field[1]);

        endwin();
        return 0;
}

上の例は非常に簡単なものだ。
new_field()関数を使い、2つのフィールドを作成する。
new_field()関数は、高さ、幅、開始行、開始列、非表示画面の行番号、追加するワークバッファの番号を引数に取る。
5つ目の引数である非表示画面の行番号は、画面に表示する行数を決める。
もし0なら、常に全てのフィールドを表示し、それ以外であればユーザが表示されていないフィールドの一部にアクセスした時スクロールされることを示す。
フォームライブラリはユーザが入力したデータを蓄積するためにフィールドあたり1つのバッファを確保する。
new_field()関数の最後の引数は確保した追加のバッファを指定する。
これはプログラマが任意の目的のために使うことができる。

フィールドを作成した後、set_field_back()関数を用いてアンダーラインの背景属性を2つのフィールドに設定している。
AUTOSKIPオプションはfield_opts_off()関数で停止している。
もしこのオプションがONなら、有効がフィールドが完全に一杯になるまで入力されると、次のフィールドへフォーカスが移動するだろう。

フィールドとフォームを結びつけたあと、フォームを確定している。 この段階で、ユーザの入力は関連する要求を添えてform_driver()関数に渡す形で、whileループ内で処理される。
form_driver()関数に渡せる全ての要求の詳細は後述する。


18.3. フィールドと戯れる

フィールドは沢山のAttribute(属性)を持っている。
それらのAttributeは、求める効果を得て楽しむために操作することができる。
待っていられないだろ?


18.3.1. フィールドのサイズと配置場所の取得

フィールドを生成する時に与えたパラメタは、field_info()関数で取得することができる。
その関数は、パラメタとしてフィールドに与えた、高さ、幅、配置行、配置列、非表示画面の行番号、追加のバッファ番号が返ってくる。
new_field()関数とは逆方向に。

int field_info(     FIELD *field,              /* field from which to fetch */
                    int *height, *int width,   /* field size */
                    int *top, int *left,       /* upper left corner */
                    int *offscreen,            /* number of offscreen rows */
                    int *nbuf);                /* number of working buffers */


18.3.2. フィールドの移動

フィールドの配置場所は、move_field()関数を用いて異なる場所へ移動することができる。

int move_field(    FIELD *field,              /* field to alter */
                   int top, int left);        /* new upper-left corner */

通常、移動後の配置場所は、field_info()関数を用いて調べることができる。


18.3.3. フィールドの行揃え

行揃えを行いたいフィールドは、set_field_just()関数を使うことで調整することができる。

    int set_field_just(FIELD *field,          /* field to alter */
                       int justmode);         /* mode to set */
    int field_just(FIELD *field);          /* fetch justify mode of field */

行揃えモードとして受理されたり、返されたりする値は、NO_JUSTIFICATION、JUSTIFY_RIGHT、JUSTIFY_LEFT、JUSTIFY_CENTERとなる。


18.3.4. フィールド表示属性

上の例では、フィールドの表示属性は、set_field_fore()関数とset_field_back()関数を用いてセットされていた。
これらの関数はフィールドの前景と背景の属性をセットしている。
また、フィールドの文字が記入されていない部分を埋める文字を決めることもできる。
この文字は、set_field_pad()関数を呼び出してセットされる。デフォルトはスペースとなっている。
field_fore()関数、field_back()関数、field_pad()関数は、フィールドに前景、背景の属性および(空欄部分への)埋め文字を取得するのに利用することができる。
以下のリストに関数の使い方を示す。

int set_field_fore(FIELD *field,        /* field to alter */
                   chtype attr);        /* attribute to set */

chtype field_fore(FIELD *field);        /* field to query */
                                        /* returns foreground attribute */

int set_field_back(FIELD *field,        /* field to alter */
                   chtype attr);        /* attribute to set */

chtype field_back(FIELD *field);        /* field to query */
                                        /* returns background attribute */

int set_field_pad(FIELD *field,         /* field to alter */
                  int pad);             /* pad character to set */

chtype field_pad(FIELD *field);         /* field to query */
                                        /* returns present pad character */

上の関数は単純と思えるだろうが、set_field_fore()関数の使用は最初は挫折させるものだろう。
フィールドの前景と背景の属性についてまず説明しよう。
前景の属性は文字と結び付いている。 それは、フィールドの文字が set_field_fore()関数でセットされた属性で表示されるということを示す。
背景の属性は、文字のあるなしにかかわらず、フィールドの背景を埋めるのに使う属性となる。 だとすれば、色についてだろうか?
色は常に(文字色と背景の)ペアで定義されるものである以上、フィールドの色の表示をするための正しい方法とは何だろうか?
以下に色の属性を明確にする例を示そう。

/*Example 26. Form Attributes example */

#include <form.h>

int main()
{       FIELD *field[3];
        FORM  *my_form;
        int ch;

        /* Initialize curses */
        initscr();
        start_color();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);

        /* Initialize few color pairs */
        init_pair(1, COLOR_WHITE, COLOR_BLUE);
        init_pair(2, COLOR_WHITE, COLOR_BLUE);

        /* Initialize the fields */
        field[0] = new_field(1, 10, 4, 18, 0, 0);
        field[1] = new_field(1, 10, 6, 18, 0, 0);
        field[2] = NULL;

        /* Set field options */
        set_field_fore(field[0], COLOR_PAIR(1));/* Put the field with blue background */
        set_field_back(field[0], COLOR_PAIR(2));/* and white foreground (characters */
                                                /* are printed in white         */
        field_opts_off(field[0], O_AUTOSKIP);   /* Don't go to next field when this */
                                                /* Field is filled up           */
        set_field_back(field[1], A_UNDERLINE);
        field_opts_off(field[1], O_AUTOSKIP);

        /* Create the form and post it */
        my_form = new_form(field);
        post_form(my_form);
        refresh();

        set_current_field(my_form, field[0]); /* Set focus to the colored field */
        mvprintw(4, 10, "Value 1:");
        mvprintw(6, 10, "Value 2:");
        mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields");
        refresh();

        /* Loop through to get user requests */
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
                {       case KEY_DOWN:
                                /* Go to next field */
                                form_driver(my_form, REQ_NEXT_FIELD);
                                /* Go to the end of the present buffer */
                                /* Leaves nicely at the last character */
                                form_driver(my_form, REQ_END_LINE);
                                break;
                        case KEY_UP:
                                /* Go to previous field */
                                form_driver(my_form, REQ_PREV_FIELD);
                                form_driver(my_form, REQ_END_LINE);
                                break;
                        default:
                                /* If this is a normal character, it gets */
                                /* Printed                                */
                                form_driver(my_form, ch);
                                break;
                }
        }

        /* Un post form and free the memory */
        unpost_form(my_form);
        free_form(my_form);
        free_field(field[0]);
        free_field(field[1]);

        endwin();
        return 0;
}

色のペアを用いた例で前景、背景の属性を理解しようとしてみた。
色の属性を用いた私のプログラムでは、常にset_field_back()関数で背景のみを設定している。
NCURSESでは単純に、個々の色属性の定義を認めていないからだ。


18.3.5. フィールドオプションビット

フォームの様々な見栄えを制御することができる、フィールドオプションビットというものがあり、以下の関数で操作することができる。。

int set_field_opts(FIELD *field,          /* field to alter */
                   int attr);             /* attribute to set */

int field_opts_on(FIELD *field,           /* field to alter */
                  int attr);              /* attributes to turn on */

int field_opts_off(FIELD *field,          /* field to alter */
                  int attr);              /* attributes to turn off */

int field_opts(FIELD *field);             /* field to query */

set_field_opts()関数はフィールドのオプションビット属性を直接設定するのに使うことができ、またfield_opts_on()関数とfield_opts_off()関数はいくつかのオプションビット属性を選択的にON/OFFすることができる>。
field_opts()関数を用いることでいつでもフィールドオプションビットの属性を取得することができる。
以下に利用可能なオプションビットのリストを示す。デフォルトでは、全てのオプションはONである。

オプション名 説明
O_VISIBLE フィールドを画面に表示するかどうかの制御。
親となるフィールドの値に応じて、フィールドを隠したり、表示したりするフォーム処理に使うことができるだろう。
O_ACTIVE フォーム処理中において、フィールドがアクティブかどうかの制御。
ラベルとするか、バッファの値を入力させるかをユーザではなくフォームのアプリケーションが変化させるのに使うことができる。
O_PUBLIC フィールドにデータを表示するかどうかの制御
もしこのオプションがOFFになった場合、フィールドへのデータの入力と編集は受け付けるが、入力は表示されず、フィールドのカーソルも動かない。
このフィールドオプションはパスワードを入力させるフィールドの場合OFFにするとよいだろう。
O_EDIT フィールドのデータが修正可能かどうかの制御
このフィールドオプションがOFFの場合、REQ_PREV_CHOICEとREQ_NEXT_CHOICEの要求を除く全ての編集要求は失敗する。
このような読み出し専用のフィールドはヘルプメッセージに役に立つだろう。
O_WRAP 複数行フィールドでのワードラッピングの制御
通常、(空白で区切られた)いくつかの文字からなる単語がカレント行の最後までにかかった場合、その単語全体を次の行に改行する。(1つの単語と仮定して)
このフィールドオプションがOFFの時、単語は行末で分割されるだろう。
O_BLANK フィールドのクリア制御
このフィールドオプションがONの場合、フィールドに最初の文字が入力されたら、一旦全てのフィールドの文字を消去する。(直前に入力された文字を除き)
O_AUTOSKIP 今入力中のフィールドへの記入が一杯になったとき、次のフィールドへ自動的にスキップするかの制御
通常、ユーザがフィールドに書き込める以上のデータをタイプした時、入力対象のフィールドは次のフィールドへ移動する。
このフィールドオプションがOFFの時、ユーザの入力カーソルはフィールドの最後から動かなくなる。
このオプションは、フィールドのサイズ制限に届いていない場合は無視される。
O_NULLOK 記入しない(空欄)ことを認めるかどうかの制御
通常、不要である。ユーザは終了時、一般的に値のチェックをせずにフィールドを空欄にしたたまとすることもあるだろう。
このオプションがOFFのフィールドでは、終了時、フィールドに値が入っていることのチェックが行われる。
O_PASSOK フィールドの評価を、フィールドから立ち去る時に毎回行うか、フィールドが修正された時のみ行うかの制御
通常、後者となる。
このオプションは、フィールドの評価関数がフォームの処理中常に変わるようなケースでは役にたつだろう。
O_STATIC フィールドのバッファサイズを固定とするかどうかの制御
このオプションをOFFにすると、フィールドのバッファサイズは動的となり、データ全体が入るようサイズ拡張がなされるだろう


フィールドオプションは、フィールドに選択されている間(フォーカスがある間)は変更することができない。
しかし、フィールドのフォーカスがなくなった時に変更が通知されるだろう。

フィールドオプションの値はビットマスクであり、露骨な論理ORによって構成されている。
O_AUTOSKIPオプションをオフにする使い方を見たいだろう。以下のサンプルはいくつかのオプションの使い方を明確にする。
他のオプションであっても適切に説明できるだろう。

/* Example 27. Field Options Usage example */

#include <form.h>

#define STARTX 15
#define STARTY 4
#define WIDTH 25

#define N_FIELDS 3
int main()
{       FIELD *field[N_FIELDS];
        FORM  *my_form;
        int ch, i;

        /* Initialize curses */
        initscr();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);

        /* Initialize the fields */
        for(i = 0; i < N_FIELDS - 1; ++i)
                field[i] = new_field(1, WIDTH, STARTY + i * 2, STARTX, 0, 0);
        field[N_FIELDS - 1] = NULL;

        /* Set field options */
        set_field_back(field[1], A_UNDERLINE);  /* Print a line for the option  */

        field_opts_off(field[0], O_ACTIVE); /* This field is a static label */
        field_opts_off(field[1], O_PUBLIC); /* This filed is like a password field*/
        field_opts_off(field[1], O_AUTOSKIP); /* To avoid entering the same field */
                                              /* after last character is entered */

        /* Create the form and post it */
        my_form = new_form(field);
        post_form(my_form);
        refresh();

        set_field_just(field[0], JUSTIFY_CENTER); /* Center Justification */
        set_field_buffer(field[0], 0, "This is a static Field");
                                                  /* Initialize the field  */
        mvprintw(STARTY, STARTX - 10, "Field 1:");
        mvprintw(STARTY + 2, STARTX - 10, "Field 2:");
        refresh();


        /* Loop through to get user requests */
        while((ch = getch()) != KEY_F(1))
        {       switch(ch)
                {       case KEY_DOWN:
                                /* Go to next field */
                                form_driver(my_form, REQ_NEXT_FIELD);
                                /* Go to the end of the present buffer */
                                /* Leaves nicely at the last character */
                                form_driver(my_form, REQ_END_LINE);
                                break;
                        case KEY_UP:
                                /* Go to previous field */
                                form_driver(my_form, REQ_PREV_FIELD);
                                form_driver(my_form, REQ_END_LINE);
                                break;
                        default:
                                /* If this is a normal character, it gets */
                                /* Printed                                */
                                form_driver(my_form, ch);
                                break;
                }
        }

        /* Un post form and free the memory */
        unpost_form(my_form);
        free_form(my_form);
        free_field(field[0]);
        free_field(field[1]);

        endwin();
        return 0;
}

このサンプルは、役には立たないだろうが、オプションの使い方を示している。
正しく利用すると、フォームのフィールドオプションはとても効果的なものである。
O_PUBLICではない2つ目のフィールドは、入力した文字の表示がされない。



(2013/10/03 まだまだ作成中だよ〜ん)