use utf8;
use constant TRUE  => 1;
use constant FALSE => 0;
use File::Copy;
use File::Path;
use File::Basename;
use URI::file;


sub on_gnEnvCancel_clicked {
   # 環境設定ウィンドウで"キャンセル"ボタンが押されたら
   # 単純にウィンドウを廃棄する
   my $gnEnv = $_[1];
   $gnEnv->destroy;
   $gnEnv = undef;
}

sub on_gnEnvOk_clicked {
   # 環境設定ウィンドウで"OK"ボタンが押されたら
   # 配列gnEnvArgNの内容を内部配列gnEnvArgに反映して、
   # ウィンドウを廃棄する
   my $gnEnv = $_[1];

   # 仮に格納していたハッシュ配列を本番の環境設定配列にコピー
   %gnEnvArg = %gnEnvArgN;

   $gnEnv->destroy;
   $gnEnv = undef;

}

sub on_gnEnvLog_Winent_realize {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[Windows]
   # 表示時にgnEnvArg{'Folder'}{'LogFolder'}の値を読み込み
   if ($gnEnvArg{'Folder'}{'LogFolder'}) {
      $_[0]->set_text($gnEnvArg{'Folder'}{'LogFolder'});
   }
}

sub on_gnEnvLog_Winent_changed {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[Windows]
   # 値が変更された時にgnEnvArgN{'Folder'}{'LogFolder'}に書き込み
   $gnEnvArgN{'Folder'}{'LogFolder'} = $_[0]->get_text;
   #print $gnEnvArgN{'Folder'}{'LogFolder'}; print "\n";
}

sub on_gnEnvLog_WinentDB_clicked {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[Windows] 横の
   # "選択"ボタンが押されたら、フォルダ選択ダイアログボックスを
   # 表示してフォルダを選択する。
   # 選択された値はEntry(gnEnvLog_Winent)に書き込まれる
   my $gnEnvLog_Winent = $_[1];
   
   my $gnFolderXML = Gtk2::GladeXML->new($gladefn, 'gnFolderChoose');
   $gnFolderXML->signal_autoconnect_from_package('main');
   my $gnFolderChoose = $gnFolderXML->get_widget('gnFolderChoose');
   
   my $retval = $gnFolderChoose->run;
   if($retval eq "ok") {
      $retval = &env_gtkstr_to_gtkstr($gnFolderChoose->get_filename);
      $gnEnvLog_Winent->set_text($retval);
      $gnFolderChoose->destroy;
      $gnFolderChoose = undef;
   }else{
      $gnFolderChoose->destroy;
      $gnFolderChoose = undef;
   }

}

sub on_gnEnvLog_Linent_realize {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[Unix]
   # 表示時にgnEnvArg{'Folder'}{'LogFolderUnix'}の値を読み込み
   $_[0]->set_text($gnEnvArg{'Folder'}{'LogFolderUnix'});
}

sub on_gnEnvLog_Linent_changed {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[Unix]
   # 値が変更された時にgnEnvArgN{'Folder'}{'LogFolderUnix'}に書き込み
   $gnEnvArgN{'Folder'}{'LogFolderUnix'} = $_[0]->get_text;
   #print $gnEnvArgN{'Folder'}{'LogFolderUnix'}; print "\n";
}

sub on_gnEnvLog_LinentDB_clicked {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[Unix] 横の
   # "選択"ボタンが押されたら、フォルダ選択ダイアログボックスを
   # 表示してフォルダを選択する。
   # 選択された値はEntry(gnEnvLog_Linent)に書き込まれる
   my $gnEnvLog_Linent = $_[1];
   
   my $gnFolderXML = Gtk2::GladeXML->new($gladefn, 'gnFolderChoose');
   $gnFolderXML->signal_autoconnect_from_package('main');
   my $gnFolderChoose = $gnFolderXML->get_widget('gnFolderChoose');
   
   my $retval = $gnFolderChoose->run;
   if($retval eq "ok") {
      $retval = &env_gtkstr_to_gtkstr($gnFolderChoose->get_filename);
      $gnEnvLog_Linent->set_text($retval);
      #print "$retval\n";
      $gnFolderChoose->destroy;
      $gnFolderChoose = undef;
   }else{
      $gnFolderChoose->destroy;
      $gnFolderChoose = undef;
   }

}

sub on_gnEnvLog_Ftp_path_realize {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[FTP]-[パス]
   # 表示時にgnEnvArg{'Folder'}{'LogFolderFTPPath'}の値を読み込み
   if ($gnEnvArg{'Folder'}{'LogFolderFTPPath'}) {
      $_[0]->set_text($gnEnvArg{'Folder'}{'LogFolderFTPPath'});
   }
}
sub on_gnEnvLog_Ftp_path_changed {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[FTP]-[パス]
   # 値が変更された時にgnEnvArgN{'Folder'}{'LogFolderFTPPath'}に書き込み
   $gnEnvArgN{'Folder'}{'LogFolderFTPPath'} = $_[0]->get_text;
   #print $gnEnvArgN{'Folder'}{'LogFolderUnix'}; print "\n";
}
sub on_gnEnvLog_Ftp_user_realize {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[FTP]-[ユーザ名]
   # 表示時にgnEnvArg{'Folder'}{'LogFolderFTPUser'}の値を読み込み
   if ($gnEnvArg{'Folder'}{'LogFolderFTPUser'}) {
      $_[0]->set_text($gnEnvArg{'Folder'}{'LogFolderFTPUser'});
   }
}
sub on_gnEnvLog_Ftp_user_changed {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[FTP]-[ユーザ名]
   # 値が変更された時にgnEnvArgN{'Folder'}{'LogFolderFTPUser'}に書き込み
   $gnEnvArgN{'Folder'}{'LogFolderFTPUser'} = $_[0]->get_text;
   #print $gnEnvArgN{'Folder'}{'LogFolderUnix'}; print "\n";
}
sub on_gnEnvLog_Ftp_password_realize {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[FTP]-[パスワード]
   # 表示時にgnEnvArg{'Folder'}{'LogFolderFTPPasswd'}の値を読み込み
   if ($gnEnvArg{'Folder'}{'LogFolderFTPPasswd'}) {
      $_[0]->set_text($gnEnvArg{'Folder'}{'LogFolderFTPPasswd'});
   }
}
sub on_gnEnvLog_Ftp_password_changed {
   # 環境設定ウィンドウ-[ログ保存]-[保存先]-[FTP]-[パスワード]
   # 値が変更された時にgnEnvArgN{'Folder'}{'LogFolderFTPPasswd'}に書き込み
   $gnEnvArgN{'Folder'}{'LogFolderFTPPasswd'} = $_[0]->get_text;
   #print $gnEnvArgN{'Folder'}{'LogFolderUnix'}; print "\n";
}

sub on_gnImgPrevSize_realize {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]
   # 表示時にgnEnvArg{'Browser'}{'PreviewSize'}の値を読み込み
   $_[0]->set_value($gnEnvArg{'Browser'}{'PreviewSize'});
}

sub on_gnImgPrevSize_changevalue {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]
   # で値が変更されたらgnEnvArgN{'Browser'}{'PreviewSize'}に書き込み
   $gnEnvArgN{'Browser'}{'PreviewSize'} = $_[0]->get_value;
}

sub on_gnImgPrevSize_st2_realize {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]
   # 表示時にgnEnvArg{'Browser'}{'PreviewSize'}の値を読み込み
   $_[0]->set_value($gnEnvArg{'Browser'}{'PreviewSizeSt2'});
}

sub on_gnImgPrevSize_st2_changevalue {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]
   # で値が変更されたらgnEnvArgN{'Browser'}{'PreviewSize'}に書き込み
   $gnEnvArgN{'Browser'}{'PreviewSizeSt2'} = $_[0]->get_value;
}

sub on_gnEnv_AvoidBorder_clicked {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[表示可能領域を設定する]ボタンを押した
   # Gtk2:Window"gnEnvAvoidBorder"を表示
   
   $gnEnvABXML = Gtk2::GladeXML->new($gladefn, 'gnEnvAvoidBorder');
   $gnEnvABXML->signal_autoconnect_from_package('main');

   my $gnEnvAB = $gnEnvABXML->get_widget('gnEnvAvoidBorder');
   if ($gnDebugFlag) { print Dumper($gnEnvAB) . "\n"; }

   my $gnEnv = $gnEnvXML->get_widget('gnEnv');
   $gnEnvAB->set_transient_for($gnEnv);
   $gnEnvAB->set_modal(TRUE);
   
   $gnEnvAB->show_all;
   
   &gnEnvAB_thumbredraw;
}

sub on_gnEnvAB_top_realize {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[上]スピンボタン
   # 表示時にgnEnvArg{'Browser'}{'AvoidBorderTop'}の値を読み込み
   my $gnEnvAB_top = $_[0];
   my $gnEnvAB_topimg = $_[1];

   if ($gnEnvArg{'Browser'}{'AvoidBorderTop'}>0) {
      $gnEnvAB_top->set_value($gnEnvArg{'Browser'}{'AvoidBorderTop'});
   }else{
      $gnEnvAB_top->set_value(0);
   }
   
}

sub on_gnEnvAB_bottom_realize {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[下]スピンボタン
   # 表示時にgnEnvArg{'Browser'}{'AvoidBorderBottom'}の値を読み込み
   my $gnEnvAB_bottom = $_[0];
   my $gnEnvAB_bottomimg = $_[1];
   if ($gnEnvArg{'Browser'}{'AvoidBorderBottom'}) {
      $gnEnvAB_bottom->set_value($gnEnvArg{'Browser'}{'AvoidBorderBottom'});
   }else{
      $gnEnvAB_bottom->set_value(0);
   }

}

sub on_gnEnvAB_left_realize {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[左]スピンボタン
   # 表示時にgnEnvArg{'Browser'}{'AvoidBorderLeft'}の値を読み込み
   my $gnEnvAB_left = $_[0];
   my $gnEnvAB_leftimg = $_[1];
   if ($gnEnvArg{'Browser'}{'AvoidBorderLeft'}) {
      $gnEnvAB_left->set_value($gnEnvArg{'Browser'}{'AvoidBorderLeft'});
   }else{
      $gnEnvAB_left->set_value(0);
   }

}

sub on_gnEnvAB_right_realize {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[右]スピンボタン
   # 表示時にgnEnvArg{'Browser'}{'AvoidBoarderRight'}の値を読み込み
   my $gnEnvAB_right = $_[0];
   my $gnEnvAB_rightimg = $_[1];
   if ($gnEnvArg{'Browser'}{'AvoidBorderRight'}) {
      $gnEnvAB_right->set_value($gnEnvArg{'Browser'}{'AvoidBorderRight'});
   }else{
      $gnEnvAB_right->set_value(0);
   }

}

sub on_gnEnvAB_top_value_changed {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[上]スピンボタン
   # 変更時
   my $gnSpnbtn = $_[0];
   
   # 下スピンボタンの値を取得
   my $gnEnvAB_bottom = $gnEnvABXML->get_widget('gnEnvAB_bottom');
   my $gnEnvAB_topval = $gnSpnbtn->get_value;
   my $gnEnvAB_bottomval = $gnEnvAB_bottom->get_value;
   
   # 画面全体の大きさを取得
   my $dis = Gtk2::Gdk::Display->get_default;
   my ($scr, $x, $y, $mask) = $dis->get_pointer;
   my $wid = $scr->get_width;
   my $hei = $scr->get_height;
   
   # 各スピンボタンの値とポップアップウィンドウの大きさを足した値が
   # スクリーンの描画領域を超えていたら、サムネイルの描画はキャンセルする
   if (($gnEnvAB_topval + $gnEnvAB_bottomval + $gnEnvArg{'Browser'}{'PreviewSize'}*100)>$hei) {
      if ($gnDebugFlag) { print ""; };
      $gnEnvAB_topval = $hei - ($gnEnvAB_bottomval + $gnEnvArg{'Browser'}{'PreviewSize'}*100);
      $gnSpnbtn->set_value($gnEnvAB_topval);
   }else{
      # サムネイルを再描画
      &gnEnvAB_thumbredraw;
   }
}

sub on_gnEnvAB_bottom_value_changed {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[下]スピンボタン
   # 変更時
   my $gnSpnbtn = $_[0];
   
   # 上スピンボタンの値を取得
   my $gnEnvAB_top = $gnEnvABXML->get_widget('gnEnvAB_top');
   my $gnEnvAB_bottomval = $gnSpnbtn->get_value;
   my $gnEnvAB_topval = $gnEnvAB_top->get_value;
   
   # 画面全体の大きさを取得
   my $dis = Gtk2::Gdk::Display->get_default;
   my ($scr, $x, $y, $mask) = $dis->get_pointer;
   my $wid = $scr->get_width;
   my $hei = $scr->get_height;
   
   # 各スピンボタンの値とポップアップウィンドウの大きさを足した値が
   # スクリーンの描画領域を超えていたら、サムネイルの描画はキャンセルする
   if (($gnEnvAB_topval + $gnEnvAB_bottomval + $gnEnvArg{'Browser'}{'PreviewSize'}*100)>$hei) {
      if ($gnDebugFlag) { print ""; };
      $gnEnvAB_bottomval = $hei - ($gnEnvAB_topval + $gnEnvArg{'Browser'}{'PreviewSize'}*100);
      $gnSpnbtn->set_value($gnEnvAB_bottomval);
   }else{
      # サムネイルを再描画
      &gnEnvAB_thumbredraw;
   }
}

sub on_gnEnvAB_left_value_changed {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[左]スピンボタン
   # 変更時
   my $gnSpnbtn = $_[0];
   
   # 右スピンボタンの値を取得
   my $gnEnvAB_right = $gnEnvABXML->get_widget('gnEnvAB_right');
   my $gnEnvAB_leftval = $gnSpnbtn->get_value;
   my $gnEnvAB_rightval = $gnEnvAB_right->get_value;
   
   # 画面全体の大きさを取得
   my $dis = Gtk2::Gdk::Display->get_default;
   my ($scr, $x, $y, $mask) = $dis->get_pointer;
   my $wid = $scr->get_width;
   my $hei = $scr->get_height;
   
   # 各スピンボタンの値とポップアップウィンドウの大きさを足した値が
   # スクリーンの描画領域を超えていたら、サムネイルの描画はキャンセルする
   if (($gnEnvAB_leftval + $gnEnvAB_rightval + $gnEnvArg{'Browser'}{'PreviewSize'}*100)>$wid) {
      if ($gnDebugFlag) { print ""; };
      $gnEnvAB_leftval = $wid - ($gnEnvAB_rightval + $gnEnvArg{'Browser'}{'PreviewSize'}*100);
      $gnSpnbtn->set_value($gnEnvAB_leftval);
   }else{
      # サムネイルを再描画
      &gnEnvAB_thumbredraw;
   }
}

sub on_gnEnvAB_right_value_changed {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[右]スピンボタン
   # 変更時
   my $gnSpnbtn = $_[0];
   
   # 左スピンボタンの値を取得
   my $gnEnvAB_left = $gnEnvABXML->get_widget('gnEnvAB_left');
   my $gnEnvAB_rightval = $gnSpnbtn->get_value;
   my $gnEnvAB_leftval = $gnEnvAB_left->get_value;
   
   # 画面全体の大きさを取得
   my $dis = Gtk2::Gdk::Display->get_default;
   my ($scr, $x, $y, $mask) = $dis->get_pointer;
   my $wid = $scr->get_width;
   my $hei = $scr->get_height;
   
   # 各スピンボタンの値とポップアップウィンドウの大きさを足した値が
   # スクリーンの描画領域を超えていたら、サムネイルの描画はキャンセルする
   if (($gnEnvAB_leftval + $gnEnvAB_rightval + $gnEnvArg{'Browser'}{'PreviewSize'}*100)>$wid) {
      if ($gnDebugFlag) { print ""; };
      $gnEnvAB_rightval = $wid - ($gnEnvAB_leftval + $gnEnvArg{'Browser'}{'PreviewSize'}*100);
      $gnSpnbtn->set_value($gnEnvAB_rightval);
   }else{
      # サムネイルを再描画
      &gnEnvAB_thumbredraw;
   }
}

sub on_gnEnvAB_all_value_changed {
   # 画像ポップアップ表示可能領域を指定ウィンドウ-[共通]スピンボタン
   # 変更時
   my $gnSpnbtn = $_[0];
   my $gnSpnbtnval = $gnSpnbtn->get_value;
   
   # 上下左右スピンボタンの値を取得
   my $gnEnvAB_top = $gnEnvABXML->get_widget('gnEnvAB_top');
   my $gnEnvAB_bottom = $gnEnvABXML->get_widget('gnEnvAB_bottom');
   my $gnEnvAB_left = $gnEnvABXML->get_widget('gnEnvAB_left');
   my $gnEnvAB_right = $gnEnvABXML->get_widget('gnEnvAB_right');
   $gnEnvAB_top->set_value($gnSpnbtnval);
   $gnEnvAB_bottom->set_value($gnSpnbtnval);
   $gnEnvAB_left->set_value($gnSpnbtnval);
   $gnEnvAB_right->set_value($gnSpnbtnval);
   
   # もう一回各スピンボタンの値を取得
   # 各値の最小値を出して、各スピンボタンと自分自身に適用
   my $gnEnvAB_topval = $gnEnvAB_top->get_value;
   my $gnEnvAB_bottomval = $gnEnvAB_bottom->get_value;
   my $gnEnvAB_leftval = $gnEnvAB_left->get_value;
   my $gnEnvAB_rightval = $gnEnvAB_right->get_value;
   my @gnEnvABvals = ($gnEnvAB_topval, $gnEnvAB_bottomval, $gnEnvAB_leftval, $gnEnvAB_rightval);
   my $gnEnvAB_minval = min(@gnEnvABvals);
   $gnEnvAB_top->set_value($gnEnvAB_minval);
   $gnEnvAB_bottom->set_value($gnEnvAB_minval);
   $gnEnvAB_left->set_value($gnEnvAB_minval);
   $gnEnvAB_right->set_value($gnEnvAB_minval);
   if ($gnSpnbtnval > $gnEnvAB_minval) {
      $gnSpnbtn->set_value($gnEnvAB_minval);
   }
   
}


sub gnEnvAB_thumbredraw {
   # "画像ポップアップ表示可能領域を指定"ウィンドウ-サムネイル
   # １ドットのイメージファイルを読み込んで当てはめる
   #
   if ($gnDebugFlag) { print "redraw execute.\n"; };
   
   # 上下左右のスピンボタンの値を取得
   my $gnEnvAB_top = $gnEnvABXML->get_widget('gnEnvAB_top');
   my $gnEnvAB_bottom = $gnEnvABXML->get_widget('gnEnvAB_bottom');
   my $gnEnvAB_left = $gnEnvABXML->get_widget('gnEnvAB_left');
   my $gnEnvAB_right = $gnEnvABXML->get_widget('gnEnvAB_right');
   my $gnEnvAB_topval = $gnEnvAB_top->get_value;
   my $gnEnvAB_bottomval = $gnEnvAB_bottom->get_value;
   my $gnEnvAB_leftval = $gnEnvAB_left->get_value;
   my $gnEnvAB_rightval = $gnEnvAB_right->get_value;
   
   # ウィンドウ内の描画領域の大きさを取得
   my $gnEnvAB_imgtable = $gnEnvABXML->get_widget('gnEnvAB_imgtable');
   my $gnEnvAB_imgtablealoc = $gnEnvAB_imgtable->allocation;
   my $gnEnvAB_imgtablewidth = $gnEnvAB_imgtablealoc->width;
   my $gnEnvAB_imgtableheight = $gnEnvAB_imgtablealoc->height;
   ##  Gtk2::Table のサイズがallocationされない場合には
   ##  親ウィジェットのGtk2::Vboxからサイズを取得(初回表示のみの強烈なworkaround)
   if ($gnEnvAB_imgtablewidth<2) {
      $gnEnvAB_imgtable = $gnEnvABXML->get_widget('vbox27');
      $gnEnvAB_imgtablealoc = $gnEnvAB_imgtable->allocation;
      $gnEnvAB_imgtablewidth = $gnEnvAB_imgtablealoc->width * 0.75;
      $gnEnvAB_imgtableheight = $gnEnvAB_imgtablealoc->height;
   }
   
   if ($gnDebugFlag) { print "Table Size\: $gnEnvAB_imgtablewidth, $gnEnvAB_imgtableheight\n";};

   # 画面全体の大きさを取得
   my $dis = Gtk2::Gdk::Display->get_default;
   my ($scr, $x, $y, $mask) = $dis->get_pointer;
   my $wid = $scr->get_width;
   my $hei = $scr->get_height;
   
   if ($gnDebugFlag) { print "Screen Size\: $wid, $hei\n";};
      
   # 画面の大きさに対する描画領域の比率を取得
   # 画面幅だけで計算し、高さはその比率に合わせて大きくする
   my $gnEnvABratio = $gnEnvAB_imgtablewidth / $wid;
   my $imgtop = int($gnEnvAB_topval*$gnEnvABratio);
   if ($imgtop<1) {
      $imgtop = 1;
   };
   my $imgbottom = int($gnEnvAB_bottomval*$gnEnvABratio);
   if ($imgbottom<1) {
      $imgbottom = 1;
   };
   my $imgleft = int($gnEnvAB_leftval*$gnEnvABratio);
   if ($imgleft<1) {
      $imgleft = 1;
   };
   my $imgright = int($gnEnvAB_rightval*$gnEnvABratio);
   if ($imgright<1) {
      $imgright = 1;
   };
   
   # 「上」を描画
   my $gnEnvABtopimgBuf;
   eval { $gnEnvABtopimgBuf = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($gnEnvABimgfn,
                                                                              $gnEnvAB_imgtablewidth,
                                                                              $imgtop,
                                                                              FALSE);
         };
   my $gnEnvAB_topimg = $gnEnvABXML->get_widget('gnEnvAB_topimg');
   $gnEnvAB_topimg->set_from_pixbuf($gnEnvABtopimgBuf);
   
   # 「下」を描画
   my $gnEnvABbottomimgBuf;
   eval { $gnEnvABbottomimgBuf = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($gnEnvABimgfn,
                                                                              $gnEnvAB_imgtablewidth,
                                                                              $imgbottom,
                                                                              FALSE);
         };
   my $gnEnvAB_bottomimg = $gnEnvABXML->get_widget('gnEnvAB_bottomimg');
   $gnEnvAB_bottomimg->set_from_pixbuf($gnEnvABbottomimgBuf);

   # 「左」を描画
   my $gnEnvABleftimgBuf;
   eval { $gnEnvABleftimgBuf = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($gnEnvABimgfn,
                                                                              $imgleft,
                                                                              int($hei*$gnEnvABratio) - ($imgtop + $imgbottom),
                                                                              FALSE);
         };
   my $gnEnvAB_leftimg = $gnEnvABXML->get_widget('gnEnvAB_leftimg');
   $gnEnvAB_leftimg->set_from_pixbuf($gnEnvABleftimgBuf);

   # 「右」を描画
   my $gnEnvABrightimgBuf;
   eval { $gnEnvABrightimgBuf = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($gnEnvABimgfn,
                                                                              $imgright,
                                                                              int($hei*$gnEnvABratio) - ($imgtop + $imgbottom),
                                                                              FALSE);
         };
   my $gnEnvAB_rightimg = $gnEnvABXML->get_widget('gnEnvAB_rightimg');
   $gnEnvAB_rightimg->set_from_pixbuf($gnEnvABrightimgBuf);
   
   # 「中央」を描画
   my $gnEnvABcenterimgBuf;
   eval { $gnEnvABcenterimgBuf = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($gnEnvABcenterimgfn,
                                                                                 $gnEnvAB_imgtablewidth - ($imgleft + $imgright),
                                                                                 int($hei*$gnEnvABratio) - ($imgtop + $imgbottom),
                                                                                 FALSE);
         };
   my $gnEnvAB_centerimg = $gnEnvABXML->get_widget('gnEnvAB_centerimg');
   $gnEnvAB_centerimg->set_from_pixbuf($gnEnvABcenterimgBuf);

   
   if ($gnDebugFlag) { print "Top, Bottom, Left, Right\: $imgtop, $imgbottom, $imgleft, $imgright\n";};

}

sub on_gnEnvAB_ok_clicked {
   # 画像ポップアップ表示可能領域を指定ウィンドウで"キャンセル"ボタンが押されたら
   # 各スピンボタンの値をgnEnvArgN{'Browser'}{'AvoidBoarderxxxx'}に保存
   # (xxxxはTop,Bottom,Left,Right)
   my $gnEnvAB = $_[1];

   my $gnEnvAB_top = $gnEnvABXML->get_widget('gnEnvAB_top');
   my $gnEnvAB_bottom = $gnEnvABXML->get_widget('gnEnvAB_bottom');
   my $gnEnvAB_left = $gnEnvABXML->get_widget('gnEnvAB_left');
   my $gnEnvAB_right = $gnEnvABXML->get_widget('gnEnvAB_right');

   $gnEnvArgN{'Browser'}{'AvoidBorderTop'} = $gnEnvAB_top->get_value;
   $gnEnvArgN{'Browser'}{'AvoidBorderBottom'} = $gnEnvAB_bottom->get_value;
   $gnEnvArgN{'Browser'}{'AvoidBorderLeft'} = $gnEnvAB_left->get_value;
   $gnEnvArgN{'Browser'}{'AvoidBorderRight'} = $gnEnvAB_right->get_value;
   
   $gnEnvAB->destroy;
   $gnEnvAB = undef;
   
}

sub on_gnEnvAB_cancel_clicked {
   # 画像ポップアップ表示可能領域を指定ウィンドウで"キャンセル"ボタンが押されたら
   # 単純にウィンドウを廃棄する
   my $gnEnvAB = $_[1];
   $gnEnvAB->destroy;
   $gnEnvAB = undef;
}

sub on_gnEnableImgPopup_realize {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-チェック
   # 表示時にgnEnvArg{'Browser'}{'PreviewVisible'}の値を読み込み
   $_[0]->set_active($gnEnvArg{'Browser'}{'PreviewVisible'});
}

sub on_gnEnableImgPopup_toggled {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-チェック
   # で値が変更されたらgnEnvArgN{'Browser'}{'PreviewVisible'}に書き込み
   $gnEnvArgN{'Browser'}{'PreviewVisible'} = $_[0]->get_active;
}

sub on_gnEnableImgPopup_st2_realize {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-チェック
   # 表示時にgnEnvArg{'Browser'}{'PreviewVisible'}の値を読み込み
      $_[0]->set_active($gnEnvArg{'Browser'}{'PreviewVisibleSt2'});
}

sub on_gnEnableImgPopup_st2_toggled {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-チェック
   # で値が変更されたらgnEnvArgN{'Browser'}{'PreviewVisible'}に書き込み
   $gnEnvArgN{'Browser'}{'PreviewVisibleSt2'} = $_[0]->get_active;
   
}

sub on_gnImgPrevInterval_realize {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[表示までの時間]
   # 表示時にgnEnvArg{'Browser'}{'PreviewWait'}の値を読み込み
   my $gnPreviewWait = $gnEnvArg{'Browser'}{'PreviewWait'} / 100 ;
   $_[0]->set_value($gnPreviewWait);
}

sub on_gnImgPrevInterval_value_changed {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[表示までの時間]
   # で値が変更されたらgnEnvArgN{'Browser'}{'PreviewWait'}に書き込み
   my $gnPreviewWait = $_[0]->get_value * 100;
   $gnEnvArgN{'Browser'}{'PreviewWait'} = $gnPreviewWait;
}

sub on_gnEnv_EnableCache_realize {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[キャッシュを有効にする]
   # 表示時にgnEnvArg{'Cache'}{'Enable'}の値を読み込み
   my $gnCT =$_[0];
   if($gnDebugFlag) { print "cache:\n" . Dumper($gnEnvArg{'Cache'}{'Enable'}); }
   if ($gnEnvArg{'Cache'}{'Enable'}) {
      if ($gnEnvArg{'Cache'}{'Enable'} == FALSE) {
         $gnCT->set_active(FALSE);
      }else{
         $gnCT->set_active(TRUE);
      }
   }else{
      $gnCT->set_active(FALSE);
   }
}

sub on_gnEnv_EnableCache_toggled {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[キャッシュを有効にする]
   # トグルされたらgnEnvArgN{'Cache'}{'Enable'}へ値を書き込み
   my $gnCT =$_[0];
   if ($gnCT->get_active) {
      $gnEnvArgN{'Cache'}{'Enable'} = TRUE;
   }else{
      $gnEnvArgN{'Cache'}{'Enable'} = FALSE;
   }

}

sub on_gnEnv_CacheExpire_realize {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[キャッシュ有効期間]
   # 表示時にgnEnvArg{'Cache'}{'Expire'}の値を読み込み
   my $gnCE = $_[0];
   if ($gnEnvArg{'Cache'}{'Expire'}) {
      $gnCE->set_value($gnEnvArg{'Cache'}{'Expire'});
   }else{
      $gnCE->set_value(0);
   }
}

sub on_gnEnv_CacheExpire_value_changed {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[キャッシュ有効期間]
   # 値の変更時にgnEnvArgN{'Cache'}{'Expire'}へ値を書き込み
   my $gnCE = $_[0];
   $gnEnvArgN{'Cache'}{'Expire'} = $gnCE->get_value_as_int;
}

sub on_gnEnv_ClearCacheOnExit_realize {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[終了時にキャッシュをクリア]
   # 表示時にgnEnvArg{'Cache'}{'ClearOnExit'}の値を読み込み
   my $gnCCOE = $_[0];
   if ($gnEnvArg{'Cache'}{'ClearOnExit'}) {
      if ($gnEnvArg{'Cache'}{'ClearOnExit'} == TRUE) {
         $gnCCOE->set_active(TRUE);
      }else{
         $gnCCOE->set_active(FALSE);
      }
   }else{
      $gnCCOE->set_active(FALSE);
   }

}

sub on_gnEnv_ClearCacheOnExit_toggled {
   # 環境設定ウィンドウ-[環境]-[画像プレビュー]-[終了時にキャッシュをクリア]
   # トグルされたらgnEnvArgN{'Cache'}{'ClearOnExit'}へ値を書き込み
   my $gnCCOE =$_[0];
   if ($gnCCOE->get_active) {
      $gnEnvArgN{'Cache'}{'ClearOnExit'} = TRUE;
   }else{
      $gnEnvArgN{'Cache'}{'ClearOnExit'} = FALSE;
   }

}

sub on_gnEnv_BrowseR1_realize {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windowsのみ)既定のブラウザを使用]
   # 表示時にgnEnvArg{'URLApp'}{'Select'}の値を読み込み
   my $gnBR = $_[0];
   
   if (($^O ne 'MSWin32') && ($^O ne 'darwin')) {
      $gnBR->set_sensitive(FALSE);
   }
   
   if ($gnEnvArg{'URLApp'}{'gnSelect'}) {
      if ($gnEnvArg{'URLApp'}{'gnSelect'} == 0) {
         $gnBR->set_active(TRUE);
      }
   }elsif ($gnEnvArg{'URLApp'}{'Select'}) {
      if ($gnEnvArg{'URLApp'}{'Select'} == 0) {
         $gnBR->set_active(TRUE);
      }
   }
   
}

sub on_gnEnv_BrowseR1_toggled {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windowsのみ)既定のブラウザを使用]
   # トグルされたらgnEnvArgN{'URLApp'}{'gnSelect'}へ値を書き込み
   my $gnBR = $_[0];
   
   if ($gnBR->get_active) {
      $gnEnvArgN{'URLApp'}{'gnSelect'} = 0;
   }
}

sub on_gnEnv_BrowseR2_realize {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windows以外)以下のブラウザを使用]
   # 表示時にgnEnvArg{'URLApp'}{'gnSelect'}の値を読み込み
   my $gnBR = $_[0];
   
   if ($gnEnvArg{'URLApp'}{'gnSelect'}) {
      if ($gnEnvArg{'URLApp'}{'gnSelect'} == 2) {
         $gnBR->set_active(TRUE);
      }
   }
   
}

sub on_gnEnv_BrowseR2_toggled {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windows以外)以下のブラウザを使用]
   # トグルされたらgnEnvArgN{'URLApp'}{'gnSelect'}へ値を書き込み
   my $gnBR = $_[0];
   
   if ($gnBR->get_active) {
      $gnEnvArgN{'URLApp'}{'gnSelect'} = 2;
   }
   
}

sub on_gnEnv_BrowseR3_realize {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windows以外)以下のコマンドを使用]
   # 表示時にgnEnvArg{'URLApp'}{'gnSelect'}の値を読み込み
   my $gnBR = $_[0];
   
   if ($gnEnvArg{'URLApp'}{'gnSelect'}) {
      if ($gnEnvArg{'URLApp'}{'gnSelect'} == 1) {
         $gnBR->set_active(TRUE);
      }
   }elsif ($gnEnvArg{'URLApp'}{'Select'}) {
      if ($gnEnvArg{'URLApp'}{'Select'} == 1) {
         $gnBR->set_active(TRUE);
      }
   }
   
}

