#include<stdio.h>
#include<stdarg.h>
#include<ctype.h>
#include<X11/Intrinsic.h>
#include<X11/StringDefs.h>
#include<X11/Shell.h>
#include<X11/Xaw/Toggle.h>
#include<X11/Xaw/Command.h>
#include<X11/Xaw/Label.h>
#include<X11/Xaw/Form.h>
#include<X11/Xaw/Dialog.h>
#include<X11/Xaw/Scrollbar.h>
#include<X11/Xaw/MenuButton.h>
#include<X11/Xaw/SimpleMenu.h>
#include<X11/Xaw/SmeBSB.h>
#include<X11/Xaw/SmeLine.h>
#include<X11/Xlocale.h>

#include"xyag.h"
#include"resource.h"

#include"bitmap/cross.xbm"
#include"bitmap/cross_msk.xbm"


/* 変数 */

/* X関係 */

XtAppContext App;  /* アプリケーションコンテクスト */
Display *Disp;     /* ディスプレイ構造体 */


/* ドロワブル */

Window Scr_w;      /* メインスクリーンのウインドウ                 */
Pixmap Scr_p;      /* メインスクリーンのバッファ（ピックスマップ） */


/* グラフィックコンテクスト */

GC Gc,             /* 以下のGCを代入して使う   */
   Gc_fg,          /* ライン描画用             */
   Gc_bg,          /* 線・画面クリア用         */
   Gc_prev,        /* 前ページ描画用           */
   Gc_next,        /* 次ページ描画用           */
   Gc_xor;         /* 補助線描画用             */


/* ウィジット */

static Widget Top,          /* トップクラス */
              Num_page,     /* ページ番号表示／ラベル */
              Bar_page,     /* ページ用スクロールバー／スクロールバー */
              Tog_pp,       /* 前ページ表示切替／トグル */
              Tog_np,       /* 次ページ切替／トグル */
              Menu,         /* メニュー／メニューボタン */
              Sh_save,Sh_load,Sh_fps,
              Sh_quit,Sh_query, Sh_err,   /* ポップアップシェル */
              Dl_save,Dl_load,Dl_fps;     /* ポップアップダイアログ */


/* インターバルタイマ関係 */

static int Interval;              /* 再生中の画面更新のインターバル */
static XtIntervalId IntervalId;   /* インターバルタイマID           */
static int IntervalState;         /* タイマが作動中か？             */


/* その他 */

option_t Option = { True,True,False,10,False,1,1,True,NULL }; /* 環境設定 */

page_t Head_page; /* ダミーページ（先頭ページの前ページ） */

page_t *Cur_page;  /* 編集中のページ */           

static int Mode = 0;   /* モード／0:通常  1:ライン編集中  2:再生中 */

static char *Myname; /* プログラム名 */

/* メニュー関係の定数 */

#define NONE  0
#define LOAD  1
#define CLEAR 2
#define QUIT  3


/* セーブ後に実行するコマンド */

static int Next_func;


/* ダイアログでリターンキーを押したとき実行する関数 */

static void (*Yes_callback)( Widget w,XtPointer p,XtPointer q );


/* ポップアップ／位置補正 */

static void popup( Widget w )
{
  int x,y;
  int di;
  Window dw;

  XQueryPointer( Disp,DefaultRootWindow( Disp ),
		&dw,&dw,&x,&y,&di,&di,&di );

  XtVaSetValues( w,XtNx,x-8,XtNy,y-8,NULL );
  XtPopup( w,XtGrabExclusive );
}


/* エラーダイアログ消去 */

static void clr_err( Widget w, XtPointer p, XtPointer q )
{
  XtDestroyWidget( Sh_err );
}



/* エラーダイアログ表示 */

void put_error( char *msg, ... )
{
  va_list ap;
  char *s;
  char buf[512];
  Widget dialog;

  va_start( ap,msg );
  buf[0] = '\0';
  while( msg != NULL ){
    strcat( buf,msg );
    msg = va_arg( ap,char* );
  }
  va_end( ap );

  Sh_err = XtVaCreatePopupShell( "error",transientShellWidgetClass,Top,NULL );
  dialog = XtVaCreateManagedWidget( "dialog",dialogWidgetClass,Sh_err,
				   XtNlabel,buf,
				   NULL );
  XawDialogAddButton( dialog,"ok",(XtCallbackProc)clr_err,NULL );
  Yes_callback = clr_err;
  popup( Sh_err );
}


