パッド

ウィンドウは物理画面や親ウィンドウの範囲内に収まっていなければならない.パッドとはそのような制約なしに自由な大きさで作成できる論理画面である.
パッドの利点
パッドの生成,操作,削除
なんちゃってless

パッドの利点

ウィンドウはかならず画面内に収まっていなければならないので,「後で表示するかもしれないが今は画面外にあるデータ」はなんらかの方法で確保しておくか,表示するときに取得,生成などをしなければならない.後者の場合は処理が複雑になりがちである.前者はシンプルに実装できそうだが,リソースを食いそうだ.パッドはこの前者の実装に便利である.すなわち,パッドを使用すると論理画面にまとめて表示データを格納しておいて,必要な分だけ物理画面で表示することが可能になる.しかも簡単に.

パッドの生成,操作,削除

パッドの生成は
WINDOW *newpad(int nlines, int ncols);
でできる.nlines,ncolsはそれぞれ行数と行幅で,上記の通りこれらは物理画面のサイズに制限されない.そしてパッドを表示するには,
int prefresh(WINDOW *pad, int pminrow, int pmincol,
             int sminrow, int smincol, int smaxrow, int smaxcol);
を使用する.padのpminrow,pmincolを左上にして,画面(stdscr)のsminrow,smincol,smaxrow,smaxcloの範囲に表示できるだけ表示する.そしてパッドを削除するにはウィンドウと同じ
int delwin(WINDOW *win);
を使用する.また,touchwinもパッドに対して使用できる.

なんちゃってless

スクロールのところでなんちゃってmoreを作成した.そしてパッドには上記のような利点がある.ということで,パッドを使用するサンプルはなんちゃってlessにしてみた.

#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <errno.h>

#define KEY_CTRL_F 6
#define KEY_CTRL_B 2
#define KEY_CTRL_N 14
#define KEY_CTRL_P 16
#define KEY_CTRL_V 22
#define KEY_ALT_V 118

/* curses�Τ���ν�λ���� */
void curses_finalize();
/* �ե�����򥯥��������� */
void file_finalize();
/* �ե����뤫�����ɤ߹��� */
long count_cols(FILE *fp);

FILE *fp = NULL;

int main(int argc, char *argv[])
{
  long pad_lines = 0;
  long pad_cols = 0;
  long pad_x;
  long pad_y;
  char *buf;
  WINDOW *pad;
  int key_in;
  short repeat_flag = 1;

  if(argc != 2){
    fprintf(stderr, "Usage: %s filename\n", argv[0]);
    exit(EXIT_FAILURE);
  }else{
    errno = 0;
    if((fp = fopen(argv[1], "r")) == NULL){
      perror("fopen failure");
      exit(EXIT_FAILURE);
    }else{
      atexit(file_finalize);
    }
  }

  /* �ե�����Υ�������Ĵ��,�ѥåɤΥ���������� */
  while(feof(fp) == 0){
    int temp_cols = 0;
    temp_cols = count_cols(fp);
    pad_lines++;
    if(temp_cols > pad_cols){
      pad_cols = temp_cols;
    }
  }

  if(initscr() == NULL){
    fprintf(stderr, "initscr failure\n");
    exit(EXIT_FAILURE);
  }else{
    atexit(curses_finalize);
  }

  cbreak();
  noecho();
  /* ���Υ���ץ�Ǥϥ�������������� */
  scrollok(stdscr, FALSE);

  /* �ѥåɤκ��� */
  pad = newpad(pad_lines, pad_cols);
  /* �ե�����Υǡ�����ѥåɤ˽񤭹��� */
  fseek(fp, 0, SEEK_SET);
  buf = malloc(sizeof(char) * pad_cols);
  for(pad_y = 0; pad_y < pad_lines; pad_y++){
    fgets(buf, pad_cols, fp);
    mvwprintw(pad, pad_y, 0, "%s", buf);
  }
  free(buf);

  /* ����򤷤ʤ��ȵ�ưľ���ɽ������ʤ� */
  refresh();
  prefresh(pad, 0, 0, 0, 0, LINES-1, COLS-1);
  pad_y = 0;
  pad_x = 0;
  /* �������Ϥ��Ȥν��� */
  while(repeat_flag == 1){
    key_in = getch();
    switch(key_in){
    case KEY_CTRL_F:
      if(pad_x < pad_cols - COLS -1){
	++pad_x;
      }
      break;
    case KEY_CTRL_B:
      if(pad_x > 0){
	--pad_x;
      }
      break;
    case KEY_CTRL_N:
      if(pad_y < pad_lines - LINES - 1){
	++pad_y;
      }
      break;
    case KEY_CTRL_P:
      if(pad_y > 0){
	--pad_y;
      }
      break;
    case KEY_CTRL_V:
      pad_y += LINES-1;
      if(pad_y >= pad_lines - LINES -1){
	pad_y = pad_lines - LINES -1;
      }
      break;
    case KEY_ALT_V:
      pad_y -= LINES-1;
      if(pad_y < 0){
	pad_y = 0;
      }
      break;
    case 'q':
      repeat_flag = 0;
      break;
    }
    /* �ѥåɤΥ�ե�å��� */
    prefresh(pad, pad_y, pad_x, 0, 0, LINES-1, COLS-1);
  }

  exit(EXIT_SUCCESS);
}

/* curses�Τ���ν�λ���� */
void curses_finalize(void)
{
  endwin();
}

/* �ե�����򥯥��������� */
void file_finalize(void)
{
  if(fp != NULL){
    fclose(fp);
  }
}

long count_cols(FILE *fp)
{
  long read_num = 0;
  int ch;

  while((ch = fgetc(fp)) != '\n' && ch != EOF){
    read_num++;
  }
  /* ���ԥ�����ʬ������ */
  read_num += 2;

  return read_num;
}

これはlessっぽい機能を持つ.C-n,C-p,C-v,M-vで画面を上下にスクロールする.そして’q’で終了する.また,画面右端での折り返しはしない.そのかわりC-f,C-bでの左右のスクロールを可能にしてみた.ちょっと残念なのが,冒頭のいくつかのdefine文だ.たぶんどっかのヘッダなどで定義されていると思うのだが,発見できなかった.また,ハマった点は,最初はrefreshしてからでないといきなりprefreshしても起動直後になにも表示されないということだった(サンプル中,main関数のswitch文の手前).ただし,何も表示されていない状態でキーを押すとファイルが表示される.

This article was written by Fujiko