sub on_gnEnv_BrowseR3_toggled {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windows以外)以下のブラウザを使用]
   # トグルされたらgnEnvArgN{'URLApp'}{'gnSelect'}へ値を書き込み
   my $gnBR = $_[0];
   
   if ($gnBR->get_active) {
      $gnEnvArgN{'URLApp'}{'gnSelect'} = 1;
   }
   
}

sub on_gnEnv_BrowserSel_realize {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windows以外)以下のブラウザを使用]-[使用ブラウザ]
   # 表示時にgnEnvArg{'URLApp'}{'gnSelBrowser'}の値を読み込み
   my $gnBR = $_[0];
   
   if ($gnEnvArg{'URLApp'}{'gnSelBrowser'}) {
      $gnBR->set_active($gnEnvArg{'URLApp'}{'gnSelBrowser'});
   }
}

sub on_gnEnv_BrowserSel_changed {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windows以外)以下のブラウザを使用]-[使用ブラウザ]
   # トグルされたらgnEnvArgN{'URLApp'}{'gnSelect'}へ値を書き込み
   my $gnBR = $_[0];
   
   $gnEnvArgN{'URLApp'}{'gnSelBrowser'} = $gnBR->get_active;
   
}

sub on_gnEnv_BrowserCmd_realize {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windows以外)以下のブラウザを使用]-[コマンド名]
   # 表示時にgnEnvArg{'URLApp'}{'gnSelBrowser'}の値を読み込み
   my $gnBR = $_[0];
   
   if ($gnEnvArg{'URLApp'}{'gnFile'}) {
      $gnBR->set_text($gnEnvArg{'URLApp'}{'gnFile'});
   }elsif ($gnEnvArg{'URLApp'}{'File'}) {
      $gnBR->set_text($gnEnvArg{'URLApp'}{'File'});
   }
}

sub on_gnEnv_BrowserCmd_changed {
   # 環境設定ウィンドウ-[環境]-[ブラウザ]-[(Windows以外)以下のブラウザを使用]-[コマンド名]
   # トグルされたらgnEnvArgN{'URLApp'}{'gnSelect'}へ値を書き込み
   my $gnBR = $_[0];
   
   $gnEnvArgN{'URLApp'}{'gnFile'} = $gnBR->get_text;
   
}

sub on_gnEnv_Proxy_none_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[使用しない]
   # 表示時にgnEnvArg{'ReadProxy'}{'Proxy'}の値を読み込み
   my $gnEP = $_[0];
   
   if ($gnEnvArg{'ReadProxy'}{'Proxy'} || $gnEnvArg{'WriteProxy'}{'Proxy'}) {
      if (($gnEnvArg{'ReadProxy'}{'Proxy'} == 0) && ($gnEnvArg{'WriteProxy'}{'Proxy'} == 0)) {
         $gnEP->set_active(TRUE);
      }
   }else{
         $gnEP->set_active(FALSE);
   }
}

sub on_gnEnv_Proxy_none_toggled {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[使用しない]
   # 変更時にgnEnvArgN{'ReadProxy'}{'Proxy'}, gnEnvArgN{'WriteProxy'}{'Proxy'}を無効に
   my $gnEP = $_[0];
   
   if ($gnEP->get_active) {
      if ($gnDebugFlag) { print "active\n";};
      my $gnRP = $gnEnvXML->get_widget('gnEnv_proxy_use');
      $gnRP->set_active(FALSE);
      $gnRP = $gnEnvXML->get_widget('gnEnv_wproxy_use');
      $gnRP->set_active(FALSE);
      $gnEnvArgN{'ReadProxy'}{'Proxy'} = 0;
      $gnEnvArgN{'WriteProxy'}{'Proxy'} = 0;
   }
}

sub on_gnEnv_Proxy_http_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]
   # 表示時にgnEnvArg{'ReadProxy'}{'Proxy'}の値を読み込み
   my $gnEP = $_[0];
   
   if ($gnEnvArg{'ReadProxy'}{'Proxy'} || $gnEnvArg{'WriteProxy'}{'Proxy'}) {
      if (($gnEnvArg{'ReadProxy'}{'Proxy'} == 1) || ($gnEnvArg{'WriteProxy'}{'Proxy'} == 1)) {
         $gnEP->set_active(TRUE);
      }
   }else{
         $gnEP->set_active(FALSE);
   }
}

sub on_gnEnv_proxy_use_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]
   # 表示時にgnEnvArg{'ReadProxy'}{'Proxy'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'ReadProxy'}{'Proxy'} == 1) {
         $gnEP->set_active(TRUE);
   }else{
         $gnEP->set_active(FALSE);
   }
}

sub on_gnEnv_proxy_use_toggled {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]
   # 変更時にgnEnvArgN{'ReadProxy'}{'Proxy'}へ値を書き込み
   my $gnEP = $_[0];
   my $val = $gnEP->get_active;
   if ($val == TRUE) {
         $gnEnvArgN{'ReadProxy'}{'Proxy'} = 1;
   }else{
         $gnEnvArgN{'ReadProxy'}{'Proxy'} = 0;
   }
}

sub on_gnEnv_proxy_url_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]-[URL]
   # 表示時にgnEnvArg{'ReadProxy'}{'Address'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'ReadProxy'}{'Address'}) {
         $gnEP->set_text($gnEnvArg{'ReadProxy'}{'Address'});
   }
}

sub on_gnEnv_proxy_url_changed {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]-[URL]
   # 変更時にgnEnvArgN{'ReadProxy'}{'Address'}へ値を書き込み
   my $gnEP = $_[0];
   $gnEnvArgN{'ReadProxy'}{'Address'} = $gnEP->get_text;
}

sub on_gnEnv_proxy_port_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]-[ポート番号]
   # 表示時にgnEnvArg{'ReadProxy'}{'Port'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'ReadProxy'}{'Port'}) {
         $gnEP->set_text($gnEnvArg{'ReadProxy'}{'Port'});
   }
}

sub on_gnEnv_proxy_port_changed {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]-[ポート番号]
   # 変更時にgnEnvArgN{'ReadProxy'}{'Port'}へ値を書き込み
   my $gnEP = $_[0];
   $gnEnvArgN{'ReadProxy'}{'Port'} = $gnEP->get_text;
}

sub on_gnEnv_proxy_username_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]-[ユーザ名]
   # 表示時にgnEnvArg{'ReadProxy'}{'UserID'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'ReadProxy'}{'UserID'}) {
         $gnEP->set_text($gnEnvArg{'ReadProxy'}{'UserID'});
   }
}

sub on_gnEnv_proxy_username_changed {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]-[ユーザ名]
   # 変更時にgnEnvArgN{'ReadProxy'}{'UserID'}へ値を書き込み
   my $gnEP = $_[0];
   $gnEnvArgN{'ReadProxy'}{'UserID'} = $gnEP->get_text;
}

sub on_gnEnv_proxy_password_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]-[パスワード]
   # 表示時にgnEnvArg{'ReadProxy'}{'Password'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'ReadProxy'}{'Password'}) {
         $gnEP->set_text($gnEnvArg{'ReadProxy'}{'Password'});
   }
}

sub on_gnEnv_proxy_password_changed {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[読み込み用プロキシを使用]-[パスワード]
   # 変更時にgnEnvArgN{'ReadProxy'}{'Password'}へ値を書き込み
   my $gnEP = $_[0];
   $gnEnvArgN{'ReadProxy'}{'Password'} = $gnEP->get_text;
}

sub on_gnEnv_wproxy_use_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]
   # 表示時にgnEnvArg{'WriteProxy'}{'Proxy'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'WriteProxy'}{'Proxy'} == 1) {
         $gnEP->set_active(TRUE);
   }else{
         $gnEP->set_active(FALSE);
   }
}

sub on_gnEnv_wproxy_use_toggled {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]
   # 変更時にgnEnvArgN{'WriteProxy'}{'Proxy'}へ値を書き込み
   my $gnEP = $_[0];
   my $val = $gnEP->get_active;
   if ($val == TRUE) {
         $gnEnvArgN{'WriteProxy'}{'Proxy'} = 1;
   }else{
         $gnEnvArgN{'WriteProxy'}{'Proxy'} = 0;
   }
}

sub on_gnEnv_wproxy_url_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]-[URL]
   # 表示時にgnEnvArg{'WriteProxy'}{'Address'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'WriteProxy'}{'Address'}) {
         $gnEP->set_text($gnEnvArg{'WriteProxy'}{'Address'});
   }
}

sub on_gnEnv_wproxy_url_changed {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]-[URL]
   # 変更時にgnEnvArgN{'WriteProxy'}{'Address'}へ値を書き込み
   my $gnEP = $_[0];
   $gnEnvArgN{'WriteProxy'}{'Address'} = $gnEP->get_text;
}

sub on_gnEnv_wproxy_port_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]-[ポート番号]
   # 表示時にgnEnvArg{'WriteProxy'}{'Port'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'WriteProxy'}{'Port'}) {
         $gnEP->set_text($gnEnvArg{'WriteProxy'}{'Port'});
   }
}

sub on_gnEnv_wproxy_port_changed {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]-[ポート番号]
   # 変更時にgnEnvArgN{'WriteProxy'}{'Port'}へ値を書き込み
   my $gnEP = $_[0];
   $gnEnvArgN{'WriteProxy'}{'Port'} = $gnEP->get_text;
}

sub on_gnEnv_wproxy_username_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]-[ユーザ名]
   # 表示時にgnEnvArg{'WriteProxy'}{'UserID'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'WriteProxy'}{'UserID'}) {
         $gnEP->set_text($gnEnvArg{'WriteProxy'}{'UserID'});
   }
}

sub on_gnEnv_wproxy_username_changed {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]-[ユーザ名]
   # 変更時にgnEnvArgN{'WriteProxy'}{'UserID'}へ値を書き込み
   my $gnEP = $_[0];
   $gnEnvArgN{'WriteProxy'}{'UserID'} = $gnEP->get_text;
}

sub on_gnEnv_wproxy_password_realize {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]-[パスワード]
   # 表示時にgnEnvArg{'WriteProxy'}{'Password'}の値を読み込み
   my $gnEP = $_[0];
   if ($gnEnvArg{'WriteProxy'}{'Password'}) {
         $gnEP->set_text($gnEnvArg{'WriteProxy'}{'Password'});
   }
}

sub on_gnEnv_wproxy_password_changed {
   # 環境設定ウィンドウ-[ネットワーク]-[プロキシ]-[HTTPプロキシ]-[書き込み用プロキシを使用]-[パスワード]
   # 変更時にgnEnvArgN{'WriteProxy'}{'Password'}へ値を書き込み
   my $gnEP = $_[0];
   $gnEnvArgN{'WriteProxy'}{'Password'} = $gnEP->get_text;
}

sub on_gnEnv_permitPopup_Ext_realize {
   # 環境設定ウィンドウ-[文字とリンク]-[リンク]-[ポップアップする拡張子]
   # 表示時にgnEnvArg{'Browser'}{'gnPermitPopupExt'}の値を読み込み
   my $gnE_PPExt = $_[0];
   if($gnEnvArg{'Browser'}{'gnPermitPopupExt'}) {
      $gnE_PPExt->set_text($gnEnvArg{'Browser'}{'gnPermitPopupExt'});
   }else{
      $gnE_PPExt->set_text("jpg,gif,bmp,tif,png");
   }
}

sub on_gnEnv_permitPopup_Ext_changed {
   # 環境設定ウィンドウ-[文字とリンク]-[リンク]-[ポップアップする拡張子]
   # 変更時にgnEnvArgN{'Browser'}{'gnPermitPopupExt'}へ値を書き込み
   my $gnE_PPExt = $_[0];
   $gnEnvArg{'Browser'}{'gnPermitPopupExt'} = $gnE_PPExt->get_text;
}

sub on_gnEnv_Color_notYetCached_realize {
   # 環境設定ウィンドウ-[文字とリンク]-[リンク]-[未キャッシュリンクの色]
   # 表示時にgnEnvArg{'Thread'}{'gnLinkColorNotCached'}の値を読み込み
   my $gnE_C = $_[0];
   if($gnEnvArg{'Thread'}{'gnLinkColorNotCached'}) {
      my $gnE_C_color_red = substr($gnEnvArg{'Thread'}{'gnLinkColorNotCached'}, 1, 2);
      $gnE_C_color_red = hex($gnE_C_color_red) * 257;
      my $gnE_C_color_green = substr($gnEnvArg{'Thread'}{'gnLinkColorNotCached'}, 3, 2);
      $gnE_C_color_green = hex($gnE_C_color_green) * 257;
      my $gnE_C_color_blue = substr($gnEnvArg{'Thread'}{'gnLinkColorNotCached'}, 5, 2);
      $gnE_C_color_blue = hex($gnE_C_color_blue) * 257;
      
      my $gnE_C_color = Gtk2::Gdk::Color->new($gnE_C_color_red, $gnE_C_color_green, $gnE_C_color_blue);
      $gnE_C->set_color($gnE_C_color);
   }else{
      # gnEnvArg{'Thread'}{'gnLinkColorNotCached'}がセットされていなかったら
      # 初期値をセット
      $gnEnvArgN{'Thread'}{'gnLinkColorNotCached'} = "\#0000FF";
      my $gnE_C_color = Gtk2::Gdk::Color->new(0,0,65535);
      $gnE_C->set_color($gnE_C_color);
   }
}

sub on_gnEnv_Color_notYetCached_color_set {
   # 環境設定ウィンドウ-[文字とリンク]-[リンク]-[未キャッシュリンクの色]
   # 変更時にgnEnvArgN{'Thread'}{'gnLinkColorNotCached'}へ値を書き込み
   my $gnE_C = $_[0];
   my $gnE_C_color = $gnE_C->get_color;
   my $gnE_C_color_red = $gnE_C_color->red;
   $gnE_C_color_red = $gnE_C_color_red/257;
   $gnE_C_color_red = sprintf("%.2X", $gnE_C_color_red);
   my $gnE_C_color_green = $gnE_C_color->green;
   $gnE_C_color_green = $gnE_C_color_green/257;
   $gnE_C_color_green = sprintf("%.2X", $gnE_C_color_green);
   my $gnE_C_color_blue = $gnE_C_color->blue;
   $gnE_C_color_blue = $gnE_C_color_blue/257;
   $gnE_C_color_blue = sprintf("%.2X", $gnE_C_color_blue);
   if($gnDebugFlag) { print "on_gnEnv_Color_notYetCached_color_set\: \#${gnE_C_color_red}${gnE_C_color_green}${gnE_C_color_blue}\n"; }
   $gnEnvArgN{'Thread'}{'gnLinkColorNotCached'} = "\#${gnE_C_color_red}${gnE_C_color_green}${gnE_C_color_blue}";

}

sub on_gnEnv_Color_alreadyCached_realize {
   # 環境設定ウィンドウ-[文字とリンク]-[リンク]-[キャッシュ済みリンクの色]
   # 表示時にgnEnvArg{'Thread'}{'gnLinkColorAlreadyCached'}の値を読み込み
   my $gnE_C = $_[0];
   if($gnEnvArg{'Thread'}{'gnLinkColorAlreadyCached'}) {
      my $gnE_C_color_red = substr($gnEnvArg{'Thread'}{'gnLinkColorAlreadyCached'}, 1, 2);
      $gnE_C_color_red = hex($gnE_C_color_red) * 257;
      my $gnE_C_color_green = substr($gnEnvArg{'Thread'}{'gnLinkColorAlreadyCached'}, 3, 2);
      $gnE_C_color_green = hex($gnE_C_color_green) * 257;
      my $gnE_C_color_blue = substr($gnEnvArg{'Thread'}{'gnLinkColorAlreadyCached'}, 5, 2);
      $gnE_C_color_blue = hex($gnE_C_color_blue) * 257;
      
      my $gnE_C_color = Gtk2::Gdk::Color->new($gnE_C_color_red, $gnE_C_color_green, $gnE_C_color_blue);
      $gnE_C->set_color($gnE_C_color);
   }else{
      # gnEnvArg{'Thread'}{'gnLinkColorAlreadyCached'}がセットされていなかったら
      # 初期値をセット
      $gnEnvArgN{'Thread'}{'gnLinkColorAlreadyCached'} = "\#0000FF";
      my $gnE_C_color = Gtk2::Gdk::Color->new(0,0,65535);
      $gnE_C->set_color($gnE_C_color);
   }

}

sub on_gnEnv_Color_alreadyCached_color_set {
   # 環境設定ウィンドウ-[文字とリンク]-[リンク]-[キャッシュ済みリンクの色]
   # 変更時にgnEnvArgN{'Thread'}{'gnLinkColorAlreadyCached'}へ値を書き込み
   my $gnE_C = $_[0];
   my $gnE_C_color = $gnE_C->get_color;
   my $gnE_C_color_red = $gnE_C_color->red;
   $gnE_C_color_red = $gnE_C_color_red/257;
   $gnE_C_color_red = sprintf("%.2X", $gnE_C_color_red);
   my $gnE_C_color_green = $gnE_C_color->green;
   $gnE_C_color_green = $gnE_C_color_green/257;
   $gnE_C_color_green = sprintf("%.2X", $gnE_C_color_green);
   my $gnE_C_color_blue = $gnE_C_color->blue;
   $gnE_C_color_blue = $gnE_C_color_blue/257;
   $gnE_C_color_blue = sprintf("%.2X", $gnE_C_color_blue);
   if($gnDebugFlag) { print "on_gnEnv_Color_alreadyCached_color_set\: \#${gnE_C_color_red}${gnE_C_color_green}${gnE_C_color_blue}\n"; }
   $gnEnvArgN{'Thread'}{'gnLinkColorAlreadyCached'} = "\#${gnE_C_color_red}${gnE_C_color_green}${gnE_C_color_blue}";
}

sub on_gnEnv_Font_useCustom_realize {
   # 環境設定ウィンドウ-[文字とリンク]-[フォント]-[システム標準のフォントを使用する]
   # 表示時にgnEnvArg{'Browser'}{'gnBrowserFontUseCustom'}の値を読み込み
   my $gnEFuD = $_[0];
   my $gnEF = $gnEnvXML->get_widget('gnEnv_Font');
   
   if($gnEnvArg{'Browser'}{'gnBrowserFontUseCustom'}) {
         $gnEFuD->set_active(FALSE);
         $gnEF->sensitive(TRUE);
         if($gnDebugFlag) { print "on_gnEnv_Font_useDefault_realize\: env is set and trued\n"; }
   }else{
         $gnEFuD->set_active(TRUE);
         $gnEF->sensitive(FALSE);
         if($gnDebugFlag) { print "on_gnEnv_Font_useDefault_realize\: env is NOT set or setandfalsed\n"; }
   }
}

sub on_gnEnv_Font_useCustom_toggled {
   # 環境設定ウィンドウ-[文字とリンク]-[フォント]-[システム標準のフォントを使用する]
   # 変更時にgnEnvArgN{'Browser'}{'gnBrowserFontUseCustom'}へ値を書き込み
   my $gnEFuD = $_[0];
   my $gnEF = $gnEnvXML->get_widget('gnEnv_Font');
   
   my $flg = $gnEFuD->get_active;
   if($flg == TRUE) {
      $gnEnvArgN{'Browser'}{'gnBrowserFontUseCustom'} = FALSE;
      $gnEF->sensitive(FALSE);
   }else{
      $gnEnvArgN{'Browser'}{'gnBrowserFontUseCustom'} = TRUE;
      $gnEF->sensitive(TRUE);
   }
}

sub on_gnEnv_Font_realize {
   # 環境設定ウィンドウ-[文字とリンク]-[フォント]-[フォント]
   # 表示時に
   # gnEnvArg{'Browser'}{'BrowserFontName'}
   # gnEnvArg{'Browser'}{'BrowserFontBold'}
   # gnEnvArg{'Browser'}{'BrowserFontItalic'}
   # gnEnvArg{'Browser'}{'BrowserFontSize'}
   # の値を読み込み
   my $gnEF = $_[0];
   my $fntstr = "";
   if($gnEnvArg{'Browser'}{'BrowserFontName'}) {
      $fntstr = $gnEnvArg{'Browser'}{'BrowserFontName'};
   }
   if($gnEnvArg{'Browser'}{'BrowserFontBold'} != -1) {
      $fntstr .= " Bold";
   }
   if($gnEnvArg{'Browser'}{'BrowserFontItalic'} != -1) {
      $fntstr .= " Italic";
   }
   if($gnEnvArg{'Browser'}{'BrowserFontSize'}) {
      $fntstr .= " $gnEnvArg{'Browser'}{'BrowserFontSize'}";
   }
   $gnEF->set_font_name($fntstr);
}

sub on_gnEnv_Font_font_set {
   # 環境設定ウィンドウ-[文字とリンク]-[フォント]-[フォント]
   # 変更時に
   # gnEnvArgN{'Browser'}{'BrowserFontName'}
   # gnEnvArgN{'Browser'}{'BrowserFontBold'}
   # gnEnvArgN{'Browser'}{'BrowserFontItalic'}
   # gnEnvArgN{'Browser'}{'BrowserFontSize'}
   # へ値を書き込み
   my $gnEF = $_[0];
   my $fntname = "";
   my $fntbold_flg = FALSE;
   my $fntitalic_flg = FALSE;
   my $gnEF_fontname = $gnEF->get_font_name;
   my @ar = split(/ /, $gnEF_fontname);
   $gnEnvArgN{'Browser'}{'BrowserFontSize'} = pop(@ar);
   foreach my $val (@ar) {
      if($val eq "Italic") {
         $fntitalic_flg = TRUE;
      }elsif($val eq "Bold") {
         $fntbold_flg = TRUE;
      }else{
         if($fntname eq "") {
            $fntname = $val;
         }else{
            $fntname = $fntname . " " . $val;
         }
      }
      
   }
   $gnEnvArgN{'Browser'}{'BrowserFontName'} = $fntname;
   if($fntitalic_flg == TRUE) {
      $gnEnvArgN{'Browser'}{'BrowserFontItalic'} = 1;
   }else{
      $gnEnvArgN{'Browser'}{'BrowserFontItalic'} = -1;
   }
   if($fntbold_flg == TRUE) {
      $gnEnvArgN{'Browser'}{'BrowserFontBold'} = 1;
   }else{
      $gnEnvArgN{'Browser'}{'BrowserFontBold'} = -1;
   }
   if($gnDebugFlag) {
      &gnLogger("on_gnEnv_Font_font_set\: font_name\: $gnEnvArgN{'Browser'}{'BrowserFontName'}");
      &gnLogger("on_gnEnv_Font_font_set\: font_bold\: $gnEnvArgN{'Browser'}{'BrowserFontBold'}");
      &gnLogger("on_gnEnv_Font_font_set\: font_italic\: $gnEnvArgN{'Browser'}{'BrowserFontItalic'}");
      &gnLogger("on_gnEnv_Font_font_set\: font_size\: $gnEnvArgN{'Browser'}{'BrowserFontSize'}");
   }
}

#--------------------------------------
# GUIメソッド(アクション)対応関数
#--------------------------------------

sub on_gnSI_filcl_clicked {
   # クリアボタンが押されたらスレフィルタをクリア
   my $gnSIFil = $gnGUIxml->get_widget('gnSI_fil');
   $gnSIFil->child->set_text('');
}

sub on_gnSI_fil_changed {
   # スレフィルタに入力、もしくはプルダウンから選択された
   my $gnSIFil = $_[0];
   my $gnItaView = $gnGUIxml->get_widget('gnII');
   &gnSleIchiranSelect($gnItaView,"none");

}

sub on_gnSI_fil_realize {
   # スレフィルタ初期化

   my $gnSIFil = $_[0];
   if($gnDebugFlag) { print "realize\:\n" . Dumper($gnSIFil)."\n"; }

   my @gnSIFilHashKeys = keys(%{$gnEnvArg{'SelectText'}});
   foreach my $val(@gnSIFilHashKeys) {
     if ($gnEnvArg{'SelectText'}{$val}) {
        $gnSIFil->append_text($gnEnvArg{'SelectText'}{$val});
     }
  }

}

sub on_gnTBS_write_clicked {
   # 書き込むボタンが押された
   &gnWrite;
}

sub on_gnMS_write_activate {
   # 書き込むボタンが押された
   &gnWrite;
}

sub on_mW_unrealize {
   # メインウィンドウが閉じたら終了
   &gnExit;
}


