#!/usr/bin/env ruby
$KCODE='e'
# goRua -- Gtk+ on Ruby User Agent for 2ch version
GORUA_VERSION = '0.15-pre'
# 春山 征吾 haruyama@unixuser.org
# $Id: goRua.rb,v 1.144 2002/07/06 08:02:33 haruyama Exp $

# このファイルはGPLで配付されています.
# このプログラムは無保証です.
require 'getopts'
require 'gtk'

RUBY_GTK_VERSION = Gtk::BINDING_VERSION.join
if RUBY_GTK_VERSION == '027'
  STDERR.print "Ruby/Gtk 0.27には問題があるため、一部の機能が正しく動きません。\n"
  ERROR_OF_SWITCH_PAGE = true
elsif RUBY_GTK_VERSION == '028'
  STDERR.print "Ruby/Gtk 0.28 と versionがふられた CVS snapshotには問題があるものがあります。\nその場合異常終了します。ご了承下さい。\n"
  ERROR_OF_SWITCH_PAGE = false
else
  ERROR_OF_SWITCH_PAGE = false
end

require 'nkf'
require 'jcode'

require 'net/http'
# require 'thread'
begin
  require 'zlib'
  ENABLE_GZIP =true
rescue LoadError
  STDERR.print "ruby-zlibがありませんでした。\ngzipで圧縮されたストリームを扱うことができません。\nruby-zlibのインストールを推奨します。\n"
  ENABLE_GZIP =false
#  exit
end

require 'connect2ch'

# 各ディストリビューション毎に変更してください
# >> ディストリビュータ様
# Thanks: むらけん@kondara さん
GORUA_DATADIR = '/usr/share/goRua'

##### 各種設定 #####
# 関連ファイルのベースディレクトリ
BASE_DIR = ENV['HOME']+ '/.goRua_2ch/'

# Thanks: むらけん@kondara さん
Dir.mkdir(BASE_DIR) unless File.exist?(BASE_DIR)
Dir.mkdir("#{BASE_DIR}/thread") unless File.exist?("#{BASE_DIR}/thread")
Dir.mkdir("#{BASE_DIR}/subject") unless File.exist?("#{BASE_DIR}/subject")
unless File.exist?("#{BASE_DIR}/board_info")
  system "cp #{GORUA_DATADIR}/board_info #{BASE_DIR}/board_info"
end
unless File.exist?("#{BASE_DIR}/bookmarks")
  system "cp #{GORUA_DATADIR}/bookmarks #{BASE_DIR}/bookmarks"
end

#デフォルト設定
$config={
  'proxy_addr' => nil,
  'proxy_port' => nil,
  'get_proxy_addr' => nil,
  'get_proxy_port' => nil,
  'post_proxy_addr' => nil,
  'post_proxy_port' => nil,
  'http_get_timeout' => 60,
  'http_command' => 'galeon -n %u',
  'tab_string_size' => 7,
  'height' => 650,
  'select_width' => 275,
  'view_width' => 675,
  'post_width' => 600,
  'post_height' => 400,
  'hankaku_to_zenkaku' => nil,
  'thread_tooltips_delay' => 1500,
  'post_name_candidate' => [''],
  'post_mailto_candidate' => ['','sage'],
#  'user_agent' => "goRua.rb #{GORUA_VERSION}",
  'user_agent' => "Monazilla/1.00 (goRua.rb/#{GORUA_VERSION})",
  'x_2ch_ua' => "goRua.rb #{GORUA_VERSION}",
#  'background_get' => nil,
  'show_old_message' => nil,
  'colored_text' => true,
  'color0' => 'black',
  'color1' => 'red',
  'color2' => 'blue',
  'color3' => 'green',
  'set_last_page' => true,
  'tab_type' => 0,
  'via_dat' => true,
}

config = BASE_DIR + 'config'

# Thanks: むらけん@kondara さん
#if File.exist?(config)
begin
  unless File.exist?(config)
    config = "#{GORUA_DATADIR}/config"
  end
  open(config,'r'){|file|
    while line=file.gets
      line=NKF::nkf('-ex',line)
      line.chop!
      if /^\#/ =~ line
	next
      elsif /^([a-z_0-9]+)\s*=\s*([0-9\-][0-9]*)\s*$/ =~ line
	tmp1=$1
	tmp2=$2
	if $config[tmp1].is_a? Fixnum  
	  $config[tmp1]=tmp2.to_i
	elsif !$config[tmp1]
	  $config[tmp1]=tmp2.to_i
	elsif $config[tmp1].is_a? String
	  $config[tmp1]=tmp2.strip
	else
	  STDERR.print "#{$1} に正しくない値が指定されています。\n"
	end
      elsif /^([a-z_0-9]+)\s*=\s*\[(.*)\]\s*$/ =~ line
	if $config[$1].is_a? Array
	  $config[$1]=$2.split(/,/)
	else
	  STDERR.print "#{$1} に配列は指定できません。\n"
	end
      elsif /^([a-z_0-9]+)\s*=\s*(.*)\s*$/ =~ line
	tmp1=$1
	tmp2=$2.strip
	if  $config[tmp1].is_a? String
	  $config[tmp1] = tmp2
	elsif  $config[tmp1].is_a? TrueClass
	  if tmp2 == 'true'
	    $config[tmp1] = true
	  elsif tmp2 == 'false'
	    $config[tmp1] = false
	  else
	    STDERR.print "#{tmp1} には trueかfalseを指定する必要があります。\n"
	  end
	elsif !$config[tmp1]
	  $config[tmp1] = tmp2
	else
	  STDERR.print "#{tmp1} に文字列は指定できません。\n"
	end
      end
    end
  }
rescue
end


if $config['colored_text']
  begin
    require 'goRua_color_table'

    if COLOR_TABLE[$config['color0'].downcase]
      COLOR_OTHERS = COLOR_TABLE[$config['color0'].downcase]
    else
      STDERR.print "#{$config['color0'].downcase} という色は見つかりませんでした\n"
      COLOR_OTHERS = COLOR_TABLE['black']
    end
    
    if COLOR_TABLE[$config['color1'].downcase]
      COLOR_CITE_NUMBER = COLOR_TABLE[$config['color1'].downcase]
    else
      STDERR.print "#{$config['color1'].downcase} という色は見つかりませんでした\n"
      COLOR_CITE_NUMBER = COLOR_TABLE['red']
    end

    if COLOR_TABLE[$config['color2'].downcase]
      COLOR_CITE = COLOR_TABLE[$config['color2'].downcase]
    else
      STDERR.print "#{$config['color2'].downcase} という色は見つかりませんでした\n"
      COLOR_CITE = COLOR_TABLE['blue']
    end

    if COLOR_TABLE[$config['color3'].downcase]
      COLOR_URL = COLOR_TABLE[$config['color3'].downcase]
    else
      STDERR.print "#{$config['color3'].downcase} という色は見つかりませんでした\n"
      COLOR_URL = COLOR_TABLE['green']
    end


    #指定された色があるかチェック
    
  rescue LoadError
#    $config['colored_text']=false
    STDERR.print "color_table.rbを見つけられませんでした.標準の色を使います。\n"
    COLOR_OTHERS = Gdk::Color.new(0,0,0)
    COLOR_CITE_NUMBER = Gdk::Color.new(65535,0,0)
    COLOR_CITE = Gdk::Color.new(0,0,65535)
    COLOR_URL = Gdk::Color.new(0,32896,0)
  end
else
  COLOR_OTHERS = nil
  COLOR_CITE_NUMBER = nil
  COLOR_CITE = nil
  COLOR_URL = nil
end


if $config['hankaku_to_zenkaku']
  NKF_SJIS_TO_EUC = '-eS'
  NKF_TO_EUC = '-e'
  NKF_EUC_TO_SJIS = '-sE'
#  NKF_TO_SJIS = '-s'
else
  NKF_SJIS_TO_EUC = '-exS'
  NKF_TO_EUC = '-ex'
  NKF_EUC_TO_SJIS = '-sxE'
#  NKF_TO_SJIS = '-sx'
end

=begin
if $config['background_get']
  if RUBY_RELEASE_DATE >= '2002-01-30'
    STDERR.print "バックグラウンド読みこみは実験的な機能です。\nご注意下さい。\n"

  else
    STDERR.print "バックグラウンド読みこみは おそらくRUBY_RELEASE_DATEが\n2002-01-30以降のrubyでなければ正常に機能しません。\n"
    $config['background_get'] = nil
  end
end
=end


$config['user_agent'].gsub!('%v',"#{GORUA_VERSION}")
$config['x_2ch_ua'].gsub!('%v',"#{GORUA_VERSION}")

#  183(matzの向かいの席)さんのご示唆による
# connect2chの設定(0.12以降)
if $config['get_proxy_addr'] && $config['get_proxy_port']
  $connect2ch_config['get_proxy_addr']=$config['get_proxy_addr']
  $connect2ch_config['get_proxy_port']=$config['get_proxy_port']
elsif $config['proxy_addr'] && $config['proxy_port']
  $connect2ch_config['get_proxy_addr']=$config['proxy_addr']
  $connect2ch_config['get_proxy_port']=$config['proxy_port']
end

if $config['post_proxy_addr'] && $config['post_proxy_port']
  $connect2ch_config['post_proxy_addr']=$config['post_proxy_addr']
  $connect2ch_config['post_proxy_port']=$config['post_proxy_port']
elsif $config['proxy_addr'] && $config['proxy_port']
  $connect2ch_config['post_proxy_addr']=$config['proxy_addr']
  $connect2ch_config['post_proxy_port']=$config['proxy_port']