/* オールクリア */

static void exec_clear( void )
{
  page_t *p,*p2;
  obj_t *o,*o2;

  p = Head_page.next;
  while( p != &Head_page ){
    o = p->data_head.next;
    while( o != NULL ){
      o2 = o;
      o = o->next;
      free( o2 );
    }
    p2 = p;
    p = p->next;
    free( p2 );
  }
  XtVaSetValues( Dl_save,XtNvalue,"",NULL );
  if( init_page() < 0 ){
    put_error( "Out of memry --- exit program.",NULL );
    exit( 1 );
  }
}


/* ロード関係 */

void skip_space( FILE *fp )
{
  int c;

  for(;;){
    c = fgetc( fp );
    if( c == '#' ){
      while( ( c = fgetc( fp ) ) != EOF && c != '\n' )
	;
    }else if( !isspace( c ) ){
      ungetc( c,fp );
      return;
    }
  }
}


int read_num( FILE *fp,int *num )
{
  int c;
  int res;
  int sign;

  res = -1;
  *num = 0;
  sign = 1;

  skip_space( fp );

  for(;;){
    c = fgetc( fp );
    if( c == '-' && sign == 1 )
      sign = -1;
    else if( isdigit( c ) ){
      *num = *num * 10 + c - '0';
      res = 0;
    }else{
      ungetc( c,fp );
      return res;
    }
  }
}


int read_point( FILE *fp,XPoint *p )
{
  int i;

  if( read_num( fp,&i ) < 0 )
    return -1;

  p->x = i;

  skip_space( fp );

  if( fgetc( fp ) != ',' )
    return -1;

  if( read_num( fp,&i ) < 0 )
    return -1;

  p->y = i;

  return 0;
}


/* ロード実行 */

static void load( void )
{
  FILE *fp;
  page_t *p,*pn;
  obj_t *o,*on;
  int i,c;
  XPoint xp[1024];

  exec_clear();

  if( (fp = fopen( Option.file,"r" )) == NULL ){
    put_error( "Can't open file: ",Option.file,NULL );
    return;
  }

  if( read_num( fp,&i ) < 0 )
    goto error;

  skip_space( fp );
  if( fgetc( fp ) != 'S' )
    goto error;

  Option.fps = i;
  set_fps();

  p = Head_page.next;

  o = &p->data_head;
  for( ;; ){
    i = 0;
    while( read_point( fp,xp+i ) == 0 )
      if( ++i == 1024 )
	goto error;

    c = fgetc( fp );
    if( c == 'E' )
      break;

    if( c == 'P' ){
      pn = (page_t*)XtMalloc( sizeof(page_t) );
      pn->next = p->next;
      p->next->prev = pn;
      p->next = pn;
      pn->prev = p;
      pn->data_head.next = NULL;
      p = pn;
      Option.page_max++;
      o = &p->data_head;
      continue;
    }

    if( c != 'O' )
      goto error;

    on = (obj_t*)XtMalloc( sizeof(obj_t) );

    on->data = (XPoint*)XtCalloc( i,sizeof(XPoint) );

    bcopy( xp,on->data,sizeof(XPoint) * i );
    on->length = i;
    o->next = on;
    on->prev = o;
    on->next = NULL;
    o = on;
  }
  fclose( fp );
  XtVaSetValues( Dl_save,XtNvalue,Option.file,NULL );
  change_page();
  return;
  
 error:
  put_error( "Bad file format: ",Option.file,NULL );
  fclose( fp );
  change_page();
  return;

}



static void exec_load( Widget w, XtPointer p1,XtPointer p2 )
{
  XtPopdown( Sh_load );
  Option.file = XawDialogGetValueString( Dl_load );

  load();
}



/* FPS設定 */

static void exec_fps( Widget w,XtPointer p,XtPointer q )
{
  int speed;

  XtPopdown( Sh_fps );

  speed = atoi( XawDialogGetValueString( Dl_fps ) );
  if( speed < 1 || speed > 1000 )
    return;

  if( speed == Option.fps )
    return;

  Option.changed = True;
  Option.fps = speed;
  set_fps();
}



/* 処理再開 */

static void query_restart( void )
{
  switch( Next_func ){
  case LOAD:
    Yes_callback = exec_load;
    popup( Sh_load );
    break;

  case CLEAR:
    exec_clear();
    break;

  case QUIT:
    exit(0);
    break;
  }
}