sub gnSureViewMouseMove {
   # スレ表示のTextViewの中でマウスが動いたら
   # この関数が呼び出される
   # ここから各ポップアップの呼び出しに分岐する
   my ($gnSureV, $gnEvent) = @_;
   my $gnHover = FALSE;
   my $gnLink = '';
   my ($x, $y) = $gnSureV->window_to_buffer_coords('widget', #GTK_TEXT_WINDOW_WIDGET,
                                                         $gnEvent->x, $gnEvent->y);
   
   my $gnSV_iter = $gnSureV->get_iter_at_location ($x, $y);
   
   foreach my $gnTag ($gnSV_iter->get_tags) {
      if($gnTag->{page}) {
         # タグの中に"page"タグを含んでいれば$gnHoverフラグをTRUEにして
         # ループを抜ける
         $gnLink = $gnTag->{page};
         $gnHover = TRUE;
         last;
      }
   }
   
   if($gnHover == TRUE) {
      if($gnHoverOverLink != $gnHover) {
         $gnHoverOverLink = $gnHover;
         $gnSureV->get_window('text')->set_cursor($gnCursor_Link);
         my $gnLinkFlg = substr($gnLink, 0, 4);
         if($gnLinkFlg eq "resn") {
                         if($gnDebugFlag) { print "resn\n"; }

         }elsif($gnLinkFlg eq "repl") {
                         if($gnDebugFlag) { print "repl\n"; }
                         # レス先をポップアップ表示
                         if($gnPopupText eq "") {
                            $gnLink = substr($gnLink, 10);
                            $gnPopupText = &gnResPopup($gnLink);
                         }

         }elsif($gnLinkFlg eq "datl") {
                         # read.cgiを含んだリンク
                         if($gnDebugFlag) { print "datl\n"; }
         }elsif($gnLinkFlg eq "http") {
                         # httpの場合は別のスレをさしている場合と
                         # 画像などの場合がある
                         # 画像の場合は読み込んでポップアップ表示する
                         if($gnDebugFlag) { print "http\n"; }
                         
                         # 拡張子が該当のものの場合だけポップアップを表示
                         my $testptn = $gnEnvArg{'Browser'}{'gnPermitPopupExt'};
                         $testptn =~ s/\,/\|/g;
                         
                         my $gnPop_URLFNbuf = substr($gnLink, rindex($gnLink, "\/")+1);
                         my @URLFNbuf = split(/\./, $gnPop_URLFNbuf);
                         my $gnPop_URLFN = "";
                         my $gnPop_URLExt = pop(@URLFNbuf);
                         
                         if(($gnEnvArg{'Browser'}{'PreviewVisible'} != 0) && ($gnPop_URLExt =~ m/(${testptn})/i)) {
                         
                         

                            
                         # 指定の時間waitするための
                         # Gtk2のtimeout関数をセット
                         if (!($gnPopupCallback)) {
                            
                            
                            $gnPopupCallback = Glib::Timeout->add($gnEnvArg{'Browser'}{'PreviewWait'},
                                                  sub{ 
                                                     # ポップアップ画像の情報を作成
                                                     my $gnNoteP = $gnSureV->parent->parent->get_current_page;
                                                     my $gnDatInfo;
                                                     my $gnItaDir;
                                                     my $gnDatNo;
                                                     for my $i ( 0 .. $#gnSureViewInfo ) {
#                                                        if ($gnNoteP == $gnSureViewInfo[$i][1]) {
                                                        if ($gnNoteP == $i) {
                                                           $gnDatInfo = $gnSureViewInfo[$i][0];
                                                           ($gnItaDir, $gnDatNo) = split(/\//, $gnDatInfo);
                                                           last;
                                                        }
                                                     }
                                                     my $sute;
                                                     ($gnDatNo, $sute) = split(/\./, $gnDatNo);
                                                     
                                                     my ($itaname, $itaurl) = split(chr(001), $gnBoardTbl{$gnItaDir});
                                                     
                                                     my $gnPop_SI = $gnGUIxml->get_widget('gnSI');
                                                     my $gnPop_SIM = $gnPop_SI->get_model;
                                                     my $gnPop_SureName = &gnGetSureVarFromDat($gnPop_SIM, $gnDatInfo, 0);
                                                     $gnPop_SureName = substr($gnPop_SureName, index($gnPop_SureName, "\: ")+2);
                                                     $gnPop_SureName =~ s/\//_/g;
                                                     
#                                                     my $gnPop_URLFNbuf = substr($gnLink, rindex($gnLink, "\/")+1);
#                                                     my @URLFNbuf = split(/\./, $gnPop_URLFNbuf);
#                                                     my $gnPop_URLFN = "";
#                                                     my $gnPop_URLExt = pop(@URLFNbuf);
                                                     for (my $j=0;$j<@URLFNbuf;$j++) {
                                                        $gnPop_URLFN .= $URLFNbuf[$j] . "\.";
                                                     }
                                                     chop($gnPop_URLFN);
                                                     
                                                     my $gnPop_cachetime;
                                                     my $gnPop_cachefn;
                                                     my @gnPop_cachetimear;
                                                     if($gnCacheTbl{'Cache'}{$gnLink}) {
                                                        ($gnPop_cachetime, $gnPop_cachefn) = split(chr(001), $gnCacheTbl{'Cache'}{$gnLink});
#                                                        @gnPop_cachetimear = localtime($gnPop_cachetime);
                                                     }else{
                                                        $gnPop_cachetime = 0;
                                                        $gnPop_cachefn = "";
                                                     }
                                                     @gnPop_cachetimear = localtime($gnPop_cachetime);

                                                     
                                                     if ($gnDebugFlag) {
                                                        print("gnSureViewMouseMove:\t$gnLink\t$itaname\t$gnItaDir\t$gnDatNo\t$gnPop_SureName\t$gnPop_URLFN\t$gnPop_URLExt\t$gnPop_cachetime\t$gnPop_cachefn\t$gnPop_cachetimear[5]\t$gnPop_cachetimear[4]\t$gnPop_cachetimear[2]\t$gnPop_cachetimear[1]\t$gnPop_cachetimear[0]\n");
                                                     }
                                                     %gnImgPopup_info = ('URL' => $gnLink,
                                                                           'ItaName' => $itaname,
                                                                           'ItaDir' => $gnItaDir,
                                                                           'DatNo' => $gnDatNo,
                                                                           'SureName' => $gnPop_SureName,
                                                                           'URLFN' => $gnPop_URLFN,
                                                                           'URLExt' => $gnPop_URLExt,
                                                                           'CacheTime' => $gnPop_cachetime,
                                                                           'CacheFN' => $gnPop_cachefn,
                                                                           'Year' => $gnPop_cachetimear[5]+1900,
                                                                           'Month' => sprintf("%02d", $gnPop_cachetimear[4]+1),
                                                                           'Day' => sprintf("%02d", $gnPop_cachetimear[3]),
                                                                           'Hour' => sprintf("%02d", $gnPop_cachetimear[2]),
                                                                           'Minute' => sprintf("%02d", $gnPop_cachetimear[1]),
                                                                           'Second' => sprintf("%02d", $gnPop_cachetimear[0]),
                                                                           );
                                                     if ($gnDebugFlag) { print Dumper(%gnImgPopup_info) . "\n";}
                                                     
                                                     # ポップアップを表示
                                                     my $ref = &gnImgPopup2($gnLink);
                                                     ($gnPopupImg, $gnPop2) = @$ref;
                                                     return(FALSE);
                                                  },
                                                  undef,
                                                  0);
                           # gnImgBufferUpdateを定期的に呼び出すtimeout関数をセット
                           if (!($gnImgUpdateCallback)) {
                           $gnImgUpdateCallback = Glib::Timeout->add($gnEnvArg{'Browser'}{'PreviewWait'},
                                                                        sub {
                                                                        my $retval;
                                                                        my $ref = &gnImgBufferUpdate($gnPopupImg, $gnPop2);
                                                                        ($gnPopupImg, $gnPop2, $retval) = @$ref;
                                                                        return($retval);
                                                                        },
                                                                        undef,
                                                                        0);
                        
                           }
                         }
                         }
                        
           }
           else {
              if($gnDebugFlag) { print "Unknown Code\:$gnLinkFlg\n"; }
           }
            
#         }
      }else{
         $gnSureV->get_window('text')->set_cursor($gnCursor_Link);
      }
   }else{
      if($gnHoverOverLink != $gnHover) {
         $gnHoverOverLink = $gnHover;
         $gnSureV->get_window('text')->set_cursor($gnCursor_Beam);

         # ポップアップウィンドウが出てたらdestroyする
         if($gnPopupText ne "") {
            $gnPopupText->destroy;
            $gnPopupText = "";
         }
         if ($gnPopupImg) {
            if($gnPopupImg ne "") {
               if($gnDebugFlag) { print "linkout-img2\:" . Dumper($gnPopupImg) . "\n"; }
               Gtk2::Object::destroy($gnPopupImg);
               $gnPop2->clear;
               $gnPopupImg = "";
            }
         }
            
         # 2段階ポップアップの段階をリセット
         $gnPopupImgSt = 1;
         
         # timeout関数をremoveする。
         # removeしないと次のポップアップが出ない
         if ($gnPopupCallback) {
            Glib::Source->remove($gnPopupCallback);
            undef($gnPopupCallback);
         }
         if ($gnImgUpdateCallback) {
            if ($gnImgUpdateCallback != NULL) {
               Glib::Source->remove($gnImgUpdateCallback);
               undef($gnImgUpdateCallback);
            }
         }
      }else{
         $gnSureV->get_window('text')->set_cursor($gnCursor_Beam);
      }
   }
   
   # 必ずget_pointerしてFALSEを返すこと
   # get_pointerしないと、Linuxではクリックしたとき(正確に言うとPRESS_AFTER)しかイベントを
   # 検出しない。
   # またFALSEを入れないとTextView内で
   # 文字の選択ができないようになってしまう
   $gnSureV->window->get_pointer;
   return FALSE;
}

sub gnSureViewLinkPress {
   # リンクをクリック(マウスダウン)したときに実行する関数
   # ポップアップウィンドウを消去する
   
   if($gnDebugFlag) { print "LinkClick\(button_press_event\)\n"; }
   my ($gnSureV, $gnEvent) = @_;
   
   if ($gnPopupCallback) {
      Glib::Source->remove($gnPopupCallback);
      undef($gnPopupCallback);
   }
   if ($gnImgUpdateCallback) {
      Glib::Source->remove($gnImgUpdateCallback);
      undef($gnPopupCallback);
   }
   # ポップアップウィンドウが出てたらdestroyする
   if($gnPopupText ne "") {
      $gnPopupText->destroy;
      $gnPopupText = "";
   }
   if ($gnPopupImg) {
      if($gnPopupImg ne "") {
         if($gnDebugFlag) { print "linkout-img2\:" . Dumper($gnPopupImg); }
         Gtk2::Object::destroy($gnPopupImg);
         # 2段階ポップアップの段階をリセット
         $gnPopupImgSt = 1;
         if ($gnPop2) {
            $gnPop2->clear;
         }
         $gnPopupImg = "";
      }
   }
   $gnSureV->window->get_pointer;
   return(FALSE);
}

sub gnSureViewLinkRelease {
   # リンクをクリック(マウスリリース)したときに実行する関数
   # ポップアップウィンドウを消去する
   if($gnDebugFlag) { print STDERR "LinkClick\(button_release_event\)\n"; }
   if($gnDebugFlag>1) { print Dumper(@_);}
   my ($gnSureV, $gnEvent) = @_;

   my $gnHover = FALSE;
   my $gnLink = '';
   my ($x, $y) = $gnSureV->window_to_buffer_coords('widget', #GTK_TEXT_WINDOW_WIDGET,
                                                         $gnEvent->x, $gnEvent->y);
   
   my $gnSV_iter = $gnSureV->get_iter_at_location ($x, $y);
   
   foreach my $gnTag ($gnSV_iter->get_tags) {
      if($gnTag->{page}) {
         # タグの中に"page"タグを含んでいれば$gnHoverフラグをTRUEにして
         # ループを抜ける
         $gnLink = $gnTag->{page};
         $gnHover = TRUE;
         last;
      }
   }
   
   if ($gnHover) {
      # リンクの中にあったら以下の処理をする
      $gnSureV->get_window('text')->set_cursor($gnCursor_Link);
      my $gnLinkFlg = substr($gnLink, 0, 4);
            
      if($gnLinkFlg eq "resn") {
                       if($gnDebugFlag) { print STDERR "\(PressAfter-resn\)\n"; }
      }elsif($gnLinkFlg eq "repl"){
                       if($gnDebugFlag) { print STDERR "\(PressAfter-repl\)\n"; }
      }elsif($gnLinkFlg eq "datl") {
                       if($gnDebugFlag) { print STDERR "\(PressAfter-datl\)\n"; }
                       $gnLink = substr($gnLink, 7);
                       if($gnDebugFlag) { print "$gnLink\n"; }
                       my($buf1, $linktgt) =  split(/read\.cgi/, $gnLink);
                       $linktgt = &gnGetlinktgt($linktgt); #read.cgiの仕様違いを吸収
                       my ($ita, $datno, $sute) = split(/\//, $linktgt);
                       my ($tgtname, $tgturl) = split(chr(001), $gnBoardTbl{$ita});
                       
                       # 取得したdat番号がすでにFolder.idx内にあるかどうかチェック
                       my $idxfn = $logdir . $ita . "\/Folder.idx";
                       my $datfn = $datno . "\.dat";
                       if($gnDebugFlag) { print "ita=$ita, datno=$datno, sute=$sute\n"; }
                       if($gnDebugFlag) { print "tgtname=$tgtname, tgturl=$tgturl\n"; }
                       if($gnDebugFlag) { print "Folder\.idx\(fullpath\)\: $idxfn\n"; }
                       if($gnDebugFlag) { print "datfilename\: $datfn\n"; }
                       
                       if (!(-r ($logdir . $ita))) {
                       my $dirres = &gnYesNo("Folder.idxを保管するディレクトリがないのでスレが取れません\n板名\: $tgtname\nディレクトリ\: " . $logdir . $ita . "\nURL\: $tgturl\nディレクトリを作成しますか？");
                          if ($dirres eq "yes") {
                             mkdir($logdir . $ita);
                          }else{
                             &gnLogger("スレの取得をキャンセルしました");
                             last;
                          }
                       }
                       
                       if($gnDebugFlag) { print "chekking Folder.idx.\(datfn=$datfn, idxfn=$idxfn\)\n"; }
                       my $gnIsDatNameAlrdyLocalFLG = &gnIsDatNameAlrdyLocal($datfn, $idxfn);
                       
                       if ($gnIsDatNameAlrdyLocalFLG) {
                          # 既にFolder.idx内にある
                          if($gnDebugFlag) { print "already in Folder.idx\n"; }
                          
                          my $gnSS_Genre = $gnGUIxml->get_widget('gnII');
                          my $gnSS_GenreM = $gnSS_Genre->get_model;
                          my ($gnSS_GenreP, $gnSS_GenreC) = $gnSS_Genre->get_cursor;
                          my $gnSS_GenreI = $gnSS_GenreM->get_iter($gnSS_GenreP);
                          my $gnSS_Genre_now = $gnSS_GenreM->get($gnSS_GenreI, 1);

                          $gnSS_Genre_now = &gnGetGenreFromURL($gnSS_Genre_now);
                          if($gnDebugFlag) { print "$gnSS_Genre_now\n"; }

                          if ($gnSS_Genre_now ne $ita) {
                             $gnSS_GenreM->foreach(\&gnGetItaPathFromStr, $ita);
         
                             my $gnSS_SuresNow = $gnGUIxml->get_widget('gnSI');
                             $gnSS_SuresNow->scroll_to_point(0, 0);
                             $gnSS_SuresNow->set_cursor(&gnSureSelectFromDat(($ita . "\/" . $datno . "\.dat")));
                          }else{
                             my $gnSS_SuresNow = $gnGUIxml->get_widget('gnSI');
                             $gnSS_SuresNow->scroll_to_point(0, 0);
#                             $gnSS_SuresNow->set_cursor(&gnSureSelectFromDat(($ita . "\/" . $datno . "\.dat")));
                             my $gncursor = &gnSureSelectFromDat($ita . "\/" . $datno . "\.dat");
                             if($gnDebugFlag) { print Dumper($gncursor)."\n"; }
                             $gnSS_SuresNow->set_cursor($gncursor);
                          }
                          
                       }else{
                          # Folder.idxの中にはない
                          if($gnDebugFlag) { print "no entry in Folder.idx\n"; }
                          
                          # subject.txtを取得してFolder.idxを更新し再チェック
                          &gnLogger("対象の板の現在のスレ一覧を取得します");
                          my $idxdir = $logdir . $ita . "\/";
                          my $suburl = $tgturl . "subject.txt";
                          my $gnSIUFlg = &gnSureItiranUpdate($suburl, $idxdir);
                          if($gnSIUFlg == TRUE) {
                               &gnLogger("スレ一覧を更新しました");
                          }else{
                               &gnLogger("スレ一覧更新に失敗しました");
                          }
                          
                          if($gnDebugFlag) { print "chekking Folder.idx\(AGAIN\).\(datfn=$datfn, idxfn=$idxfn\)\n"; }
                          my $gnIsDatNameAlrdyLocalFLG2 = &gnIsDatNameAlrdyLocal($datfn, $idxfn);
                       
                          if ($gnIsDatNameAlrdyLocalFLG2) {
                             # 再取得の結果、既にFolder.idx内にある
                             if($gnDebugFlag) { print "\(retry\)already in Folder.idx\n"; }
                          
                             my $gnSS_Genre = $gnGUIxml->get_widget('gnII');
                             my $gnSS_GenreM = $gnSS_Genre->get_model;
                             my ($gnSS_GenreP, $gnSS_GenreC) = $gnSS_Genre->get_cursor;
                             my $gnSS_GenreI = $gnSS_GenreM->get_iter($gnSS_GenreP);
                             my $gnSS_Genre_now = $gnSS_GenreM->get($gnSS_GenreI, 1);
 
                             $gnSS_Genre_now = &gnGetGenreFromURL($gnSS_Genre_now);
                             if($gnDebugFlag) { print "$gnSS_Genre_now\n"; }

                             $gnSS_GenreM->foreach(\&gnGetItaPathFromStr, $ita);
         
                             my $gnSS_SuresNow = $gnGUIxml->get_widget('gnSI');
                             $gnSS_SuresNow->scroll_to_point(0, 0);
                             $gnSS_SuresNow->set_cursor(&gnSureSelectFromDat(($ita . "\/" . $datno . "\.dat")));
                          
                          }else{
                          
                             # 再取得してもFolder.idxの中にはない
                             # datを取得し
                             # Folder.idxにスレの情報を追加
                             &gnLogger("スレタイの取得が出来ませんでした。datの取得を試みます");
                             my ($gnGetDatRST, $gnGetDatRes) = &gnGetDat($tgturl, $datfn);
                             if ($gnGetDatRST eq "gn200OK") {
                                my $gnNow = time();
                                # 取得数を更新
                                my $gnDatNumRow = 0;
                                my $gnItaDatFN = $logdir . $ita . "\/$datfn";
                                my $gnItaDatFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaDatFN);
                                open(gnDatF2, "$gnItaDatFN_1");
                                while(<gnDatF2>) {
                                   $gnDatNumRow++;
                                }
                                close(gnDatF2);
                                my $gnFolderIdxUpdateFLG = "";
                                my $gnFolderIdxGetValFLG = "";
                             
                                my $gngmt = str2time($gnGetDatRes->header('Last-Modified'));
                                $gngmt += 32400;
                                my @pushvar = ($datfn,
                                                "",
                                                sprintf("%X", $gnDatNumRow),
                                                0,
                                                sprintf("%X", $gnDatNumRow),
                                                sprintf("%X", $gngmt),
                                                sprintf("%X", $gnDatNumRow),
                                                0,0,0,0,
                                                sprintf("%X", $gnDatNumRow),
                                                0,
                                                3
                                                );
                                &gnFolderIdxPush($idxfn, \@pushvar);
                                
                                # 取得したスレを表示
                                &gnItaSelect($ita);

                                my $gnSS_SuresNow = $gnGUIxml->get_widget('gnSI');
                                $gnSS_SuresNow->scroll_to_point(0, 0);
                                my $aaa = &gnSureSelectFromDat($ita . "\/" . $datno . "\.dat");
                                if($gnDebugFlag) { print "sureselectfromdat\: " . Dumper($aaa); }
                                $gnSS_SuresNow->set_cursor($aaa);
                             }else{
                                # datの取得に失敗
                             }
                          }
                       }
                       
         }elsif($gnLinkFlg eq "http") {
                       if($gnDebugFlag) { print STDERR "\(PressAfter-http\)\n"; }
                       #my $gnLinkURL = substr($gnLink, 7);
                       &gnJumpToURL($gnLink);
         }else{
                       if($gnDebugFlag) { print STDERR "\(PressAfter-else\)\n"; }
         }
#      }
   }else{
      if($gnDebugFlag) { print STDERR "\(PressAfter-outoflink\)\n"; }
      
   }

   $gnSureV->window->get_pointer;
   return(FALSE);
}

sub on_gnII_size_allocate {
   # カテゴリ一覧のハンドルバーが動いたら
   # 下にあるGtkHPanのハンドルバー位置を環境変数の配列に記憶
   my $gnCatHPan = $_[2];
   my $gnCatHPan_pos = $gnCatHPan->get_position;
   $gnEnvArg{'CategoryColumnWidth'}{'ID0'} = $gnCatHPan_pos;
}

sub on_gnSI_size_allocate {
   # 板一覧のハンドルバーが動いたら
   # 下にあるGtkVPanのハンドルバー位置を環境変数の配列に記憶
   my $gnItaVPan = $_[2];
   my $gnItaVPan_pos = $gnItaVPan->get_position;
   $gnEnvArg{'BoardColumnWidth'}{'ID7'} = $gnItaVPan_pos;
}


sub on_gnMH_about_activate {
   # aboutボックスの表示
   my $gnAboutxml = Gtk2::GladeXML->new($gladefn, 'gnAbout');
   $gnAboutxml->signal_autoconnect_from_package;
   my $gnAbout = $gnAboutxml->get_widget('gnAbout');
   my $gnAboutIcon = $gnAboutxml->get_widget('gnAboutIcon');
   $gnAboutIcon->set_from_pixbuf(&gnGetgnIconPixbuf());
   $gnAbout->show;
}

sub on_gnTB_bupdate_clicked {
   # 板更新
   &gnGetItaIchiran;
}

sub on_gnMF_bupdate_activate {
   # 板更新
   &gnGetItaIchiran;
}

sub on_gnSureUpdate_clicked {
   # スレ一覧更新
   #my @gnForceFLG = ("force");
   
   my $gnItaView = $gnGUIxml->get_widget('gnII');
   &gnLogger("スレ一覧を更新します");
   &gnSleIchiranSelect($gnItaView, "force");
}

sub on_gnMS_sureitiranupdate_activate {
   # スレ一覧更新
   my $gnItaView = $gnGUIxml->get_widget('gnII');
   &gnLogger("スレ一覧を更新します");
   &gnSleIchiranSelect($gnItaView, "force");

}

sub on_gnME_copy_activate {
   # メニュー[編集]-[コピー]が選択された、もしくは<Ctrl>+Cキーが押された
   my $gnNote = $gnGUIxml->get_widget('gnNote');
   my $gnNote_nowpagenum = $gnNote->get_current_page;
   if ($gnNote_nowpagenum < 0) {
      return;
   }
   
   my $gnTxtV = $gnNote->get_nth_page($gnNote_nowpagenum);
   if($gnDebugFlag) { print Dumper($gnTxtV) . "\n"; }
   if (!($gnTxtV)) {
       return;
   }else{
       $gnTxtV = $gnTxtV->child;
       if($gnDebugFlag) { print Dumper($gnTxtV) . "\n"; }
   }
   my $gnTxtBuf = $gnTxtV->get_buffer;
   my $gnTxtClip = $gnTxtV->get_clipboard();
   $gnTxtBuf->copy_clipboard($gnTxtClip);
   
}

sub on_gnMF_quit_activate {
   # 終了処理
   &gnExit;
}


sub on_gnAB_ok_clicked {
   # Aboutダイアログの「閉じる」ボタンがクリックされたら
   # ダイアログボックスを閉じる
   my $gnAbout = $_[1];
   $gnAbout->destroy;
   $gnAbout = undef;
}

sub on_gnAbout_close {
   # ダイアログボックスを閉じる
   my $gnAbout = $_[0];
   $gnAbout->destroy;
   $gnAbout = undef;
}


sub on_gnMS_Env_activate {
   # メニューから「設定」が選択されたので
   # 環境設定ウィンドウを開く
   $gnEnvXML = Gtk2::GladeXML->new($gladefn, 'gnEnv');
   $gnEnvXML->signal_autoconnect_from_package('main');

   my $gnEnv = $gnEnvXML->get_widget('gnEnv');
   
   my $mW = $gnGUIxml->get_widget('mW');
   $gnEnv->set_transient_for($mW);
   $gnEnv->set_modal(TRUE);

   $gnEnv->show_all;
}



sub on_gnEnvNote_switch_page { } # ページが変わっても別に何もしない

sub on_eventbox2_button_release_event {
   # ポップアップウィンドウが出てたらdestroyする
   if($gnDebugFlag) { print "button_press_event\n"; }
   my $evwidget = $_[0];
   if($gnDebugFlag) { print Dumper($evwidget) . "\n"; }
   my $evbutton = $_[1];
   my $evbtnum = $evbutton->button;
   my $evwindow = $evwidget->get_parent_window;
   if($gnDebugFlag) { print Dumper($evwindow) . "\n"; }

   if($evwindow) {
      if($evbtnum == 3) {
         # 右クリックならポップアップメニューを表示
         
         $gnIPxml = Gtk2::GladeXML->new($gladefn, 'gnIP');
         $gnIPxml->signal_autoconnect_from_package('main');
         my $gnIP = $gnIPxml->get_widget('gnIP');
         # 【俺メモ】画像がキャッシュしきれてなければ「画像に名前をつけて保存」は
         # disableすること
         $gnIP->popup(undef,undef,undef,undef,undef,undef);
         $gnIP->set_parent_window($evwindow);
      }else{
         # 右クリック以外なら
         
         # 2段階ポップアップが無効
         # もしくは有効で今が2段目ならウィンドウを閉じる
         if (($gnEnvArg{'Browser'}{'PreviewVisibleSt2'} == 1) && ($gnPopupImgSt == 1)) {
            $gnPopupImgSt = 2;
            
            &gnImgBufferUpdate($gnPopupImg, $gnPop2);

         }else{
         
            $evwindow->destroy;
            $gnPopupImg = "";
            $gnPop2->clear;
            $gnPopupImgSt = 1;
      
            # timeout関数をremoveする。
            # removeしないと次のポップアップが出ない
            if ($gnPopupCallback) {
               Glib::Source->remove($gnPopupCallback);
               undef($gnPopupCallback);
            }
            if ($gnImgUpdateCallback) {
               Glib::Source->remove($gnImgUpdateCallback);
               undef($gnPopupCallback);
            }
         }
      }
   }

}

sub on_gnIS_pattern_changed {
  # 「画像を名前をつけて保存」ダイアログボックスで
  # 保存パターンを変更した
  my $gnIS_pattern = $_[0];
  my $gnIS_ptnstr = $gnIS_pattern->get_text;
  my @ptnar = split("\%", $gnIS_ptnstr);
  my $outbuf = "";
  foreach my $val (@ptnar) {
     my $flg = substr($val, 0, 1);
     if($flg eq "I") {
        $outbuf .= $gnImgPopup_info{'ItaName'};
     }elsif($flg eq "i") {
        $outbuf .= $gnImgPopup_info{'ItaDir'};
     }elsif($flg eq "T") {
        $outbuf .= $gnImgPopup_info{'SureName'};
     }elsif($flg eq "t") {
        $outbuf .= $gnImgPopup_info{'DatNo'};
     }elsif($flg eq "F") {
        $outbuf .= $gnImgPopup_info{'URLFN'};
     }elsif($flg eq "f") {
        $outbuf .= $gnImgPopup_info{'URLExt'};
     }elsif($flg eq "Y") {
        $outbuf .= $gnImgPopup_info{'Year'};
     }elsif($flg eq "M") {
        $outbuf .= $gnImgPopup_info{'Month'};
     }elsif($flg eq "D") {
        $outbuf .= $gnImgPopup_info{'Day'};
     }elsif($flg eq "h") {
        $outbuf .= $gnImgPopup_info{'Hour'};
     }elsif($flg eq "m") {
        $outbuf .= $gnImgPopup_info{'Minute'};
     }elsif($flg eq "s") {
        $outbuf .= $gnImgPopup_info{'Second'};
     }elsif($flg eq '/') {
        $outbuf .= '/';
     }else{
        $outbuf .= $flg;
     }
     
     $outbuf .= substr($val, 1);
     
  }
  
  my $gnIS_fn = $gnISxml->get_widget('gnIS_fn');
  $gnIS_fn->set_text($outbuf);
}

sub on_gnIP_saveas_activate {
  # 画像のポップアップ表示からのポップアップメニュー
  # 「画像を名前をつけて保存」を選択

  $gnISxml = Gtk2::GladeXML->new($gladefn, 'gnIS');
  $gnISxml->signal_autoconnect_from_package('main');
  my $gnIS = $gnISxml->get_widget('gnIS');

  # 画像保存の親ディレクトリをセット
  my $gnIS_dir = $gnISxml->get_widget('gnIS_dir');
  if ($gnCacheTbl{'SavePattern'}{'BaseDir'} ne "") {

     # setするときはURIにエンコードして渡してあげないと、日本語を含むファイルパスのときエラーになる
     my $fld = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnCacheTbl{'SavePattern'}{'BaseDir'});
     $fld = URI::file->new($fld);
     if($gnDebugFlag) { print Dumper($fld);}
     $gnIS_dir->set_current_folder_uri($fld);

# なぜかこうすると日本語を含むパスを入れるとエラーになる。バグ？
#     my $fld = Encode::encode('euc-jp', $gnCacheTbl{'SavePattern'}{'BaseDir'});
#     $gnIS_dir->set_current_folder($fld);
  }
  
  # 保存オプションを読み込み
  my $gnISkeepopt = $gnISxml->get_widget('gnIS_keepopt_thread');
  my $gnISkeepopt_arref = $gnISkeepopt->get_group;
  my @gnISkeepopt_ar = @$gnISkeepopt_arref;
  
  my $gnIS_pattern = $gnISxml->get_widget('gnIS_pattern');
  my $gnIS_ptntgt = $gnImgPopup_info{'ItaDir'} . chr(001) . $gnImgPopup_info{'DatNo'};
  if ($gnCacheTbl{'SavePattern'}{$gnIS_ptntgt} ne "") {
     # スレ共通設定を読み込み
     $gnIS_pattern->set_text($gnCacheTbl{'SavePattern'}{$gnIS_ptntgt});
     $gnISkeepopt_ar[3]->set_active(1);
  }elsif ($gnCacheTbl{'SavePattern'}{$gnImgPopup_info{'ItaDir'}} ne "") {
     # 板共通設定を読み込み
     $gnIS_pattern->set_text($gnCacheTbl{'SavePattern'}{$gnImgPopup_info{'ItaDir'}});
     $gnISkeepopt_ar[2]->set_active(1);
  }elsif ($gnCacheTbl{'SavePattern'}{'global'} ne "") {
     # 全体共通設定を読み込み
     $gnIS_pattern->set_text($gnCacheTbl{'SavePattern'}{'global'});
     $gnISkeepopt_ar[1]->set_active(1);
  }else{
     # その他の場合はデフォルト値(ファイル名のみ)をセット
     $gnIS_pattern->set_text('%F.%f');
     $gnISkeepopt_ar[0]->set_active(1);
  }
  $gnIS->show_all;
  
}

sub on_gnIP_reload_activate {
  # 画像のポップアップ表示からのポップアップメニュー
  # 「再読み込み」を選択
   my $evwidget = $_[0];
   
   # キャッシュファイルを削除
   &gnLogger("キャッシュを廃棄し再読み込みします");
   unlink($gnTmpfn);
   
#   if (!($gnImgUpdateCallback)) {
   # 画像バッファのアップデート関数を再度セット
   $gnImgUpdateCallback = Glib::Timeout->add($gnEnvArg{'Browser'}{'PreviewWait'},
                                                       sub {
                                                                        my $retval;
                                                                        my $ref = &gnImgBufferUpdate($gnPopupImg, $gnPop2);
                                                                        ($gnPopupImg, $gnPop2, $retval) = @$ref;
                                                                        return($retval);
                                                                        },
                                                                        undef,
                                                                        0);
                        
#   }

   
   # runnnerスレッドに所定のURL読み込みをキューイングする
   $gnURLTo = $gnImgPopup_info{'URL'};
   my $uid;
   my $pass;
   if (!($gnEnvArg{'ReadProxy'}{'UserID'})) { $uid = "-1";}
   if (!($gnEnvArg{'ReadProxy'}{'Password'})) { $pass = "-1";}      
   $queue->enqueue("geturl", $gnURLTo, $gnTmpfn, $gnEnvArg{'ReadProxy'}{'Proxy'},
                        $gnEnvArg{'ReadProxy'}{'Address'}, $gnEnvArg{'ReadProxy'}{'Port'},
                        $uid, $pass);
   my $cachetime = time;
   $gnCacheTbl{'Cache'}{$gnURLTo} = $cachetime . chr(001) . $gnTmpfn;

}

sub on_gnIP_URLc_activate{
  # 画像のポップアップ表示からのポップアップメニュー
  # 「URLをコピー」を選択
}

sub on_gnIS_save_clicked {
  # 画像を名前をつけて保存ダイアログで保存ボタンが押された
  my $gnIS_save = $_[0];
  my $gnIS = $gnIS_save->parent->parent->parent->parent;
  
  # キャッシュファイルを指定場所にコピー
  my $gnIS_dir = $gnISxml->get_widget('gnIS_dir');
  if ($gnDebugFlag) { print Dumper($gnIS_dir) . "\n";}
  my $gnIS_basedir = $gnIS_dir->get_current_folder;
  $gnIS_basedir = Glib::filename_display_name($gnIS_basedir);
#  my $gnIS_basedir2 = $gnIS_dir->get_current_folder_uri;
#  if ($gnDebugFlag) { print "get_current_folder " . Dumper($gnIS_basedir2); }
#  $gnIS_basedir = &env_gtkstr_to_gtkstr($gnIS_basedir);
  
  my $gnIS_fn = $gnISxml->get_widget('gnIS_fn');
  my $gnIS_fnstr = $gnIS_fn->get_text;
  $gnIS_fnstr = &env_gtkstr_to_gtkstr($gnIS_fnstr);

  my @gnIS_fnar = split(/\//, $gnIS_fnstr);
  my $gnIS_fname = pop(@gnIS_fnar);
  
  $gnIS_fnstr = $gnIS_basedir . "\/" . $gnIS_fnstr;
  my $gnIS_path = dirname($gnIS_fnstr);
  
  if ($gnDebugFlag) { print "dir\: $gnIS_path\n";
                        print "filename\: $gnIS_fname\n";
                        print "full\: $gnIS_fnstr\n";}
  
  my $gnIS_path2 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnIS_path);
  if (!(-d $gnIS_path2)) {
     eval{ mkpath($gnIS_path2) };
     if ($@) {
        my $str = "ディレクトリの作成に失敗しました\: " . $gnIS_path;
        &gnLogger($str);
     }else{
        my $str = "ディレクトリの作成しました\: " . $gnIS_path;
        &gnLogger($str);
     }
  }
  
  my $gnIS_fnstr2 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnIS_fnstr);
  my $gnCachefn2 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), &env_gtkstr_to_gtkstr($gnImgPopup_info{'CacheFN'}));
  if (-e $gnIS_fnstr2) {
     if ($gnDebugFlag) { print "already exist\: $gnIS_fnstr2\n"; }
     my $flg = &gnYesNo("ファイルが既に存在します。\n上書きしますか？\n$gnIS_fnstr");
     if ($flg ne "yes") {
        $gnIS->destroy;
        return;
     }
  }else{
     if ($gnDebugFlag) { print "new file\: $gnIS_fnstr2\n"; }
  }
  eval { copy($gnCachefn2, $gnIS_fnstr2) };
  if ($@) {
     if ($gnDebugFlag) { print "file save failed\n"; }
     my $str = "ファイルの作成に失敗しました\: $gnIS_fnstr";
     &gnLogger($str);
  }else{
     if ($gnDebugFlag) { print "file save succeed\n"; }
     my $str = "ファイルを保存しました\: $gnIS_fnstr";
     &gnLogger($str);
  }
  
  # 保存オプションをセーブ
  $gnCacheTbl{'SavePattern'}{'BaseDir'} = $gnIS_basedir;
  
  my $gnIS_pattern = $gnISxml->get_widget('gnIS_pattern');
  my $gnISkeepopt = $gnISxml->get_widget('gnIS_keepopt_thread');
  my $gnISkeepopt_arref = $gnISkeepopt->get_group;
  my @gnISkeepopt_ar = @$gnISkeepopt_arref;
  if ($gnISkeepopt_ar[3]->get_active == 1) {
     # スレ共通設定として保存
     my $tgt = $gnImgPopup_info{'ItaDir'} . chr(001) . $gnImgPopup_info{'DatNo'};
     $gnCacheTbl{'SavePattern'}{$tgt} = $gnIS_pattern->get_text;
  }elsif ($gnISkeepopt_ar[2]->get_active == 1) {
     # 板共通設定として保存
     my $tgt = $gnImgPopup_info{'ItaDir'};
     $gnCacheTbl{'SavePattern'}{$tgt} = $gnIS_pattern->get_text;
  }elsif ($gnISkeepopt_ar[1]->get_active == 1) {
     # 全体設定として保存
     $gnCacheTbl{'SavePattern'}{'global'} = $gnIS_pattern->get_text;
  }else {
     # 保存しない
  }
  
  $gnIS->destroy;
}

sub on_gnIS_cancel_clicked {
   # 画像を名前をつけて保存ダイアログでキャンセルボタンが押された
   my $gnIS_cancel = $_[0];
   my $gnIS = $gnIS_cancel->parent->parent->parent->parent;
   $gnIS->destroy;
}

sub on_gnIS_expander_activate {
   my $gnIS_expander = $_[0];
   my $gnIS = $gnIS_expander->parent->parent->parent;

   if ($gnIS_expander->get_expanded) {
    $gnIS->set_resizable(0);
   }else{
    $gnIS->set_resizable(1);  
   }
}

sub on_gnTBS_supdate_clicked {
   # メニューボタンからスレを更新
   my $gnSureView = $gnGUIxml->get_widget('gnSI');
   &gnSureUpdate($gnSureView);
}

sub on_gnMS_sureupdate_activate {
   # メニューからスレを更新
   my $gnSureView = $gnGUIxml->get_widget('gnSI');
   &gnSureUpdate($gnSureView);
}

sub on_gnNote_switch_page {
   # スレ本文を表示するNotebookのタブを切り替えた
   if($gnDebugFlag) { print "Switch Page\n"; }
   &gnSureSwitch;
}

sub on_gnWM_close_activate {
   # 書き込みウィンドウで、メニューの閉じるを選択したか、<Ctrl>+Wキーを押した
   my $gnW = $gnWxml->get_widget('gnWrite');
   $gnW->destroy;
}

sub on_gnW_cutB_clicked {
   &on_gnWE_cut_activate();
}

sub on_gnWE_cut_activate {
   # 書き込みウィンドウで、メニューの切り取りを選択したか、切り取りボタンを押したか、
   # <Ctrl>+Xキーを押した
   my $gnWcont = $gnWxml->get_widget('gnW_cont');
   my $gnWcontBuf = $gnWcont->get_buffer;
   if (!($gnWcontBuf)) {
      return;
   }
   my $gnTxtClip = $gnWcont->get_clipboard();
   $gnWcontBuf->cut_clipboard($gnTxtClip, TRUE);
}

sub on_gnW_copyB_clicked {
   &on_gnWE_copy_activate();
}

sub on_gnWE_copy_activate {
   # 書き込みウィンドウで、メニューのコピーを選択したか、コピーボタンを押したか、
   # <Ctrl>+Cキーを押した
   my $gnWcont = $gnWxml->get_widget('gnW_cont');
   my $gnWcontBuf = $gnWcont->get_buffer;
   if (!($gnWcontBuf)) {
      return;
   }
   my $gnTxtClip = $gnWcont->get_clipboard();
   $gnWcontBuf->copy_clipboard($gnTxtClip);
}

sub on_gnW_pasteB_clicked {
   &on_gnWE_paste_activate();
}

sub on_gnWE_paste_activate {
   # 書き込みウィンドウで、メニューの張り付けを選択したか、張り付けボタンを押したか、
   # <Ctrl>+Vキーを押した
   my $gnWcont = $gnWxml->get_widget('gnW_cont');
   my $gnWcontBuf = $gnWcont->get_buffer;
   if (!($gnWcontBuf)) {
      return;
   }
   my $gnWcontBuf_iter = $gnWcontBuf->get_end_iter;

   my $gnTxtClip = $gnWcont->get_clipboard();

   $gnWcontBuf->paste_clipboard($gnTxtClip, undef, TRUE);

}

sub on_gnW_qpasteB_clicked {
   &on_gnWE_qpaste_activate();
}