end
$connect2ch_config['http_get_timeout']=$config['http_get_timeout']
# $connect2ch_config['user_agent']=  $config['user_agent'] + ' (' +$connect2ch_config['user_agent'] +')'
$connect2ch_config['user_agent']=  $config['user_agent']
$connect2ch_config['x_2ch_ua']=  $config['x_2ch_ua'] + ' (' +$connect2ch_config['x_2ch_ua'] +')'

#同時に同じファイルへの読みこみを避けるための
#mutex,配列
# どうも意味なさそうなので消し
=begin
$mutex_for_get = Mutex.new
$filenames_in_getting = []
=end


STATUS_BAR_ID_KAKIKOMI = 1
STATUS_BAR_ID_BYOUGA = 2
STATUS_BAR_ID_YOMIKOMI = 3
STATUS_BAR_ID_MATCH = 4
STATUS_BAR_ID_ERROR = 5

# コマンドラインオプション
getopts('s')


#フォントの設定
gtkrc = BASE_DIR + 'gtkrc'
if File.exist?(gtkrc)
  Gtk::RC.parse(gtkrc)
# Thanks: むらけん@kondara さん
elsif File.exist?("#{GORUA_DATADIR}/gtkrc")
  Gtk::RC.parse("#{GORUA_DATADIR}/gtkrc")
else
  Gtk::RC.parse_string <<EOS
style "mona_14"
{
  fontset = "-mona-gothic-medium-r-normal--14-*"
}
widget_class "*" style "mona_14"
style "mona_12"
{
  fontset = "-mona-gothic-medium-r-normal--12-*"
}
widget "*NotebookTab*" style "mona_12"
EOS
end

# Text中のfontの高さ(フォントの設定の後に!)
text = Gtk::Text.new
text.set_name 'Text'

style = Gtk::RC.get_style(text)

TEXT_FONT = style.font

TEXT_FONT_HEIGHT =  TEXT_FONT.string_height('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')

#X印のPixmap用
BATU_XPM = [
"12 12 2 1",
"  c black",
".       c None s None",

"  ........  ",
".  ......  .",
"..  ....  ..",
"...  ..  ...",
"....    ....",
".....  .....",
"....    ....",
"...  ..  ...",
"..  ....  ..",
".  ......  .",
"  ........  ",
"............"

]

# 右クリック popup 制御用変数
$menu_popup = false

# 検索結果表示

# $kensaku_kekka = '検索結果'
KENSAKU_KEKKA = '検索結果'
NAIBU_CATEGORY = '内部カテゴリ'