/* セーブ実行 */

static void exec_save( Widget w,XtPointer p1,XtPointer p2 )
{
  FILE *fp;
  page_t *p;
  obj_t *o;
  int i,c,space;

  XtPopdown( Sh_save );

  Option.file = XawDialogGetValueString( Dl_save );

  if( (fp = fopen( Option.file,"w" )) == NULL ){
    put_error( "Can't open file: ",Option.file,NULL );
    return;
  }

  if( fprintf( fp,"%d\nS\n\n",Option.fps ) < 0 )
    goto error;

  p = Head_page.next;
  for(;;){
    o = p->data_head.next;
    while( o != NULL ){
      c = 0;
      for( i = 0 ; i < o->length ; i++ ){
	if( ++c == 8 ){
	  space = '\n';
	  c = 0;
	}else
	  space = ' ';
	if( fprintf( fp,"%d,%d%c",
		    (int)o->data[i].x,(int)o->data[i].y,space ) < 0 )
	  goto error;
      }
      if( fprintf( fp,"\nO\n" ) < 0 )
	goto error;
      o = o->next;
    }
    if( p->next != &Head_page ){
      if( fprintf( fp,"P\n\n" ) < 0 )
	goto error;
      p = p->next;
    }else
      break;
  }
  if( fprintf( fp,"E\n" ) < 0 )
    goto error;

  if( fclose( fp ) == EOF )
    goto error;

  Option.changed = False;

  query_restart();
  return;

 error:
  put_error( "Save error: ",Option.file,NULL );
}



/* ダイアログポップダウン(コールバック) */

static void popdown( Widget w,Widget target,XtPointer p )
{
  XtPopdown( target );
}


/* queryのコールバック */

static void query_yes( Widget w,XtPointer p,XtPointer q )
{
  XtPopdown( Sh_query );
  Yes_callback = exec_save;
  popup( Sh_save );
}


static void query_no(Widget w,XtPointer p,XtPointer q )
{
  XtPopdown( Sh_query );
  query_restart();
}


/* メニューから呼び出すコールバック */

static void menu_save( Widget w, XtPointer p,XtPointer q )
{
  Next_func = NONE;
  Yes_callback = exec_save;
  popup( Sh_save );
}


static void menu_load( Widget w,XtPointer p,XtPointer q )
{
  XtVaSetValues( Dl_load,XtNvalue,"",NULL );
  if( Option.changed ){
    Next_func = LOAD;
    popup( Sh_query );
  }else{
    Yes_callback = exec_load;
    popup( Sh_load );
  }
}


static void menu_clear( Widget w,XtPointer p,XtPointer q )
{
  if( Option.changed ){
    Next_func = CLEAR;
    popup( Sh_query );
  }else
    exec_clear();
}


static void menu_fps( Widget w,XtPointer p,XtPointer q )
{
  char buf[8];

  sprintf( buf,"%d",Option.fps );
  XtVaSetValues( Dl_fps,XtNvalue,buf,NULL );
  Yes_callback = exec_fps;
  popup( Sh_fps );
}


static void menu_quit( Widget w,XtPointer p,XtPointer q )
{
  if( Option.changed ){
    Next_func = QUIT;
    popup( Sh_query );
  }else
    exit( 0 );
}

/* １行入力完了(アクションハンドラ) */

static void select_yes( Widget w,XEvent *e,String *s,Cardinal *num )
{
  Yes_callback( w,NULL,NULL );
}


/* メニュー／ダイアログ初期化 */