sub on_gnWE_qpaste_activate {
   # 書き込みウィンドウで、メニューの引用符付張り付けを選択したか、引用符ボタンを押したか、
   # <Ctrl>+<Shift>+Vキーを押した
   my $gnWcont = $gnWxml->get_widget('gnW_cont');
   my $gnWcontBuf = $gnWcont->get_buffer;
   if (!($gnWcontBuf)) {
      return;
   }

   my $gnTxtClip = $gnWcont->get_clipboard();
   
   my $gnTmpTxtBuf = Gtk2::TextBuffer->new(undef);
   $gnTmpTxtBuf->paste_clipboard($gnTxtClip, undef, TRUE);
   my $gnTmpTxtBuf_start = $gnTmpTxtBuf->get_start_iter;
   my $gnTmpTxtBuf_end = $gnTmpTxtBuf->get_end_iter;
   my $gnTmpTxt = $gnTmpTxtBuf->get_text($gnTmpTxtBuf_start, $gnTmpTxtBuf_end, TRUE);
   if($gnTmpTxt ne "") {
      $gnTmpTxt =~ s/\n/\n> /g;
      $gnTmpTxt = "> " . $gnTmpTxt;
   }
   if ($gnDebugFlag) {
      print("paste-with-quote\: \n" . $gnTmpTxt . "\n");
   }

   $gnWcontBuf->insert_at_cursor($gnTmpTxt);

}

sub on_gnWE_delete_activate {
   # 書き込みウィンドウで、メニューの削除を選択した
   my $gnWcont = $gnWxml->get_widget('gnW_cont');
   my $gnWcontBuf = $gnWcont->get_buffer;
   if (!($gnWcontBuf)) {
      return;
   }

   $gnWcontBuf->delete_selection(TRUE, TRUE);

}

sub on_gnW_sage_toggled {
   # 書き込みウィンドウで、sageチェックボックスをトグルした
   my $gnSage = $_[0];
   my $gnW_mail = $gnWxml->get_widget('gnW_mail');
   if ($gnSage->get_active) {
      $gnW_mail->child->set_text("sage");
      $gnW_mail->set_sensitive(FALSE);
   }else{
      $gnW_mail->child->set_text("");
      $gnW_mail->set_sensitive(TRUE);
   }
}

sub on_gnW_SubmitBtn_clicked {
   # 書き込みウィンドウの送信ボタンが押された
   &gnSubmit;
}
sub on_gnWM_submit_activate {
   # 書き込みウィンドウのメニュー[ファイル]-[送信]が選択された
   &gnSubmit;
}

sub on_gnWrite_unrealize {
   # 書き込みウィンドウが閉じた
      # 終了時のウィンドウ位置、大きさを記憶
      my $gnWrite = $gnWxml->get_widget('gnWrite');
      ($gnEnvArg{'EditorWindowSize'}{'Left'}, $gnEnvArg{'EditorWindowSize'}{'Top'}) = Gtk2::Gdk::Window::get_position($gnWrite->window);
      my $sute;
      ($sute, $sute,
       $gnEnvArg{'EditorWindowSize'}{'Width'},
       $gnEnvArg{'EditorWindowSize'}{'Height'},) = Gtk2::Gdk::Window::get_geometry($gnWrite->window);

   $gnWxml = "";
}

sub on_gnPopupText_check_resize {
   # ポップアップ画面がウィンドウからはみ出している場合は
   # 出現位置を調整
   my $gnPopupTxt = $_[0];
   my ($gnPopupWinX, $gnPopupWinY) = $gnPopupTxt->get_position;
   my ($gnResizedWidth, $gnResizedHeight) = $gnPopupTxt->get_size;
   my $dis = Gtk2::Gdk::Display->get_default;
   my ($scr, $x, $y, $mask) = $dis->get_pointer;
   my ($wid, $hei) = $gnPopupTxt->get_size;
   if (($x + int($wid/2)) >= Gtk2::Gdk->screen_width) { $gnPopupWinX = $x + int($wid/2); }
   if (($y + int($hei/2)) >= Gtk2::Gdk->screen_width) { $gnPopupWinY = $y + int($hei/2); }
      
   # ポップアップウィンドウのoriginが
   # ポップアップ表示禁止領域内だったら表示領域外に移動
   if ($gnPopupWinX <= $gnEnvArg{'Browser'}{'AvoidBorderLeft'}) {
      $gnPopupWinX = $gnEnvArg{'Browser'}{'AvoidBorderLeft'} + 1;
   }
   if ($gnPopupWinY <= $gnEnvArg{'Browser'}{'AvoidBorderTop'}) {
      $gnPopupWinY += $gnEnvArg{'Browser'}{'AvoidBorderTop'} + 1;
   }
   
   # ポップアップ画面がウィンドウからはみ出している場合は
   # 出現位置を調整
      
   if ($gnResizedWidth > 0) {
      my $gnScreenHeight = Gtk2::Gdk->screen_height;
      my $gnScreenWidth = Gtk2::Gdk->screen_width;
      if($gnDebugFlag) { print "gnPopupTxt-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)\n"; }
      if($gnDebugFlag) { print "gnPopupTxt-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)\n"; }
      if (($gnPopupWinX + $gnResizedWidth + $gnEnvArg{'Browser'}{'AvoidBorderRight'}) > $gnScreenWidth) { $gnPopupWinX = $gnScreenWidth - $gnResizedWidth - $gnEnvArg{'Browser'}{'AvoidBorderRight'}; }
      if (($gnPopupWinY + $gnResizedHeight + $gnEnvArg{'Browser'}{'AvoidBorderBottom'}) > $gnScreenHeight) { $gnPopupWinY = $gnScreenHeight - $gnResizedHeight - $gnEnvArg{'Browser'}{'AvoidBorderBottom'}; }
      $gnPopupTxt->move($gnPopupWinX, $gnPopupWinY);
      if($gnDebugFlag) { print "gnPopupTxt-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)"; }

      }

#   my $gnPopupTxt = $_[0];
#   my ($gnPopupWinX, $gnPopupWinY) = $gnPopupTxt->get_position;
#   my ($gnResizedWidth, $gnResizedHeight) = $gnPopupTxt->get_size;
#   my $gnScreenHeight = Gtk2::Gdk->screen_height;
#   my $gnScreenWidth = Gtk2::Gdk->screen_width;
#   if($gnDebugFlag) { print "gnResPopup-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)\n"; }
#   if (($gnPopupWinX + $gnResizedWidth) > $gnScreenWidth) { $gnPopupWinX = $gnScreenWidth - $gnResizedWidth; }
#   if (($gnPopupWinY + $gnResizedHeight) > $gnScreenHeight) { $gnPopupWinY = $gnScreenHeight - $gnResizedHeight; }
#   
#   $gnPopupTxt->move($gnPopupWinX, $gnPopupWinY);
#   if($gnDebugFlag) { print "gnResPopup-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)"; }
}

#--------------------------------------
# カスタム関数(gnview内部用)
#--------------------------------------

sub gnSubmit {
   # レスを送信する関数
   #
   # 引数: なし
   
   my $gnW = $gnWxml->get_widget('gnWrite');
   # 名前を取得
   my $gnWname = $gnWxml->get_widget('gnW_name');
   my $gnFROM = $gnWname->child->get_text;
   if($gnDebugFlag) { print "submit-FROM\: $gnFROM\n"; }
   # メールアドレスを取得
   my $gnWmail = $gnWxml->get_widget('gnW_mail');
   my $gnMAIL = $gnWmail->child->get_text;
   if($gnDebugFlag) { print "submit-mail\: $gnMAIL\n"; }
   # 本文を取得
   my $gnWcont = $gnWxml->get_widget('gnW_cont');
   my $gnWcont_buf = $gnWcont->get_buffer;
   my $gnMESSAGE = $gnWcont_buf->get_text($gnWcont_buf->get_start_iter, $gnWcont_buf->get_end_iter, FALSE);
   
   # 波ダッシュ問題対応１：UTF8の全角チルダ、波ダッシュを変換用の文字に変換
   $gnMESSAGE = Encode::encode('utf8', $gnMESSAGE);
   $gnMESSAGE =~ s/\xEF\xBD\x9E/gnmychilder/g;
   $gnMESSAGE =~ s/\xE3\x80\x9C/gnmychilder/g;
   $gnMESSAGE = Encode::decode('utf8', $gnMESSAGE);
   
   if($gnDebugFlag) { print "submit-cont\: $gnMESSAGE\n"; }
   
   # 本文がない場合はエラー
   if (!($gnMESSAGE)) {
      &gnWarn("本文がないと投稿できません");
      $gnW->present;
      return;
   }
   
   # 投稿時に必要なキー
   substr($gnW->get_title, rindex($gnW->get_title, "\(")+1) =~ m/\d{9,}/;
   my $gnkey = $&;
   my $gnbbs = $`;									# ` gedit誤認識対策用コメント
   chop($gnbbs);
   if($gnDebugFlag) { print "submit-bbs\: $gnbbs\n"; }
   if($gnDebugFlag) { print "submit-key\: $gnkey\n"; }
   
   # 投稿時間
   my $gntime = $gnKeyTime;
   if ($gnEnvArg{'2ch'}{'time'}) {
      $gntime = $gnEnvArg{'2ch'}{'time'};
   }
   if($gnDebugFlag) { print "submit-time\: $gntime\n"; }
   
   # 板名とドメイン名取得
   my $gndomain = $gnBoardTbl{$gnbbs};
   my $sep = chr(001);
   $gndomain =~ m/$sep/;
   $gndomain = $';									# ' gedit誤認識対策用コメント
   $gndomain = substr($gndomain, 7);
   $sep = q{(?:(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.)} .
           q{*[a-zA-Z](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.?|[0-9]+\.[0-9]+\.[0-9]+\.} .
           q{[0-9]+)(?::[0-9]*)?};
   $gndomain =~ m/$sep/;
   $gndomain = $&;
   if($gnDebugFlag) { print "domainname\: $gndomain\n"; }

   # クッキーがあれば取り込み
   my $gnFolIniFN = $logdir . $gnbbs . "\/Folder.ini";
   if($gnDebugFlag) { print "folderini\: $gnFolIniFN\n"; }
   my $gnPON;
   my $gnPONTime;
   my $gnHAP;
   my %gnFI;
   my $gnFolIniFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnFolIniFN);
   if (!(-e $gnFolIniFN)) {
      &gnMakeFolderIni($gnFolIniFN);
      &gnLogger("Folder.iniを作成しました\($gnFolIniFN\)");
   }
      my $gnFIRef = &gnLoadEnv($gnFolIniFN);
      %gnFI = %$gnFIRef;
      if ($gnFI{'Cookie'}{'PON'}) {
        if($gnFI{'Cookie'}{'PON'} ne "") {
         $gnPON = $gnFI{'Cookie'}{'PON'};
         my($a, $b) = split(/ /, $gnFI{'Cookie'}{'Expires'});
         my($y, $m, $d) = split(/\//, $a);
         my($hh, $mm, $ss) = split(/:/, $b);
         $m -= 1;
         $gnPONTime = timegm($ss, $mm, $hh, $d, $m, $y);
         $gnPONTime -= time;
        }
      }
      if ($gnFI{'Cookie'}{'Cookie=HAP'}) {
         $gnHAP = $gnFI{'Cookie'}{'Cookie=HAP'};
         chop($gnHAP);
      }
   if($gnDebugFlag) { print "PON\: $gnPON, HAP\: $gnHAP\n"; }
   
   # リクエスト組み立て
   my $gnSUA = LWP::UserAgent->new;
   my $gnSCo = HTTP::Cookies->new;
   $gnSUA->agent($gnUsrAgent);
   $gnSUA->requests_redirectable([]);
   if($gnEnvArg{'WriteProxy'}{'Proxy'} == 1) {
      my $gnSUA_proxystr = "http\:\/\/" . $gnEnvArg{'WriteProxy'}{'Address'} . "\:" . $gnEnvArg{'WriteProxy'}{'Port'} . "\/";
      $gnSUA->proxy('http', $gnSUA_proxystr);
   }
   $gnSCo->set_cookie(1, "NAME", Encode::encode('cp932', $gnFROM), "\/", $gndomain, 80, 0, 0, 2592000, 0);
   $gnSCo->set_cookie(1, "MAIL", Encode::encode('cp932', $gnMAIL), "\/", $gndomain, 80, 0, 0, 2592000, 0);
   $gnSCo->set_cookie(1, "hana", Encode::encode('cp932', "mogera"), "\/", $gndomain, 80, 0, 0, undef, 0);
   if ($gnPON) {
      $gnSCo->set_cookie(0, "PON", Encode::encode('cp932', $gnPON), "\/", $gndomain, 80, 0, 0, $gnPONTime, 0);
   }else{
      $gnSCo->set_cookie(0, "PON", Encode::encode('cp932', ""), "\/", $gndomain, 80, 0, 0, $gnPONTime, 0);
   }
   if ($gnHAP) {
      $gnSCo->set_cookie(0, "HAP", Encode::encode('cp932', $gnHAP), "\/", $gndomain, 80, 0, 0, $gnPONTime, 0);
   }else{
      $gnSCo->set_cookie(0, "HAP", Encode::encode('cp932', ""), "\/", $gndomain, 80, 0, 0, $gnPONTime, 0);
   }
   $gnSUA->cookie_jar($gnSCo);
   if ($gnDebugFlag) { print "request-cookies\: " . Dumper($gnSCo->as_string) . "\n"; }
   
   my $gnSRqURL = "http\:\/\/" . $gndomain . "\/test\/bbs.cgi\?UTF-8";
   my $gnSRq = HTTP::Request->new(POST => $gnSRqURL);
   $gnSRq->content_type("application/x-www-form-urlencoded");
   my $gnSRq_ref = "http\:\/\/" . $gndomain . "\/test\/read.cgi\/$gnbbs\/$gnkey\/l50";
   $gnSRq->header(Referer => $gnSRq_ref, 
                         Accept_Language => "ja",
                         Accept_Charset => "Shift-JIS");
   my $gncont = "submit=" . "書き込む" . "\&FROM=" . $gnFROM . "\&mail=" . $gnMAIL . "\&MESSAGE=" . $gnMESSAGE . "\&bbs=" . $gnbbs . "\&key=" . $gnkey . "\&time=1";
   $gncont = Encode::encode('cp932', $gncont);
   
   # 波ダッシュ問題対応2：変換用文字をShiftjisの全角チルダに変換
   $gncont =~ s/gnmychilder/\x81\x60/g; 
   
   $gncont = URI->new($gncont);
   $gnSRq->content($gncont);
   if ($gnDebugFlag) { print "POST Content\: \n"; }
   if ($gnDebugFlag) { print Dumper($gnSUA) . "\n" . Dumper($gnSRq); }
   
   my $response = $gnSUA->request($gnSRq);
   if ($gnDebugFlag) { print "response\:\n" . Dumper($response) . "\n" . Encode::decode('cp932', $response->content) . "\n"; }
   
   # レスポンスをゲット
   my $gnRes_cont = Encode::decode('cp932', $response->content);
   my $gnRes = HTML::TokeParser->new(\$gnRes_cont);
   my $gnRes_txt;
   my $gn2chXflg;
   my $gnRes_title = FALSE;
   my %gn2chForm;
   while (my $gnRes_tkn = $gnRes->get_token) {
      my $flg = $gnRes_tkn->[0];
      my $tag = $gnRes_tkn->[1];
      
      if ($flg eq "C") {
         
         if ($gnDebugFlag) { print "response-comment\: " . $tag . "\n"; }
         if (substr($tag, 5, 5) eq "2ch_X") {
            # 2ch_Xステータスを取得
            $gn2chXflg = substr($tag, 11, 4);
         }
      }
      if ($flg eq "T") {
         if ($gnRes_title == TRUE) {
            if ($tag eq "ＥＲＲＯＲ！") {
               $gn2chXflg = "erro";
            }
         }else{
            $gnRes_txt .= $tag;
         }
      }
      if (($flg eq "S") && ($tag eq "br")) {
         $gnRes_txt .= "\n";
      }
      if (($flg eq "S") && ($tag eq "input")) {
         if ($gnDebugFlag) { print "response-input\: " . Dumper($gnRes_tkn->[2]) . "\n"; }
      }
      if (($flg eq "S") && ($tag eq "title")) {
         $gnRes_title = TRUE;
      }
      if (($flg eq "E") && ($tag eq "title")) {
         $gnRes_title = FALSE;
      }
      
   }
   
   
   # 2ch_Xステータスを元に処理を分岐
   my $gn2chXflg_res = FALSE;
   if ($gn2chXflg) {
      if ($gnDebugFlag) { print "2ch_X-flag \: $gn2chXflg\n"; }
      if ($gn2chXflg eq "cook") {
         # クッキー確認
         my $gnCook = &gnYesNo($gnRes_txt . "\n\nクッキーを設定しますか？");
         if($gnCook eq "yes") {
            $gnSCo->extract_cookies($response);
            if ($gnDebugFlag) { print scalar localtime(time) . "\n"; }
            if ($gnDebugFlag) { print "response-cookies\: " . Dumper($gnSCo->as_string) . "\n"; }
            &gnSetCookie($gnFolIniFN, $gnSCo, \%gnFI);
         
            &gnWarn("クッキーを設定しました。再度送信ボタンを押して投稿してください");
            $gnW->present;
            
         }
      }elsif($gn2chXflg eq "erro") {
        # エラー
        &gnWarn(decode_entities($gnRes_txt));
        
      }elsif($gn2chXflg eq "fals") {
        # 注意付き書き込み成功
        &gnWarn(decode_entities($gnRes_txt));
        $gn2chXflg_res = TRUE;
      }elsif($gn2chXflg eq "fals") {
        # 正常終了
        $gn2chXflg_res = TRUE;
      }else{
        #未対応(今のところ2ch_X:checkは未対応
        &gnWarn(decode_entities($gnRes_txt) . "\n現在のところ\(バージョン$gnVersion\)未対応です\n");
      }
   }else{
      # 2ch_Xステータスがなかったら成功と見なす
      $gn2chXflg_res = TRUE;
   }
   
   # 書き込み成功時の処理
   if ($gn2chXflg_res == TRUE) {
      substr($gnW->get_title, 5) =~ m/\s+\-\s+/;
      my $title = $`;									# `gnview誤認識対策コメント
      $title = substr($title, index($title, ":")+1);
      &gnLogger("書き込みが完了しました\($title\)");
      
      # sent.ini に履歴を書き込み
      my $gnEnvMSentIni = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnInitCfg{'Main'}{'sent.ini'});
      if (-e $gnEnvMSentIni) {
         open(gnSIN, ">>$gnEnvMSentIni");
            print gnSIN "[" . time ."]\n";
            print gnSIN "Name=" . Encode::encode('cp932', $gnFROM) . "\n";
            print gnSIN "EMail=" . Encode::encode('cp932', $gnMAIL) . "\n";
            my $gnSIN_mes = URI->new(Encode::encode('cp932', $gnMESSAGE));
            $gnSIN_mes = $gnSIN_mes->as_string;
            print gnSIN "Body=" . $gnSIN_mes . "\n";
            print gnSIN "Status=200\n";
            my @now = localtime;
            my $gnSIN_time = ($now[5]+1900) . "\/" . sprintf("%.2d", ($now[4]+1)) . "\/" . sprintf("%.2d", $now[3]) . " " .
                               sprintf("%.2d", $now[2]) . ":" . sprintf("%.2d", $now[1]) . ":" . sprintf("%.2d", $now[0]);
            print gnSIN "Date=$gnSIN_time\n";
            print gnSIN "Title=" . Encode::encode('cp932', $title) . "\n";
            print gnSIN "URL=$gnSRq_ref\n";
            print gnSIN "Key=$gnkey\n";
            print gnSIN "\n";
         close(gnSIN);
         
      }
      $gnW->destroy;
      return(0);
   }
   
   

}

sub gnSetCookie {
   # 書き込み用のクッキーを保存する関数
   #
   # 引数: 1: 文字列(書き込み先のFolder.iniのフルパス)
   #       2: HTTP::Cookiesオブジェクト
   #       3: Folder.iniの内容をgnLoadEnvで読み取ったハッシュ配列へのリファレント
   # 返り値: なし
   my $gnFIFN = $_[0];
   my $gnHCo = $_[1];
   my $gnFIRef = $_[2];
   my %gnFI = %$gnFIRef;
   if ($gnDebugFlag) { print "gnSetCookie-Folder.ini\: $gnFIFN\n"; }
   if ($gnDebugFlag) { print "gnSetCookie-cookie\: " . Dumper($gnHCo->as_string) . "\n"; }
   my $lnsl = $gnHCo->as_string;
   my @lns = split(/\n/, $lnsl);
   foreach (@lns) {
      my $ln = $_; chomp($ln);
      my ($name, $path, $domain, $port, $expires, $version) = split(/; /, substr($ln, 13));
      if ($gnDebugFlag) { print "gnSetCookie-each\: $name\n"; }
      my ($key, $val) = split(/=/, $name);
      if(($key ne "MAIL") && ($key ne "NAME")) {
         if ($key eq "HAP") {
            $gnFI{'Cookie'}{'Cookie=HAP'} = $val . ";";				# HAPの格納だけ書式が違うので対処
         }else{
            $gnFI{'Cookie'}{$key} = $val;
         }
         if ($key eq "PON") {
            my($x,$y) = split(/=/, $expires);
            $y = substr($y, 1, 19);
            $y =~ s/-/\//g;
            if ($gnDebugFlag) { print "gnSetCookie-PONExpire\: $y\n"; }
            $gnFI{'Cookie'}{'Expires'} = $y;
         }
      }
   }
   
   if ($gnDebugFlag) { print "gnSetCookie-Folder.ini\(update\)\:\n" . Dumper(%gnFI); }
   &gnSaveEnv($gnFIFN, \%gnFI);
}

sub gnWrite {
   # 書き込みウィンドウを表示する関数
   #
   # 引数: なし
   # 返り値: なし
   
   # 既に書き込みウィンドウが出ていたら何もしない
   if ($gnWxml ne "") {
      &gnLogger("今のところ書き込みウィンドウは１つしか開けません");
      return;
   }
   # 現在選択しているノートのdat(xxxx/xxx.dat)を得る
   my $gnSS_Note = $gnGUIxml->get_widget('gnNote');
   if($gnDebugFlag) { print "\n".Dumper($gnSS_Note)."\n"; }
   my $gnSS_Sures = $gnGUIxml->get_widget('gnSI');
   my $gnSS_SuresM = $gnSS_Sures->get_model;
   my ($gnSortCol, $gnSortOrder) = $gnSS_SuresM->get_sort_column_id;
   
   my $gnSS_TgtDat = "";
   
   my $gnSS_NoteP = $gnSS_Note->get_current_page;
   for my $i ( 0 .. $#gnSureViewInfo ) {
#      if ($gnSS_NoteP == $gnSureViewInfo[$i][1]) {
      if ($gnSS_NoteP == $i) {
         $gnSS_TgtDat = $gnSureViewInfo[$i][0];
         last;
      }
   }
   
   
   if ($gnSS_TgtDat) {
      # xxxx/xxx.datをキーにしてスレの名前を取得
      my $gnSureName = &gnGetSureVarFromDat($gnSS_SuresM, $gnSS_TgtDat, 0);
      
      # Sent.iniからハンドル名とメールアドレスを取得
      my ($gnSS_GenN, $gnSS_DatN) = split(/\//, $gnSS_TgtDat);
      $gnSS_DatN = substr($gnSS_DatN, 0, rindex($gnSS_DatN, "."));
      if ($gnDebugFlag) { print "datname=$gnSS_DatN\n"; }
      my $gnSentRef = &gnLoadEnv($gnSentIniFile);
      my %gnSent = %$gnSentRef;
      my $gnSent_hdlRef = &gnHashSel(\%gnSent, "", "Key", $gnSS_DatN);
      my %gnSent_hdl = %$gnSent_hdlRef;
      if ($gnDebugFlag) { print "handledata=" . Dumper(%gnSent_hdl); }
      
      # 書き込み用ウィンドウ生成
      if ($gnWxml eq "") {
         $gnWxml = Gtk2::GladeXML->new($gladefn, 'gnWrite');
         $gnWxml->signal_autoconnect_from_package('main');
      }
      my $gnWritew = $gnWxml->get_widget('gnWrite');
      if ($gnDebugFlag) { print Dumper($gnWritew); }
      
      # タイトルをセット
      my $gnWritew_title = $gnWritew->get_title;
      $gnWritew_title .= " - " . $gnSureName . " - " . "\($gnSS_TgtDat\)";
      $gnWritew->set_title($gnWritew_title);
      
      # ハンドル名、メールアドレスをセット
      my $gnW_name = $gnWxml->get_widget('gnW_name');
      my $gnW_mail = $gnWxml->get_widget('gnW_mail');
      
      my @gnHashKeys = keys(%gnSent_hdl);
      my $namebuf = "";
      my $emailbuf ="";
      my $namecnt = 1;
      foreach my $val(@gnHashKeys) {
         if ($gnSent_hdl{$val}{'Name'}) {
            if(!($namebuf =~ $gnSent_hdl{$val}{'Name'})) {
               $gnW_name->append_text($gnSent_hdl{$val}{'Name'});
               $namebuf .= $gnSent_hdl{$val}{'Name'};
               $namecnt++;
            }
         }
         if ($gnSent_hdl{$val}{'EMail'}) {
            if (!($emailbuf =~ $gnSent_hdl{$val}{'EMail'})) {
               $gnW_mail->append_text($gnSent_hdl{$val}{'EMail'});
               $emailbuf .= $gnSent_hdl{$val}{'EMail'};
            }
         }
      }

      # ウィンドウのリサイズ
      $gnWritew->resize($gnEnvArg{'EditorWindowSize'}{'Width'}, $gnEnvArg{'EditorWindowSize'}{'Height'});
      $gnWritew->move($gnEnvArg{'EditorWindowSize'}{'Left'}, $gnEnvArg{'EditorWindowSize'}{'Top'});
      $gnWritew->queue_resize;

      $gnWritew->show_all;

   }else{
     &gnLogger("書き込み対象のスレが選択されていません");
   }
   
   
}

sub gnHashSel {
   # 2次元ハッシュ配列の中からある特定のkey=valueの組み合わせが含まれるもののみを
   # 抽出して返す関数
   #
   # 例えば、$aaa{'bbb'}{'ccc'}="ddd"
   #         $aaa{'bbb'}{'eee'}="fff" というハッシュ配列がある場合、
   # &gnHashSel(\%aaa, "", "ccc", "ddd"); と関数を呼び出すと、返り値は
   # $aaa{'bbb'}{'ccc'}="ddd" のみが含まれるハッシュ配列へのリファレンスが返る
   #
   # 引数: 1: ハッシュ配列へのリファレンス(元のハッシュ配列)
   #       2: 文字列: 1番目のkey(NULLの場合は*とみなす)
   #       3: 文字列: 2番目のkey(NULLの場合は*とみなす)
   #       4: 文字列: 抽出条件のvalue(NULLの場合は*とみなす)
   #          2~4のそれぞれの要素は部分一致で大文字小文字を区別する。
   #          各要素はANDされるものとする
   my $gnRefHash_Ref = $_[0];
   my %gnRefHash = %$gnRefHash_Ref;
   my $gnKey1 = $_[1];
   my $gnKey2 = $_[2];
   my $gnVal = $_[3];
   my %gnTgtHash;
   
   if ($gnKey1 eq "") { $gnKey1 = "\(.*\)"; }
   if ($gnKey2 eq "") { $gnKey2 = "\(.*\)"; }
   if ($gnVal eq "") { $gnVal = "\(.*\)"; }
   
   my @gnRefHash_cat = keys(%gnRefHash);
   foreach my $val1(@gnRefHash_cat) {
      if ($val1 =~ $gnKey1) {
         my @gnRefHash_keys = keys(%{$gnRefHash{$val1}});
         foreach my $val2(@gnRefHash_keys) {
            if ($val2 =~ $gnKey2) {
               my $val3 = $gnRefHash{$val1}{$val2};
               if ($val3 =~ $gnVal) {
                  $gnTgtHash{$val1} = $gnRefHash{$val1};
               }
            }
         }
      }
   }
   
   return(\%gnTgtHash);
   
}

sub gnGetSureVarFromDat {
   # dat名(xxxx/xxxx.dat)をキーにして、スレ一覧の中の
   # 任意の列の要素を取得する関数
   #
   # 引数: 1: 文字列(xxxxx/xxxx.dat)
   #       2: 整数(取り出す列。0スタート)
   # 返り値: 文字列
   my $gnTrModel = $_[0];
   my $gnTgtGenre = $_[1];
   my $gnTrTgtCol = $_[2];
   
   my $gnTrIter = $gnTrModel->get_iter_first;
   
   my $cnt = 0;
   my $gnRetVal;
   while (1) {
      my $gnRefDat = $gnTrModel->get($gnTrIter, 10);
      if ($gnDebugFlag>1) { print "referense\: " . Dumper($gnRefDat); }
      if ($gnRefDat eq $gnTgtGenre) {
         $gnRetVal = $gnTrModel->get($gnTrIter, $gnTrTgtCol);
         last;
      }else{
         $gnTrIter = $gnTrModel->iter_next($gnTrIter);
      }
   }

   if ($gnRetVal) {
      return($gnRetVal);
   }else{
      return("");
   }
}

sub gnJumpToURL {
   # URLを引数にして外部アプリを呼び出す関数
   #
   # 引数: 文字列: (URL)
   # 返り値: なし
   my $gnToURL = $_[0];
   if($gnDebugFlag) { print "$gnToURL\n"; }
   
   if ($gnEnvArg{'URLApp'}{'gnSelect'} == 0) {
      
      # Windows/Mac専用：既定のブラウザを起動する
      if($^O eq "darwin") {
         system("open $gnToURL");
      }else{
         system("cmd \/c start $gnToURL");
      }
      
   }elsif ($gnEnvArg{'URLApp'}{'gnSelect'} == 1) {
      
      # 汎用：指定したコマンドを使用する
      my $URLcmdln = $gnEnvArg{'URLApp'}{'gnFile'} . " $gnToURL";
      if($gnDebugFlag) { print "$URLcmdln\n"; }
      system("$URLcmdln \&");
      
   }elsif ($gnEnvArg{'URLApp'}{'gnSelect'} == 2) {
      
      # Windows以外：指定したブラウザを起動する
      if ($gnEnvArg{'URLApp'}{'gnSelBrowser'} == 0) {
         
         # Mozilla(Seamonkey)
         if($^O eq "darwin") {
            system("open -a Seamonkey.app $gnToURL");
         }else{
            system("mozilla $gnToURL \&");
         }

      }elsif ($gnEnvArg{'URLApp'}{'gnSelBrowser'} == 1) {
         
         # Mozilla Firefox
         if($^O eq "darwin") {
            system("open -a Firefox.app $gnToURL");
         }else{
            system("firefox -UILocale ja $gnToURL \&");
         }

      }elsif ($gnEnvArg{'URLApp'}{'gnSelBrowser'} == 2) {
         
         # Opera
         if($^O eq "darwin") {
            system("open -a Opera.app $gnToURL");
         }else{
            system("opera -newpage $gnToURL \&");
         }

      }elsif ($gnEnvArg{'URLApp'}{'gnSelBrowser'} == 3) {
         
         # Konqueror
         system("konqueror --silent $gnToURL \&");
         
      }elsif ($gnEnvArg{'URLApp'}{'gnSelBrowser'} == 4) {
         
         # Epiphany
         system("epiphany $gnToURL \&");
         
      }elsif ($gnEnvArg{'URLApp'}{'gnSelBrowser'} == 5) {
         
         # Safari
         if($^O eq "darwin") {
            system("open -a Safari.app $gnToURL");
         }else{
            gnLogger()
         }
      }
   }
   
   return(FALSE);
}

sub gnItaSelect {
   # 板のジャンル名を引数にして、ツリーから該当の板を選択する
   #
   # 引数: 文字列(板のURLからホスト名を除いたもの。例えばLinux板なら"linux"。)
   # 返り値; なし
   my $tgtgenre = $_[0];
   
   my $gnTrV = $gnGUIxml->get_widget('gnII');
   my $gnTrV_Model = $gnTrV->get_model;
   $gnTrV_Model->foreach(\&gnGetItaPathFromStr, $tgtgenre);
}

sub gnExit {
   # 終了処理関数
   #
   # 引数: なし
   # 返り値: なし

      # 終了時のウィンドウ位置、大きさを記憶
      my $gnmW = $gnGUIxml->get_widget('mW');
      ($gnEnvArg{'WindowSize'}{'Left'}, $gnEnvArg{'WindowSize'}{'Top'}) = Gtk2::Gdk::Window::get_position($gnmW->window);
      my $sute;
      ($sute, $sute,
       $gnEnvArg{'WindowSize'}{'Width'},
       $gnEnvArg{'WindowSize'}{'Height'},) = Gtk2::Gdk::Window::get_geometry($gnmW->window);

      # キャッシュファイルをセーブ
      if ($gnEnvArg{'Cache'}{'Enable'}) {
         if ($gnEnvArg{'Cache'}{'ClearOnExit'}) {
            &gnClearAllCache;
         }else{
            &gnClearExpiredCache;
         }
      }
      &gnSaveEnv($gnCacheTblFile, \%gnCacheTbl);
      # gikoNavi.iniに設定値を書き出し
      &gnSaveEnv($gnEnvFile, \%gnEnvArg);
      
      # URL読み込みスレッドを終了
      $queue->enqueue("exit");
      
      exit(0);
}

sub gnSureSwitch {
   # スレを表示するNotebookのタブを切り替えたらそれに対応する
   # カテゴリ、スレ一覧の該当部分を選択するようにする
   #
   # 引数: なし
   # 返り値: なし
   my $gnSS_Note = $gnGUIxml->get_widget('gnNote');
   if($gnDebugFlag) { print "\n".Dumper($gnSS_Note)."\n"; }
   my $gnSS_Genre = $gnGUIxml->get_widget('gnII');
   my $gnSS_Sures = $gnGUIxml->get_widget('gnSI');
   my $gnSS_SuresM = $gnSS_Sures->get_model;
   my ($gnSortCol, $gnSortOrder) = $gnSS_SuresM->get_sort_column_id;
   
   # スレフィルタをクリア
   my $gnSIFil = $gnGUIxml->get_widget('gnSI_fil');
   $gnSIFil->child->set_text("");
   
   #
   my $gnSS_TgtDat = "";
   
   my $gnSS_NoteP = $gnSS_Note->get_current_page;
   for my $i ( 0 .. $#gnSureViewInfo ) {
#      if ($gnSS_NoteP == $gnSureViewInfo[$i][1]) {
      if ($gnSS_NoteP == $i) {
         $gnSS_TgtDat = $gnSureViewInfo[$i][0];
         last;
      }
   }
   
   if ($gnSS_TgtDat ne "") {
      my ($gnSS_TgtDat_Ita, $gnSS_TgtDat_dat) = split(/\//, $gnSS_TgtDat);
      # ジャンルを選択

      my $gnSS_GenreM = $gnSS_Genre->get_model;
      my ($gnSS_GenreP, $gnSS_GenreC) = $gnSS_Genre->get_cursor;
      my $gnSS_GenreI = $gnSS_GenreM->get_iter($gnSS_GenreP);
      my $gnSS_Genre_now = $gnSS_GenreM->get($gnSS_GenreI, 1);

      $gnSS_Genre_now = &gnGetGenreFromURL($gnSS_Genre_now);
      if($gnDebugFlag) { print "$gnSS_Genre_now\n"; }

      if ($gnSS_Genre_now ne $gnSS_TgtDat_Ita) {
         $gnSS_GenreM->foreach(\&gnGetItaPathFromStr, $gnSS_TgtDat_Ita);
         
         my $gnSS_SuresNow = $gnGUIxml->get_widget('gnSI');
         $gnSS_SuresNow->scroll_to_point(0, 0);
         $gnSS_SuresNow->set_cursor(&gnSureSelectFromDat($gnSS_TgtDat));
         
      }else{
         my $gnSS_SuresNow = $gnGUIxml->get_widget('gnSI');
         $gnSS_SuresNow->scroll_to_point(0, 0);
         $gnSS_SuresNow->set_cursor(&gnSureSelectFromDat($gnSS_TgtDat));
      
      }
      
      
   }
   
}

sub gnGetGenreFromURL {
   # 板のURL(http://xxx.xxx.xxx/xxxxx/)からジャンル名を返す関数
   #
   # 引数: 1: 文字列(板のURL。http://xxxxx.xxxx.xxxx/yyyyyy/)<-最後にスラッシュが必ずあり、その前がジャンル名であること
   # 返り値:  文字列(引数のyyyyyの部分)
   my $gnGGFU_url = $_[0];
   
   my @gnGGFU_val = split(/\//, $gnGGFU_url);
   my $gnGGFU_valcnt = @gnGGFU_val;
   return($gnGGFU_val[$gnGGFU_valcnt-1]);
   
}

sub gnGetItaPathFromStr {
   # 板名"xxxxx"をキーにして板一覧の該当行を選択する関数
   #
   # 引数: 1: 文字列(板名。例えばLinux板なら"linux")
   # 返り値: なし
   my $gnTrModel = $_[0];
   my $gnTrPath = $_[1];
   my $gnTrIter = $_[2];
   my $gnTgtGenre = $_[3];
   #my @val = @_;
   
   my $gnRefGenreURL = $gnTrModel->get($gnTrIter, 1);
   my @Str = split(/\//, $gnRefGenreURL);
   my $strCnt = @Str;
   my $gnRefGenre = $Str[$strCnt-1];
   
   if($gnRefGenre) {
      if($gnDebugFlag) { print "Target=$gnTgtGenre, Reference=$gnRefGenre\n"; }
      if ($gnRefGenre eq $gnTgtGenre) {
         my $gnTrV = $gnGUIxml->get_widget('gnII');
         $gnTrV->expand_to_path($gnTrPath);
         $gnTrV->set_cursor($gnTrPath);
         return(1);
      }else{
         return(0);
      }
   }else{
      return(0);
   }
}

sub gnSureIchiranInit {
   # スレ一覧を作成する関数
   #
   # 引数: 1: Gtk2::TreeStore(スレ一覧に入れるTreeStore(スレの一覧データ))
   #       2: array(参照するGtk2::TreeViewColumnの配列(スレ一覧の並び替え状態を再現する)。省略可)
   # 返り値: なし(この関数内でスレ一覧を作成するため戻り値はなし)
   my $gnSureStore = shift;
   my @gnTrVwColumn = @_;
   
   my $checkval = Gtk2::TreeViewColumn->new();
   my $checkref = Data::Dumper->new([$checkval]);
   my $checkref_flg = $checkref->Dump;
   
   my $checktgt = Data::Dumper->new([$gnTrVwColumn[0]]);
   my $checktgt_flg = $checktgt->Dump;
   
   my $check_flg;
   if($checkref_flg eq $checktgt_flg) {
       $check_flg = "matched";
       if($gnDebugFlag) { print "matched\n"; }
   }else{
       $check_flg = "unmatched";
       if($gnDebugFlag) { print "unmatched\n"; }
   }
  
   # TreeView作成
   my $gnSureView = $gnGUIxml->get_widget('gnSI');
   
   # 既にcolumnが設定してあったら削除
   if ($check_flg eq "matched") {
      my @gnSureViewCols = $gnSureView->get_columns;
      my $gnSureViewColN = @gnSureViewCols;
      for (my $i = 0 ; $i<$gnSureViewColN ; $i++) {
         $gnSureView->remove_column($i);
      }
   }
   
   $gnSureView->set_model($gnSureStore);
   $gnSureView->set_reorderable(TRUE);
   $gnSureView->signal_connect('row-activated' => sub{ &gnSureUpdate; });
   $gnSureView->signal_connect('cursor-changed' => sub{ &gnSureSelect($gnSureView, FALSE); });

   my @gnSICol ='';
   # スレ一覧の表タイトル
   my @gnSICol_title = ('スレッド名','カウント','取得','未取得','新着',
                        '未読','巡回予約','取得日時','スレ作成日時','最終更新日時',
                        'スレデータファイル名(隠し)'
                       );
   my $gnSIColRend = Gtk2::CellRendererText->new;
   for(my $cnt=0;$cnt<12;$cnt++) {
      $gnSICol[$cnt] = Gtk2::TreeViewColumn->new();
      $gnSICol[$cnt]->set_title($gnSICol_title[$cnt]);
      $gnSICol[$cnt]->set_resizable(TRUE);
      $gnSICol[$cnt]->set_sort_column_id($cnt);
      $gnSICol[$cnt]->pack_start($gnSIColRend, FALSE);
      $gnSICol[$cnt]->add_attribute($gnSIColRend, text => $cnt);
      if($cnt < 10) {
         if ($check_flg eq "matched") {
            if($gnDebugFlag) { print "aaa\n" . Dumper($gnTrVwColumn[$cnt]); }
               my $gnTrVwColumn_sortType = $gnTrVwColumn[$cnt]->get_sort_order;
               $gnSICol[$cnt]->set_sort_order($gnTrVwColumn_sortType);
            }
         $gnSureView->append_column($gnSICol[$cnt]);
      }

   }


}

sub gnGetLogDir {
   # ログの保管ディレクトリ取得用関数
   # 環境設定に記述があればそれを使用
   # ない場合は"./Log/2ch/"を返す
   # 引数: なし
   # 返り値: 文字列(ログ保管ディレクトリへのフルパス)
   my $logdir = '';
   if($^O eq 'MSWin32') {
      if($gnEnvArg{'Folder'}{'LogFolder'} eq '') {
         $logdir   = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), "Log/2ch/");
         
      }else{
         $logdir   = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnEnvArg{'Folder'}{'LogFolder'} . "/2ch/");
         
      }
   }else{
      if($gnEnvArg{'Folder'}{'LogFolderUnix'} eq '') {
         $logdir   = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), "Log/2ch/");
      }else{
         $logdir   = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnEnvArg{'Folder'}{'LogFolderUnix'} . "/2ch/");
      }
   }
   
   return($logdir);

}