=begin
# w3m-0.2.2-inu-1.1 entity.cより
LATIN1_EUCJP_MAP =
[
    ' ', '!',  '¢', '£', 'CUR','￥', '|',  '§',   
    '¨', '(C)','-a', '≪', '¬', '-',  '(R)','￣',  
    '°', '±', '^2', '^3', ''',  'μ', '¶', '・',  
    ',',  '^1', '-o', '≫', '1/4','1/2','3/4','?',   
    'A`', 'A'', 'A^', 'A~', 'A:', 'Å',  'AE','C,',  
    'E`', 'E'', 'E^', 'E', 'I`',  'I'', 'I^', 'I:',  
    'D-', 'N~', 'O`', 'O'', 'O^', 'O~', 'Oe', '×',  
    'φ', 'U`', 'U'', 'U^', 'U:', 'Y'', 'th', 'ss',  
    'a`', 'a'', 'a^', 'a~', 'a:', 'a',  'ae', 'c',   
    'e`', 'e'', 'e^', 'e:', 'i`', 'i'', 'i^', 'i:',  
    'd-', 'n~', 'o`', 'o'', 'o^', 'o~', 'oe', '÷',  
    'φ', 'u`', 'u'', 'u^', 'u:', 'y'', 'th', 'y:'   
]


# w3m-0.2.2-inu-1.1 entity.c conv_entityより
def unquote_char(num)
  ret = ' '
  if num < 0x80
    ret[0] = num 
  elsif num < 0xa0 
    # ここは手で似た文字にするか...
    ret = ' '
  elsif num < 0x100 
    ret = $latin1_eucjp_map[num-0xa0]
  else
    ret = '  '
    # ここは手で似た文字にするか...
  end
  return ret
end
=end

CHAR_HASH_AD = 
  {
# 〇付き数字
    0xa1 => '１',
    0xa2 => '２',
    0xa3 => '３',
    0xa4 => '４',
    0xa5 => '５',
    0xa6 => '６',
    0xa7 => '７',
    0xa8 => '８',
    0xa9 => '９',
    0xaa => '10',
    0xab => '11',
    0xac => '12',
    0xad => '13',
    0xae => '14',
    0xaf => '15',
    0xb0 => '16',
    0xb1 => '17',
    0xb2 => '18',
    0xb3 => '19',
    0xb4 => '20',

# ローマ数字
    0xb5 => 'I',
    0xb6 => 'II',
    0xb7 => 'III',
    0xb8 => 'IV',
    0xb9 => 'V',
    0xba => 'VI',
    0xbb => 'VII',
    0xbc => 'VIII',
    0xbd => 'IX',
    0xbe => 'X',
# 単位    
    0xc0 => 'ﾐﾘ',
    0xc1 => 'ｷﾛ',
    0xc2 => 'ｾﾝﾁ',
    0xc3 => 'ﾒ-ﾄﾙ',
    0xc4 => 'ｸﾞﾗﾑ',
    0xc5 => 'ﾄﾝ',
    0xc6 => 'ｱ-ﾙ',
    0xc7 => 'ﾍｸﾀ-ﾙ',
    0xc8 => 'ﾘｯﾄﾙ',
    0xc9 => 'ﾜｯﾄ',
    0xca => 'ｶﾛﾘ-',
    0xcb => 'ﾄﾞﾙ',
    0xcc => 'ｾﾝﾄ',
    0xcd => 'ﾊﾟ-ｾﾝﾄ',
    0xce => 'ﾐﾘﾒ-ﾄﾙ',
    0xcf => 'ﾍﾟ-ｼﾞ',
# 単位
    0xd0 => 'mm',
    0xd1 => 'cm',
    0xd2 => 'km',
    0xd3 => 'mg',
    0xd4 => 'kg',
    0xd5 => 'cc',
    0xd6 => 'm^2',
    
    0xdf => '平成',
# その他
    0xe0 => '``',
    0xe1 => ',,',
    0xe2 => 'No',
    0xe3 => 'KK',
    0xe4 => 'Tel',
    0xe5 => '上',
    0xe6 => '中',
    0xe7 => '下',
    0xe8 => '左',
    0xe9 => '右',
    0xea => '(株)',
    0xeb => '(有)',
    0xec => '(代)',
    0xed => '明治',
    0xee => '大正',
    0xef => '昭和',
    0xf0 => '≒',
    0xf1 => '≡',
    0xf2 => '∫',
    0xf3 => '§', # ト音記号.. 
    0xf4 => 'Σ',
    0xf5 => '√',
    0xf6 => '⊥',
    0xf7 => '∠',
    0xf8 => '└',
    0xf9 => '△',
    0xfa => '∵',
    0xfb => '∩',
    0xfc => '∪', 
}
=begin
CHAR_HASH_FC = {
  0xa3 => '  ',  #これは勘違いか?

}
=end
# よく使うconfig変数の定数化(動的な設定が可能になったらもとの戻す)

#Textの大きさ
# いまのところ動的にとってないので定数にする
TEXT_WIDTH = $config['view_width']-32
TEXT_WIDTH2 = TEXT_WIDTH-20
SELECT_WIDTH = $config['select_width']
SELECT_WIDTH2 = $config['select_width'] - 10
HEIGHT = $config['height']
HEIGHT2 = $config['height'] - 70
HEIGHT3 = $config['height'] - 140
THREAD_TOOLTIPS_DELAY = $config['thread_tooltips_delay']
TAB_STRING_SIZE = $config['tab_string_size']
SET_LAST_PAGE = $config['set_last_page']
VIEW_WIDTH = $config['view_width']
VIEW_WIDTH2 = $config['view_width'] - 10
COLORED_TEXT =  $config['colored_text']
TAB_TYPE = $config['tab_type']
VIA_DAT = $config['via_dat']
#雑多な関数

#高速化版 require 'jcode' が必要
def replace_char(text)
  ret = ''
  if text
    text.each_char{|char|
      if char[0] == 0xad && tmp = CHAR_HASH_AD[char[1]]
	ret += tmp
      elsif  (char[0] <= 0xac && char[0] >= 0xa9 ) || char[0] > 0xfc
	ret += '  '
      else
	ret += char
      end
    }
  end

  return ret
end


def unquote(str)
  if str 
  #手抜き
    str.gsub! '&gt;', '>'
    str.gsub! '&lt;', '<'
    str.gsub! '&nbsp;', ' '
    str.gsub! '&quot;', '"'

#こういうのを利用したAAなどある?
#    str.gsub! "&reg;","\$"
#    str.gsub! "&copy;","@"
#    str.gsub! "&shy;","-"

#やるとuuencodeされているものなどがおかしくなる可能性がある
#toggleできるようにする
#    str = str.gsub(/&#(\d+);/){ |s|
#      unquote_char($1.to_i)
#    }

#    str = str.gsub (/&#x([\da-fA-F]+);/){
#      unquote_char($1.hex)
#    }

#結局これはされていない?
#    str.gsub! "&amp;", "&"
#時々こうなってるのが.....
#    str.gsub! "&amp", "&"

  end
  return str
end


def thread_info (thread)
  if thread.is_a? Thread2ch
    if thread.get_dat 
      return "S #{thread.get_url} #{thread.get_title}\n"
    else
      return "T #{thread.get_url} #{thread.get_title}\n"	
    end
  elsif thread.is_a? ThreadShitaraba
    return  "U #{thread.get_url} #{thread.get_title}\n"
  end	   
  return ''
end

# bookmarksファイルを書く
def write_bookmarks(array,filename)

  open(filename,'w') { |file|
    str = ''
    array.each{|e|
      if e.is_a? String
	# カテゴリーとコメント
#	str += "C #{e}\n"
	str += "#{e}\n"
      elsif e.is_a? Board2ch
#      elsif e.is_a? Board
	if e.get_dat
	  str += "A #{e.get_url} #{e.get_title}\n"
	else
	  str += "B #{e.get_url} #{e.get_title}\n"
	end
      elsif e.is_a? SpecialBoard
	str += "D #{e.get_title}\n"
	e.get_thread_array.each { |thread|
	  str += thread_info(thread)
	}
      elsif e.is_a? BoardShitaraba
	str += "E #{e.get_url} #{e.get_title}\n"
      end
    }
    file.print NKF::nkf(NKF_EUC_TO_SJIS,str)
  }
end

#Text上で右クリックした際に出るメニューのItem
class UrlMenuItem < Gtk::MenuItem
  def initialize(text)
    @url = text
#2重に処理することになるので、いまいち
#将来整理
    @text=nil
    host,port,path=parse_http_url(text)

    if host
# offlaw.cgi は認証情報が必要になった模様
      if /\.(2ch|bbspink)\./=~ host  && %r!/test/read\.cgi! =~ path

	# Thanks: >>66
	@text = 'goRuaで処理(therad): ' +text
      elsif %r!^(.*(2ch\.net|bbspink\.com)/)([^/]+)/?! =~ text
	# Thanks: >>202
	@text = 'goRuaで処理(board): ' +text
      else
	@text = '外部ブラウザで処理: ' +text
      end
    else
      @text = text
    end
    
    super @text
    self.set_name 'UrlMenuItem'
    
    self.signal_connect('button_press_event'){|w,e|
      VVBOX.exec_entry_text(@url)
    }

  end

end

# 発言1セットを含むフレーム
class MessageFrame < Gtk::Frame
  def initialize(num,name,mailto,date_id,body)
    super "#{num}"
    self.set_label_align(0, 0)
    self.border_width(1)

    url = []

    table = Gtk::Table.new(2,2,false)
    self.add table
    name.gsub!(/<.+?>/,'')
    name=unquote(name)

    # mailto があるなら Buttonにしてtooltipsを付ける
    if mailto && mailto != ''
      mailto.gsub!(/<.+?>/,'')
      mailto=unquote(mailto)
      name_label = Gtk::Button.new(name)
      tooltips = Gtk::Tooltips::new()
      tooltips.set_tip(name_label,mailto,'')
      name_label.set_name 'NameLabel'
    else
      name_label = Gtk::Label.new(name)
      name_label.set_name 'NameLabel'
    end

    table.attach name_label,0,1,0,1,0,0

    date_id_label =  Gtk::Label.new(date_id)
    date_id_label.set_name 'DateIdLabel'

    table.attach date_id_label,1,2,0,1,0,0

    if body
      lines  = body.split(/<br>/i)
      text = Gtk::Text.new
      text.set_name 'Text'

      text.set_word_wrap false
      table.attach text,0,2,1,2,nil,0
      
      tmp = lines.size+1
      
      lines.each { |tmp_line|
	
#	tmp_line.sub!(/\s+$/,'')
	# 先頭に空白がはいってるのを削除
	tmp_line.sub!(/^ /,'') 
	
# したらばに対してはよくないかも
	tmp_line.gsub!(/<.+?>/,'')
	tmp_line=unquote(tmp_line)	    

# 適当にさっぴかないとあふれる
	tmp+=TEXT_FONT.string_width(tmp_line)/ TEXT_WIDTH2


# 文字列の描画
#	Thanks: M.Suzuki

	base_color = COLOR_OTHERS
	while true
	  case tmp_line
	  when /^([>＞〉]+(\d+-\d+))(.*)$/  #番号引用
	    url << $2	      
	    text.insert nil,COLOR_CITE_NUMBER,nil, $1
	    tmp_line = $3
	  when /^([>＞〉]+(\d+-))(.*)$/
	    url << $2	      
	    text.insert nil,COLOR_CITE_NUMBER,nil, $1
	    tmp_line = $3
	  when /^([>＞〉]+(\d+))(.*)$/
	    url << $2    
	    text.insert nil,COLOR_CITE_NUMBER,nil, $1
	    tmp_line = $3
	  when /^([>＞〉])(.*)/     # 引用
	    text.insert nil,COLOR_CITE,nil, $1
	    base_color = COLOR_CITE
	    tmp_line = $2
	  when /^(.*?)(h?ttps?:[a-zA-Z0-9%&\?\/\;\:\@\&\=\+\$\,\-\_\.\!\~\*\'\\(\)\#]+)(.*)$/	# url
	    text.insert nil,base_color,nil, $1
	    text.insert nil,COLOR_URL,nil, $2
	    host,port,path=parse_http_url($2)
	    tmp_line = $3
	    if host
	      url << $2
	    end
	  else
	    text.insert nil,base_color,nil, tmp_line + "\n"	    
	    break
	  end
	end
      }
      
      text.set_usize TEXT_WIDTH,tmp*TEXT_FONT_HEIGHT

# メニューの生成
      text.signal_connect('button_press_event'){|w,e|
	if e.instance_of?(Gdk::EventButton) &&  e.button==3
	  $menu_popup = true
	  menu = Gtk::Menu.new
	  menu.signal_connect('button_press_event'){
	    menu.popdown
	  }

	  item = Gtk::MenuItem.new('Menu')
	  menu.append item
	  separator = Gtk::MenuItem.new()
	  menu.append separator

	  url.each{ |text|
	    item = UrlMenuItem.new(text)
	    menu.append item
	  }
	  menu.popup(nil, nil, nil, e.button, e.time)
	  menu.show_all
	end
      }
    end

# 将来使うかも
=begin
    @name = name
    @mailto= mailto
    @date_id=date_id
    @body=body
=end
  end

end

# 投稿用Window
class PostWindow < Gtk::Window
  include PostMessage2ch

  def initialize(thread)
    super Gtk::WINDOW_TOPLEVEL


    self.set_wmclass('POST_WINDOW','goRua')

    @thread = thread
    title = @thread.get_title
    self.set_title('書きこむ: ' + title)
    self.set_position(Gtk::WIN_POS_CENTER)
    self.set_transient_for(MAIN_WINDOW)

    vbox = Gtk::VBox.new false,0
    
    title_label = Gtk::Label.new(title + ' への書きこみ')
    vbox.pack_start title_label,false,false


    table = Gtk::Table.new(2,2,false)
    name_label = Gtk::Label.new('名前:')
    @name_combo = Gtk::Combo.new
    @name_combo.entry.set_editable(true)
    @name_combo.set_popdown_strings($config['post_name_candidate'])

    table.attach name_label,0,1,0,1,0,0
    table.attach @name_combo,1,2,0,1

    mailto_label = Gtk::Label.new('mailto:')
    @mailto_combo = Gtk::Combo.new
    @mailto_combo.entry.set_editable(true)
    @mailto_combo.set_popdown_strings($config['post_mailto_candidate'])
    table.attach mailto_label,0,1,1,2,0,0
    table.attach @mailto_combo,1,2,1,2
    vbox.pack_start table,false,false

    @message = Gtk::Text.new
    @message.set_name 'PostText'
    @message.set_editable(true)
    vbox.pack_start @message

    self.add vbox

    hbox = Gtk::HBox.new false,10
    #書きこむボタン
    kakikomu_button = Gtk::Button.new('書きこむ')
    #キャンセルボタン
    cancel_button = Gtk::Button.new('キャンセル')
    
    hbox.pack_start kakikomu_button
    hbox.pack_start cancel_button
    vbox.pack_start hbox,false,false
    self.set_usize $config['post_width'], $config['post_height']
    self.show_all

    cancel_button.signal_connect('clicked'){
      self.hide
    }

    kakikomu_button.signal_connect('clicked'){
      name = @name_combo.entry.get_text
      mailto = @mailto_combo.entry.get_text
      message=@message.get_chars(0,@message.get_length)
      status,message=post_message(thread,name,mailto,message)
      if status
	self.hide
	# 失敗した際どうするか
	# とりあえずなにもしない
#       else
      end

      if message
	STATUS_BAR.push(STATUS_BAR_ID_KAKIKOMI ,message)
	Thread.start{
	  sleep 60
	  STATUS_BAR.pop(STATUS_BAR_ID_KAKIKOMI)
	}
      end      
    }

  end
end

#Threadの内容を表示するVBox
class ViewThreadVBox < Gtk::VBox

  def initialize(thread)
    @sw=nil
    super false,0

#    @start = 1
    @num_of_message = 100
    @size = 0
    @thread = thread
#    @file = @thread.get_file
# こうしてもあまり早くならないかな?

    self.set_file(1,@thread.get_file)
    self.show


    self.signal_connect_after('button_press_event'){|w,e|
      if e.instance_of?(Gdk::EventButton) &&  e.button==3
	if $menu_popup
	  $menu_popup=false
	  break
	end

	menu = Gtk::Menu.new
	menu.signal_connect('button_press_event'){
	  menu.popdown
	}
	item = Gtk::MenuItem.new('Menu')
	menu.append item

	separator = Gtk::MenuItem.new()
	menu.append separator

	prev_item = Gtk::MenuItem.new('Prev')
	menu.append prev_item
	if @start > 1
	  prev_item.set_sensitive true
	else
	  prev_item.set_sensitive false
	end
	next_item = Gtk::MenuItem.new('Next')
	menu.append next_item
	if @start+@num_of_message-1 < @size
	  next_item.set_sensitive true
	else
	  next_item.set_sensitive false
	end
	menu.popup(nil, nil, nil, e.button, e.time)
	menu.show_all
	
	prev_item.signal_connect('button_press_event'){
	  self.set_start(@start-@num_of_message)
	  VVBOX.set_notebook_page(self)
	}
	  
	next_item.signal_connect('button_press_event'){
	  self.set_start(@start+@num_of_message)
	  VVBOX.set_notebook_page(self)
	}
      end
    }
  end

# 現在Threadのどこから閲覧しているか
  def get_start
    return @start 
  end

# 現在Thread中のレスを何個閲覧しているか
  def get_num_of_message
    return @num_of_message 
  end

# Thread全体のサイズ
  def get_size
    return @size 
  end

# Threadの閲覧開始場所のセット
  def set_start(num)
    if num>0
      @start = num
    else
      @start =1
    end
  end

# Thread中のレスの閲覧個数のセット
  def set_num_of_message(num)
    @num_of_message   = num
  end

# サイズのセット
  def set_size(num)
    @size  = num
  end

# ScrolledWindowのセット
  def set_sw(sw)
    if @sw
      self.remove @sw
    end
    @sw  = sw
    

    self.pack_start @sw
  end

  def get_sw
    return @sw
  end

  def get_thread
    return @thread
  end
  
  #Threadの内容をparseし表示
  def make_view  
    
    if @num_of_message <= 0
      return
    end

  
#    GC.disable
    if RUBY_GTK_VERSION >= '027'
      MAIN_WINDOW.window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::WATCH))
    end

#    VVBOX.push_entry_text('描画中')
    STATUS_BAR.push(STATUS_BAR_ID_BYOUGA,'描画中')
    vbox_view = Gtk::VBox.new(false,0)

    prev_title = self.get_thread.get_title 


    @file.rewind

    if  prev_title ==nil || prev_title== ''
# スレッドのタイトルがわからない場合
# タイトルをセット
      begin
	(name,mailto,date_id,body,title)=replace_char(@lines[0]).split(/<>/)

	title.strip!
	if title && title !=''
	  self.get_thread.set_title(title)
	  tmp = title+ " (#{self.get_size})"
	  self.get_thread.set_title_and_size(tmp)
	  VVBOX.set_title(tmp)
	  VVBOX.show_all
	  VVBOX.write_sessions
	end

#	  break

#	end
#      }
      rescue
      end
#      @file.rewind
    end

# Threadの描画
    @sw.add_with_viewport vbox_view

#    i=1 # 現在のレス番号
#    j=0 # 描画したレスの数
#    while line=@file.gets

    num_of_trials = 0


    begin
      tmp  = @lines[@start-1,@num_of_message]

# たぶんこうはならなくなったはずだが一応
      unless tmp
	STDERR.print "行情報の抽出エラー\n"
	raise
      end
    rescue
      if num_of_trials < 3
	num_of_trials += 1
	self.set_file
	retry
      end
      STDERR.print "再試行中止\n"

      STATUS_BAR.pop(STATUS_BAR_ID_BYOUGA)
      
      if RUBY_GTK_VERSION >= '027'
	MAIN_WINDOW.window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::LEFT_PTR))    end
      STATUS_BAR.push(STATUS_BAR_ID_ERROR ,'表示時にエラーがありました.もしかしたらファイルが壊れているのかもしれません.')
      Thread.start{
	sleep 60
	STATUS_BAR.pop(STATUS_BAR_ID_ERROR)
      }
      return
    end


    i = @start    
    tmp.each { |line|
      begin           
	(name,mailto,date_id,body,title)= replace_char(line.strip).split(/<>/)

# 以下だど全部nilになることがあった
#	(name,mailto,date_id,body,title)= replace_char(line.strip!).split(/<>/)

	vbox_view.pack_start MessageFrame::new(i,name,mailto,date_id,body),false,false
#	@sw.show_all

	i += 1
      rescue
=begin
	p line
	p i,name,mailto,date_id,body
=end
	STDERR.print "#{i} におかしな情報がありました\n"
	i += 1
      rescue NameError
=begin
	p line
	p replace_char(line)
	p replace_char(line).split(/<>/)
	p i,name,mailto,date_id,body
=end
	STDERR.print "NameError: #{i} におかしな情報がありました\n"
	i += 1
      end

#      j+=1
#      if j>=@num_of_message
#	break
#      end
#      i+=1

#	M.Suzuki
      while Gtk.events_pending
	@sw.show_all
	Gtk.main_iteration
      end
#    end 
    }

    # 表示部末尾のボタン
    hbox = Gtk::HBox.new false,10

    one_button = Gtk::Button.new('1-')
    prev_button = Gtk::Button.new('Prev')
    next_button = Gtk::Button.new('Next')
    new_button = Gtk::Button.new('最新')
    reload_button = Gtk::Button.new('再読込')

    hbox.pack_start one_button
    hbox.pack_start prev_button
    hbox.pack_start next_button
    hbox.pack_start new_button
    hbox.pack_start reload_button

    if @start > 1
      one_button.set_sensitive true
      prev_button.set_sensitive true
    else
      one_button.set_sensitive false
      prev_button.set_sensitive false
    end

    if @start+@num_of_message-1 < @size
      next_button.set_sensitive true
    else
      next_button.set_sensitive false
    end

    if @num_of_message < @size
      new_button.set_sensitive true
    else
      new_button.set_sensitive false
    end

    # activate_ホゲ という名は良くないのでそのうち変更すること
    one_button.signal_connect('clicked'){
      self.activate_one_item
    }
    prev_button.signal_connect('clicked'){
      self.activate_prev_item
    }

    next_button.signal_connect('clicked'){
      self.activate_next_item
    }


    new_button.signal_connect('clicked'){
      self.activate_new_item
    }
      
    reload_button.signal_connect('clicked'){
      self.activate_reload_item
    }

    vbox_view.pack_start hbox,false,false
    @sw.show_all
    STATUS_BAR.pop(STATUS_BAR_ID_BYOUGA)

    if RUBY_GTK_VERSION >= '027'
      MAIN_WINDOW.window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::LEFT_PTR))    end
  end

# これに統一する
  def set_start_and_num(start,num)
    self.set_start(start)
    self.set_num_of_message(num)
    self.remake_view
  end

  def activate_one_item
    self.set_start(1)
    self.remake_view
  end

  def activate_prev_item
    self.set_start(self.get_start-self.get_num_of_message)
    self.remake_view      

  end

  def activate_next_item
    self.set_start(self.get_start+self.get_num_of_message)
    self.remake_view      
  end

  def activate_new_item
    self.set_start(self.get_size-self.get_num_of_message+1)
    self.remake_view
  end

  def activate_reload_item
    pre_size = self.get_thread.load_file
    if pre_size
      self.set_file(pre_size,self.get_thread.get_file)
      tmp = self.get_thread.get_title  + " (#{self.get_size})"
      VVBOX.set_title(tmp)
      VVBOX.set_notebook_page(self)
    end
  end

  def remake_view
    sw = Gtk::ScrolledWindow.new(nil,nil)
    sw.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_ALWAYS)
    self.set_sw(sw)
    self.make_view
    VVBOX.set_item
  end

  def set_file(start=nil,file=nil)

    if start
      self.set_start(start)
    end

    if file
      @file =file
    end

    @file.rewind
    @size=0
    @lines = []
    while line=@file.gets
      if @thread.get_sjis
	line = NKF::nkf(NKF_SJIS_TO_EUC,line)
      end
      if /^(?:\+OK|-INCR)\s+(\d+)/ =~ line
	next
      end
      @lines << line
      @size+=1
    end
    @file.rewind

  end
end



#Threadの内容を見るBox
class ViewVBox < Gtk::VBox


  def initialize()
    super(false,0)
    
    @sessions=[]
   
    #メニュー と そのItem
    mbar = Gtk::MenuBar.new
    file_item = Gtk::MenuItem.new('File')
    file_menu = Gtk::Menu.new

    @check_new_item = Gtk::CheckMenuItem.new('新着の表示')
    if !$config['show_old_message']
      @check_new_item.activate
    end

    file_menu.add @check_new_item

#Thanks: >> 63
    @toziru_item = Gtk::MenuItem.new('全部閉じる')
    file_menu.add @toziru_item

    separator = Gtk::MenuItem.new()
    file_menu.add separator

    @quit_item = Gtk::MenuItem.new('Quit')
    file_menu.add @quit_item
    file_item.set_submenu file_menu
    mbar.append file_item

    @one_item = Gtk::MenuItem.new('1-')
    mbar.append @one_item

    
    @prev_item = Gtk::MenuItem.new('Prev')
    mbar.append @prev_item
    @next_item = Gtk::MenuItem.new('Next')
    mbar.append @next_item

    @new_item = Gtk::MenuItem.new('最新')
    mbar.append @new_item

    @reload_item = Gtk::MenuItem.new('再読込')
    mbar.append @reload_item

    @url_item = Gtk::MenuItem.new('URL表示')
    mbar.append @url_item

    @bookmark_item = Gtk::MenuItem.new('ブックマークに追加')
    mbar.append @bookmark_item

    @kakikomi_item = Gtk::MenuItem.new('書きこむ')
    mbar.append @kakikomi_item


    handlebox = Gtk::HandleBox.new
    handlebox.add(mbar)

    self.pack_start handlebox, false, false, 0

    @title_label = Gtk::Label.new('')
    @title_label.set_name 'TitleLabel'



    start_spin_adjust = Gtk::Adjustment.new(1,1,1001,1,10,0)
    @start_spin_button = Gtk::SpinButton.new start_spin_adjust,0,0
    

    num_spin_adjust = Gtk::Adjustment.new(100,1,1000,1,50,0)
    @num_spin_button = Gtk::SpinButton.new num_spin_adjust,0,0

    button = Gtk::Button.new('設定')

    hbox = Gtk::HBox.new
    
#    self.pack_start @title_label,false,false,0
    hbox.pack_start @title_label

    hbox.pack_start @start_spin_button ,false,false,0

    hbox.pack_start Gtk::Label.new('から') ,false,false,0

    hbox.pack_start @num_spin_button ,false,false,0
    
    hbox.pack_start Gtk::Label.new('個') ,false,false,0
    
    hbox.pack_start button ,false,false,0

    button.signal_connect('clicked'){
      self.set_start_and_num(@start_spin_button.get_value_as_int,@num_spin_button.get_value_as_int)
    }

    self.pack_start hbox ,false,false,0

    @entry = Gtk::Entry.new
    self.pack_start @entry,false,false,0
    @entry.set_editable(true)
    @entry_text_stack = []

    # ノートブック
    @notebook = Gtk::Notebook::new()

    @notebook.set_usize VIEW_WIDTH2, HEIGHT3
    @notebook.set_scrollable(true)
    @notebook.popup_enable

#    if !$error_of_switch_page
    unless ERROR_OF_SWITCH_PAGE
      # 0.27 0.28(4/19のsnapshot) では switch_page signal にconnectすると
      # 落ちる
      # http://sourceforge.net/tracker/?func=detail&atid=415644&aid=534381&group_id=35786
      @notebook.signal_connect_after('switch_page') { |widget, page, num_page|
	self.page_switch(widget, page, num_page)
      }
    end

    self.pack_start @notebook

    # itemの設定

    self.set_item

    @url_item.signal_connect('activate'){
      page = @notebook.cur_page
      if page
	if page.child
	  url = page.child.get_thread.get_url
#しばらばの場合はまた別
	  if /^(.*(2ch\.net|bbspink\.com))\/(.+?)\/dat\/(.+?)\.dat$/ =~ url
#	    url = $1 + '/test/read.cgi?' + 'bbs=' + $2 + '&key=' + $3
	    url = $1 + '/test/read.cgi/' + $3 + '/' + $4 +'/'
	  elsif  /^(.*)\/(.+?)\/dat\/(.+?)\.dat$/ =~ url
	    url = $1 + '/test/read.cgi?' + 'bbs=' + $2 + '&key=' + $3
	  end
	  @entry.set_text url
	end
      end
    }


    @quit_item.signal_connect('activate'){
      Gtk.main_quit
    }

    @next_item.signal_connect('activate'){
      self.activate_next_item
    }

    @one_item.signal_connect('activate'){
      self.activate_one_item
    }



    @prev_item.signal_connect('activate'){
      self.activate_prev_item
    }

    @new_item.signal_connect('activate'){
      self.activate_new_item
    }

    @reload_item.signal_connect('activate'){
      self.activate_reload_item
    }

    @bookmark_item.signal_connect('activate'){

      page = @notebook.cur_page
      if page
	if page.child
	  title=NKF::nkf(NKF_EUC_TO_SJIS,page.child.get_thread.get_title)
	  url = page.child.get_thread.get_url
	  if $bookmarks_board 
	    page.child.get_thread.set_special_board($bookmarks_board)
	    $bookmarks_board.append_thread page.child.get_thread
	    $bookmarks_board.get_item.set_bookmarks_item
	    write_bookmarks($bookmarks_array,BOOKMARKS_FILE)
	  end
	end 
      end
    }

    @kakikomi_item.signal_connect('activate'){
      page = @notebook.cur_page
      if page
	if page.child
	  window = PostWindow.new(page.child.get_thread)
	end
      end

    }

    @toziru_item.signal_connect('activate'){
      page = @notebook.cur_page
      if page
	v_thread = page.child
	while v_thread
	  @notebook.remove_page(@notebook.page_num(v_thread))
	  if @notebook.get_current_page == -1
	    v_thread= nil
	  else
	    v_thread=@notebook.cur_page.child
	  end
	end
      end
      @sessions = []
      self.write_sessions
      self.set_item
    }



    @entry.signal_connect('activate') {|widget|
      text = @entry.get_text
      text.strip!
      exec_entry_text(text)
    }
  end


  def set_start_and_num(start,num)
    page = @notebook.cur_page
    if page
      if page.child
	page.child.set_start_and_num(start,num)
      end
    end
  end


  def activate_reload_item
    page = @notebook.cur_page
    if page
      if page.child
	page.child.activate_reload_item
      end
    end
  end

  def activate_one_item
    page = @notebook.cur_page
    if page
      if page.child
	page.child.activate_one_item
      end
    end
  end

  def activate_new_item
    page = @notebook.cur_page
    if page
      if page.child
	page.child.activate_new_item
      end
    end
  end

  def activate_prev_item
    page = @notebook.cur_page
    if page
      if page.child
	page.child.activate_prev_item
      end
    end
  end

  def activate_next_item
    page = @notebook.cur_page
    if page
      if page.child
	page.child.activate_next_item
      end
    end
  end


  # entry上のテキストを処理(UrlMenuItemでも使用)
  def exec_entry_text(text)


    if !text 
      return
    end
    text.strip!
    if text == ''
      return
    end

    host,port,path=parse_http_url(text)

    if host
      if /^ttp/ =~ text
	text = 'h' +text
      end
      url = ''
      internal=false
      catch(:tag) do
	if /\.(2ch|bbspink)\./=~ host 
#	  if %r!^(.*/test/read\.cgi/.+?/[0-9]+?/).*! =~ text
	  if %r!^((.*)/test/read\.cgi/(.+?)/([0-9]+)).*! =~ text
	    if VIA_DAT
	      url  =  $2 + '/' + $3 + '/dat/' + $4 + '.dat'
	    else
	      url  = $1 + '/'
	    end
	  elsif %r!^(.*)/test/read\.cgi\?(.*)! =~ text
	    base = $1
	    tmp = $3
	    if /bbs=([^&]+)&.*key=([^&]+)/i =~ tmp
	      bbs = $1
	      key = $2
	    elsif  /key=([^&]+)&.*bbs=([^&]+)/i =~ tmp
	      bbs = $2
	      key = $1
	    else
	      throw :tag
	    end
	    if VIA_DAT
	      url  =  base + '/' + bbs + '/dat/' + key + '.dat'	      
	    else
	      url = base+'/test/read.cgi/'+bbs+'/'+key+'/'
	    end
	  elsif %r!^(.*(2ch\.net|bbspink\.com)/)([^/]+)/?$! =~ text
	    internal=true	 
#	    text = $1 + $3 + '/'
	    url = $1 + $3 + '/'
#	    board = Board2ch.new(text,text,false)
	    board = Board2ch.new(text,url,VIA_DAT)
#	    item = Gtk::BoardTreeItem.new(board)
	    SVBOX.set_kensaku_board(board)
	    throw :tag
	  else
	    throw :tag
	  end
	  internal=true
#	  thread = Thread2ch.new('',text,nil,false)
#	  thread = Thread2ch.new('',text,nil,VIA_DAT)
	  thread = Thread2ch.new('',url,nil,VIA_DAT)
	  pre_size=thread.load_file
	  if pre_size
	    if thread.get_file
	      VVBOX.set_thread(thread,pre_size)
	    end
	  end
	end
      end
      if !internal
	fork do
	  $SAFE = 2
	  if $config['http_command']
	    tmp =$config['http_command'].dup

	    text.gsub!(/\'/,'\\\'')

=begin
#	    * ? {} [] <> () ~ & | \ $ ; ' ` "
	    text.gsub!(/\\/,'\\\\\\')
	    text.gsub!(/\'/,'\\\\\'')
	    text.gsub!(/\*/,'\*')
	    text.gsub!(/\?/,'\?')
	    text.gsub!(/\{/,'\{')
	    text.gsub!(/\}/,'\}')
	    text.gsub!(/\[/,'\[')
	    text.gsub!(/\]/,'\]')
	    text.gsub!(/\</,'\<')
	    text.gsub!(/\>/,'\>')
	    text.gsub!(/\(/,'\(')
	    text.gsub!(/\)/,'\)')
	    text.gsub!(/\~/,'\~')
	    text.gsub!(/\|/,'\|')

	    text.gsub!(/\$/,'\\$')
	    text.gsub!(/\;/,'\\;')
	    text.gsub!(/\`/,'\\`')
	    text.gsub!(/\"/,'\\"')
=end
	    tmp.sub!('%u','\''+text+'\'')
	    tmp.gsub!(/\&/,'\\\&')
	    
	    tmp.untaint
	    exec(tmp)
	  end
	end
      end
    end

#    unless @v_thread
#      return
#    end

    page = @notebook.cur_page
    unless page
      return
    end
    unless page.child
      return
    end
    v_thread = page.child
    
    text.sub!('>>','')
    text.strip!
    _size=v_thread.get_size
      
    if text =~ /^(\d+)$/
      start = Integer($1)
      if start <= _size && start > 0
	v_thread.set_start(start)
	v_thread.set_num_of_message(1)
	self.set_notebook_page(v_thread)
      end
    elsif text =~ /^(\d+)\s*-$/
      start = Integer($1)
      if start < _size && start > 0
	v_thread.set_start(start)	  
      end
      self.set_notebook_page(v_thread)
    elsif text =~ /^(\d+)\s*-\s*(\d+)$/
      start = Integer($1)
      stop = Integer($2)
      num_of_message = stop - start + 1
      if start <= _size && start > 0 && num_of_message > 0
	v_thread.set_start(start)
	v_thread.set_num_of_message(num_of_message)  
	self.set_notebook_page(v_thread)
      end
    end
    self.set_item
  end

  def get_check_new_item
    return @check_new_item 
  end
=begin
  def push_entry_text(text)
    @entry_text_stack.push(@entry.get_text)
    @entry.set_text(text)
    if $display_status_iterationp
# && !$config['background_get']
      while Gtk.main_iteration()
      end
    end 

  end 

  def pop_entry_text
    text=@entry_text_stack.pop
    @entry.set_text(text)
  end 
=end
  
  def set_thread(thread,start)
    sw = Gtk::ScrolledWindow.new(nil,nil)
    sw.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_ALWAYS)
    v_thread = ViewThreadVBox.new(thread)
    v_thread.set_sw(sw)
    v_thread.set_file(start)
    self.set_notebook_page(v_thread)
  end

  def get_notebook
    return @notebook
  end

  # ノートのページを設定
  def set_notebook_page(v_thread)


    tmp =@notebook.page_num(v_thread)
    if tmp == -1
      v_thread.make_view

      tmp = v_thread.get_thread.get_title + " (#{v_thread.get_size})"

      hbox =create_tab_label(tmp,v_thread)
      hbox.show_all

      @notebook.append_page_menu(v_thread,hbox,Gtk::Label.new(tmp))

      if ! $init  && SET_LAST_PAGE
	# なぜかentryからいれると 最後のページに行かないな…
	@notebook.set_page(-1)
	# @notebook.set_page(@notebook.page_num(v_thread))
      end

      @sessions <<  v_thread.get_thread
      self.write_sessions
    else
      sw = Gtk::ScrolledWindow.new(nil,nil)
      sw.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_ALWAYS)
      v_thread.set_sw(sw)
      v_thread.make_view
    end
  end

# sessions ファイルの書きこみ
  def write_sessions
    open(SESSIONS_FILE,'w') { |file|
      str=''
      @sessions.each{ |thread|
=begin
	if thread.get_dat 
	  str +=  "S #{thread.get_url} #{thread.get_title}\n"
	else
	  str += "T #{thread.get_url} #{thread.get_title}\n"	
	end
=end
	str += thread_info(thread)
      }
	
      file.print NKF::nkf(NKF_EUC_TO_SJIS,str)

    }
  end 

=begin
#  おそらく使わなくなる
  def append_str_to_file(str,filename)
    open(filename,'a') { |file|
      file.print "#{str}\n"
    }
  end 
=end
    
# notebookのタブのラベルの作成
  def create_tab_label(str,v_thread)
    hbox = Gtk::HBox.new false,5
    trunc = trunc_string(str,TAB_STRING_SIZE)
    label = Gtk::Label.new(trunc)
    label.set_name 'NotebookTab'
#    hbox.pack_start label
    button = Gtk::Button.new
    button.add Gtk::Pixmap.new(BATU_PIX, BATU_MASK)
    button.signal_connect('clicked'){
      num = @notebook.page_num(v_thread)
      @notebook.remove_page(@notebook.page_num(v_thread))
      # これだと指定したものと同じThreadがより前(の番号)にある場合
      # そっちを消しちゃう
      #      @sessions.delete(v_thread.get_thread)
      @sessions.delete_at(num)

      
      self.write_sessions
      self.set_item
    }
      
    # Thanks: liangtai
    case TAB_TYPE 
    when 1
      hbox.pack_start button,false,false
      hbox.pack_start label 
    else
      hbox.pack_start label 
      hbox.pack_start button,false,false
    end

    hbox.show_all
    return hbox
  end


#外に出す?
  def trunc_string(str,trunc)

     if  /^(.{1,#{trunc}}).*$/ =~ str
       return $1
     else
       return str
     end
  end

#notobookのページが代わったときの処理
  def page_switch(widget, page, page_num)
    oldpage = @notebook.cur_page
    if (page == oldpage)
      return
    end

    v_thread=page.child
    self.set_title(v_thread.get_thread.get_title+ " (#{v_thread.get_size})")
    self.set_item
  end


  def set_title(title)
    @title_label.set title
  end

  def get_new_item
    return @new_item
  end

  def get_one_item
    return @one_item
  end

  def get_prev_item
    return @prev_item
  end

  def get_next_item
    return @next_item
  end


# menu_itemの設定
  def set_item
    page = @notebook.cur_page
    if page
      v_thread=page.child
    else
      v_thread=nil
    end

    unless v_thread
      @one_item.set_sensitive false
      @prev_item.set_sensitive false
      @next_item.set_sensitive false
      @new_item.set_sensitive false
      @reload_item.set_sensitive false
      @url_item.set_sensitive false
      @bookmark_item.set_sensitive false
      @kakikomi_item.set_sensitive false
      @toziru_item.set_sensitive false
      return
    end

    @reload_item.set_sensitive true
    @url_item.set_sensitive true
    @bookmark_item.set_sensitive true
    @kakikomi_item.set_sensitive true
    @toziru_item.set_sensitive true

    start= v_thread.get_start
    num_of_message=v_thread.get_num_of_message
    size=v_thread.get_size


    start_spin_adjust = Gtk::Adjustment.new(start,1,size,1,10,0)
    @start_spin_button.set_adjustment(start_spin_adjust)
    @start_spin_button.set_value(start)

    num_spin_adjust = Gtk::Adjustment.new(num_of_message,1,1000,1,50,0)
    @num_spin_button.set_adjustment(num_spin_adjust)
    @num_spin_button.set_value(num_of_message)

    if start > 1
      @one_item.set_sensitive true
      @prev_item.set_sensitive true
    else
      @one_item.set_sensitive false
      @prev_item.set_sensitive false
    end

    if num_of_message < size
      @new_item.set_sensitive true
    else
      @new_item.set_sensitive false
    end

    if start+num_of_message-1 < size
      @next_item.set_sensitive true
    else
      @next_item.set_sensitive false
    end
  end
end 

#スレッドを管理する
class Thread_
#  include GetThread2ch

  # initializeの引数から見直さんと

  # special_board には SpecialBoard クラスのインスタンスがはいる
  def initialize(title,url_base,dat_file,dat,special_board=false)
    title = replace_char(title)
    @special_board = special_board
    @title_and_size = title
    if @special_board
      @title = title
    else
      @title = title.gsub(/\s+\(\d+\)\s*$/,'')
    end
    @dat = dat
    @bbs = nil
    @key = nil
    @base_path = nil
    if dat_file
      if @dat
	@url = url_base + dat_file
      else
	@url = url_base + dat_file.sub(/\.dat$/,'')+'/'
      end
    else
      url_base.strip!
      @url = url_base
    end


    @sjis = true
    @file= nil
    @size= 0
    @num_of_statement= 0

    @host,@port,@path = parse_http_url(@url)
    if @host
      @filename = BASE_DIR + 'thread/'+ @host + '_' + "#{@port}" + '_' + @path.gsub(/\//,'_')
    # 将来整理
      # dat 板ってあったら困るかな?
      if %r!^(/.*/|/)(.+?)/dat/(.+?)$! =~ @path
	# dat版
	@base_path = $1
	@bbs = $2
	@key = $3.sub(/\.dat$/,'')
      elsif %r!^(/.*)/(.+?)/(.+?)/?$! =~ @path
	# read.cgi版
	tmp = $1
	@bbs = $2
	@key = $3.sub(/\.dat$/,'')
	@base_path = tmp.sub(/test\/read\.cgi$/,'')
      else
	STDERR.print "thread: おかしなURL: #{@url}\n"
      end
    else
      @filename =nil
      STDERR.print "thread: 対応しているURLではありません。: #{@url}\n"
    end

  end

  def get_sjis
    return @sjis
  end

  def get_special_board
    return @special_board
  end

  def set_special_board(special_board)
    @special_board = special_board
  end



  def get_bbs
    return @bbs
  end

  def get_key
    return @key
  end

  def get_base_path
    return @base_path
  end

  def set_title(title)
    @title = title
  end


  def get_title
    return @title
  end

  def set_title_and_size(title)
    @title_and_size = title
  end


  def get_title_and_size
    return @title_and_size
  end

  def get_dat
    return @dat
  end


  def get_url
    return @url
  end

  def get_host
    return @host
  end
  def get_port
    return @port
  end
  def get_path
    return @path
  end
  def get_filename
    return @filename
  end


  def get_file
    return @file
  end

  def load_file
=begin
    $mutex_for_get.lock
    if $filenames_in_getting.include?(self.get_filename)
      STDERR.print "同じファイル #{self.get_filename} を読みこんでいます。\nこのThread読込を中止します.\n"
      $mutex_for_get.unlock
      return nil
    end
    $filenames_in_getting << self.get_filename
    $mutex_for_get.unlock
=end

# Thanks: M. Suzuki
    if RUBY_GTK_VERSION >= '027'
      MAIN_WINDOW.window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::WATCH))
    end

    STATUS_BAR.push(STATUS_BAR_ID_YOMIKOMI,'Thread読込中')


    @file,pre_size,error = get_thread_file(self)
=begin
    $mutex_for_get.lock
    $filenames_in_getting.delete(self.get_filename)
    $mutex_for_get.unlock
=end

    if error
      STATUS_BAR.push(STATUS_BAR_ID_ERROR ,error)
      Thread.start{
	sleep 60
	STATUS_BAR.pop(STATUS_BAR_ID_ERROR)
      }
    end
    
    if !pre_size || pre_size==0 
      size=1
      pre_size = 1
    end
    STATUS_BAR.pop(STATUS_BAR_ID_YOMIKOMI)

    if RUBY_GTK_VERSION >= '027'
      MAIN_WINDOW.window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::LEFT_PTR))
    end


    if VVBOX.get_check_new_item.active?
      return pre_size
    end 
    return 1
  end

  def set_size(size)
    @size = size
  end

  def get_size
    return @size 
  end


  def set_num_of_statement(num)
    @num_of_statement = num
  end

  def get_num_of_statement
    return @num_of_statement
  end

end

class Thread2ch < Thread_
  include GetThread2ch
  def initialize(title,url_base,dat_file,dat,special_board=false)
    super title,url_base,dat_file,dat,special_board
  end
end

class ThreadShitaraba < Thread_
  include GetThread2ch
  def initialize(title,url_base,dat_file,dat,special_board=false)
    super title,url_base,dat_file,dat,special_board
    @sjis = false
  end
end

#板を管理する
class Board
  def initialize(title,url,dat,special=false)

    @title = title
    @url = url
    @host,@port,@path = parse_http_url(@url)
    @special = special
    if @host
      if @path == nil
	@path = '/'
      end

      @filename = BASE_DIR + 'subject/'+ @host + '_' + "#{@port}" + '_' + @path.gsub(/\//,'_')
    else
      @filename =nil
      if @special ==false
	STDERR.print "Board: 対応しているURLではありません。: #{@url}\n"
      end 
    end

    @dat = dat
    @thread_array=[]
    @item = nil

    @sjis = true
  end

  def get_sjis
    return @sjis
  end

  def get_item
    return @item
  end

  def set_item(item)
    @item = item
  end

  def get_special
    return @special
  end

  def append_thread(thread)
    @thread_array << thread
  end

  def delete_thread(thread)
    @thread_array.delete(thread)
  end


  def get_thread_array
    return @thread_array
  end

  def set_filename(filename)
    @filename = filename
  end

  def get_title
    return @title
  end

  def get_url
    return @url
  end

  def get_host
    return @host
  end
  def get_port
    return @port
  end
  def get_path
    return @path
  end
  def get_filename
    return @filename
  end

  def get_dat
    return @dat
  end
end

# SJISデータを扱うことにする?
class Board2ch < Board
  include GetBoardSubject2ch

  def initialize(title,url,dat)
    super title,url,dat
  end
end

class SpecialBoard < Board
  def initialize(title)
    super title,nil,false,true
  end
end


class BoardShitaraba < Board
  include GetBoardSubject2ch

  def initialize(title,url)
    super title,url,true
    @sjis = false
  end
end



#板用のTreeItem
class BoardTreeItem < Gtk::TreeItem

  def initialize(board)
    @board=board    
    super(@board.get_title)
    self.set_name 'BoardTreeItem'
    @num_of_thread=0
    @subtree=nil
    @special = @board.get_special

    @board.set_item self
    self.signal_connect('select') { 
#      if @special
#      else
      unless @special
	self.get_board_subject
      end
      self.expand
    }
    @destroyed=false
    self.signal_connect('destroy'){
      @destroyed=true
    }
  end



  def set_bookmarks_item
    if @destroyed
      return
    end

    array = @board.get_thread_array
    if array  != [] 
      subtree = Gtk::Tree.new
      self.set_subtree subtree
      i=0
      array.each{ |thread|
	thread_item = Gtk::ThreadTreeItem.new(thread,self)
	subtree.append thread_item
	thread_item.show_all
	i+=1
      }
      self.set_num_of_thread(i)
    end 
    self.show_all
    self.expand
  end

  def get_board_subject

=begin
    $mutex_for_get.lock
    if $filenames_in_getting.include?(@board.get_filename)
      STDERR.print "同じファイル #{@board.get_filename} を読みこんでいます。\nこのSubject読込を中止します.\n"
      $mutex_for_get.unlock
      return
    end
    $filenames_in_getting << @board.get_filename
    $mutex_for_get.unlock
=end
#    VVBOX.push_entry_text('Subject読込中')
    if RUBY_GTK_VERSION >= '027'
      MAIN_WINDOW.window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::WATCH))
    end

    STATUS_BAR.push(STATUS_BAR_ID_YOMIKOMI ,'Subject読込中')


    file,error = @board.get_board_subject_file(@board)

    if error
      STATUS_BAR.push(STATUS_BAR_ID_ERROR ,error)
      Thread.start{
	sleep 60
	STATUS_BAR.pop(STATUS_BAR_ID_ERROR)
      }
    end
=begin
    $mutex_for_get.lock
    $filenames_in_getting.delete(@board.get_filename)
    $mutex_for_get.unlock
=end
    unless file
      STATUS_BAR.pop(STATUS_BAR_ID_YOMIKOMI)
      return
    end
    
    file.rewind

    subtree = Gtk::Tree.new

    if @board.get_port == 80
      url = 'http://' + @board.get_host 
    else
      url = 'http://' + @board.get_host + ":#{@board.get_port}" 
    end

    path = @board.get_path 
    

    if @board.get_dat
      url += path + 'dat/'
    else
      if /^(\/.*)(\/.+)$/i =~ path
	url += $1
	path = $2
      end
      url += '/test/read.cgi' + path
    end

#   ここでsubtreeしちゃうと描画が大変

    i=0
    file.each{ |line|
      line.chop!
      if @board.get_sjis
	line = NKF::nkf(NKF_SJIS_TO_EUC,line)
      end
#      if /<>/ =~ NKF::nkf(NKF_SJIS_TO_EUC,line.chop!)
      if /^(.+?)<>(.*)$/ =~ line
	thread = $1
	title = $2
#	thread = Thread2ch.new(unquote($'),url,$`,@board.get_dat)
	if /\.dat$/ =~ thread 
	  thread = Thread2ch.new(unquote(title),url,thread,@board.get_dat)
	else
	  thread = thread + '.dat'
	  title,num,sute,null = title.split(/<>/)
	  title = title + " (#{num})"
	  thread = ThreadShitaraba.new(unquote(title),url,thread,@board.get_dat)
	end


	@board.append_thread thread
	thread_item = Gtk::ThreadTreeItem.new(thread,self)
	subtree.append thread_item
	thread_item.show
	while Gtk.events_pending
	  Gtk.main_iteration
	end
	i+=1      
      end
    }
    self.set_num_of_thread(i)
    file.close
    self.set_subtree subtree
    self.show_all

    STATUS_BAR.pop(STATUS_BAR_ID_YOMIKOMI)
    if RUBY_GTK_VERSION >= '027'
      MAIN_WINDOW.window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::LEFT_PTR))
    end

  end


  def get_board
    return @board
  end

  def set_num_of_thread(num)
    @num_of_thread=num
  end
  def get_num_of_thread
    return @num_of_thread
  end

  def set_subtree(subtree)
    if @subtree
      begin
	@subtree.clear_items 0,@num_of_thread
      rescue ArgumentError
      end
    end
    @subtree=subtree
    super
  end
end

#Thread用のTreeItem
class ThreadTreeItem < Gtk::TreeItem

  def initialize(thread,board_item)
    @thread=thread
    @board_item = board_item
    super(@thread.get_title_and_size)
    self.set_name 'ThreadTreeItem'

    tooltips = Gtk::Tooltips.new()
    tooltips.set_tip(self,@thread.get_title_and_size,'')
    tooltips.set_delay(THREAD_TOOLTIPS_DELAY)

    self.signal_connect('select') { 

      pre_size=@thread.load_file
      if pre_size
	if @thread.get_file
	  VVBOX.set_thread(@thread,pre_size)
	end
      end

    }

    self.signal_connect('button_press_event'){|w,e|
      if e.instance_of?(Gdk::EventButton) &&  e.button==3
	if @thread.get_special_board
	  menu = Gtk::Menu.new
	  menu.signal_connect_after('button_press_event'){
	    menu.popdown
	  }
	  item = Gtk::MenuItem.new('Menu')
	  menu.append item
	  separator = Gtk::MenuItem.new()
	  menu.append separator

	  toziru_item = Gtk::MenuItem.new('閉じる')
	  menu.append toziru_item
	  
	  toziru_item.signal_connect('button_press_event'){|w,e|
	    @board_item.collapse
	  }
	  sakuzyo_item = Gtk::MenuItem.new('削除')
	  menu.append sakuzyo_item
	  
	  sakuzyo_item.signal_connect('button_press_event'){|w,e|
	    @thread.get_special_board.delete_thread(@thread)
	    @board_item.set_bookmarks_item
	    write_bookmarks($bookmarks_array,BOOKMARKS_FILE)
	  }

	  menu.popup(nil, nil, nil, e.button, e.time)
	  menu.show_all

	else
	  @board_item.collapse
	end
      end
    }

  end
  def get_thread
    return @thread
  end

end

class SelectionVBox < Gtk::VBox

  def initialize(board_file_array)

    @board_hash = Hash.new
    @category_array = []
    @sw=nil
    $bookmarks_board =nil

    $bookmarks_array=[]

    bookmarks_flag=true
    board_file_array.each { |board_file|
      while line = board_file.gets

	line=NKF::nkf(NKF_SJIS_TO_EUC,line).strip

	case line
	when /^C\s+(.+)/
	  category = $1.strip
	  @category_array.push category
	  @board_hash[category] = []
	  if bookmarks_flag
	    $bookmarks_array << "C #{category}"
	  end

	when /^D\s+(.+)/
#	  board = Board2ch.new($1,'',false,true)
	  board = SpecialBoard.new($1)
	  unless category
	    category= make_bookmarks_category
	  end
	  @board_hash[category] << board
	  $bookmarks_board =board
	  if bookmarks_flag
	    $bookmarks_array << board
	  end
	when /^U\s+(\S+)\s+(.+)/
	  if  $bookmarks_board == nil &&  bookmarks_flag
	    category,board = make_bookmarks_board(category)
	  end
	  thread = ThreadShitaraba.new($2.strip,$1,nil,true,$bookmarks_board)
	  $bookmarks_board.append_thread thread
	when /^T\s+(\S+)\s+(.+)/
	  if  $bookmarks_board == nil &&  bookmarks_flag
	    category,board = make_bookmarks_board(category)
	  end
	  thread = Thread2ch.new($2.strip,$1,nil,false,$bookmarks_board)
	  $bookmarks_board.append_thread thread

	when /^S\s+(\S+)\s+(.+)/
	  if  $bookmarks_board == nil &&  bookmarks_flag
	    category,board = make_bookmarks_board(category)
	  end
	  thread = Thread2ch.new($2.strip,$1,nil,true,$bookmarks_board)
	  $bookmarks_board.append_thread thread

	when /^B\s+(\S+)\s+(.+)/
	  if ENABLE_GZIP
	    board = Board2ch.new($2.strip,$1,false)
	  else
	    board = Board2ch.new($2.strip,$1,true)
	  end
	  @board_hash[category] << board
	  if bookmarks_flag
	    $bookmarks_array << board
	  end

	when /^A\s+(\S+)\s(.+)/
	  board = Board2ch.new($2.strip,$1,true)
	  @board_hash[category] << board
	  if bookmarks_flag
	    $bookmarks_array << board
	  end
	when /^E\s+(\S+)\s(.+)/
	  board = BoardShitaraba.new($2.strip,$1)
	  @board_hash[category] << board
	  if bookmarks_flag
	    $bookmarks_array << board
	  end
	else
	  if bookmarks_flag
	    $bookmarks_array << line
	  end
	end
      end
      if bookmarks_flag
	if $bookmarks_board == nil
	  category,board = make_bookmarks_board(category)
	end
      end
      bookmarks_flag=false
      board_file.close
    }

    category = NAIBU_CATEGORY
    @category_array.push category
    @board_hash[category] = []
    
    @cur_category = @category_array[0]
    super(false,0)

    @combo = Gtk::Combo.new
    @combo.entry.set_editable(false)
    @combo.set_popdown_strings(@category_array)

    @combo.entry.add_events( Gdk::KEY_RELEASE_MASK)

    @combo.entry.signal_connect('key_release_event'){
      @cur_category = @combo.entry.get_text
      self.set_sw
    }

    @combo.list.signal_connect('button_release_event'){
      @cur_category = @combo.entry.get_text
      self.set_sw
    }

# これだと確定するまえの変更でもカテゴリがかわり
# うざい
#    @combo.entry.signal_connect('changed'){
#      unless @combo.list.visible?
#	@cur_category = @combo.entry.get_text
#	self.set_sw
#      end
#    }

    self.pack_start @combo,nil

    entry = Gtk::Entry.new
    self.pack_start entry,false,false,0
    entry.set_editable(true)
    self.show_all
    entry.signal_connect('activate'){ |widget|
      text=entry.get_text.strip
      if text == ''
	break
      end 
      re = Regexp.new(text,true)

#      kensaku_board = Board2ch.new(KENSAKU_KEKKA + ': ' + text,'','',true)
      kensaku_board = SpecialBoard.new(KENSAKU_KEKKA + ': ' + text)

      @board_hash[@cur_category].each{ |board|
	board.get_thread_array.each{|thread|
	  if re =~ thread.get_title_and_size
	    kensaku_board.append_thread(thread)
	  end
	}
      }
      if kensaku_board.get_thread_array == []
	STATUS_BAR.push(STATUS_BAR_ID_MATCH,'マッチするThreadはありませんでした。')

	Thread.start{
	  sleep 10
	  STATUS_BAR.pop(STATUS_BAR_ID_MATCH)
	}
      else
	self.set_kensaku_board(kensaku_board)
      end
    }

    self.set_sw

  end

  def set_kensaku_board(board)
    @board_hash[NAIBU_CATEGORY] << board
    @combo.entry.set_text(NAIBU_CATEGORY)
    @cur_category = @combo.entry.get_text
    self.set_sw
  end
  
  def make_bookmarks_board(category)
#    board = Board2ch.new('Threadのブックマーク','',false,true)
    board = SpecialBoard.new('Threadのブックマーク')
    unless category
      category= make_bookmarks_category
    end
    @board_hash[category] << board
    $bookmarks_board = board
    $bookmarks_array << board
    return category,board
  end

  def make_bookmarks_category
    category = 'お気に入り'
    @category_array.push category
    @board_hash[category] = []
    $bookmarks_array << "C #{category}"
    return category
  end

  def set_sw
    if @sw
      self.remove @sw
    end
    @sw = set_tree(@board_hash[@combo.entry.get_text])
    self.pack_start @sw      
    @sw.show_all
  end

  def set_tree(hash)
    @tree = Gtk::Tree.new
    @tree_size=0

    sw =  Gtk::ScrolledWindow.new()
    sw.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC)
    sw.set_usize SELECT_WIDTH2,HEIGHT2
    sw.add_with_viewport @tree   

    @tree.clear_items 0,@tree_size
    @tree_size=0
    hash.each{ |board|
      @tree_size+=1

      item = Gtk::BoardTreeItem.new(board)
      @tree.append item
      array = board.get_thread_array
      if array  != []
	subtree = Gtk::Tree.new
	item.set_subtree subtree
	i=0
	array.each{ |thread|
	  thread_item = Gtk::ThreadTreeItem.new(thread,item)
	  subtree.append thread_item
	  thread_item.show_all
	  i+=1
	}
	item.set_num_of_thread(i)
	item.show_all
      end 
    }
    @tree.show_all

    return sw
  end

end

board_info = BASE_DIR + 'board_info'

if File.exist?(board_info)
  file = open(board_info,'r')
else
  STDERR.print "not found: ~/.goRua_2ch/board_info. see README.\n"
end

BOOKMARKS_FILE = BASE_DIR + 'bookmarks'
SESSIONS_FILE = BASE_DIR + 'sessions'
sessions_backup = BASE_DIR + 'sessions_backup'


MAIN_WINDOW = Gtk::Window.new(Gtk::WINDOW_TOPLEVEL)
MAIN_WINDOW.set_title("goRua -- Gtk+ on Ruby User Agent for 2ch version #{GORUA_VERSION}")

# Thanks : honeto@mail.goo.ne.jp さん
hpaned = Gtk::HPaned.new

VVBOX = ViewVBox.new()

file_array=[]
if File.exist?(BOOKMARKS_FILE)
  bm_file = open(BOOKMARKS_FILE)
  file_array << bm_file
else
  bm_file = open(BOOKMARKS_FILE,'w+')
end 
file_array << file
SVBOX = SelectionVBox.new(file_array)

# Thanks : honeto@mail.goo.ne.jp さん
hpaned.pack1 SVBOX, nil, true
hpaned.pack2 VVBOX, nil, true

vbox = Gtk::VBox.new()
vbox.pack_start hpaned
STATUS_BAR = Gtk::Statusbar.new()

# サイズをかえても大きさがかわらんように
# Thanks: 231さん
vbox.pack_start STATUS_BAR,false
MAIN_WINDOW.add vbox
MAIN_WINDOW.set_wmclass('MAIN_WINDOW','goRua')

MAIN_WINDOW.show_all
BATU_PIX, BATU_MASK =
  Gdk::Pixmap::create_from_xpm_d(MAIN_WINDOW.window, nil, BATU_XPM)

# Thanks: >>73
MAIN_WINDOW.signal_connect('destroy') {
  exit
#  Gtk.main_quit
}

$v_thread_for_keyevent =nil

MAIN_WINDOW.signal_connect('key_press_event') { |w,event|
  notebook = VVBOX.get_notebook
  page = notebook.cur_page
  if page && page.child
    $v_thread_for_keyevent = page.child
  else
    $v_thread_for_keyevent = nil
    break
  end

}
MAIN_WINDOW.set_usize SELECT_WIDTH+VIEW_WIDTH,HEIGHT

MAIN_WINDOW.signal_connect_after('key_press_event') { |w,event|
  v_thread = $v_thread_for_keyevent
  notebook = VVBOX.get_notebook
  unless v_thread
    page = notebook.cur_page
    if page && page.child
      v_thread=page.child
    else
      break
    end
  end
  notebook.set_page(notebook.page_num(v_thread))


  vadj = v_thread.get_sw.get_vadjustment
  
  tmp = vadj.value
  case event.keyval
  when Gdk::GDK_Up,Gdk::GDK_KP_Up
    tmp -= vadj.page_size/2
  when Gdk::GDK_Down,Gdk::GDK_KP_Down
    tmp  += vadj.page_size/2
#  when Gdk::GDK_space,Gdk::GDK_KP_Space
#    tmp  += vadj.page_size/2
  when Gdk::GDK_Page_Up,Gdk::GDK_KP_Page_Up
    tmp -=  vadj.page_size
  when Gdk::GDK_Page_Down,Gdk::GDK_KP_Page_Down
    tmp += vadj.page_size    
  else
#    Thanks: >>175
    false
    break
  end
  if tmp > vadj.upper - vadj.page_size
    tmp =  vadj.upper - vadj.page_size
  end
  vadj.set_value(tmp)
  false
}

# $display_status_iterationp = false

if File.exist?(SESSIONS_FILE) 
  open(SESSIONS_FILE,'r'){ |file|
    open(sessions_backup,'w'){|backup|
      backup.write file.read
    }
  }
  if !$OPT_s
    $init = true
    open(SESSIONS_FILE,'r'){ |file|
      while line=file.gets
	line=NKF::nkf(NKF_TO_EUC,line)
	thread=nil
	line.strip!
	if  /^S\s+(\S+)\s+(.+)/ =~ line
	  thread = Thread2ch.new($2.strip,$1,nil,true)
	elsif  /^T\s+(\S+)\s+(.+)/ =~ line
	  thread = Thread2ch.new($2.strip,$1,nil,false)
	elsif  /^U\s+(\S+)\s+(.+)/ =~ line	  
	  thread = ThreadShitaraba.new($2.strip,$1,nil,true)
	end
	if thread
	  pre_size=thread.load_file
	  if pre_size
	    if thread.get_file
	      VVBOX.set_thread(thread,pre_size)
	    end
	  end
	end
      end
    }
  end
end
$init = false
# $display_status_iterationp = true

Gtk.main