void init_menu( void )
{
  Widget menu,w;

  menu = XtVaCreatePopupShell( "menu",simpleMenuWidgetClass,Menu,NULL );

  w = XtVaCreateManagedWidget( "save",smeBSBObjectClass,menu,NULL );
  XtAddCallback( w,XtNcallback,(XtCallbackProc)menu_save,NULL );

  w = XtVaCreateManagedWidget( "load",smeBSBObjectClass,menu,NULL );
  XtAddCallback( w,XtNcallback,(XtCallbackProc)menu_load,NULL );

  w = XtVaCreateManagedWidget( "clear",smeBSBObjectClass,menu,NULL );
  XtAddCallback( w,XtNcallback,(XtCallbackProc)menu_clear,NULL );

  XtVaCreateManagedWidget( "line1",smeLineObjectClass,menu,NULL );

  w = XtVaCreateManagedWidget( "fps",smeBSBObjectClass,menu,NULL );
  XtAddCallback( w,XtNcallback,(XtCallbackProc)menu_fps,NULL );

  XtVaCreateManagedWidget( "line2",smeLineObjectClass,menu,NULL );

  w = XtVaCreateManagedWidget( "quit",smeBSBObjectClass,menu,NULL );
  XtAddCallback( w,XtNcallback,(XtCallbackProc)menu_quit,NULL );


  Sh_save = XtVaCreatePopupShell( "save",transientShellWidgetClass,Top,
				 NULL );
  Dl_save = XtVaCreateManagedWidget( "dialog",dialogWidgetClass,Sh_save,
				    XtNvalue,"",
				    NULL );
  XawDialogAddButton( Dl_save,"ok",(XtCallbackProc)exec_save,NULL );
  XawDialogAddButton( Dl_save,"cancel",(XtCallbackProc)popdown,Sh_save );

  Sh_load = XtVaCreatePopupShell( "load",transientShellWidgetClass,Top,
				 NULL );
  Dl_load = XtVaCreateManagedWidget( "dialog",dialogWidgetClass,Sh_load,
				    XtNvalue,"",
				    NULL );
  XawDialogAddButton( Dl_load,"ok",(XtCallbackProc)exec_load,NULL );
  XawDialogAddButton( Dl_load,"cancel",(XtCallbackProc)popdown,Sh_load );

  Sh_fps = XtVaCreatePopupShell( "fps",transientShellWidgetClass,Top,
				NULL );
  Dl_fps = XtVaCreateManagedWidget( "dialog",dialogWidgetClass,Sh_fps,
				   XtNvalue,"",
				   NULL );
  XawDialogAddButton( Dl_fps,"ok",(XtCallbackProc)exec_fps,NULL );
  XawDialogAddButton( Dl_fps,"cancel",(XtCallbackProc)popdown,Sh_fps );

  Sh_query = XtVaCreatePopupShell( "query",transientShellWidgetClass,Top,
				  NULL );
  w = XtVaCreateManagedWidget( "dialog",dialogWidgetClass,Sh_query,
			      NULL );
  XawDialogAddButton( w,"yes",(XtCallbackProc)query_yes,NULL );
  XawDialogAddButton( w,"no",(XtCallbackProc)query_no,NULL );
  XawDialogAddButton( w,"cancel",(XtCallbackProc)popdown,Sh_query );
}


/* タイマ処理ルーチンのプロトタイプ */

static void timi( XtPointer c, XtIntervalId id );


/* タイマ解除 */

static void reset_timer( void )
{
  if( IntervalState ){
    XtRemoveTimeOut( IntervalId );
    IntervalState = 0;
  }
}


/* タイマ設定 */

static void set_timer( void )
{
  if( IntervalState )
    reset_timer();
  IntervalId = XtAppAddTimeOut( App, Interval,(XtTimerCallbackProc)timi,NULL );
  IntervalState = 1;
}


/* モード切替え */

static void change_mode( int mode )
{
  Mode = mode;
  if( Mode == 0 )
    XtVaSetValues( Menu,XtNsensitive,True,NULL );
  else
    XtVaSetValues( Menu,XtNsensitive,False,NULL );
}


/* タイマ割り込みルーチン（アニメーション再生処理）*/

static void timi( XtPointer c,XtIntervalId id )
{
  IntervalState = 0;

  if( Cur_page == &Head_page ){
    if( Option.play_only )
      exit( 0 );

    change_mode ( 0 );
    Cur_page = Head_page.next;
    Option.page = 1;
    change_page();
    return;
  }

  set_timer();

  flush_scr();
  if( Option.page % ((Option.fps+1)/2) == 0 )
    number_page();

  Cur_page = Cur_page->next;
  if( Cur_page != &Head_page ){
    clear_scr();
    draw_page( Cur_page );
    Option.page++;
  }
}


/* FPS設定 */

void set_fps( void )
{
  if( (Interval = 1000 / Option.fps) < 1 )
    Interval = 1;
}  


/* アニメーション再生（アクションハンドラ）*/