sub gnLoadEnv {
   # 環境設定ファイル(gikoNavi.ini)を読み込んで
   # 内部管理用のハッシュ配列に格納する
   # 引数: 文字列("gikoNavi.ini"へのパス)
   # 返り値: 2次元ハッシュ配列(カテゴリ名, Key)
   # ----------------------
   # 備考:   各変数へのアクセスのしかたは
   #         <$変数>{<カテゴリの名前>}{<keyの名前>} とすると値が出てくる
   #         <$変数>{<カテゴリの名前>}{<keyの名前>} = <新しい値> とすれば値を変更できる
   #         (例1)
   #           my $testvar = $gnEnvArg{'WindowSize'}{'Top'};
   #                       $testvar に入るのは、[WindowSize]カテゴリの Top=xxxxxx の値(xxxxxxx)
   #         (例2)
   #           $gnEnvArg{'WindowSize'}{'Top'} = "500";
   #                       [WindowSize]カテゴリの Top=xxxxxx の値(xxxxxxx) が 500 になる
   #        ---------------
   #         読み込むファイルはSJISだけど、配列の中にはutf8フラグ付きのshiftjisで
   #         記録されているので、Perl内部ではUTF-8として扱われている・・・はず
   
   # 引数を取得
   my $fn = $_[0];
   if($gnDebugFlag) { print "gnLoadEnv Filename=$fn\n"; }
   
   my $inibuf = '';
   # ファイルをオープンして読み込み
   $fn = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $fn);
   open(gnLE, $fn);
      $inibuf = join '', <gnLE>;
   close(gnLE);
   
   $inibuf =~ s/\r//g;
   $inibuf = Encode::decode('shiftjis', $inibuf);
   my @inibufarray = split(/\n/, $inibuf);
   
   my $iniGenre= '';
   my $iniKey = '';
   my $iniVal = '';
   my %retarray;
   foreach(@inibufarray) {
      my $iniln = $_;
      if($iniln =~ /^\[/) {
         chop($iniln);
         $iniGenre = substr($iniln, 1);
      }else{
         $iniKey = substr($iniln, 0, rindex($iniln, "="));
         $iniVal = substr($iniln, rindex($iniln, "=")+1);
         $retarray{$iniGenre}{$iniKey} = $iniVal;
      }
   }
   return(\%retarray);
}

sub gnSaveEnv {
   # 環境設定ファイル(*.ini)へ、内部で保管したハッシュ配列を書き込む
   # 引数: 1:文字列("*.ini"へのパス)
   #       2:ハッシュ配列
   # 返り値: なし
   # ----------------------
   # 備考:   各変数は
   #         <$変数>{<カテゴリの名前>}{<keyの名前>} という形式で保管されている
   #         iniファイルに書き込むときは
   #         [カテゴリの名前]
   #         <keyの名前>=<値>crlf
   #         という形で書き込まれる
   #        ---------------
   #         ハッシュ配列はutf8フラグ付きのshiftjisで記録されているが
   #         iniファイルに書き出す際にはutf8フラグを落とし、ShiftJISで書き込まれる
   
   # 引数を取得
   my $fn = $_[0];
   my $gnEnvArg1 = $_[1];
   my %gnEnvArg2 = %$gnEnvArg1;
   if($gnDebugFlag) { print "saveenv\: $fn\n"; }

   my @gnEA_cat = keys(%gnEnvArg2);
   
   # ファイルをオープン
   $fn = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $fn);
   open(gnLE, ">$fn");
   foreach my $val(@gnEA_cat) {
      # カテゴリの一覧を取得
      my @gnEA_keys = keys(%{$gnEnvArg2{$val}});
      
      my $flg1 = TRUE;
      foreach my $val2(@gnEA_keys) {
         # カテゴリの中のそれぞれのkeyを取り出し
         
         if($flg1) {
            my $str = "[" . $val . "]\n";
            print gnLE $str;
            $flg1 = FALSE;
         
         }
         
         my $val3 = $gnEnvArg2{$val}{$val2};
         
         my $str = $val2 . "=" . $val3 . "\n";
         $str = Encode::encode('shiftjis', $str);
         
         print gnLE $str;
      
      }
   }
   close(gnLE);
   
}


sub gnMakeItaTreeStore {
   # 板一覧のファイル名を引数として受け取り、
   # TreeStore型のウィジェットを返す
   # またグローバル変数%gnBoardTblも更新する
   my $cfgfile = shift;
   
   my $gnGenreid;
   my $gnGenreStr;
   
   my @gnItaGenre;
   my @gnItaInfo;
   
   # TreeStoreを作成
   my $gnItaStore = Gtk2::TreeStore->new( qw/ Glib::String Glib::String / );

   # 一番上「２ちゃんねる」を作成
   my $gnItaStore_iter1 = $gnItaStore->append(undef);
   $gnItaStore->set($gnItaStore_iter1, 0 => "２ちゃんねる", 1 => '');
   
   # TreeStoreのトップレベルになるジャンル抽出
   open(bnMITS, $cfgfile);
      while(<bnMITS>) {
         my $gnStr = $_; chomp($gnStr);
         $gnStr = Encode::decode('utf8', $gnStr);
         if($gnStr =~ /-0::/) {
            (my $gnGenreid, my $gnGenreStr) = split(/-0::/, $gnStr);
            push(@gnItaGenre, [$gnGenreid, $gnGenreStr])
         }
      }
   close(bnMITS);
   
   # ジャンルごとに板一覧を抽出
   my $loadcnt = 0;
   my $gnItaGenreCnt = @gnItaGenre;
   foreach my $gnGenreTmp (@gnItaGenre) {
      $gnGenreid  = $gnGenreTmp->[0];  # ジャンル番号
      $gnGenreStr = $gnGenreTmp->[1];  # ジャンルの名称
      
      # 第2レベルとなる各ジャンルをTreeStoreに追加
      my $gnItaStore_iter2 = $gnItaStore->append($gnItaStore_iter1);
      $gnItaStore->set($gnItaStore_iter2, 0 => $gnGenreStr, 1 => '');
      
      
      open(bnMITS, $cfgfile);
         while(<bnMITS>) {
            my $gnStr = $_; chomp($gnStr);
            $gnStr = Encode::decode('utf8', $gnStr);
            
            my $gnValStr = "/${gnGenreid}-/";
            if($gnStr =~ /^$gnGenreid-/) {
              if($gnStr !~ /$gnGenreid-0::/) {
              
                 # 第3レベルとなる各板を作成
                 my $gnItaStore_iter3 = $gnItaStore->append($gnItaStore_iter2);
                 (my $gnGenreid, my $gnItaName, my $gnItaUrl) = split(/::/, $gnStr);
                 $gnItaStore->set($gnItaStore_iter3, 0 => $gnItaName, 1 => $gnItaUrl);
                 
                 # 環境変数%gnBoardTblを更新
                 $gnBoardTbl{&gnGetGenreFromURL($gnItaUrl)} = $gnItaName . chr(001) . $gnItaUrl;
                 
                 print STDERR ".";
                 
              }
            }
         }
      close(bnMITS);
      
      $loadcnt++;
      print STDERR int($loadcnt / $gnItaGenreCnt * 100) . "\%";
   }
   
   return($gnItaStore);
}

sub gnMakeInitialSureLst {
   # 引数なし
   # スレ一覧の表に起動時の空データを渡す
   # 返り値はTreeStore型のウィジェット(実データは空)
   
   # TreeStoreを作成                       0: スレッド名(Str)
   #                                       1: カウント(Int)
   #                                       2: 取得(Int)
   #                                       3: 未取得(Int)
   #                                       4: 新着(Int)
   #                                       5: 未読(Int)
   #                                       6: 巡回予約(Str)
   #                                       7: 取得日時(Str)
   #                                       8: スレ作成日時(Str)
   #                                       9: 最終更新日時(Str)
   #                                      10: スレデータファイル名(Str, 隠し要素)
   my $gnSureStore = Gtk2::TreeStore->new( qw/ Glib::String Glib::String Glib::String
                                               Glib::String Glib::String Glib::String
                                               Glib::String Glib::String Glib::String
                                               Glib::String Glib::String / );
   my $gnSureStore_iter1 = $gnSureStore->append(undef);
   $gnSureStore->set($gnSureStore_iter1, 0 => '', 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0,
                     6 => '', 7 => '', 8 => '', 9 => '', 10 => '', 11 => '');
   
   return($gnSureStore);
}

sub gnSleIchiranSelect {
   # 板がクリックされたらスレの情報を読み込む
   # <ログディレクトリ>/Folder.idxがなかった場合はそのまま終了。
   # 一度もスレの一覧をとったことがない場合は確認の上、スレの一覧を取得する。(別関数へ)
   # あったら"<ログディレクトリ>/Folder.idx"を引数に
   # gnSleIchiranReadを呼ぶ
   #-------------------------------------------------------------------

   # 引数: 1: Gtk2::TreeView
   #       2: 文字列(強制的にFolder.idxの再読み込みをするか指定。"force"なら再読み込み。"none"ならしない。)
   
  if ($gnSuppressEventFlg == FALSE) { 
   my $gnSISHikisu =$_[0];
   if($gnDebugFlag) { print Dumper($gnSISHikisu); }
   
   my $gnSISForceFLG = $_[1];
   
   my ($gnMyTree, $gnMyTreePath, $gnMyTreeCol) = $gnSISHikisu;

   # 選択している部分を取得(返り値はGtk2::TreeSelecton)
   my $gnTreeSel = $gnMyTree->get_selection;

   # 得られたTreeSelectionからTreeModelとTreeIterを得る
   my ($gnMyTreeModel, $gnTM_iter) = $gnTreeSel->get_selected;

   # TreeIterを引数にしてget_valueすると、選択した行の配列が得られる
   my ($gnItaName, $gnItaUrl) = $gnMyTreeModel->get_value($gnTM_iter);

   
   # URLの最後はログのディレクトリ名でもあるので取り出す
   my @gnItaUrl_val = split(/\//, $gnItaUrl);
   my $gnIUv_cnt = @gnItaUrl_val;
   $gnIUv_cnt--;
   
   if($gnIUv_cnt > 0) {

      $gnItaLogDir = $gnItaUrl_val[$gnIUv_cnt];
      if($gnDebugFlag) { print "gnItaLogDir: " . $gnItaLogDir ."\n"; }

      # ここでログのあるディレクトリが確定
      if($gnDebugFlag) { print "logdir: " . $logdir ."\n"; }
      my $gnItaSfx = $logdir . $gnItaLogDir . "/";
      if($gnDebugFlag) { print "folder.idx_sfx: $gnItaSfx \n"; }
      my $gnItaIdxFN = $gnItaSfx . "Folder.idx";
      #$gnItaIdxFN = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaIdxFN);
      if($gnDebugFlag) { print "folder.idx_path: $gnItaIdxFN \n"; }
      
      # Folder.idxがなかったら新規作成用関数に飛ぶ
      my $gnItaIdxFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaIdxFN);
      if(!(-e "$gnItaIdxFN_1")) {
          my $gnItaSfx_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaSfx);
          if (!(-d $gnItaSfx_1)) {
             &gnLogger("板を格納するディレクトリがありません\($gnItaSfx\)");
             my $mW = $gnGUIxml->get_widget('mW');
             my $gnMsgDialog = &gnYesNo("スレ一覧を格納するディレクトリがありません。\n作成してスレ一覧を取得しますか？");
             if($gnMsgDialog eq 'yes') {
                #$gnMsgDialog->destroy;
                my $gnItaSfx_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaSfx);
                eval{ mkdir($gnItaSfx_1); };
                if($@) {
                   if($gnDebugFlag) { print "mkdir failed: $gnItaSfx_1 \n"; }
                }else{
                   &gnLogger("板を格納するディレクトリを作成しました\($gnItaSfx\)");
                }

                &gnMakeFolderIni($gnItaSfx . "\/Folder.ini");
                &gnLogger("Folder.iniを作成しました\($gnItaSfx\/Folder.ini\)");

                my $gnSINFlg = &gnSureItiranNew($gnItaUrl, $gnItaSfx);
                if ($gnSINFlg == FALSE) {
                   &gnLogger("スレ一覧更新に失敗しました");
                   return(FALSE);
                }
             }else{
                if($gnDebugFlag) { print "gnMsgDialog_response: " . Dumper($gnMsgDialog)."\n"; }
                &gnLogger("この板はまだ一覧を取得していません\($gnItaName\)");
                #$gnMsgDialog->destroy;
                return(FALSE);
             }
          }
      }
      
      if ($gnSISForceFLG eq "force") {
         my $gnSINFlg = &gnSureItiranNew($gnItaUrl, $gnItaSfx);
         if ($gnSINFlg == FALSE) {
            &gnLogger("スレ一覧更新に失敗しました");
            return(FALSE);
         }
      }
      
      # ファイルパスを引数にして
      # gnSleIchiranReadを呼ぶ
      &gnSleIchiranRead($gnItaIdxFN);
      return(TRUE);
      
   }else{
      return(FALSE);
   }
 }  
}

sub gnSureItiranNew {
   # スレ一覧を更新(もしくは新規作成)する関数
   #
   # 引数: 1: 文字列(板のベースURL)
   #       2: 文字列(板のディレクトリ)
   
   my $gnItaUrl = $_[0];
   my $gnItaSfx = $_[1];
       
       &gnLogger("subject.txtを取得します");
       my $gnItaIdxFURL = $gnItaUrl . "subject.txt";
       
       my $gnSIUFlg = &gnSureItiranUpdate($gnItaIdxFURL, $gnItaSfx);
       if($gnSIUFlg == TRUE) {
           &gnLogger("スレ一覧を更新しました");
           return(TRUE);
       }else{
           &gnLogger("スレ一覧更新に失敗しました");
           return(FALSE);
       }
}

sub gnSureItiranUpdate {
   # 指定されたURLからsubject.txtを取得して
   # gnSub2FldIniを呼び出す。
   #
   # 引数: 1: 文字列(取得するsubject.txtのURL)
   #       2: 文字列(subject.txt, Folder.idxを保存するディレクトリ)
   my $gnItaIdxFURL = $_[0];
   my $gnItaSfx = $_[1];

   &gnLogger("取得するURL：$gnItaIdxFURL");
                
   my $gnUA = LWP::UserAgent->new;
   $gnUA->agent($gnUsrAgent);
   if($gnEnvArg{'ReadProxy'}{'Proxy'} == 1) {
      my $gnUA_proxystr = "http\:\/\/" . $gnEnvArg{'ReadProxy'}{'Address'} . "\:" . $gnEnvArg{'ReadProxy'}{'Port'} . "\/";
      $gnUA->proxy('http', $gnUA_proxystr);
   }
             
   my $gnUA_res = $gnUA->get($gnItaIdxFURL);
   
   if($gnUA_res->is_success) {
       # subject.txtを取得したのでローカルに保存
       my $gnItaSubFN = $gnItaSfx . "subject.txt";
               
       my $gnItaSubFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaSubFN);
       open(gnIdxF, ">$gnItaSubFN_1");
          print gnIdxF $gnUA_res->content;
       close(gnIdxF);
               
       &gnLogger("subject.txtの取得に成功しました");
       
       # subject.txtからFolder.idxを生成(既にある場合はアップデート)
       my $gnSub2FldIniRST = &gnSub2FldIni($gnItaSubFN, $gnItaSfx);
               
       if($gnSub2FldIniRST eq FALSE) {
           &gnLogger("インデックスファイルの更新に失敗しました");
           return(FALSE);
       }else{
         return(TRUE);
       }
               
    }else{
       &gnLogger("subject.txtの取得に失敗しました");
    }
          
}

sub gnSub2FldIni {
   # 板のインデックスファイル(<logdirname>/subject.txt)とワーキングディレクトリを引数として受け取り
   # ワーキングディレクトリのFolder.idxを更新する。
   # 既にFolder.idxがあれば差分のみ追加
   #
   # 引数: 1: 文字列(<logdirname>/subject.txt)
   #       2: 文字列(<logdirname>)
   # 返り値: int(TRUEが成功、FALSEが失敗)
   
   my $gnSubFN = $_[0];
   my $gnFldIdxDir = $_[1];
   my $gnSureNo = 0;
   
   my $gnFldIdxFN = $gnFldIdxDir . "Folder.idx";
   # もしFolder.idxがなかったら作っておいて、行数を計っておく
   # (いちおう行数と行内の1カラム目が同期していない場合を考えて・・・
   my $gnFldIdxFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnFldIdxFN);
   if (!(-e $gnFldIdxFN_1)) {
      open(gnFIdx, ">>$gnFldIdxFN_1");
         print gnFIdx "$gnIdxFileVersion\n";
      close(gnFIdx);
   }
   open(gnFIdx2, "$gnFldIdxFN_1");
   while(<gnFIdx2>) {
      $gnSureNo++;
   }
   close(gnFIdx2);

   my $gnSubFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnSubFN);
   open(gnFSub, "$gnSubFN_1");
   my $aaa = 0;
   while(<gnFSub>) {
      # subject.txtの行をUTF8で処理できるように変換
      my $gnBuf = $_; chomp($gnBuf);
      $gnBuf = Encode::decode('shiftjis', $gnBuf);

      # datファイル名とスレタイを分離
      my ($gnDatName, $gnSureTitle) = split(/\<\>/, $gnBuf);
      # コメント数を取得
      my $gnSureCnt = substr($gnSureTitle,
                                rindex($gnSureTitle, "\(")+1,
                                (length($gnSureTitle) - rindex($gnSureTitle, "\)"))
                                );

      # コメント数が取得できなかったらその行は飛ばす
      # subject.txtの最後の行にchr(000)が続く行があるため
      if (!($gnSureCnt)) {
         next;
      }
      # タイトル名を取得
      my $bbb = rindex($gnSureTitle, "\(");
      if ($bbb != -1) {
        $gnSureTitle = substr($gnSureTitle, 0, $bbb-1);
      }
      
      my $gnDatFileFN = $gnFldIdxDir . $gnDatName;
      my $gnSureNoHex = sprintf("%X", $gnSureNo);
      my $gnIdxBuf = $gnSureNoHex . chr(001) .
                       $gnDatName . chr(001) .
                       $gnSureTitle . chr(001) .
                       "0" . chr(001) .
                       "0" . chr(001) .
                       "0" . chr(001) .
                       "0" . chr(001) .
                       "0" . chr(001) .
                       "0" . chr(001) .
                       "0" . chr(001) .
                       "0" . chr(001) .
                       "0" . chr(001) .
                       $gnSureCnt . chr(001) .
                       $gnSureCnt . chr(001) .
                       "0";
      $gnIdxBuf = Encode::encode("shiftjis", $gnIdxBuf);
      
      # 現在のFolder.idxの中に$gnDatNameのエントリがなかったら追加する
      my $gnIsDatNameAlrdyLocalFLG = &gnIsDatNameAlrdyLocal($gnDatName, $gnFldIdxFN);
      if ($gnIsDatNameAlrdyLocalFLG == FALSE) {
         my $gnFldIdxFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnFldIdxFN);
         open(gnFIdx, ">>$gnFldIdxFN_1");
            print gnFIdx "$gnIdxBuf\n";
         close(gnFIdx);
         $gnSureNo++;
      }
      
   }
   close(gnFSub);
   
   #仮置き
   return(TRUE);
}

sub gnIsDatNameAlrdyLocal {
   # 指定したスレ(xxxxxx.dat)が既にFolder.idxの中に
   # いるかどうかを調べる関数
   #
   # 引数: 1: 文字列(スレファイル名(xxxxxx.dat)。パスなし)
   #       2: 文字列(Folder.idxのフルパス)
   # 返り値 : TRUE あった
   #        : FALSE なかった
   my $gnDatName = $_[0];
   my $gnFldIdxFN = $_[1];
   
   my $ret = FALSE;
   my $pos = -1;
   my $gnFldIdxFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnFldIdxFN);
   if (!(-e $gnFldIdxFN_1)) {
      &gnWarn("指定されたFolder.idx\($gnFldIdxFN\)が存在しません。\nFolder.idxを入れるディレクトリがない可能性があります");
      return(FALSE);
   }
   open(gnFIDNAL, "$gnFldIdxFN_1");
   while(<gnFIDNAL>) {
      my $buf = $_; chomp($buf);
      $buf = Encode::decode('shiftjis', $buf);
      $pos = index($buf, $gnDatName);
      if($pos>0) {
         $ret = TRUE;
         last;
      }
   }
   close(gnFIDNAL);
   
   if($ret == TRUE) {
      return(TRUE);
   }else{
      return(FALSE);
   }
}

sub gnSleIchiranRead {
   # 板のインデックスファイル(<logdirname>/Folder.idx)を引数として受け取り
   # オープンし、TreeStoreを構築し、gnSureViewに当てはめる
   my $gnSureIdxFN = shift;
   if($gnDebugFlag) { print "$gnSureIdxFN\n"; }
   
   # TreeStoreを作成                       0: スレッド名(Str)
   #                                       1: カウント(Int)
   #                                       2: 取得(Int)
   #                                       3: 未取得(Int)
   #                                       4: 新着(Int)
   #                                       5: 未読(Int)
   #                                       6: 巡回予約(Str)
   #                                       7: 取得日時(Str)
   #                                       8: スレ作成日時(Str)
   #                                       9: 最終更新日時(Str)
   #                                      10: スレデータファイル名(Str, 隠し要素)
   my $gnSureStore = Gtk2::TreeStore->new( qw/ Glib::String Glib::Int    Glib::Int
                                               Glib::Int    Glib::Int    Glib::Int
                                               Glib::String Glib::String Glib::String
                                               Glib::String Glib::String  / );

   
   my $gnSureDir = substr($gnSureIdxFN,
                            length($logdir),
                            (length($gnSureIdxFN) - (length($logdir)+length("Folder.idx")))-1
                            );
   if($gnDebugFlag) { print "$logdir\n"; }
   if($gnDebugFlag) { print "$gnSureDir\n"; }
   
   # スレフィルタの情報を取得
   my $gnSIFil = $gnGUIxml->get_widget('gnSI_fil');
   my $gnSIFilTxt = $gnSIFil->child->get_text;
   if ($gnDebugFlag) { print "sure_filter\: $gnSIFilTxt\n";}

   # gnSureStoreにデータを入れる
   if ($gnDebugFlag) { print "open Folder.idx to TreeStore\n";}
   my $gnSureStrcnt = 0;
   my $gnSureIdxFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnSureIdxFN);
   open(gnSIDX, "$gnSureIdxFN_1");
      while(<gnSIDX>) {
         if ($gnDebugFlag>1) { print "\tFolder.idx-line\: " . sprintf("%x",$gnSureStrcnt) . "\n";}
         my $gnSureInfoStr = $_;
         $gnSureInfoStr = Encode::decode('shiftjis', $gnSureInfoStr);
         chomp($gnSureInfoStr);
         
         if($gnSureStrcnt > 0) {
         
            my @gnSureInfoStr_ar = split(/\001/, $gnSureInfoStr);

            if (@gnSureInfoStr_ar != 15) {
            	# 要素数が15個じゃなかったデータは華麗にヌルー
            	if ($gnDebugFlag) { print "\tCorrupted Data Found\n" . Dumper(@gnSureInfoStr_ar);}
            	$gnSureStrcnt++;
            	&gnLogger("スレ一覧の中に壊れたデータを見つけました。スキップします\:" . $gnSureIdxFN . " " . $gnSureStrcnt . "行目");
            	next;
            }
            # スレデータ1行を各要素に分割
            my($gnSureId, $gnSureFN, $gnSureName, $gnSureShutoku, $gnSureByte, $gnSureShutokuDate,
               $gnSureLastUpdate, $gnSureKidoku, $gnSurenazo1, $gnSurenazo2, $gnSurenazo3,
               $gnSurenazo4, $gnSurecnt, $gnSureShinchaku, $gnSurenazo7) = @gnSureInfoStr_ar;
            
            # 未取得はカウント-取得で算出
            my $gnSureMishutoku = hex($gnSurecnt) - hex($gnSureShutoku);

            # 取得日時・最終更新は文字列変換
            if(hex($gnSureShutokuDate) > 0) {
               my ($ss, $mm, $hh, $DD, $MM, $YY, $WKD, $YD, $ID) = gmtime(hex($gnSureShutokuDate));
               $MM++; $YY += 1900;
               $gnSureShutokuDate = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $YY, $MM, $DD, $hh, $mm, $ss);
               ($ss, $mm, $hh, $DD, $MM, $YY, $WKD, $YD, $ID) = gmtime(hex($gnSureLastUpdate));
               $MM++; $YY += 1900;
               $gnSureLastUpdate = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $YY, $MM, $DD, $hh, $mm, $ss);
            } else {
               $gnSureShutokuDate = '';
               $gnSureLastUpdate = '';
            }

            # スレタイトルは先頭にidをつける
            $gnSureName = sprintf("%05d",hex($gnSureId)) . ": " . decode_entities($gnSureName);
            # スレ作成日時はファイル名から抽出する
            my ($gnSureMkDate, $gnSureExt) = split(/\./, $gnSureFN);
            my ($ss, $mm, $hh, $DD, $MM, $YY, $WKD, $YD, $ID) = localtime($gnSureMkDate);
            $MM++; $YY += 1900;
            $gnSureMkDate = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $YY, $MM, $DD, $hh, $mm, $ss);

            # スレフィルタにヒットしなければ次へ
            if (!($gnSIFilTxt =~ m/^ *$/)) {
               # スレフィルタが空白しか含まない場合は評価しない
               my $val = index($gnSureName, $gnSIFilTxt);
               if ($val < 0) {
                  if ($gnDebugFlag>1) { print "s";}
                  $gnSureStrcnt++;
                  next;
               }else{
                  if ($gnDebugFlag>1) { print "h";}
               }
            }else{
                  if ($gnDebugFlag>1) { print "b";}
            }
            
            
            # $gnSureStore(TreeStore)に行を追加
            my $gnSureStore_iter = $gnSureStore->append(undef);
            $gnSureStore->set($gnSureStore_iter,
                              0 => $gnSureName,
                              1 => hex($gnSurecnt), 2 => hex($gnSureShutoku), 3 => $gnSureMishutoku,
                              4 => hex($gnSureShinchaku), 5 => '', 6 => '',
                              7 => $gnSureShutokuDate, 8 => $gnSureMkDate,
                              9 => $gnSureLastUpdate, 10 => ($gnSureDir . "\/" . $gnSureFN)
                              );
         
         }
         $gnSureStrcnt++;
      }
   close(gnSIDX);
   if ($gnDebugFlag>1) { print "\n";}
   
   # スレ一覧(TreeView)を呼び出し、新しく作ったTreeStoreを当てはめる
   my $gnSureLst = $gnGUIxml->get_widget('gnSI');
   $gnSureLst->set_model($gnSureStore);
   

}