static void play(Widget w,XEvent *e,String *s,Cardinal *num )
{
  if( *num == 0 )
    return;

  if( Option.play_only )
    return;

  switch( **s ){
  case 'P':                  /* PLAY */
    if( Mode == 0 ){
      clear_scr();
      draw_page( Cur_page );
      change_mode( 2 );
      set_timer();
    }
    break;

  case 'S':                  /* STOP */
    if( Mode == 2 ){
      reset_timer();
      if( Cur_page == &Head_page ){
	Cur_page = Head_page.next;Option.page = 1;
      }
      change_mode ( 0 );
      change_page();
    }
    break;

  case 'T':                  /* TOGGLE */
    if( Mode == 0 ){
      clear_scr();
      draw_page( Cur_page );
      change_mode ( 2 );
      set_timer();
    }else if( Mode == 2 ){
      reset_timer();
      if( Cur_page == &Head_page ){
	Cur_page = Head_page.next;Option.page = 1;
      }
      change_mode ( 0 );
      change_page();
    }
    break;
  }
}


/* マウス処理(アクションハンドラ） */

static int point(Widget w,XEvent *e,String *s,Cardinal *num )
{
  if( *num == 0 )
    return;

  switch( **s ){
  case 'S':                /* 始点設定(START) */
    if( Mode == 0 ){
      set_point( e->xbutton.x,e->xbutton.y );
      change_mode ( 1 );
    }
    break;

  case 'N':                /* 経由点設定(NEXT) */
    if( Mode == 1 )
      next_point( e->xbutton.x,e->xbutton.y );
    break;

  case 'C':                /* 線のカット（CUT） */
    if( Mode == 1 ){
      cut_point();
      change_mode ( 0 );
    }
    break;

  case 'B':                /* アンドゥ(BUCK) */
    if( Mode == 0 ){
      del_obj();
      break;
    }else if ( Mode == 1 )
      change_mode( prev_point() );
                                             /* FALL-THROUGH */
  case 'M':                /* マウス移動(MOTION) */
    if( Mode == 1 )
      draw_xor( e->xbutton.x,e->xbutton.y );
    break;
  }
}


/* ページ番号表示 */

void number_page( void )
{
  char buf[14];

  sprintf( buf,"%05d / %05d",Option.page,Option.page_max );
  XtVaSetValues( Num_page,XtNlabel,buf,NULL );

  XawScrollbarSetThumb( Bar_page,
		       (float)(Option.page-1)/(float)(Option.page_max ),
		       1/(float)Option.page_max );
}


/* ページ単位処理（アクションハンドラ）*/

static int page( Widget w,XEvent *e,String *s,Cardinal *num )
{
  if( *num == 0 )
    return;

  if( Mode != 0 )
    return;

  switch( **s ){
  case '+':           /* 次ページ(+)       */
    next_page();
    break;

  case '-':           /* 前ページ(-)       */
    prev_page();
    break;

  case 'F':           /* 先頭ページ(FIRST) */
    Cur_page = Head_page.next;
    Option.page = 1;
    change_page();
    break;

  case 'L':           /* 最終ページ(LAST)  */
    Cur_page = Head_page.prev;
    Option.page = Option.page_max;
    change_page();
    break;

  case 'A':           /* ページ追加(ADD)   */
    add_page();
    break;

  case 'I':           /* ページ挿入(INS)   */
    ins_page();
    break;

  case 'D':           /* ページ削除(DEL)   */
    del_page();
    break;

  case 'C':           /* ページ初期化(CLEAR)*/
    clear_page();
    break;
  }
}



/* メニューのホットキー(アクションハンドラ) */

static void file_action( Widget w,XEvent *e,String *s,Cardinal *num )
{
  if( *num == 0 )
    return;

  if( Mode != 0 )
    return;

  switch( **s ){
  case 'S':                     /* セーブ */
    menu_save( w,NULL,NULL );
    break;

  case 'L':                     /* ロード */
    menu_load( w,NULL,NULL );
    break;

  case 'C':                     /* クリア */
    menu_clear( w,NULL,NULL );
    break;

  case 'F':                     /* スピード */
    menu_fps( w,NULL,NULL );
    break;

  case 'Q':                      /* 終了 */
    menu_quit( w,NULL,NULL );
    break;
  }
}


/* 前後ページを見るかの切替 （アクションハンドラ）*/