sub gnSureSelect {
   # スレ一覧から選択されたスレのファイルを読み込む
   # 既にNotebookに読み込んでいたら読み込んだページを表示
   # 読み込んでいなかったらデータファイルを読み込みNotebookウィジェットに追加。
   
   # 引数: 1: Gtk2::TreeView
   
   my $gnTrView = $_[0];
   my $gnSureUpdateForce = $_[1];

   # 処理の間はswitch_pageシグナルをブロック
   my $gnNotebook = $gnGUIxml->get_widget('gnNote');
   $gnNotebook->signal_handler_block($gnSignalHandler->{gnNote}{switch_page});

   # 選択している部分を取得(返り値はGtk2::TreeSelecton)
   my $gnTreeSel = $gnTrView->get_selection;

   # 得られたTreeSelectionからTreeModelとTreeIterを得る
   my ($gnMyTreeModel, $gnTM_iter) = $gnTreeSel->get_selected;
   if ($gnDebugFlag) { print "line3447 hikisu: \n" . Dumper($gnTrView, $gnSureUpdateForce,
                                                  $gnMyTreeModel, $gnTM_iter), "\n"; }
   

   # TreeIterを引数にしてget_valueすると、選択した行の配列が得られる
   my @gnSureInfo = $gnMyTreeModel->get_value($gnTM_iter);
   
   # 管理用の配列を参照して、Notebook内に既に展開されているかを
   # 確認する
   my $gnFlg_in = FALSE;
   my $gnSVI_fn = '';
   my $gnSVI_idx = -1;
   my $gntmpval1 = @gnSureViewInfo;
   my @gnSureViewInfoTmp = @gnSureViewInfo;
   my @gnSureViewInfoNew;
   if ($gnDebugFlag) { print "line3462 whole" . Dumper(@gnSureViewInfo); }
   
   for my $i ( 0 .. $#gnSureViewInfo ){
      my $gnSVI_fnBuf = $gnSureViewInfo[$i][0];
 #     my $gnSVI_idxBuf = $gnSureViewInfo[$i][1];
      my $gnSVI_idxBuf = $i;
      
      if ($gnDebugFlag) { print "line3468 confirm: $gnSVI_fnBuf, $gnSVI_idxBuf, $gnSureInfo[10]\n"; }
      if($gnSVI_fnBuf eq $gnSureInfo[10]) {
         $gnFlg_in = TRUE;
         $gnSVI_fn = $gnSVI_fnBuf;
         $gnSVI_idx = $gnSVI_idxBuf;
         if($gnSureUpdateForce == FALSE) {
            if ($gnDebugFlag) { print "line3474 found and pushed.\n"; }
            push(@gnSureViewInfoNew, ([$gnSVI_fnBuf, $gnSVI_idxBuf]));
         }else{
            if ($gnDebugFlag) { print "line3477 found and NOT pushed.\n"; }
         }
      }else{
         if ($gnDebugFlag) { print "line3480 not found but pushed.\n"; }
         push(@gnSureViewInfoNew, ([$gnSVI_fnBuf, $gnSVI_idxBuf]));
      }
      
      if ($gnDebugFlag) { print "line3484 loop examined num=$i.\n"; }
   }
   if ($gnDebugFlag) { print "line3486 result: \n$gnSVI_fn, $gnSureInfo[10], $gnSVI_idx, $gnFlg_in\n"; }
   if ($gnDebugFlag) {
      if ($gnFlg_in == TRUE) {
         print "line3489 result: found\n";
      }
   }
   @gnSureViewInfo = @gnSureViewInfoNew;
   if ($gnDebugFlag) { print "line3494 whole2 :\n" . Dumper(@gnSureViewInfo); }
   

   if($gnFlg_in == TRUE) {
       
       if($gnSureUpdateForce == TRUE) {
          if ($gnDebugFlag) { print "line3499 sure already in notebook BUT force update\n"; }
          
          
          # ノートブックのページを削除する
          $gnNotebook->remove_page($gnSVI_idx);
          if ($gnDebugFlag) { print "line3504 page removed: $gnSVI_idx\n"; }
          
          # 新規にページを追加する
          # スレ表示用にNotebookに新規ページを作成
          my $gnNB_tv = Gtk2::TextView->new;
          $gnNB_tv->set_property('left-margin' => 5);
          $gnNB_tv->set_property('pixels-above-lines' => 1);
          $gnNB_tv->set_property('pixels-below-lines' => 2);
          $gnNB_tv->set_property('editable' => FALSE);
          $gnNB_tv->signal_connect (motion_notify_event => \&gnSureViewMouseMove);     # マウスが動いたらイベント発生
          $gnNB_tv->signal_connect (button_press_event => \&gnSureViewLinkPress);
          $gnNB_tv->signal_connect (button_release_event => \&gnSureViewLinkRelease);
          
          # フォントを指定する
          my $gnNB_tv_context = $gnNB_tv->get_pango_context();
          my $gnNB_tv_fontDesc = $gnNB_tv_context->get_font_description();
          my $gnNB_tv_fontscale = Gtk2::Pango->scale;
          if($gnEnvArg{'Browser'}{'gnBrowserFontUseCustom'} == TRUE) {
             $gnNB_tv_fontDesc->set_family($gnEnvArg{'Browser'}{'BrowserFontName'});
             $gnNB_tv_fontDesc->set_size($gnEnvArg{'Browser'}{'BrowserFontSize'} * $gnNB_tv_fontscale);
             if($gnEnvArg{'Browser'}{'BrowserFontBold'} != -1) {
                $gnNB_tv_fontDesc->set_weiget('bold');
             }
             if($gnEnvArg{'Browser'}{'BrowserFontItalic'} != -1) {
                $gnNB_tv_fontDesc->set_style('italic');
             }
             $gnNB_tv->modify_font($gnNB_tv_fontDesc);
          }
          
          # TextBufferを取得して初期化(必要ないかも)
          my $gnNB_tvbuf = $gnNB_tv->get_buffer;
          $gnNB_tvbuf->set_text('');
          
          # ScrolledWindowに入れる
          my $gnNB_tvsw = Gtk2::ScrolledWindow->new(undef, undef);
          $gnNB_tvsw->set_shadow_type('etched-out');
          $gnNB_tvsw->set_policy('automatic', 'automatic');
          $gnNB_tvsw->add($gnNB_tv);

          # スレの名前からタブ用のラベルを作成
          my ($gnSureNum, $gnSureName) = split(/: /, $gnSureInfo[0]);
          my $gnNB_lb = "";
          if ($gnDebugFlag) { 
             $gnNB_lb = "${gnSureName}\n${gnSVI_fn}:${gnSVI_idx}";
             $gnNB_lb = Gtk2::Label->new($gnNB_lb);
          }else{
             $gnNB_lb = Gtk2::Label->new($gnSureName);
          }

          # フォントサイズを小さくする
          # デフォルトの85%に【今後】将来的には設定できるように？
          my $gnNB_lb_context = $gnNB_lb->get_pango_context();
          my $gnNB_lb_fontDesc = $gnNB_lb_context->get_font_description();
          my $gnNB_lb_fontsize = $gnNB_lb_fontDesc->get_size();
          my $gnNB_lb_newsize = int($gnNB_lb_fontsize * 0.85);
          $gnNB_lb_fontDesc->set_size($gnNB_lb_newsize);
          $gnNB_lb->modify_font($gnNB_lb_fontDesc);
       
          # ファイルパスを関数gnParseSureにあたえると
          # 整形済みのTextBufferが返って来るので$gnNB_tvbuf(TextBuffer)に格納
          my $gnSureFN = $logdir . $gnSureInfo[10];
          if($gnDebugFlag) { print "$gnSureFN\n"; }
          $gnSureFN = &env_FNconv($gnSureFN);

          my $gnSureFN1 = $gnSureInfo[10];
          if($gnDebugFlag) { print "$gnSureFN1\n"; }
          $gnSureFN1 = &env_FNconv($gnSureFN1);
          $gnNB_tvbuf = &gnParseSure($gnSureFN, $gnSureFN1);
          $gnNB_tv->set_buffer($gnNB_tvbuf);

          # カーソルを先頭に(後々変える必要あり)
          $gnNB_tvbuf->place_cursor($gnNB_tvbuf->get_start_iter);
       
          # 用意できたページをNotebookに追加
          my $gnSureView_newpagenum = $gnNotebook->append_page($gnNB_tvsw, $gnNB_lb);
          
          if($gnDebugFlag) { print "page appended.num=$gnSureView_newpagenum\n"; }
       
          # Notebookを再表示(なぜかこれをしないと追加したページが表示されない)
          $gnNotebook->show_all;
          $gnNotebook->set_current_page($gnSureView_newpagenum);
          if($gnDebugFlag) { print "show again.\n"; }

          # 管理配列にスレの情報を書き込み
          push(@gnSureViewInfo, ([$gnSureInfo[10], $gnSureView_newpagenum]));
          

       }else{
          # スレは既にNotebook内に表示されているので
          # 該当のページを表示
          if ($gnDebugFlag) { print "page already in notebook\n"; }
          $gnNotebook->set_current_page($gnSVI_idx);
       }
       
   }else{
       # スレはNotebook内にはない。
       if ($gnDebugFlag) { print "page NOT already in notebook\n"; }
       if ($gnDebugFlag) { print "need to create new page\n"; }
       # スレが取得されていないかを確認
       if($gnSureInfo[2] eq "0") {
          # スレはまだ取得されていない
          # ログウィンドウにエラーログを出力
          
          my ($gnSureNum, $gnSureName) = split(/: /, $gnSureInfo[0]);
          my $gnLogStr = "このスレはまだ取得していません(" . $gnSureName . ")";
          if ($gnDebugFlag) { print "sure is never gotton: $gnSureInfo[2]\n"; }
          
          &gnLogger($gnLogStr);
          
       }else{
          # スレは取得されているので、ファイルから読み込み
          
          # スレ表示用にNotebookに新規ページを作成
          my $gnNB_tv = Gtk2::TextView->new;
          $gnNB_tv->set_property('left-margin' => 5);
          $gnNB_tv->set_property('pixels-above-lines' => 1);
          $gnNB_tv->set_property('pixels-below-lines' => 2);
          $gnNB_tv->set_property('editable' => FALSE);
          $gnNB_tv->signal_connect (motion_notify_event => \&gnSureViewMouseMove);     # マウスが動いたらイベント発生
          $gnNB_tv->signal_connect (button_press_event => \&gnSureViewLinkPress);
          $gnNB_tv->signal_connect (button_release_event => \&gnSureViewLinkRelease);
          
          # フォントを指定する
          my $gnNB_tv_context = $gnNB_tv->get_pango_context();
          my $gnNB_tv_fontDesc = $gnNB_tv_context->get_font_description();
          my $gnNB_tv_fontscale = Gtk2::Pango->scale;
          if($gnEnvArg{'Browser'}{'gnBrowserFontUseCustom'} == TRUE) {
             $gnNB_tv_fontDesc->set_family($gnEnvArg{'Browser'}{'BrowserFontName'});
             $gnNB_tv_fontDesc->set_size($gnEnvArg{'Browser'}{'BrowserFontSize'} * $gnNB_tv_fontscale);
             if($gnEnvArg{'Browser'}{'BrowserFontBold'} != -1) {
                $gnNB_tv_fontDesc->set_weiget('bold');
             }
             if($gnEnvArg{'Browser'}{'BrowserFontItalic'} != -1) {
                $gnNB_tv_fontDesc->set_style('italic');
             }
             $gnNB_tv->modify_font($gnNB_tv_fontDesc);
          }

          # TextBufferを取得して初期化(必要ないかも)
          my $gnNB_tvbuf = $gnNB_tv->get_buffer;
          $gnNB_tvbuf->set_text('');
          
          # ScrolledWindowに入れる
          my $gnNB_tvsw = Gtk2::ScrolledWindow->new(undef, undef);
          $gnNB_tvsw->set_shadow_type('etched-out');
          $gnNB_tvsw->set_policy('automatic', 'automatic');
          $gnNB_tvsw->add($gnNB_tv);

          # スレの名前からタブ用のラベルを作成
          my ($gnSureNum, $gnSureName) = split(/: /, $gnSureInfo[0]);
#          my $gnSureInfoStr = sprintf("%s", $gnSureInfo[10]);
#          my $gnNB_lb = "";
#          if ($gnDebugFlag) { 
#             $gnNB_lb = "${gnSureName}\n${gnSureInfoStr}:${gnSVI_idx}";
#             $gnNB_lb = Gtk2::Label->new($gnNB_lb);
#          }else{
          my $gnNB_lb = Gtk2::Label->new($gnSureName);
#          }

          # フォントサイズを小さくする
          # デフォルトの85%に【今後】将来的には設定できるように？
          my $gnNB_lb_context = $gnNB_lb->get_pango_context();
          my $gnNB_lb_fontDesc = $gnNB_lb_context->get_font_description();
          my $gnNB_lb_fontsize = $gnNB_lb_fontDesc->get_size();
          my $gnNB_lb_newsize = int($gnNB_lb_fontsize * 0.85);
          $gnNB_lb_fontDesc->set_size($gnNB_lb_newsize);
          $gnNB_lb->modify_font($gnNB_lb_fontDesc);
       
          # ファイルパスを関数gnParseSureにあたえると
          # 整形済みのTextBufferが返って来るので$gnNB_tvbuf(TextBuffer)に格納
          my $gnSureFN = $logdir . $gnSureInfo[10];
          if($gnDebugFlag) { print "$gnSureFN\n"; }
          $gnSureFN = &env_FNconv($gnSureFN);

          my $gnSureFN1 = $gnSureInfo[10];
          if($gnDebugFlag) { print "$gnSureFN1\n"; }
          $gnSureFN1 = &env_FNconv($gnSureFN1);
          $gnNB_tvbuf = &gnParseSure($gnSureFN, $gnSureFN1);
          $gnNB_tv->set_buffer($gnNB_tvbuf);

          # カーソルを先頭に(後々変える必要あり)
          $gnNB_tvbuf->place_cursor($gnNB_tvbuf->get_start_iter);
       
          # 用意できたページをNotebookに追加
          my $gnSureView_newpagenum = $gnNotebook->append_page($gnNB_tvsw, $gnNB_lb);
          if($gnDebugFlag) { print "page appended\(normal, num=$gnSureView_newpagenum\).\n"; }
       
          # Notebookを再表示(なぜかこれをしないと追加したページが表示されない)
          $gnNotebook->show_all;
          $gnNotebook->set_current_page($gnSureView_newpagenum);
          if($gnDebugFlag) { print "show again\(normal\).\n"; }

          # 管理配列にスレの情報を書き込み
          my $gnSureInfoStr = sprintf("%s", $gnSureInfo[10]);
          my @gnNewSureInfo = ($gnSureInfoStr, $gnSureView_newpagenum);
          if($gnDebugFlag) { print "added\n" . Dumper("@gnNewSureInfo"); }
          
          # デバッグフラグがたっていたら、ページラベルに情報を追加
          if ($gnDebugFlag) {
             my $gnNB_lb_childwidget = $gnNotebook->get_nth_page($gnSureView_newpagenum);
             my $gnNB_lb_label = $gnNotebook->get_tab_label($gnNB_lb_childwidget);
             $gnNB_lb_label->set_text("${gnSureName}\n${gnSureInfoStr}:${gnSureView_newpagenum}");
          }
          
          push(@gnSureViewInfo, ([$gnSureInfoStr, $gnSureView_newpagenum]));
          undef(@gnNewSureInfo);
          if($gnDebugFlag) { print "whole\n" . Dumper(@gnSureViewInfo) . "\n"; }
       
       }

   }
   
   &gnSIFilUpdate;

   # switch_pageシグナルのブロックを解除
   $gnNotebook->signal_handler_unblock($gnSignalHandler->{gnNote}{switch_page});

}

sub gnSIFilUpdate {
   # スレフィルタを更新する
   # つまり現在のテキストボックスの入力内容をプルダウンメニューに追加する
   my $gnSIFil = $gnGUIxml->get_widget('gnSI_fil');
   my $gnSIFil_txt = $gnSIFil->child->get_text;
   
   if ($gnSIFil_txt =~ m/^ *$/) {
     # スレフィルタが空白しか含まない場合はそのままリターンする
     return;
   }   

   # フィルタリストのなかになかったらリストに追加
   my $gnSIFilHit = FALSE;
   my $cnt = 0;
   my @gnSIFilHashKeys = keys(%{$gnEnvArg{'SelectText'}});
   foreach my $val(@gnSIFilHashKeys) {
     if ($gnEnvArg{'SelectText'}{$val} eq $gnSIFil_txt) {
        $gnSIFilHit = TRUE;
     }
     $cnt++;
   }
  
  if ($gnSIFilHit == FALSE) {
     $cnt++;
     my $cntstr = sprintf("%02d", $cnt);
     $gnEnvArg{'SelectText'}{$cntstr} = $gnSIFil_txt;
     $gnSIFil->append_text($gnSIFil_txt);
  }

}

sub gnSureUpdate {
   # 選択したスレを更新する
   #
   # 引数: 1: Gtk2::TreeView

   # 引数(Gtk2::TreeView)を代入
   my $gnTrView = $_[0];
   if($gnDebugFlag) { print "gnSureUpdate\n"; }
   
   # Notebook内のページを再読み込みするか
   my $gnIsNoteReload = FALSE;

   # 選択している部分を取得(返り値はGtk2::TreeSelecton)
   my $gnTreeSel = $gnTrView->get_selection;
   my ($gnTreePath,) = $gnTrView->get_cursor;

   # 得られたTreeSelectionからTreeModelとTreeIterを得る
   my ($gnMyTreeModel, $gnTM_iter) = $gnTreeSel->get_selected;
   my @gnTreeSelRows = $gnTreeSel->get_selected_rows;

   # TreeIterを引数にしてget_valueすると、選択した行の配列が得られる
   # [10]の中身が板のディレクトリとdatファイル名
   my @gnSureInfo = $gnMyTreeModel->get_value($gnTM_iter);
   my ($linktgt_ita, $linktgt_sure) = split(/\//, $gnSureInfo[10]);
   if($gnDebugFlag) { print "gnSureUpdate:\t$gnSureInfo[10]\n"; }
   
   # 板のURL取得
   # 【俺メモ】要改善：板一覧をいじった後にスレ取得しようとすると、現在の板が取得できないので
   #                  URLが取得できずエラーになる
   my $gnIIBuf = $gnGUIxml->get_widget('gnII');
   my ($gnMyTree, $gnMyTreePath, $gnMyTreeCol) = $gnIIBuf;

   # 選択している部分を取得(返り値はGtk2::TreeSelecton)
   my $gnTreeSel2 = $gnMyTree->get_selection;

   # 得られたTreeSelectionからTreeModelとTreeIterを得る
   my ($gnMyTreeModel2, $gnTM_iter2) = $gnTreeSel2->get_selected;

   # TreeIterを引数にしてget_valueすると、選択した行の配列が得られる
   my ($gnItaName, $gnItaUrl) = $gnMyTreeModel2->get_value($gnTM_iter2);
   
   # リクエストヘッダを生成
   my $gnItaDatURL = $gnItaUrl . "dat\/" . $linktgt_sure;
   my $gnItaDatFN = $logdir . "\/" . $gnSureInfo[10];
   if($gnDebugFlag) { print "gnSureUpdate:\t$gnItaDatURL\n"; }

   my $gnUpdateFLG = FALSE; # 既にあるファイルの更新なのか新規取得なのか
   my $gnDatSize = 0;
   
   my $gnDatLastModified = time2str(0);
   my $gnItaDatFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaDatFN);
   if (-e $gnItaDatFN_1) {
      $gnUpdateFLG = TRUE;
      my @gnDatStat = stat($gnItaDatFN_1);
      $gnDatLastModified = time2str($gnDatStat[9]);
      $gnDatSize = $gnDatStat[7];
      $gnDatSize++;
   }
   if($gnDebugFlag) { print "gnSureUpdate:\t$gnDatLastModified\n"; }
   
   # datファイルを取得
   my ($gnGetDatRST, $gnGetDatRes) = &gnGetDat($gnItaUrl, $linktgt_sure);
   
   if ($gnGetDatRST eq "gn200OK") {
       my $gnNow = time();
       # 取得数を更新
       my $gnDatNumRow = 0;
       my $gnItaDatFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaDatFN);
       open(gnDatF2, "$gnItaDatFN_1");
       while(<gnDatF2>) {
          $gnDatNumRow++;
       }
       close(gnDatF2);
       my $gnFolderIdxUpdateFLG = "";
       my $gnFolderIdxGetValFLG = "";
       $gnFolderIdxUpdateFLG = &gnFolderIdxUpdate($logdir . "\/" . $linktgt_ita . "/Folder.idx",
                                                        $linktgt_sure,
                                                        "3",
                                                        sprintf("%X", $gnDatNumRow)
                                                        );
       $gnFolderIdxUpdateFLG = &gnFolderIdxUpdate($logdir . "\/" . $linktgt_ita . "/Folder.idx",
                                                        $linktgt_sure,
                                                        "12",
                                                        sprintf("%X", $gnDatNumRow)
                                                        );
       # 取得日時が0なら更新
       $gnFolderIdxUpdateFLG = &gnFolderIdxUpdate($logdir . "\/" . $linktgt_ita . "/Folder.idx",
                                                           $linktgt_sure,
                                                           "5",
                                                           sprintf("%X",$gnNow+32400));  # quick-hack
       # 最終更新日を取得
       my $gnSureUpdateTime = str2time($gnGetDatRes->header('Last-Modified'));
       
       $gnFolderIdxUpdateFLG = &gnFolderIdxUpdate($logdir . "\/" . $linktgt_ita . "/Folder.idx",
                                                           $linktgt_sure,
                                                           "6",
                                                           sprintf("%X", $gnSureUpdateTime+32400)); # quick-hack

       if($gnDebugFlag) { print "gnSureUpdate:\tLast-Modified\: $gnSureUpdateTime"; }
       &gnLogger("Folder.idxを更新しました：" . $logdir . "\/" . $linktgt_ita . "/Folder.idx");
       if($gnDebugFlag) { print "gnSureUpdate:\tupdate aquired.\n"; }
       
       # スレ一覧をアップデート
       &gnSureIchiranSetVal($gnSureInfo[10], 1, $gnDatNumRow);
       &gnSureIchiranSetVal($gnSureInfo[10], 2, $gnDatNumRow);
       my ($ss, $mm, $hh, $DD, $MM, $YY, $WKD, $YD, $ID) = localtime($gnNow);
       $MM++; $YY += 1900;
       my $gnGetTime = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $YY, $MM, $DD, $hh, $mm, $ss);
       &gnSureIchiranSetVal($gnSureInfo[10], 7, $gnGetTime);

       ($ss, $mm, $hh, $DD, $MM, $YY, $WKD, $YD, $ID) = localtime($gnSureUpdateTime);
       $MM++; $YY += 1900;
       my $gnLastUpdate = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $YY, $MM, $DD, $hh, $mm, $ss);
       &gnSureIchiranSetVal($gnSureInfo[10], 9, $gnLastUpdate);
       &gnLogger("スレ一覧の情報を更新しました");
       if($gnDebugFlag) { print "gnSureUpdate:\tupdate sure-list.\n"; }
       
       # ソート順序を再現
       my ($gnSortCol, $gnSortOrder) = $gnMyTreeModel->get_sort_column_id;
       if ($gnSortOrder) {
          my $gnTrViewAfter = $gnGUIxml->get_widget('gnSI');
          my $gnTrViewA_Model = $gnTrViewAfter->get_model;
          $gnTrViewA_Model->set_sort_column_id($gnSortCol, $gnSortOrder);
       }
       if($gnDebugFlag) { print "gnSureUpdate:\tid=$gnSortCol, order=$gnSortOrder\n"; }
       
       # アップデート前のコピーを元にスレ一覧の並び替え状況を復元
       #
       $gnTrView->scroll_to_point(0, 0);
       my $gnNewPath = &gnSureSelectFromDat($gnSureInfo[10]);
       $gnTrView->set_cursor($gnNewPath);
       $gnTrView->scroll_to_cell($gnNewPath);

       # スレを再読み込み
       &gnSureSelect($gnTrView, TRUE);
       if($gnDebugFlag) { print "gnSureUpdate:\t\(force\)about to re-show.\n"; }
       
   }else{
       &gnLogger("datの取得に失敗しました\($gnItaDatURL\)");
       if($gnDebugFlag) { print "gnSureUpdate:\tsure update canceled.\n"; }
   }

   if($gnDebugFlag) { print "gnSureUpdate:\tEndSub\n"; }
}

sub gnSureSelectFromDat {
   # "xxxxx/xxxxx.dat"をキーにしてスレ一覧の該当行(Gtk2::TreePath)を返す
   #
   # 引数: 1: 文字列(xxxxxx/xxxxx.dat)
   # 返り値: Gtk2::TreePath
   my $gnDatStr = $_[0];
   my $gnRetPath;
   
   my $gnTrV = $gnGUIxml->get_widget('gnSI');
   my $gnTrVModel = $gnTrV->get_model;
   my $gnTrvModel_iter = $gnTrVModel->get_iter_first;
   my $gnTrVModelR;
   while ($gnTrvModel_iter) {
      $gnTrVModelR = $gnTrVModel->get($gnTrvModel_iter, 10);
      if ($gnTrVModelR eq $gnDatStr) {
         $gnRetPath = $gnTrVModel->get_path($gnTrvModel_iter);
         last;
      }
   $gnTrvModel_iter = $gnTrVModel->iter_next($gnTrvModel_iter);
   }
   
   return($gnRetPath);
}

sub gnSureIchiranSetVal {
   # スレ一覧の要素を書き換える関数
   #
   # 引数: 1: 文字列(xxxxxx/xxxxx.datの書式で記述。更新する行を指定)
   #       2: 整数(0スタート。更新する列を指定)
   #       3: 文字列(更新内容を指定(この関数内で型変換などは特にはしない))
   my $gnDatStr = $_[0];
   my $gnSelCol = $_[1];
   my $gnUpdtVar = $_[2];
   
   my $gnTrV = $gnGUIxml->get_widget('gnSI');
   my $gnTrVModel = $gnTrV->get_model;
   my $gnTrvModel_iter = $gnTrVModel->get_iter_first;
   my $gnTrVModelR;
   while ($gnTrvModel_iter) {
      $gnTrVModelR = $gnTrVModel->get($gnTrvModel_iter, 10);
      if ($gnTrVModelR eq $gnDatStr) {
         $gnTrVModel->set($gnTrvModel_iter,
                            $gnSelCol, $gnUpdtVar);
         last;
      }
   $gnTrvModel_iter = $gnTrVModel->iter_next($gnTrvModel_iter);
   }
   

}

sub gnSetTreeViewOrder {

}

sub gnGetDat {
   # datファイル名を指定してレスを取得するラッパー関数
   # 2ch, bbspinkに対応して、notfoundに対してURLを変えながらdatの取得を試みる
   #
   # 引数: 1: 板のURL(http://xxx.xxx.xxx/yyyy/)
   #       2: datファイル名(xxxxx.dat)
   # 返り値 1: 文字列(ステータスコード: "gn200OK"=正常完了, "gn404NOTFOUND"=dat存在せず, "gn999UNKNOWN"=不明のエラー)
   #        2: HTTP::Response構造体(返り値1が"gn200OK"の場合のみ含まれる。返り値1がそれ以外の場合はNULLとなる)
   my $gnDatURL = $_[0];
   my $gnDatFN = $_[1];
   my $gnGetDatRetVal1;
   my $gnGetDatRetVal2;
   if($gnDebugFlag) { print ("gnGetDat\:\ngnGetDat-Filename/:$gnDatFN"); }

   my @gnDatURLVal = split(/\//, $gnDatURL);
   my $gnDatURLValCnt = @gnDatURLVal;
   if($gnDebugFlag) { print ("$gnDatURL\n" . "$gnDatURLVal[$gnDatURLValCnt-1]\n"); }
   
   my $gnItaDatFN = $logdir .
                      $gnDatURLVal[$gnDatURLValCnt-1] . "\/" .
                      $gnDatFN;
   if($gnDebugFlag) { print "$logdir\n" . "$gnDatURL\n" . "$gnDatURLVal[$gnDatURLValCnt-1]\n" . "$gnItaDatFN\n"; }

   my $gnDatURLFLG = "else";
   my $gnDatURLFLG1;
   $gnDatURLFLG1 = index($gnDatURL, "2ch\.net");
   if ($gnDatURLFLG1>-1) { $gnDatURLFLG = "2ch"; }
   $gnDatURLFLG1 = index($gnDatURL, "bbspink\.com");
   if ($gnDatURLFLG1>-1) { $gnDatURLFLG = "bbspink"; }
   if($gnDebugFlag) { print "$gnDatURLFLG\n"; }
   
   my $gnReq = HTTP::Request->new;
   my $gnUA  = LWP::UserAgent->new;
   my $gnHed = HTTP::Headers->new;
   $gnUA->agent($gnUsrAgent);
   $gnUA->requests_redirectable([]); # 自動リダイレクト禁止
   if($gnEnvArg{'ReadProxy'}{'Proxy'} == 1) {
      my $gnUA_proxystr = "http\:\/\/" . $gnEnvArg{'ReadProxy'}{'Address'} . "\:" . $gnEnvArg{'ReadProxy'}{'Port'} . "\/";
      $gnUA->proxy('http', $gnUA_proxystr);
   }

   my $gnItaDatFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnItaDatFN);
   if (-e $gnItaDatFN_1) {
         my @gnDatStat = stat($gnItaDatFN_1);
         my $gnDatLastModified = time2str($gnDatStat[9]);
         my $gnIfModSince = $gnDatLastModified;
         chop($gnIfModSince); chop($gnIfModSince); chop($gnIfModSince); chop($gnIfModSince);
         my $gnDatSize = $gnDatStat[7];
         
         # 更新用ヘッダの生成
         $gnHed->header ( 'Last-Modified' => $gnDatLastModified,
                            'Accept' => "text\/html, \*\/\*",
                            'If-Modified-Since' => $gnIfModSince . " \+0900",
                            'Cache-control' => "no-cache",
                            'Pragma' => "no-cache",
                            'Range' => "$gnDatSize-");
         $gnHed->remove_header('TE', 'Conection', 'Date');
   }else{
         my $gnDatLastModified = time2str(0);
         
         # 新規取得用ヘッダの生成
         $gnHed->header ( 'Last-Modified' => $gnDatLastModified,
                            'Accept' => "text\/html, \*\/\*",
                            'Accept-Encoding' => 'gzip',
                            'If-Modified-Since' => $gnDatLastModified);
   }
             
   my $gnUA_res;
   my $gnUA_rescode;
   my $gnDatURL1;
   
   if (($gnDatURLFLG eq "2ch") || ($gnDatURLFLG eq "bbspink")) {
      		my $gnDatFN1;
      		my $gnDatFN2;
      			
      		$gnDatURL1 = $gnDatURL . "dat\/" . $gnDatFN;
      		$gnReq->url($gnDatURL1);
      		$gnUA->default_headers($gnHed);
      		$gnReq->method('GET');
      		#$gnHed->remove_header('TE', 'Conection', 'Date');
      		$gnUA_res = $gnUA->request($gnReq);
      		if($gnDebugFlag) { print "First Try\!\!\($gnDatURL1\)\n"; }
      		&gnLogger("アクセス結果\($gnDatURL1\)：" . $gnUA_res->code);
      			
      		if (!($gnUA_res->is_success)) {
      			   $gnUA_rescode = substr($gnUA_res->code, 0, 1);
      			   if ($gnUA_rescode == "5") {
      			      # 500番台はテクニカルエラー
      			      
      			   } elsif (!($gnUA_res->code == "304")) {
      				my $gnDatFNLength = length($gnDatFN);
      				if ($gnDatFNLength == 13) {
      					$gnDatFN1 = substr($gnDatFN, 0, 3);
      					$gnDatURL1 = $gnDatURL . "kako\/" . "$gnDatFN1\/" . $gnDatFN . "\.gz";
      					$gnReq->url($gnDatURL1);
      					$gnReq->method('GET');
      					$gnUA_res = $gnUA->request($gnReq);
      					if ($gnDebugFlag) { print "Second Try\(1\)\!\!\($gnDatURL1\)\n"; }
      					&gnLogger("アクセス結果\($gnDatURL1\)：" . $gnUA_res->code);

      			
      					if (!($gnUA_res->is_success)) {
      						$gnDatFN1 = substr($gnDatFN, 0, 3);
      						$gnDatURL1 = $gnDatURL . "kako\/" . "$gnDatFN1\/" . $gnDatFN;
      						$gnReq->url($gnDatURL1);
      						$gnReq->method('GET');
      						$gnUA_res = $gnUA->request($gnReq);
      						if($gnDebugFlag) { print "Second Try\(2\)\!\!\($gnDatURL1\)\n"; }
      						&gnLogger("アクセス結果\($gnDatURL1\)：" . $gnUA_res->code);
      					}
      				}else{

      				
      					$gnDatFN1 = substr($gnDatFN, 0, 4);
      					$gnDatFN2 = substr($gnDatFN, 0, 5);
      					$gnDatURL1 = $gnDatURL . "kako\/" . "$gnDatFN1\/" . "$gnDatFN2\/" . $gnDatFN . "\.gz";
      					$gnReq->url($gnDatURL1);
      					$gnReq->method('GET');
      					$gnUA_res = $gnUA->request($gnReq);
      					if($gnDebugFlag) { print "Third Try\(1\)\!\!\($gnDatURL1\)\n"; }
      					&gnLogger("アクセス結果\($gnDatURL1\)：" . $gnUA_res->code);


      					if (!($gnUA_res->is_success)) {
      						$gnDatFN1 = substr($gnDatFN, 0, 4);
      						$gnDatFN2 = substr($gnDatFN, 0, 5);
      						$gnDatURL1 = $gnDatURL . "kako\/" . "$gnDatFN1\/" . "$gnDatFN2\/" . $gnDatFN;
      						$gnReq->url($gnDatURL1);
      						$gnReq->method('GET');
      						$gnUA_res = $gnUA->request($gnReq);
      						if($gnDebugFlag) { print "Third Try\(2\)\!\!\($gnDatURL1\)\n"; }
      						&gnLogger("アクセス結果\($gnDatURL1\)：" . $gnUA_res->code);
      					}
      				}
      			}
      		}
   }else{
      		# 今のところ2ch.netとbbspink.com以外のログ取得は
      		# 対応していませんorz
      		&gnLogger("2ch.netとbbspink.com以外のログ取得は対応していません");
   }
   
   if ($gnUA_res) {
      if($gnUA_res->is_success) {
      	$gnGetDatRetVal1 = "gn200OK";
      }else{
      	if ($gnUA_res->code == "304") {
      	   $gnGetDatRetVal1 = "gn302NOCHANGE";
      	}elsif ($gnUA_rescode == "5") {
      	   $gnGetDatRetVal1 = "gn" . $gnUA_res->code . "SERVERERROR";
      	}else{
      	   $gnGetDatRetVal1 = "gn404NOTFOUND";
      	}
      }
   }else{
      $gnGetDatRetVal1 = "gn999NOFUNCTION";
   }
   #
   # ここから下の処理は将来的には別関数として処理
   #
   if($gnUA_res->is_success) {
       # datを取得したのでローカルに保存
       my $gnContEnc = $gnUA_res->header('Content-Encoding');
       if($gnDebugFlag) { print ("Content-Encoding\: $gnContEnc\n"); }
       
       
       
       my $gnContBuf = "";
       if ($gnContEnc) {
          if($gnContEnc eq "gzip") {
             $gnContBuf = Compress::Zlib::memGunzip($gnUA_res->content);
          }else{
          $gnContBuf = $gnUA_res->content;
          }
       }else{
          $gnContBuf = $gnUA_res->content;
       }
       my $gnResCode = $gnUA_res->code;
       if($gnResCode eq "200") { # 全体を正常取得
                     my $gnModTimeStr = $gnUA_res->header('Last-Modified');
                     my $gnModTime = str2time($gnModTimeStr);
                     
                     my $gnItaDatFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}),, $gnItaDatFN);
                     open(gnDatF, ">$gnItaDatFN_1");
                     print gnDatF $gnContBuf;
                     close(gnDatF);
                     
                     # 更新日時はサーバ側の最終更新と同じにする
                     utime($gnModTime, $gnModTime, $gnItaDatFN);
                     &gnLogger("スレ全体を取得しました");
       }elsif($gnResCode eq "206") { # 一部(更新された部分)を正常取得
                     my $gnModTimeStr = $gnUA_res->header('Last-Modified');
                     my $gnModTime = str2time($gnModTimeStr);
                     
                     my $gnItaDatFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}),, $gnItaDatFN);
                     open(gnDatF, ">$gnItaDatFN_1");
                     print gnDatF $gnContBuf;
                     close(gnDatF);
                     
                     # 更新日時はサーバ側の最終更新と同じにする
                     #my $gnModTime = str2time($gnModTimeStr);
                     &gnLogger("差分を取得しました");
       }else {
                     &gnLogger("対応していないリターンコードです\($gnResCode\)");
       }

       my $gnNow = time();

   }else{
       &gnLogger("datの取得に失敗しました");
       my $gnResCode = $gnUA_res->code;
       if($gnResCode eq "302") { # bbspinkだけ？notfond
                     &gnLogger("datファイルが存在しません\(code\:302\)");
       }elsif($gnResCode eq "304") { # 更新なし
                     &gnLogger("スレは更新されていません\(code\:304\)");
       }elsif($gnResCode eq "404") { # datがなかった
                     &gnLogger("datファイルが存在しません\(code\:404\)");
       }else {
                     &gnLogger("対応していないリターンコードです\(code\:$gnResCode\)");
       }
   }

   return($gnGetDatRetVal1, $gnUA_res);
}

sub gnFolderIdxGetVal {
   # Folder.idx内の項目を返す関数
   #
   # 引数: 1: 文字列(Folder.idxへのフルパス)
   #       2: 文字列(xxxxxx.dat で取得する行を指定)
   #       3: 整数(0スタート。取得する列)
   # 返り値: 文字列(エラーの場合はNULLを返す)
   my $gnFIdxFN = $_[0];
   my $gnFIdxDatName = $_[1];
   my $gnFIdxCol = $_[2];
   my $retval = "";

   my $gnFIdxFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnFIdxFN);
   if (-e $gnFIdxFN_1) {
      open(gnFIdxFrom, "$gnFIdxFN_1");
      while(<gnFIdxFrom>) {
         my $buf = $_; chomp($buf);
         $buf = Encode::decode('shiftjis', $buf);
         
         my $isHit = index($buf, $gnFIdxDatName);
         if($isHit>0) {
            # 該当行
            my $separator = chr(001);
            my @colsbuf = split($separator, $buf);
            $retval = $colsbuf[$gnFIdxCol];
         }
      }
      close(gnFIdxFrom);
            
      &gnLogger("Folder.idx\($gnFIdxFN\)の値：$retval");
      return("$retval");
      
   }else{
      &gnLogger("Folder.idxが存在しません：$gnFIdxFN");
      return("");
   }
}

sub gnFolderIdxUpdate {
   # Folder.idx内の項目を更新する関数
   #
   # 引数: 1: 文字列(Folder.idxへのフルパス)
   #       2: 文字列(xxxxxx.dat で更新する行を指定)
   #       3: 整数(0スタート。更新する列)
   #       4: 文字列(更新する内容)
   # 返り値: TRUE/FALSE(TRUE=成功。FALSE=失敗)
   my $gnFIdxFN = $_[0];
   my $gnFIdxDatName = $_[1];
   my $gnFIdxCol = $_[2];
   my $gnFIdxUpdateBuf = $_[3];
   
   my $gnFIdxFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}),, $gnFIdxFN);
   if (-e $gnFIdxFN_1) {
      my $gnFIdxTmpFN = $gnFIdxFN . &genRandStr("8");
      my $gnFIdxFN_2 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnFIdxFN);
      my $gnFIdxTmpFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnFIdxTmpFN);
      open(gnFIdxFrom, "$gnFIdxFN_2");
      open(gnFIdxTo, ">$gnFIdxTmpFN_1");
      while(<gnFIdxFrom>) {
         my $buf = $_; chomp($buf);
         $buf = Encode::decode('shiftjis', $buf);
         
         my $isHit = index($buf, $gnFIdxDatName);
         if($isHit<0) {
            # 該当行じゃない場合はそのままoutfileに書き出し
            my $outbuf = Encode::encode('shiftjis', $buf);
            print gnFIdxTo "$outbuf\n";
         }else{
            # 該当行
            my $separator = chr(001);
            my @colsbuf = split($separator, $buf);
            $colsbuf[$gnFIdxCol] = $gnFIdxUpdateBuf;
            
            my $outbuf = "";
            my $cnt = 0;
            foreach(@colsbuf) {
               $outbuf = $outbuf . $colsbuf[$cnt] . chr(001);
               $cnt++;
            }
            
            $outbuf = Encode::encode('shiftjis', $outbuf);
            print gnFIdxTo "$outbuf\n";

         }
      }
      close(gnFIdxTo);
      close(gnFIdxFrom);
      
      rename($gnFIdxTmpFN_1, $gnFIdxFN_2);
      
      if($gnDebugFlag) { print "Update Complete Folder.idx\: $gnFIdxFN\n"; }
      return(TRUE);
      
   }else{
      if($gnDebugFlag) { print "File miss\: $gnFIdxFN_1\n"; }
      return(FALSE);
   }
}

sub gnFolderIdxPush {
   # 指定したFolder.idxに指定したデータを追加する関数
   #
   # 引数: 1: 文字列(Folder.idxへのフルパス)
   #       2: arrayへのリファレンス(arrayにはFolder.idxの書式で入れるべきデータが1エントリ分入っている)
   # 返り値 : TRUE/FALSE(TRUE=成功、FALSE=失敗)
   my $idxfn = $_[0];
   my $varref = $_[1];
   my @var = @$varref;
   
   my $cnt = 0;
   $idxfn = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $idxfn);
   if (-e $idxfn) {
      open(gnFIP, "$idxfn");
         while(<gnFIP>) {
            $cnt++;
         }
      close(gnFIP);
   }else{
      open(gnFIP, ">$idxfn");
         print gnFIP "$gnIdxFileVersion\n";
      close(gnFIP);
   }
   
   my $buf = sprintf("%X", $cnt);
   
   my $cnt2 = 0;
   foreach (@var) {
      $buf = $buf . chr(001) . $var[$cnt2];
      $cnt2++;
   }
   open(gnFIP, ">>$idxfn");
      print gnFIP "$buf\n";
   close(gnFIP);
   
   return(TRUE);
}

sub gnMakeFolderIni {
   # Folder.iniを初期作成する関数
   #
   # 引数: 1: 文字列(Folder.iniへのフルパス)
   # 返り値: なし
   my $gnFI_path = $_[0];
   
   my %gnMFI;
   $gnMFI{'Status'}{'RoundDate'} = &gnGetNowTime;
   $gnMFI{'Status'}{'LastModified'} = &gnGetNowTime;
   $gnMFI{'Status'}{'LastGetTime'} = &gnGetNowTime;
   $gnMFI{'Status'}{'UnRead'} = "0";
   $gnMFI{'Cookie'}{'SPID'} = "";
   $gnMFI{'Cookie'}{'PON'} = "";
   $gnMFI{'Kotehan'}{'Name'} = "";
   $gnMFI{'Kotehan'}{'Mail'} = "";
   $gnMFI{'BoardInformation'}{'SETTINGTXTTime'} = "1899\/12\/30";
   $gnMFI{'BoardInformation'}{'HEADTXTTime'} = "1899\/12\/30";
   $gnMFI{'BoardInformation'}{'IsSETTINGTXT'} = "0";
   $gnMFI{'BoardInformation'}{'IsHEADTXT'} = "0";
   $gnMFI{'BoardInformation'}{'TitlePictureURL'} = "";
   
   &gnSaveEnv($gnFI_path, \%gnMFI);

}

sub gnGetNowTime {
   # 現在の時刻を取得して文字列で返す関数(ログ記録用)
   # 引数: なし
   # 返り値: 文字列(YYYY/MM/DD HH:MM:SS)
   my ($ss, $mm, $hh, $DD, $MM, $YY, $WKD, $YD, $ID) = localtime(time);
   $MM++; $YY += 1900;
   my $retval = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $YY, $MM, $DD, $hh, $mm, $ss);
   return($retval);
}

sub gnLogger {
  # ログウィンドウに時刻をつけてログ表示する関数
  # 引数: 文字列(ログに表示する文字列)
  # 返り値: なし
  
  # 引数を取得
  my $gnLogStr = $_[0];
#  $gnLogStr = Encode::decode('utf8', $gnLogStr);
  
  # 現在の時刻を取得
  my $gnLogTime = &gnGetNowTime;
  
  # ログウィジェットを取得
  my $gnLogView = $gnGUIxml->get_widget('gnLogView');
  my $gnLV_buf = $gnLogView->get_buffer;
  my $gnLVB_iter = $gnLV_buf->get_end_iter;

  # ログ用文字列を生成
  $gnLogStr = $gnLogTime . " " . $gnLogStr . "\n";

  # ログを書き込み
  $gnLV_buf->insert($gnLVB_iter, $gnLogStr);

  # ログの末尾にスクロール
  my $gnLV_Sc = $gnGUIxml->get_widget('gnLogViewSW');
  my $gnLV_Sc_va = $gnLV_Sc->get_vadjustment;
  $gnLV_Sc_va->set_value($gnLV_Sc_va->upper);

}

sub gnParseSure {
   # ファイルから読み込まれたスレの内容を
   # 表示用に整形する。
   # 引数: 1: 文字列(スレのファイルパス)
   #       2: 文字列(スレのファイルパスから環境変数のパスを抜いたもの(ascii2d/xxxxxxxx.dat))
   # 返り値: 整形済みTextBuffer
   my $gnSureFN = $_[0];
   my $linktgt_base = $_[1];
   my ($linktgt_ita, $linktgt_sure) = split(/\//, $linktgt_base);
   my ($linktgt_sure2, $sute) = split(/\./, $linktgt_sure);

   my $gnTmpBuf;
   
   my $gnSureFN_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnSureFN);
   if ($gnSureFN_1) {
      open(gnSR, $gnSureFN_1);
        $gnTmpBuf = join '', <gnSR>;
     close(gnSR);
   }else{
      &gnLogger("【異常動作】スレの読み込みに失敗しました。ファイル名：$gnSureFN");
   }
       
   my @gnSureBuf = split(/\n/, $gnTmpBuf);
   
   # return用のTextBufferを作成。iterも作っておく
   my $rescnt = 1;
   my $gnSureTBuf = Gtk2::TextBuffer->new;
   &gnCreateTag($gnSureTBuf);
   my $gnSureTBuf_iter = $gnSureTBuf->get_start_iter;
   
   
   # 1レスづつ変換
   foreach(@gnSureBuf){
     # レス(1行)を読んで各要素に分割
     my $LineBuf = $_; chomp($LineBuf);
     # 内容をレンダリング
     &gnAppendRes($gnSureTBuf, $LineBuf, $linktgt_ita, $linktgt_sure2, $rescnt);
   
     # レス番カウンタをインクリメント
     $rescnt++;
   }
   
   return($gnSureTBuf);

}



sub gnResPopup {
   # レスポップアップ用関数
   # (レス内の">>1"にマウスを乗せた場合に出るポップアップウィンドウを表示する)
   # 引数: 文字列("<板フォルダ名>/<スレ番号>/<レス番号>")
   # 返り値: Gtk2::Window(実際に表示されているポップアップウィンドウ。
   #                      マウスが外れた際にはこれを使ってdestroyする)
   my $gnReplyTo = $_[0];
   
   # ポップアップウィンドウを取得する
   my $gnPopxml = Gtk2::GladeXML->new($gladefn, 'gnPopupText');
   $gnPopxml->signal_autoconnect_from_package('main');
   my $gnPopupWin = $gnPopxml->get_widget('gnPopupText');
   
   # TextViewウィジェットを取得する
   my $gnPT_Text = $gnPopxml->get_widget('gnPT_Text');
   my $gnPTT_buf = $gnPT_Text->get_buffer;
   my $gnPTTbuf_iter = $gnPTT_buf->get_end_iter;

   # フォントを指定する
   my $gnPT_Text_context = $gnPT_Text->get_pango_context();
   my $gnPT_Text_fontDesc = $gnPT_Text_context->get_font_description();
   my $gnPT_Text_fontscale = Gtk2::Pango->scale;
   if($gnEnvArg{'Browser'}{'gnBrowserFontUseCustom'} == TRUE) {
      $gnPT_Text_fontDesc->set_family($gnEnvArg{'Browser'}{'BrowserFontName'});
      $gnPT_Text_fontDesc->set_size($gnEnvArg{'Browser'}{'BrowserFontSize'} * $gnPT_Text_fontscale);
      if($gnEnvArg{'Browser'}{'BrowserFontBold'} != -1) {
         $gnPT_Text_fontDesc->set_weiget('bold');
      }
      if($gnEnvArg{'Browser'}{'BrowserFontItalic'} != -1) {
         $gnPT_Text_fontDesc->set_style('italic');
      }
      $gnPT_Text->modify_font($gnPT_Text_fontDesc);
   }

   # 該当レスを書き込み
   $gnPTT_buf = &gnGetResNo($gnReplyTo, $gnPTT_buf);
   

   # ポップアップウィンドウを表示
   $gnPopupWin->show_all;
   $gnPT_Text->set_buffer($gnPTT_buf);
   
   # ポップアップウィンドウの位置を調整
   &on_gnPopupText_check_resize($gnPopupWin);

   return($gnPopupWin);

}

sub gnImgPopup2 {
   # 画像プレビューポップアップ用関数(プログレッシブダウンロード対応版)
   # (レス内の画像と判断できるURLにマウスを乗せたときにポップアップウィンドウを表示する)
   #
   # 引数: 文字列(画像へのURL)
   # 返り値: 1:Gtk2::Window(実際には表示されているポップアップウィンドウ。
   #                        マウスが外れたときにはこれを使ってdestoryする)
   #         2: Gtk2::Image(画像の更新用)
   my $gnURLTo = $_[0];
   my $gnResizedBuf;
   my $gnResizedWidth;
   my $gnResizedHeight;

   my $gnPopxml = Gtk2::GladeXML->new($gladefn, 'gnPopupImg');
   $gnPopxml->signal_autoconnect_from_package('main');
   my $gnPopupWin = $gnPopxml->get_widget('gnPopupImg');
   my $gnPI_Image = $gnPopxml->get_widget('gnPI_Img');

   if($gnDebugFlag) { print "\(start\)gnImgPopup2\n"; }
   # キャッシュテーブルをチェック
   (my $gnImgCacheHitFLG, $gnTmpfn) = &gnImgCacheQuery($gnURLTo);
#   my $gnPopupSize = $gnEnvArg{'Browser'}{'PreviewSize'} . "00";
   my $gnPopupSize;
   if ($gnPopupImgSt == 1) {
      $gnPopupSize = $gnEnvArg{'Browser'}{'PreviewSize'} . "00";
   }else{
      $gnPopupSize = $gnEnvArg{'Browser'}{'PreviewSizeSt2'} . "00";
   }
   
   if ($gnImgCacheHitFLG == TRUE) {
      # キャッシュヒットしていたら
      if($gnDebugFlag) { print "\(start\)gnImgPopup2-cachehit\n"; }
      if (-e $gnTmpfn) {
         &gnLogger("キャッシュされたファイルを表示します");
         eval { $gnPopupImgBuf = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($gnTmpfn,$gnPopupSize,
                                                                        $gnPopupSize,TRUE); };
         if ($@) {
            $gnPI_Image->set_from_stock('gtk-missing-image', 'GTK_ICON_SIZE_DIALOG');
            ($gnResizedWidth, $gnResizedHeight) = ($gnPopupSize, $gnPopupSize);
         }else{
            $gnPI_Image->set_from_pixbuf($gnPopupImgBuf);
            $gnResizedWidth = $gnPopupImgBuf->get_width;
            $gnResizedHeight = $gnPopupImgBuf->get_height;
         }
      }else{
         $gnPI_Image->set_from_stock('gtk-missing-image', 'GTK_ICON_SIZE_DIALOG');
         ($gnResizedWidth, $gnResizedHeight) = ($gnPopupSize, $gnPopupSize);
      }
   }else{
      # ヒットしていなかったら仮のウィンドウを生成
      if($gnDebugFlag) { print "\(start\)gnImgPopup2-cachemiss\n"; }
      $gnPI_Image->set_from_stock('gtk-refresh', 'GTK_ICON_SIZE_DIALOG');
      ($gnResizedWidth, $gnResizedHeight) = ($gnPopupSize, $gnPopupSize);
   }
         $gnPopupWin->set_size_request($gnResizedWidth, $gnResizedHeight);
   
   $gnPopupWin->show_all;
   
   
   my $gnScreenHeight = Gtk2::Gdk->screen_height;
   my $gnScreenWidth = Gtk2::Gdk->screen_width;
   my $dis = Gtk2::Gdk::Display->get_default;
   my ($scr, $x, $y, $mask) = $dis->get_pointer;
   my ($gnPopupWinX, $gnPopupWinY) = $gnPopupWin->get_position;
   if ($gnPopupWinX == 0) {
      $gnPopupWinX = $x - 50;
   }
   if ($gnPopupWinY == 0) {
      $gnPopupWinY = $y - 50;
   }
   if($gnDebugFlag) { print "gnImgPopup2-regeo\(origin-positiion\)\:\($gnPopupWinX, $gnPopupWinY\)\n"; }

   # ポップアップウィンドウのoriginが
   # ポップアップ表示禁止領域内だったら表示領域外に移動
   if ($gnPopupWinX <= $gnEnvArg{'Browser'}{'AvoidBorderLeft'}) {
      $gnPopupWinX = $gnEnvArg{'Browser'}{'AvoidBorderLeft'} + 1;
   }
   if ($gnPopupWinY <= $gnEnvArg{'Browser'}{'AvoidBorderTop'}) {
      $gnPopupWinY += $gnEnvArg{'Browser'}{'AvoidBorderTop'} + 1;
   }
   
   # ポップアップ画面がウィンドウからはみ出している場合、
   # ポップアップ表示禁止領域内に入っていた場合は
   # 出現位置を調整
   if($gnDebugFlag) { print "gnImgPopup2-regeo\:\($gnPopupWinX, $gnPopupWinY\)\n"; }
   if (($gnPopupWinX + $gnResizedWidth + $gnEnvArg{'Browser'}{'AvoidBorderRight'}) > $gnScreenWidth) { $gnPopupWinX = $gnScreenWidth - $gnResizedWidth - $gnEnvArg{'Browser'}{'AvoidBorderRight'}; }
   if (($gnPopupWinY + $gnResizedHeight + $gnEnvArg{'Browser'}{'AvoidBorderBottom'}) > $gnScreenHeight) { $gnPopupWinY = $gnScreenHeight - $gnResizedHeight - $gnEnvArg{'Browser'}{'AvoidBorderRight'}; }
   $gnPopupWin->move($gnPopupWinX, $gnPopupWinY);
   if($gnDebugFlag) { print "gnImgPopup2-regeo\:\($gnPopupWinX, $gnPopupWinY\)\n"; }
   
   # キャッシュにヒットしていない場合は
   #   1. HTTPリクエストを別スレッドにして呼び出す
   #      別スレッドではURLからデータを取り込みキャッシュファイルに書き込んでいく
   if ($gnImgCacheHitFLG == FALSE) {
      &gnLogger("キャッシュにないので取得します");
      $hold = 1;
      $gnTmpfn = $tmpdir . "/gnTmp" . &genRandStr(12);
      my @val = ("geturl", $gnURLTo, $gnTmpfn);
      if($gnDebugFlag) { print "\(start\)gnImgPopup2-enqueue\: proxy = $gnEnvArg{'ReadProxy'}{'Proxy'}\n"; }
      my $uid;
      my $pass;
      if (!($gnEnvArg{'ReadProxy'}{'UserID'})) { $uid = "-1";}
      if (!($gnEnvArg{'ReadProxy'}{'Password'})) { $pass = "-1";}      
      $queue->enqueue("geturl", $gnURLTo, $gnTmpfn, $gnEnvArg{'ReadProxy'}{'Proxy'},
                        $gnEnvArg{'ReadProxy'}{'Address'}, $gnEnvArg{'ReadProxy'}{'Port'},
                        $uid, $pass);
      my $cachetime = time;
      $gnCacheTbl{'Cache'}{$gnURLTo} = $cachetime . chr(001) . $gnTmpfn;
      
      $gnImgPopup_info{'CacheFN'} = $gnTmpfn;
      $gnImgPopup_info{'CacheTime'} = $cachetime;
      my @gnPop_cachetimear = localtime($cachetime);
      $gnImgPopup_info{'Year'} = $gnPop_cachetimear[5]+1900;
      $gnImgPopup_info{'Month'} = sprintf("%02d", $gnPop_cachetimear[4]+1);
      $gnImgPopup_info{'Day'} = sprintf("%02d", $gnPop_cachetimear[3]);
      $gnImgPopup_info{'Hour'} = sprintf("%02d", $gnPop_cachetimear[2]);
      $gnImgPopup_info{'Minute'} = sprintf("%02d", $gnPop_cachetimear[1]);
      $gnImgPopup_info{'Second'} = sprintf("%02d", $gnPop_cachetimear[0]);

   }


   if($gnDebugFlag) { print "\(end\)gnImgPopup2\n"; }
   my @retval = ($gnPopupWin, $gnPI_Image);
   return(\@retval);
}

sub gnImgBufferUpdate {
   # ポップアップ用画像バッファを定期的にアップデートする
   #
   # 引数:   1: Gtk2::Window(更新対象のポップアップウィンドウ)
   #         2: Gtk2::Image(更新対象のImageオブジェクト)
   #         3: Gtk2::Label(更新対象のウィンドウに含まれるURL(隠しラベルオブジェクト))
   #         4: Gtk2::Label(更新対象のウィンドウに含まれるキャッシュファイルのファイル名(隠しラベルオブジェクト))
   # 返り値: 1:Gtk2::Window(実際には表示されているポップアップウィンドウ。
   #                        マウスが外れたときにはこれを使ってdestoryする)
   #         2: Gtk2::Image(画像の更新用)
   #         3: TRUE/FALSE(TRUE=画像はダウンロード中, FALSE=画像のダウンロードは終了している)

   my $gnPopupWin =$_[0];
   my $gnPI_Image = $_[1];
   my $gnResizedWidth;
   my $gnResizedHeight;
   my $pointx = -1;
   my $pointy = -1;
   
   if($gnDebugFlag) { print "gnImgBufferUpdate-gnpopupwin\:" . Dumper($gnPopupWin) . "\n"; }
   if($gnDebugFlag) { print "gnImgBufferUpdate-gnPI_Image\:" . Dumper($gnPI_Image) . "\n"; }
   my $gnTmpImgfn = $gnTmpfn;
#   my $gnTmpImgEstSz = $fnsize;
#   if($gnDebugFlag) { print "gnImgBufferUpdate-estsize\:" . Dumper($gnTmpImgEstSz) . "\n"; }
   my ($gnPopupWinX, $gnPopupWinY) = $gnPopupWin->get_position;
   if($gnDebugFlag) { print "gnImgBufferUpdate-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)\n"; }
   my $dis = Gtk2::Gdk::Display->get_default;
   my ($scr, $x, $y, $mask) = $dis->get_pointer;
   ($gnPopupWinX, $gnPopupWinY) = $gnPopupWin->get_position;
   if ($gnPopupWinX == 0) {
      $gnPopupWinX = $x - 50;
   }
   if ($gnPopupWinY == 0) {
      $gnPopupWinY = $y - 50;
   }
   if($gnDebugFlag) { print "gnImgBufferUpdate-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)\n"; }
   
#   my $gnPopupSize = $gnEnvArg{'Browser'}{'PreviewSize'} . "00";
   my $gnPopupSize;
   if ($gnPopupImgSt == 1) {
      $gnPopupSize = $gnEnvArg{'Browser'}{'PreviewSize'} . "00";
   }else{
      $gnPopupSize = $gnEnvArg{'Browser'}{'PreviewSizeSt2'} . "00";
   }



   my $gnPopupImgBuf2;
   if ($gnTmpImgfn) {
      eval { $gnPopupImgBuf2 = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($gnTmpImgfn,$gnPopupSize,
                                                                  $gnPopupSize,TRUE); };
      if ($@) {
         if ($gnImgGetSt) {
            $gnPI_Image->set_from_stock('gtk-refresh', 'GTK_ICON_SIZE_DIALOG');
         }else{
            $gnPI_Image->set_from_stock('gtk-missing-image', 'GTK_ICON_SIZE_DIALOG');
         }
         ($gnResizedWidth, $gnResizedHeight) = ($gnPopupSize, $gnPopupSize);
      }else{
         if($gnDebugFlag) { print "gnImgBufferUpdate-pixbuf\:" . Dumper($gnPopupImgBuf2) .
                                        "\n" . Dumper($gnPI_Image) . "\n"; }
         $gnPI_Image->set_from_pixbuf($gnPopupImgBuf2);
         $gnResizedWidth = $gnPopupImgBuf2->get_width;
         $gnResizedHeight = $gnPopupImgBuf2->get_height;
      }
   }else{
      if ($gnImgGetSt) {
         $gnPI_Image->set_from_stock('gtk-refresh', 'GTK_ICON_SIZE_DIALOG');
      }else{
         $gnPI_Image->set_from_stock('gtk-missing-image', 'GTK_ICON_SIZE_DIALOG');
      }
      ($gnResizedWidth, $gnResizedHeight) = ($gnPopupSize, $gnPopupSize);
   }
      $gnPopupWin->set_size_request($gnResizedWidth, $gnResizedHeight);
      $gnPopupWin->resize($gnResizedWidth, $gnResizedHeight);
      
      $dis = Gtk2::Gdk::Display->get_default;
      ($scr, $x, $y, $mask) = $dis->get_pointer;
      my ($wid, $hei) = $gnPopupWin->get_size;
      if (($x + int($wid/2)) >= Gtk2::Gdk->screen_width) { $gnPopupWinX = $x + int($wid/2); }
      if (($y + int($hei/2)) >= Gtk2::Gdk->screen_width) { $gnPopupWinY = $y + int($hei/2); }
      
      # ポップアップウィンドウのoriginが
      # ポップアップ表示禁止領域内だったら表示領域外に移動
      if ($gnPopupWinX <= $gnEnvArg{'Browser'}{'AvoidBorderLeft'}) {
         $gnPopupWinX = $gnEnvArg{'Browser'}{'AvoidBorderLeft'} + 1;
      }
      if ($gnPopupWinY <= $gnEnvArg{'Browser'}{'AvoidBorderTop'}) {
         $gnPopupWinY += $gnEnvArg{'Browser'}{'AvoidBorderTop'} + 1;
      }
   
      # ポップアップ画面がウィンドウからはみ出している場合は
      # 出現位置を調整
      
      if ($gnResizedWidth > 0) {
         my $gnScreenHeight = Gtk2::Gdk->screen_height;
         my $gnScreenWidth = Gtk2::Gdk->screen_width;
         if($gnDebugFlag) { print "gnImgBufferUpdate-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)\n"; }
         if($gnDebugFlag) { print "gnImgBufferUpdate-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)\n"; }
         #if (($gnPopupWinX + $gnResizedWidth) > $gnScreenWidth) { $gnPopupWinX = $gnScreenWidth - $gnResizedWidth; }
         #if (($gnPopupWinY + $gnResizedHeight) > $gnScreenHeight) { $gnPopupWinY = $gnScreenHeight - $gnResizedHeight; }
         #$gnPopupWin->move($gnPopupWinX, $gnPopupWinY);
         if (($gnPopupWinX + $gnResizedWidth + $gnEnvArg{'Browser'}{'AvoidBorderRight'}) > $gnScreenWidth) { $gnPopupWinX = $gnScreenWidth - $gnResizedWidth - $gnEnvArg{'Browser'}{'AvoidBorderRight'}; }
         if (($gnPopupWinY + $gnResizedHeight + $gnEnvArg{'Browser'}{'AvoidBorderBottom'}) > $gnScreenHeight) { $gnPopupWinY = $gnScreenHeight - $gnResizedHeight - $gnEnvArg{'Browser'}{'AvoidBorderBottom'}; }
         $gnPopupWin->move($gnPopupWinX, $gnPopupWinY);
         if($gnDebugFlag) { print "gnImgBufferUpdate-regeo\:\($gnPopupWinX, $gnPopupWinY, $gnResizedWidth, $gnResizedHeight\)"; }

      }
   
   undef($gnPopupImgBuf2);
   
   my @retval = ($gnPopupWin, $gnPI_Image, $gnImgGetSt);
   return(\@retval);
}