static void toggle_see( Widget w,XEvent *e,String *s,Cardinal *num )
{
  if( *num == 0 )
    return;

  switch( **s ){
  case 'P':              /* PREV */
    Option.see_prev = !Option.see_prev;
    XtVaSetValues( Tog_pp,XtNstate,Option.see_prev,NULL );
    if( Mode == 0 )
      change_page();
    break;

  case 'N':              /* NEXT */
    Option.see_next = !Option.see_next;
    XtVaSetValues( Tog_np,XtNstate,Option.see_next,NULL );
    if( Mode == 0 )
      change_page();
    break;
  }
}


/* エクスポーズ（イベントハンドラ) */

static void expose( Widget w,XtPointer p,XEvent *e )
{
  XCopyArea(Disp,Scr_p,Scr_w,Gc_fg,
	    e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,
	    e->xexpose.x,e->xexpose.y);
}


/* スクロールバーのコールバック(ページ変更) */

/* 1ページ移動 */

static void ScrollProc( Widget w,XtPointer p,int pos )
{
  if( Mode != 0 )
    return;

  if( pos < 0 )
    prev_page();
  else if( pos > 0 )
    next_page();
}


/* 任意のページに移動 */

static void JumpProc( Widget w,XtPointer p,float *f )
{
  int i;

  if( Mode != 0 )
    return;

  i = (int)(Option.page_max * *f ) + 1;

  if( i <= Option.page_max && i != Option.page ){
    Option.page = i;
    Cur_page = Head_page.next;
    for( i = 1 ; i < Option.page ; i++ )
      Cur_page = Cur_page->next;
    change_page();
  }
}


/* X関係の初期化 */

static int init_graph( void )
{
  Colormap cmap;
  XColor cb,cr,cg,c1,cfg,cbg;
  int white,black;
  Pixmap cur_s,cur_m;
  Cursor cursor;

  static char dash1[] = { 2,4 };
  static char dash2[] = { 4,8 };

  Gc_fg = XCreateGC( Disp,Scr_w,0,NULL );
  Gc_bg = XCreateGC( Disp,Scr_w,0,NULL );
  Gc_next = XCreateGC( Disp,Scr_w,0,NULL );
  Gc_prev = XCreateGC( Disp,Scr_w,0,NULL );
  Gc_xor = XCreateGC( Disp,Scr_w,0,NULL );

  white = WhitePixel( Disp,DefaultScreen(Disp));
  black = BlackPixel( Disp,DefaultScreen(Disp));

  XSetForeground(Disp,Gc_fg,white);
  XSetForeground(Disp,Gc_bg,black);
  XSetFunction(Disp,Gc_xor,GXxor );

  cmap = DefaultColormap( Disp,DefaultScreen(Disp ));

  if( Option.color ){
    if( XAllocNamedColor( Disp,cmap,"blue",&cb,&c1 ) == 0 ){
      Option.color = False;
      goto alloc_err;
    }

    if( XAllocNamedColor( Disp,cmap,"green",&cg,&c1 ) == 0 ){
      Option.color = False;
      goto alloc_err;
    }

    if( XAllocNamedColor( Disp,cmap,"red",&cr,&c1 ) == 0 ){
      Option.color = False;
      goto alloc_err;
    }

    XSetForeground(Disp,Gc_prev,cb.pixel);
    XSetForeground(Disp,Gc_next,cr.pixel);
    XSetForeground(Disp,Gc_xor,cg.pixel^black);
  }

 alloc_err:

  if( !Option.color ){
    XSetForeground( Disp,Gc_prev,white );
    XSetForeground( Disp,Gc_next,white );
    XSetForeground( Disp,Gc_xor,white^black );

    XSetLineAttributes( Disp,Gc_prev,0,LineOnOffDash,CapButt,JoinMiter );
    XSetLineAttributes( Disp,Gc_next,0,LineOnOffDash,CapButt,JoinMiter );
    XSetDashes( Disp,Gc_prev,0,dash1,2 );
    XSetDashes( Disp,Gc_next,0,dash2,2 );
  }


  Scr_p = XCreatePixmap( Disp,Scr_w,Scr_width,Scr_height,
			DefaultDepth(Disp,DefaultScreen(Disp)) );

  cur_s = XCreateBitmapFromData( Disp,Scr_w,cross_bits,
				cross_width,cross_height );
  cur_m = XCreateBitmapFromData( Disp,Scr_w,cross_msk_bits,
				cross_msk_width,cross_msk_height );

  cfg.pixel = white;
  cbg.pixel = black;
  XQueryColor( Disp,cmap,&cfg );
  XQueryColor( Disp,cmap,&cbg );

  cursor = XCreatePixmapCursor( Disp,cur_s,cur_m,&cfg,&cbg,
			       cross_x_hot,cross_y_hot );

  XDefineCursor( Disp,Scr_w,cursor );

  return init_page();
}


/* ヘルプ */

static void usage( void )
{
 fprintf( stderr,"usage:%s [options] [file]\n",Myname );
 fprintf( stderr,"\t-toolkitoptions...\n" );
 fprintf( stderr,"\t-run    only play the file, and exit.\n");
 fprintf( stderr,"\t-mono   use monochrome display.\n");
 fprintf( stderr,"\t-help   show this message.\n");
}


/* コマンドライン解析 */

static void option( int argc, char **argv )
{
  int i;

  for( i = 1 ; i < argc ; i++ ){

    if( strcmp( argv[i],"-mono" ) == 0 ){
      Option.color = False;
      continue;
    }

    if( strcmp( argv[i],"-run" ) == 0 ){
      Option.play_only = True;
      continue;
    }

    if( strcmp( argv[i],"-help" ) == 0 ){
      usage();
      exit(0);
    }

    if( Option.file == NULL )
      Option.file = argv[i];
    else{
      usage();
      exit(1);
    }
  }
}


int main( int argc, char **argv )
{

  Widget yag,w,b,scr;

  static XtActionsRec a_table[] = {
    { "page",  (XtActionProc)page       },
    { "point", (XtActionProc)point      },
    { "play",  (XtActionProc)play       },
    { "see",   (XtActionProc)toggle_see },
    { "file",  (XtActionProc)file_action},
    { "select_yes", (XtActionProc)select_yes },
  };

  Myname = argv[0];

  XtSetLanguageProc( NULL,NULL,NULL );

  Top = XtVaAppInitialize( &App,"XYag",NULL,0,
			  &argc,argv,DefaultResources,NULL );
  
  option( argc,argv );

  Disp = XtDisplay( Top );

  yag = XtVaCreateManagedWidget( "yag",formWidgetClass,Top,NULL );

  w = XtVaCreateManagedWidget( "console",formWidgetClass,yag,NULL );

  Menu = XtVaCreateManagedWidget( "menu_b",menuButtonWidgetClass,w,
				 NULL );
  
  XtVaCreateManagedWidget( "plabel",labelWidgetClass,w,NULL);
  Num_page = XtVaCreateManagedWidget( "number",labelWidgetClass,w,
				     XtNlabel,"88888 / 88888",NULL );
  Tog_pp = XtVaCreateManagedWidget( "see_pp",toggleWidgetClass,w,NULL );
  Tog_np = XtVaCreateManagedWidget( "see_np",toggleWidgetClass,w,NULL );

  Bar_page = XtVaCreateManagedWidget( "bar",scrollbarWidgetClass,w,NULL);
  XtVaCreateManagedWidget( "add",commandWidgetClass,w,NULL);
  XtVaCreateManagedWidget( "ins",commandWidgetClass,w,NULL);
  XtVaCreateManagedWidget( "del",commandWidgetClass,w,NULL);
  XtVaCreateManagedWidget( "cls",commandWidgetClass,w,NULL);
  XtVaCreateManagedWidget( "play",commandWidgetClass,w,NULL);

  if( Option.see_prev )
    XtVaSetValues( Tog_pp,XtNstate,1,NULL );

  if( Option.see_next )
    XtVaSetValues( Tog_np,XtNstate,1,NULL );

  scr = XtVaCreateManagedWidget( "scr",widgetClass,yag,
				XtNwidth,Scr_width,
				XtNheight,Scr_height,
				NULL );

  XtAppAddActions( App,a_table,XtNumber( a_table ) );
  XtAddEventHandler( scr,ExposureMask,FALSE,(XtEventHandler)expose,NULL );
  XtAddCallback( Bar_page,XtNscrollProc,(XtCallbackProc)ScrollProc,NULL );
  XtAddCallback( Bar_page,XtNjumpProc,(XtCallbackProc)JumpProc,NULL );

  init_menu( );

  XtRealizeWidget( Top );

  Scr_w = XtWindow( scr );
  init_graph();

  if( Option.file != NULL )
    load();

  set_fps();

  if( Option.play_only ){
    clear_scr();
    draw_page( Cur_page );
    change_mode( 2 );
    set_timer();
  }else
    change_mode( 0 );

  XtAppMainLoop( App );

  return 0;
}