sub gnGetResNo {
  # ポップアップウィンドウ用レス取得関数
  # ポップアップ内に表示するレスの内容を取得する
  # 引数１：文字列("<板フォルダ名>/<スレ番号>/<レス番号>")
  # 　　２：GtkTextBuffer(ポップアップウィンドウ内のTextViewにはめこむもの)
  # 返り値：GtkTextBuffer(引数２で受け取ったものにレスが入ったもの)
  
  my $gnResNoInfo = $_[0];
  if ($gnDebugFlag) { print "$gnResNoInfo\n"; }
  my $gnRNI_TextBuf = $_[1];

  my $gnRNI_return = "";
  my $gnRNI_from = 0;
  my $gnRNI_to = 0;
  
  my @gnRNI_info = split(/\//, $gnResNoInfo);
      # index: 0 = 板フォルダ名, 1 = スレ番号, 2 = レス番号
  if($gnRNI_info[2] =~ /-/) {
    ($gnRNI_from, $gnRNI_to) = split(/-/, $gnRNI_info[2]);
    if ($gnRNI_from > $gnRNI_to) {
       my $tmp = $gnRNI_from;
       $gnRNI_from = $gnRNI_to;
       $gnRNI_to = $tmp;
    }
  }else{
    $gnRNI_from = $gnRNI_info[2];
    $gnRNI_to = $gnRNI_info[2];
  }
  my $gnRNI_fn = $logdir.$gnRNI_info[0]."\/".$gnRNI_info[1].".dat";
  
  my $gnRNI_fn_1 = Encode::encode(&env_locale_to_encstr($ENV{'LANG'}), $gnRNI_fn);
  if (-e $gnRNI_fn_1) {
  open(gnRNI, $gnRNI_fn_1) or die "cannot read $gnRNI_fn_1";
     my $gnRNI_cnt = 1;
     while(<gnRNI>) {
        my $gnRNI_RepBuf = $_;
        if(($gnRNI_cnt >= $gnRNI_from) & ($gnRNI_cnt <= $gnRNI_to)) {
           chomp($gnRNI_RepBuf);
           $gnRNI_TextBuf = &gnAppendRes($gnRNI_TextBuf, $gnRNI_RepBuf,
                                             $gnRNI_info[0], $gnRNI_info[1], $gnRNI_cnt);

           $gnRNI_return = "found";
        }
        $gnRNI_cnt++;
     }
  close(gnRNI);
  }else{
     $gnRNI_return = "";
  }

  if($gnRNI_return eq "") {
     &gnLogger("該当の番号がありませんでした");
  };
  return($gnRNI_TextBuf);

}

sub gnCreateTag {
   # レス表示用にGtk2::TextTagを定義する
   # 引数: Gtk2::TextBuffer(レス表示用のTextBufer)
   # 返り値: なし
   
   # 引数を取得
   my $gnBuffer = shift;
   my $gnBuffer_tagtable = $gnBuffer->get_tag_table;
   
   if(!($gnBuffer_tagtable->lookup("gn-link"))) {
      $gnBuffer->create_tag("gn-link", foreground => "blue",
                                       underline => 'single');
   }
   if(!($gnBuffer_tagtable->lookup("gn-linkname"))) {
   $gnBuffer->create_tag("gn-linkname", foreground => "blue",
                                        underline => 'single',
                                        weight => PANGO_WEIGHT_BOLD);
   }
   if(!($gnBuffer_tagtable->lookup("gn-name"))) {
   $gnBuffer->create_tag("gn-name", foreground => "#05B705",
                                    weight => PANGO_WEIGHT_BOLD);
   }
   if(!($gnBuffer_tagtable->lookup("gn-mail"))) {
   $gnBuffer->create_tag("gn-mail", foreground => "blue",
                                    weight => PANGO_WEIGHT_BOLD);
   }
   if(!($gnBuffer_tagtable->lookup("gn-cont-normal"))) {
   $gnBuffer->create_tag("gn-cont-normal", left_margin => 25,
                                           pixels_above_lines => 2);
   }
   if(!($gnBuffer_tagtable->lookup("gn-cont-link"))) {
   $gnBuffer->create_tag("gn-cont-link", foreground => "blue",
                                         underline => 'single',
                                         left_margin => 25,
                                         pixels_above_lines => 2);
   }
}

sub gnCreateResLinkTag {
  # レス内のコメントリンク(例えばレスのコメントが「>>1乙」であった場合の">>1"へのリンク)を
  # 動的に生成する
  # 引数: 1: Gtk2::TextBuffer(タグ対象のTextBuffer)
  #       2: 文字列(リンク先(板名/スレID/コメント番号)
  # 返り値: Gtk2::TextTag
  
  # 引数を取得
  my $txtbuf = $_[0];
  my $restgt = $_[1];
  
  # TextTagを作成
  my ($gnResLinkCache_flg, $gnResLinkCache_tgt) = &gnImgCacheQuery($restgt);
  my $gnResLinkTag;
  if($gnResLinkCache_flg == TRUE) {
  $gnResLinkTag = $txtbuf->create_tag(undef,
                                       foreground => "$gnEnvArg{'Thread'}{'gnLinkColorAlreadyCached'}",
                                       underline => 'single',
                                       left_margin => 25,
                                       pixels_above_lines => 2);
  $gnResLinkTag->{page} = $restgt;
  }else{
  $gnResLinkTag = $txtbuf->create_tag(undef,
                                       foreground => "$gnEnvArg{'Thread'}{'gnLinkColorNotCached'}",
                                       underline => 'single',
                                       left_margin => 25,
                                       pixels_above_lines => 2);
  $gnResLinkTag->{page} = $restgt;
  }
  
  return($gnResLinkTag);
}

sub gnCreateNoLinkTag {
  # レス番号へポップアップメニュー用リンクを
  # 動的に生成する
  # 引数: 1: Gtk2::TextBuffer(タグ対象のTextBuffer)
  #       2: 文字列(リンク先(板名/スレID/コメント番号)
  # 返り値: Gtk2::TextTag
  
  # 引数を取得
  my $txtbuf = $_[0];
  my $restgt = $_[1];
  
  # TextTagを作成
  my $gnResLinkTag = $txtbuf->create_tag(undef,
                                         foreground => "blue",
                                         underline => 'single',
                                         pixels_above_lines => 2,
                                         left_margin => 0);
  $gnResLinkTag->{page} = $restgt;
  
  return($gnResLinkTag);
}

sub gnIsURL {
   # 文字列がURLかどうかを返す関数
   # http://www.din.or.jp/~ohzaki/perl.htm#httpURL を使用
   # ありがとうございます
   # 引数: 文字列
   # 返り値: 1: Boolean (URLを含む場合はTrue(1), 含まなければFalse(0))
   #         2: 文字列 (含まれるURL(http://が補完されている))
   #         3: 文字列 (hitした前の文字列)
   #         4: 文字列 (hitした文字列(ttp://...... で抽出している))
   #         5: 文字列 (hitした後の文字列)
   
   my $url = $_[0];
   my $isurl = FALSE;
   my $urltgt = '';
   my $strBefore = '';
   my $strHit = '';
   my $strAfter = '';
   
   my $http_URL_regex = q{ttp(?:[s])*://(?:(?:[-_.!~*'()a-zA-Z0-9;:&=+$,]|%[0-9A-Fa-f]} .		# 'gedit誤認識対策用コメント
                        q{[0-9A-Fa-f])*@)?(?:(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.)} .
                        q{*[a-zA-Z](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.?|[0-9]+\.[0-9]+\.[0-9]+\.} .
                        q{[0-9]+)(?::[0-9]*)?(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f]} .
                        q{[0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-} .
                        q{Fa-f])*)*(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f} .
                        q{])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)*)} .
                        q{*)?(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])} .
                        q{*)?(?:#(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*} .
                        q{)?};
   my $pattern = $http_URL_regex;
   
   if($url =~ /$pattern/) {
      $url =~ /$pattern/;
      ($strBefore, $strHit, $strAfter) = ($`, $&, $'); # ` <--gedit誤認識対策用コメント
      $urltgt = "h" . $strHit;
      $isurl = TRUE;
   }
   
   return(($isurl, $urltgt, $strBefore, $strHit, $strAfter));

}

sub gnGetlinktgt {
  # 2ch仕様変更吸収用関数
  # 参照先コメント番号を指すURLの違いによって抽出方法をわける
  # 古い方：../test/read.cgi?bbs=unix&key=985444963&st=3&to=3&nofirst=true
  # 新しい方：../test/read.cgi/unix/985444963/303
  # 引数：文字列(URLの中のread.cgiより後の部分。古い仕様の場合は"?bbs=unix&key=xxxxx"、
  #             新しい仕様の場合は"/unix/xxxxxx"という文字列が与えられている)
  # 返り値：文字列(板名/スレID/コメント番号)
  my $gnBufURL = $_[0];
  
  if (!($gnBufURL)) { return(NULL); }
  
  my $flg = substr($gnBufURL,0,1);
  if($flg eq "\?") {
     my ($bbs, $key, $st, $to, $nofirst) = split(/\&/, substr($gnBufURL,1));
     my ($bbs_k, $bbs_v) = split(/=/, $bbs);
     my ($key_k, $key_v) = split(/=/, $key);
     my ($st_k, $st_v) = split(/=/, $st);
     
     my $retval = $bbs_v . "\/" . $key_v . "\/" . $st_v;
     return($retval);
  }else{
     if ($gnBufURL) {
        my $retval = substr($gnBufURL, 1);
        return($retval);
     }else{
        return(NULL);
     }
  }
} 

sub gnGetItaIchiran {
   # 指定した場所から2ch板一覧を取得してローカルテーブルをアップデートする関数
   # 引数：なし
   # 返り値：Integer(-1=成功
   #                  0=失敗：ネットワークがタイムアウト
   #                  1=失敗：ローカルファイルに書き込み失敗)
   my $gnURLNo = $gnURLIni{'URL'}{'selected'};
   my $gnURLTo = $gnURLIni{'URL'}{$gnURLNo};
   if($gnDebugFlag) { print "$gnURLNo\n"; }
   if($gnDebugFlag) { print "$gnURLTo\n"; }
   
   my $gnUA = LWP::UserAgent->new;
   $gnUA->agent($gnUsrAgent);
   if($gnEnvArg{'ReadProxy'}{'Proxy'} == 1) {
      my $gnUA_proxystr = "http\:\/\/" . $gnEnvArg{'ReadProxy'}{'Address'} . "\:" . $gnEnvArg{'ReadProxy'}{'Port'} . "\/";
      $gnUA->proxy('http', $gnUA_proxystr);
   }

   my $gnUA_res = $gnUA->get($gnURLTo);
   if($gnDebugFlag) { print Dumper($gnUA_res->code) . "\n"; }
   
   if($gnUA_res->is_success) {
      # 板一覧を取得したので展開
      my $gnItaTmpBuf = $gnUA_res->content;
      
      my $gnTmpCatIdx = 0; # カテゴリ順をカウント
      my $gnTmpItaIdx = 0; # 板をカウント
      my $gnTmpPos = "";   # 現在位置("category" もしくは "ita")
      my $gnItaURL = "";   # 板のURL
      my $p = HTML::TokeParser->new(\$gnItaTmpBuf);
      
      open(gnFItaIdx, ">$boardcfg");
      
      while (my $token = $p->get_token) {
         my $flg = $token->[0];
         my $tag = $token->[1];
            
            # bタグがあったらカテゴリカウントをインクリメント
            # 参照先：http://info.2ch.net/wiki/pukiwiki.php?monazilla%2Fdevelop%2Fboardlist
            if(($flg eq "S") && ($tag eq "b")) { $gnTmpCatIdx++;}

            # 1回目のbタグでカテゴリに入ったこととして、データを作り始める
            if ($gnTmpCatIdx > 0) {
            
               if(($flg eq "S") && ($tag eq "b")) { $gnTmpPos = "category"; $gnTmpItaIdx = 0; }
               if(($flg eq "E") && ($tag eq "b")) { $gnTmpPos = "other"; }
               if(($flg eq "S") && ($tag eq "a")) { $gnTmpPos = "ita";
                                                      $gnTmpItaIdx++;
                                                      $gnItaURL = $token->[2]->{'href'};}
               if(($flg eq "E") && ($tag eq "a")) { $gnTmpPos = "other"; }
               
               if($flg eq "T") {
                  my $gnPrintBuf = "";
                  if($gnTmpPos eq "category") {
                     $gnPrintBuf = $gnTmpCatIdx . "-0::" . $tag . "\n";
                  }
                  if($gnTmpPos eq "ita") {
                     $gnPrintBuf = $gnTmpCatIdx . "-" . $gnTmpItaIdx . "::" . $tag . "::" . $gnItaURL . "\n";
                  }
                  
                  $gnPrintBuf = Encode::decode('shiftjis', $gnPrintBuf);
                  $gnPrintBuf = Encode::encode('utf8', $gnPrintBuf);
                  print gnFItaIdx $gnPrintBuf;
               }

               
            }
      }
      
      close(gnFItaIdx);
      
      #板一覧を更新したらTreeViewウィジェットの内容をアップデートする
      my $gnItaStore = &gnMakeItaTreeStore($boardcfg);
      my $gnItaView = $gnGUIxml->get_widget('gnII');
      $gnItaView->set_model($gnItaStore);
      my $gnIS_pos = Gtk2::TreePath->new_from_indices(0);
      $gnItaView->expand_row($gnIS_pos, FALSE);

      &gnLogger("板一覧を更新しました");
      return(TRUE);
   }else{
      &gnLogger("板一覧取得失敗");
      return(FALSE);
   }
}

sub gnAppendRes {
   # dat1行分のコメント文字列を整形したTextBufferに変換し、与えられた
   # TextBufferの末尾に追加する
   # スレ表示、ポップアップ表示に使用
   # 引数１：Gtk2::TextBuffer (変更するTextBuffer)
   # 引数２：文字列 (追加するコメントのデータ。１行(１コメント)分。
   #                 ファイルから取得されたままの状態で、ShiftJIS+UTFフラグ無しであることを
   #                 想定する。つまりEncode::decodeはこの関数の内部で行う)
   # 引数３：文字列 (板名。板のディレクトリ名)
   # 引数４：文字列 (スレID.datの.datをとったもの)
   # 引数５：文字列 (レス番号)
 
   my $gnAR_TextBuf = $_[0];
      # iter を最後に
      my $gnAR_TextBuf_iter = $gnAR_TextBuf->get_end_iter;
      # TextTagが定義されていなかったら作成
      my $gnAR_TextBuf_tagtable = $gnAR_TextBuf->get_tag_table;
      if(!($gnAR_TextBuf_tagtable->lookup("gn_name"))) {
         &gnCreateTag($gnAR_TextBuf);
      }

#   my $gnAR_RepBuf = Encode::decode('shiftjis', $_[1], Encode::FB_DEFAULT);
   my $gnAR_RepBuf = Encode::decode('cp932', $_[1], Encode::FB_DEFAULT);
      $gnAR_RepBuf =~ s/\r//;

   my @gnAR_RepBuf = split(/<>/, $gnAR_RepBuf);
        # index: 0 = 投稿者名, 1 = メールアドレス, 2 = 投稿日時, 3 = 投稿内容, 4 = null
   my @gnAR_info = ($_[2], $_[3], $_[4]);
   
   # ヘッダ(レス番、名前、日付、ID)を書く
   # レス番
   my $linktgt = "resno://" . $gnAR_info[0] . "/" . $gnAR_info[1] . "/" . $gnAR_info[2];
   if(!($gnAR_TextBuf_tagtable->lookup($linktgt))) {
   $gnAR_TextBuf->insert_with_tags($gnAR_TextBuf_iter, $gnAR_info[2], &gnCreateNoLinkTag($gnAR_TextBuf, $linktgt));
   }else{
   $gnAR_TextBuf->insert_with_tags($gnAR_TextBuf_iter, $gnAR_info[2], $gnAR_TextBuf_tagtable->lookup($linktgt));
   }
   # 名前
   $gnAR_TextBuf->insert($gnAR_TextBuf_iter, " 名前：");
   if($gnAR_RepBuf[1] eq '') {
      $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $gnAR_RepBuf[0], "gn-name");
   }else{
      $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $gnAR_RepBuf[0], "gn-linkname");
      $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, " [", "gn-mail");
      $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $gnAR_RepBuf[1], "gn-mail");
      $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, "]", "gn-mail");
   }
   # 投稿日
   $gnAR_TextBuf->insert($gnAR_TextBuf_iter, "  投稿日：");
   $gnAR_TextBuf->insert($gnAR_TextBuf_iter, $gnAR_RepBuf[2]);
   $gnAR_TextBuf->insert($gnAR_TextBuf_iter, "\n");

   # 本文
   $gnAR_RepBuf[3] =~ s/&gt;/>/g;
   $gnAR_RepBuf[3] =~ s/&lt;/</g;

     # TokeParserの初期化
     my $p = HTML::TokeParser->new(\$gnAR_RepBuf[3]);

     # Token毎に処理を行う
     while(my $token = $p->get_token) {
       my $flg = $token->[0];
       my $tag = $token->[1];
       my $brflg = FALSE;
       if(($flg eq "S") && ($tag eq "a")) {
            # <a>のタグ内に入ったら$tinflgをTRUEにして、targetのURLを取り込む
            # タグ自体は出力しない
            my($buf1, $linktgt) =  split(/read\.cgi/, $token->[2]->{'href'});
            $linktgt = &gnGetlinktgt($linktgt); #read.cgiの仕様違いを吸収
            $linktgt = "replyto://" . $linktgt;
            if(!($gnAR_TextBuf_tagtable->lookup($linktgt))) {
               $gnAR_TextBuf->insert_with_tags($gnAR_TextBuf_iter, $p->get_text, &gnCreateResLinkTag($gnAR_TextBuf, $linktgt));
            }else{
               $gnAR_TextBuf->insert_with_tags($gnAR_TextBuf_iter, $p->get_text, $gnAR_TextBuf_tagtable->lookup($linktgt));
            }
       }elsif(($flg eq "E") && ($tag eq "a")) {
          # </a>は無視
       }elsif(($flg eq "S") && ($tag eq "br")) {
            # 改行
            $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, "\n", "gn-cont-normal");
            $brflg = TRUE;
       }elsif(($flg eq "E") && ($tag ne "br") && ($tag ne "a")) {
          # </a>, </br>(実際には<br>)以外のタグはそのまま記述
          # (レスの中には<チラシの裏>....</チラシの裏>とかあるし)
          $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $token->[2], "gn-cont-normal");
       }elsif(($flg eq "S") && ($tag ne "a") && ($tag ne "br")) {
          # <a>以外のタグはそのまま記述
          # (レスの中には<チラシの裏>....</チラシの裏>とかあるし)
          $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $token->[4], "gn-cont-normal");
       }else {
#       }elsif($flg eq "T") {
          my $str = $token->[1];
          if($brflg == TRUE){
          $str = substr($str, 1); # 先頭の空白を削除
          $brflg = FALSE;
          }

          # URLを含むかどうかを判断する
          my ($urlflg, $linktgt, $strBefore, $strHit, $strAfter) = &gnIsURL($str);

          if($urlflg) {
             # URLが含まれていたらリンクタグを生成し、
             # タグ付書式で描画する
             
             if (index($linktgt, "read\.cgi") > -1) {
                $linktgt = "datl\:\/\/" . $linktgt;
             }
             
             # リンクの前は通常のタグで作成
             $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $strBefore, "gn-cont-normal");
             
             if(!($gnAR_TextBuf_tagtable->lookup($linktgt))) {
                # リンク対象のタグがなかったら作成する
                $gnAR_TextBuf->insert_with_tags($gnAR_TextBuf_iter, $strHit,
                                                     &gnCreateResLinkTag($gnAR_TextBuf, $linktgt));
             }else{
                # リンク対象のタグが既にあったら再利用する
                $gnAR_TextBuf->insert_with_tags($gnAR_TextBuf_iter, $strHit,
                                                    $gnAR_TextBuf_tagtable->lookup($linktgt));
             }
             
             # リンクの後ろは通常のタグで作成
             $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $strAfter, "gn-cont-normal");
          }else{
             # コメントのリンクを判断
             my $str2 = decode_entities($str);
             my $ptn_regex = q{(?:[＞]|[\>])(?:[1-9])+(?:[0-9])*(?:[0-9])*} .
                               q{(?:(?:[-])+} .
                               q{(?:[1-9])+(?:[0-9])*(?:[0-9])*(?:[0-9])*)*};
             if ($str2 =~ /$ptn_regex/) {
                # コメントへの参照(らしきもの)があったらリンクする
                $str2 =~ /$ptn_regex/;
                my ($strBefore, $strHit, $strAfter) = ($`, $&, $'); # ` <--gedit誤認識対策用コメント
                $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $strBefore, "gn-cont-normal");
                my $linktgt = "replyto://" . $gnAR_info[0] . "/" . $gnAR_info[1] . "/" . substr($strHit, 1);
                if(!($gnAR_TextBuf_tagtable->lookup($linktgt))) {
                  $gnAR_TextBuf->insert_with_tags($gnAR_TextBuf_iter, $strHit,
                                                       &gnCreateResLinkTag($gnAR_TextBuf, $linktgt));
                }else{
                  $gnAR_TextBuf->insert_with_tags($gnAR_TextBuf_iter, $p->strHit,
                                                       $gnAR_TextBuf_tagtable->lookup($linktgt));
                }
                $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $strAfter, "gn-cont-normal");
               
                
             }else{

             # URLもコメントへのリンクも含まれていなければ普通に描画
             $gnAR_TextBuf->insert_with_tags_by_name($gnAR_TextBuf_iter, $str2, "gn-cont-normal");
             }
          }
       }
     }
     
     # 最後にレス間に改行を入れる(いちおうギコナビのやりかたに合わせ)
     $gnAR_TextBuf->insert($gnAR_TextBuf_iter, "\n\n");
     
     return($gnAR_TextBuf);

}


sub gnImgCacheQuery {
   # 指定されたURLがキャッシュテーブル上にあるかどうか確認する関数
   #
   # 引数: 文字列(対象のURL)
   # 返り値: 1: TRUE/FALSE(TRUE=キャッシュヒット,FALSE=キャッシュにはない)
   #         2: 返り値1がTRUEだった場合、キャッシュファイルへのフルパス
   #                    FALSEだった場合、NULL
   
   if ($gnEnvArg{'Cache'}{'Enable'} == FALSE) {
      return(FALSE, "");
   }
   
   my $gnQURL = $_[0];
   my $gnQResult;
   my $gnQFN;
   my $gnQTime;
   my $gnSeparator; $gnSeparator = chr(001);
   
   if ($gnCacheTbl{'Cache'}{$gnQURL}) {
      ($gnQTime, $gnQFN) = split(/$gnSeparator/, $gnCacheTbl{'Cache'}{$gnQURL});
   }
   if($gnDebugFlag) { print "cachehit\?: " . Dumper(($gnQTime, $gnQFN)); }
   if ($gnQTime) {
      $gnQTime += ($gnEnvArg{'Cache'}{'Expire'} * 86400);
      if ($gnQTime < time) {
         if($gnDebugFlag) { print "cache-expirerd:" . Dumper(($gnQTime, $gnQFN)); }
         $gnCacheTbl{'Cache'}{$gnQURL} = "-1" . chr(001) . "";
         unlink($gnQFN);
         #&gnLogger("キャッシュが期限切れになったので再取得します");
         return(FALSE, "");
      }else{
         if($gnDebugFlag) { print "cache-hit:" . Dumper(($gnQTime, $gnQFN)); }
         #&gnLogger("キャッシュされたファイルを表示します");
         return(TRUE, $gnQFN);
      }
   }else{
      if($gnDebugFlag) { print "cache-no-entry:" . Dumper(($gnQTime, $gnQFN)); }
      #&gnLogger("キャッシュにないので取得します");
      return(FALSE, "");
   }
   
}

sub gnClearAllCache {
   # 全てのキャッシュファイルを削除しテーブルをクリアする
   #
   # 引数: なし
   # 返り値: なし
   my @gnCT_entry = keys(%{$gnCacheTbl{'Cache'}});
   
   foreach my $val(@gnCT_entry) {
      my $gnSplitStr = chr(001);
      my ($gnCTTime, $gnCTFN) = split(/$gnSplitStr/, $gnCacheTbl{'Cache'}{$val});
      if ($gnCTTime > -1) {
         unlink($gnCTFN);
      }
   }
   
   %gnCacheTbl = "";
   $gnCacheTbl{'Cache'}{'init'} = "-1" . chr(001) . "";
}

sub gnClearExpiredCache {
   # 期限切れのキャッシュエントリをクリアする
   #
   # 引数: なし
   # 返り値: なし
   my @gnCT_entry = keys(%{$gnCacheTbl{'Cache'}});
   
   foreach my $val(@gnCT_entry) {
      my $gnSplitStr = chr(001);
      my ($gnCTTime, $gnCTFN) = split(/$gnSplitStr/, $gnCacheTbl{'Cache'}{$val});
      if ($gnCTTime < 0) {
         delete($gnCacheTbl{'Cache'}{$val});
      }
   }
}

#--------------------------------------
# カスタム関数(OS依存、文字エンコードなど)
#--------------------------------------

sub env_gtkstr_to_gtkstr {
    # gtk2-perlだけ(かつLinuxだけ?)の問題?
    # Gtk2ウィジェットから取った文字列をそのままGtk2ウィジェットにあたえると
    # 2バイト部分がなくなってしまう問題への対処
    # 引数=文字列(utf8エンコード、utf8フラグ付きを期待)
    my $mySrcStr = $_[0];
    my $myDstStr = '';
       $mySrcStr = Encode::encode(guess_encoding($mySrcStr, qw/ euc-jp shiftjis 7bit-jis utf-8 /), $mySrcStr);
       $myDstStr = Encode::decode(guess_encoding($mySrcStr, qw/ utf-8 euc-jp shiftjis 7bit-jis utf-8 /), $mySrcStr);
       
    return($myDstStr);

}

sub env_locale_to_encstr {
    # ロケール名からEncodeモジュールが解釈できる
    # 文字エンコード名を返す関数
    # 引数=文字列(ロケール名)
    
    # ロケール名を取得
    my $myLocVal = $_[0];

    # Windowsでは"Japanese_Japan"という文字列が使われているので
    # 一括変換しやすいように"ja_JPと置換"
    $myLocVal =~ s/Japanese_Japan/ja_JP/g;
    
    # "ja_JP.eucJP"の"ja_JP"部はいらないので切り取る
    my @myLocStr  = split(/\./, $myLocVal);

    # 後ろの"eucJP"の部分は小文字に置き換える
    # (実際にも大文字・小文字を区別しないよう推奨されている)
    if($myLocStr[1]) {
       $myLocStr[1] = lc($myLocStr[1]);
    }else{
        # SolarisでLocaleの２番目がなかったらeucに決めうち
       if($^O eq 'solaris') {
          push(@myLocStr, "eucjp");
        }
     }
    
    # あとはパターンマッチ
    if    ($myLocStr[1] eq "932") {    return('shiftjis');
    }elsif($myLocStr[1] eq "pck") { return('shiftjis');
    }elsif($myLocStr[1] eq "sjis") { return('shiftjis');
    }elsif($myLocStr[1] eq "20932") { return('euc-jp');
    }elsif($myLocStr[1] eq "eucjp") { return('euc-jp');
    }elsif($myLocStr[1] eq "euc") { return('euc-jp');
    }elsif($myLocStr[1] eq "ujis") { return('euc-jp');
    }elsif($myLocStr[1] eq "iso-2022-jp") { return('7bit-jis');
    }elsif($myLocStr[1] eq "utf-8") { return('utf8');
    }else                           { return('ascii'); # エンコードが判別できなかったらシングルバイトエンコード(単なるバイト列)として解釈する
    }

    
}

sub env_FNconv {
   # OS名からopenに渡すファイル名の文字コードを簡易的に変換するルーチン
   # (読み出し専用？実装依存対策???)
   # 引数はフルパスのファイル名を想定(文字列であればなんでもよい)
   
   my $myenc_os = 'utf8';
   my $myFNorg = $_[0];
   
   if($^O eq 'MSWin32') {
      # "perl -V"の"osname"がMSWin32の場合は
      # 実行環境がWindowsであると判断し、ロケールから文字エンコードを判別
      # エンコード名に従って文字列を変換する
      # Windows以外(Linuxを想定)の場合はUTF-8の文字列を
      # open関数が受け付けるので、変換しない
      $myenc_os = &env_locale_to_encstr(setlocale(LC_CTYPE));
      $myFNorg = Encode::encode($myenc_os, $myFNorg);
   }
   
   return $myFNorg;

}

sub gnGetPersonalCfgPath {
   # 設定ファイルパスをチェック
   #
   # 引数:      なし
   # 返り値: 1: 設定ファイルのフルパス
   
   my $gnCfgPath;
   if ($^O eq "MSWin32") {
      # "perl -V"の"osname"がMSWin32の場合は
      # 実行環境がWindowsであると判断
      $gnCfgPath = "$ENV{'APPDATA'}" . "\\gnview\\";
   }else{
      $gnCfgPath = "$ENV{'HOME'}" . "\/.gnview\/";
   }
      return($gnCfgPath);
   
}


sub gnWarn {
   # ワーニングのポップアップを出す関数
   #
   # 引数: 文字列(ポップアップに表示する文字)
   # 返り値: なし
   my $mes = $_[0];
   #$mes = Encode::decode('utf8', $mes);
   my $mW = $gnGUIxml->get_widget('mW');
   my $pop = Gtk2::MessageDialog->new($mW,
                                        'GTK_DIALOG_MODAL',
                                        'GTK_MESSAGE_ERROR',
                                        'GTK_BUTTONS_OK',
                                        $mes,
                                         ,
                                         'GTK_BUTTONS_OK' => 'gtk-ok');
   if ($pop->run eq 'ok') {
      $pop->destroy;
   }
}

sub gnYesNo {
   # 質問のポップアップを出す関数
   #
   # 引数: 文字列(ポップアップに表示する文字)
   # 返り値: 文字列(yes/no)
   my $mes = $_[0];
   #$mes = Encode::decode('utf8', $mes);
   my $mW = $gnGUIxml->get_widget('mW');
   my $pop = Gtk2::MessageDialog->new($mW,
                                        'GTK_DIALOG_MODAL',
                                        'GTK_MESSAGE_INFO',
                                        'GTK_BUTTONS_YES_NO',
                                        $mes,
                                        'GTK_BUTTONS_YES' => 'gtk-yes',
                                        'GTK_BUTTONS_NO' => 'gtk-no');
   if ($pop->run eq 'yes') {
      $pop->destroy;
      return("yes");
   }else{
      $pop->destroy;
      return("no");
   }
}


sub genRandStr {
    # http://orfeus.knmi.nl/pub/outgoing/chad/perl/randstr.perl より取得
    # ありがとうございます
    # 指定された文字数のランダム文字列を生成する
    # 引数: integer(文字数)
    # 返り値：string(ランダム文字列)
    my ($size) = @_;
    my $ret = '';

    my @chars=("0".."9","a".."z","A".."Z");

    foreach (1..$size) {
         $ret .= $chars[rand($#chars)];
    }
    return $ret;
}


1;
