#!/usr/bin/env ruby
$KCODE='e'
# goRua -- Gtk+ on Ruby User Agent for 2ch version
GORUA_VERSION = '0.18-snapshot'
# 春山 征吾 haruyama@unixuser.org
# $Id: goRua.rb,v 1.202 2005/10/16 15:05:48 haruyama Exp $
# このファイルはGPL2で配付されています.
# このプログラムは無保証です.
#require 'getopts'
require 'optparse'
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
#文字列定数
STRING_SPACE = ' '
STRING_2SPACE = ' '
STRING_EGT = '>'
STRING_GT = '>'
# Thanks: 275さん
STRING_ELT = '<'
STRING_LT = '<'
STRING_ENBSP = ' '
STRING_EQUOT = '"'
STRING_QUOT = '"'
STRING_SYORI_T = 'goRuaで処理(thread): '
STRING_SYORI_B = 'goRuaで処理(board): '
STRING_SYORI_G = '外部ブラウザで処理: '
STRING_URLMENUITEM = 'UrlMenuItem'
STRING_MENU = 'Menu'
STRING_BUTTON_PRESS_EVENT = 'button_press_event'
#正規表現
# Thanks: 247さん
# もっといい名前を付けたほうがいい?
# いい加減なのでもうすこしましな正規表現にする
# 文字列定数もやったほうがいいかなぁ?
#ここをカスタマイズ可能に
REGEXP_0 = /\.(2ch|bbspink)\./
REGEXP_1 = %r!/test/read\.cgi!
REGEXP_2 = %r!^(.*(2ch\.net|bbspink\.com)/)([^/]+)/?$!
REGEXP_SHITARABA_THREAD = %r!www\.shitaraba\.com/cgi-bin/read\.cgi\?(.+)!
REGEXP_SHITARABA_BOARD = %r!www\.shitaraba\.com/bbs/(.+?)/!
REGEXP_SHITARABA = %r!www\.shitaraba\.com/!
REGEXP_3 = %r!^((.*)/test/read\.cgi/(.+?)/([0-9]+)).*!
REGEXP_4 = %r!^(.*)/test/read\.cgi\?(.*)!
REGEXP_5 = %r!^(/.*/|/)(.+?)/dat/(.+?)$!
REGEXP_6 = %r!^(/.*)/(.+?)/(.+?)/?$!
REGEXP_ANYTAG = /<.+?>/
#REGEXP_8 = /
/i
REGEXP_INITIAL_SPACE = /^ /
REGEXP_LAST_SPACE = / $/
REGEXP_CITE_NUMBER_0 = /(.*?)([>>〉]+(\d{1,4}-\d{1,4}))(.*)$/
REGEXP_CITE_NUMBER_1 = /(.*?)([>>〉]+(\d{1,4}-))(.*)$/
REGEXP_CITE_NUMBER_2 = /(.*?)([>>〉]+(\d{1,4}))(.*)$/
REGEXP_CITE = /^\s*[>>〉](?![>>〉]?\d{1,4}).*/
REGEXP_BRAKET = /<>/
# REGEXP_C_READ_CGI = /^(?:\+OK|-INCR)\s+(\d+)/
REGEXP_F = /^(.*(2ch\.net|bbspink\.com))\/(.+?)\/dat\/(.+?)\.dat$/
REGEXP_G = /^(.*)\/(.+?)\/dat\/(.+?)\.dat$/
REGEXP_H = /bbs=([^&]+)&.*key=([^&]+)/i
REGEXP_I = /key=([^&]+)&.*bbs=([^&]+)/i
REGEXP_J = /test\/read\.cgi$/
REGEXP_K = /^(\/.*)(\/.+)$/i
REGEXP_L = /^(.+?)<>(.*)$/
REGEXP_O = /^([a-z_0-9]+)\s*=\s*([0-9\-][0-9]*)\s*$/
REGEXP_V = /^([a-z_0-9]+)\s*=\s*\[(.*)\]\s*$/
REGEXP_W = /^([a-z_0-9]+)\s*=\s*(.*)\s*$/
REGEXP_URL = /^(.*?)(h?ttps?:[a-zA-Z0-9%&\?\/\;\:\@\&\=\+\$\,\-\^\_\.\!\~\*\'\(\)\#]+)(.*)$/
REGEXP_TTP = /^ttp/
REGEXP_QUOTA = /\'/
REGEXP_AND = /\&/
REGEXP_DAT = /\.dat$/
REGEXP_SLASH = /\//
REGEXP_SHARP = /^\#/
REGEXP_SIZE_CUT = /\s*\(\d+\)\s*$/
#デフォルト設定
$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',
'editor_command' => 'emacs %s',
'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' => "Monazilla/1.00 (goRua.rb/#{GORUA_VERSION})",
'x_2ch_ua' => "goRua.rb #{GORUA_VERSION}",
'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,
'reload_after_post' => false,
'wait_for_reload_after_post' => 2,
'url_of_board_info' => 'http://menu.2ch.net/bbsmenu.html',
}
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
if REGEXP_SHARP =~ line
next
# elsif /^([a-z_0-9]+)\s*=\s*([0-9\-][0-9]*)\s*$/ =~ line
elsif REGEXP_O =~ 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
elsif REGEXP_V =~ 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
elsif REGEXP_W =~ 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 = 100
# コマンドラインオプション
#getopts('s')
opt = OptionParser.new
opt.on('-s') {|v|
$OPT_s = v
}
opt.parse!(ARGV)
#フォントの設定
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 < '1',
0xa2 => '2',
0xa3 => '3',
0xa4 => '4',
0xa5 => '5',
0xa6 => '6',
0xa7 => '7',
0xa8 => '8',
0xa9 => '9',
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 += ' '
ret += STRING_2SPACE
else
ret += char
end
}
end
return ret
end
def unquote(str)
if str
#手抜き
=begin
str.gsub! '>', '>'
str.gsub! '<', '<'
str.gsub! ' ', ' '
str.gsub! '"', '"'
=end
str.gsub! STRING_EGT, STRING_GT
str.gsub! STRING_ELT, STRING_LT
str.gsub! STRING_ENBSP, STRING_SPACE
str.gsub! STRING_EQUOT, STRING_QUOT
#こういうのを利用したAAなどある?
# str.gsub! "®","\$"
# str.gsub! "©","@"
# str.gsub! "","-"
#やるとuuencodeされているものなどがおかしくなる可能性がある
#toggleできるようにする
# str = str.gsub(/(\d+);/){ |s|
# unquote_char($1.to_i)
# }
# str = str.gsub (/([\da-fA-F]+);/){
# unquote_char($1.hex)
# }
#結局これはされていない?
# str.gsub! "&", "&"
#時々こうなってるのが.....
# str.gsub! "&", "&"
end
return str
end
def url_updator(new_bi_file, old_bi_file, new_file, old_file)
board_hash = {}
different_board_hash = {}
open(new_bi_file,'r'){ |file|
while line=file.gets
line=NKF::nkf('-exS',line).strip
case line
when REGEXP_BORAD_INFO_B,REGEXP_BORAD_INFO_A
board_hash[$2.strip] = $1
end
end
}
open(old_bi_file,'r'){ |file|
while line=file.gets
line=NKF::nkf('-exS',line).strip
case line
when REGEXP_BORAD_INFO_B,REGEXP_BORAD_INFO_A
if board_hash[$2.strip]
if board_hash[$2.strip] != $1
different_board_hash[$1] = board_hash[$2.strip]
end
end
end
end
}
different_board_hash.each{ |old,new|
host,port,path = parse_http_url(old)
old = host + '_' + "#{port}" + '_' + path.gsub(/\//,'_')
host,port,path = parse_http_url(new)
new = host + '_' + "#{port}" + '_' + path.gsub(/\//,'_')
Dir.chdir(BASE_DIR + "/subject/")
Dir.glob("#{old}*"){ |file|
File.rename(file,file.sub(old,new));
}
Dir.chdir(BASE_DIR + "/thread/")
Dir.glob("#{old}*"){ |file|
File.rename(file,file.sub(old,new));
}
}
open(new_file, 'w') { |nf|
open(old_file,'r'){ |f|
while line=f.gets
line=NKF::nkf('-exS',line).strip
if /^([TSBAEU])\s+(http.+)\s+(.+)/ =~ line
top = $1
url = $2
name = $3
different_board_hash.each{ |x,y|
url.sub!(x,y)
}
nf.print NKF::nkf('-sxE',"#{top} #{url} #{name}\n")
else
nf.print NKF::nkf('-sxE',line)
nf.print "\n"
end
end
}
}
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
def select_file(window_title)
selected = nil
fselection = Gtk::FileSelection.new(window_title)
fselection.hide_fileop_buttons
fselection.ok_button.signal_connect('clicked') {
selected = fselection.get_filename
Gtk.main_quit
}
fselection.cancel_button.signal_connect('clicked') {
Gtk.main_quit
}
fselection.signal_connect('destroy') {
fselection.destroy
}
fselection.show
fselection.grab_add
Gtk.main
fselection.grab_remove
fselection.destroy
selected
end
module Gtk
#Editableウィジェットに対する最低限の編集メニューを追加する
class Menu
STRING_CUT_MENUITEM = '切り取り'
STRING_COPY_MENUITEM = 'コピー'
STRING_PASTE_MENUITEM = '貼り付け'
STRING_SELECT_ALL_MENUITEM = '全選択'
def add_editting_items(ed_w)
cut_item = Gtk::MenuItem.new(STRING_CUT_MENUITEM)
cut_item.signal_connect(STRING_BUTTON_PRESS_EVENT){|w,e|
ed_w.cut_clipboard
w.signal_emit_stop_by_name(STRING_BUTTON_PRESS_EVENT)
true
}
self.append cut_item
copy_item = Gtk::MenuItem.new(STRING_COPY_MENUITEM)
copy_item.signal_connect(STRING_BUTTON_PRESS_EVENT){|w,e|
ed_w.copy_clipboard
w.signal_emit_stop_by_name(STRING_BUTTON_PRESS_EVENT)
true
}
self.append copy_item
paste_item = Gtk::MenuItem.new(STRING_PASTE_MENUITEM)
paste_item.signal_connect(STRING_BUTTON_PRESS_EVENT){|w,e|
ed_w.paste_clipboard
w.signal_emit_stop_by_name(STRING_BUTTON_PRESS_EVENT)
true
}
self.append paste_item
select_all_item = Gtk::MenuItem.new(STRING_SELECT_ALL_MENUITEM)
select_all_item.signal_connect(STRING_BUTTON_PRESS_EVENT){ |w,e|
ed_w.select_region(0,-1)
w.signal_emit_stop_by_name(STRING_BUTTON_PRESS_EVENT)
true
}
self.append select_all_item
if ! ed_w.editable? then
cut_item.set_sensitive false
paste_item.set_sensitive false
end
end
end
#書き込みダイアログで顔文字を挿入するための機能
class Menu
def add_facemark_items(msg_w)
facemenuitem = Gtk::MenuItem.new("顔文字")
submenu = Gtk::Menu.new
submenu.signal_connect(STRING_BUTTON_PRESS_EVENT){
submenu.popdown
}
FACEMARKS.each{ |f|
item = FaceMenuItem.new(msg_w,f)
submenu.append item
}
facemenuitem.set_submenu submenu
self.append facemenuitem
end
end
class FaceMenuItem < Gtk::MenuItem
def initialize(msg_w,txt)
super txt
self.signal_connect(STRING_BUTTON_PRESS_EVENT){|w,e|
# msg_w.insert(nil,nil,nil,' '+txt)
msg_w.insert_text(txt, msg_w.position)
# w.signal_emit_stop_by_name(STRING_BUTTON_PRESS_EVENT)
# true
}
end
end
# 書き込みダイアログで選択したファイルを挿入するための機能
class Menu
def add_fileinsert_item(msg_w)
fileinsert_menu_item = MenuItem.new('ファイルを挿入')
fileinsert_menu_item.signal_connect(STRING_BUTTON_PRESS_EVENT){|w,e|
self.hide
filename = select_file('挿入するファイルを選択')
if filename
begin
fp = open(filename, 'r')
msg_w.insert_text(fp.gets(nil), msg_w.position)
fp.close
rescue
$stderr.puts 'Error: ' + $!.to_str
end
end
}
self.append fileinsert_menu_item
end
end
end #module
#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
if REGEXP_0 =~ host && REGEXP_1 =~ path
# Thanks: >>66
# @text = 'goRuaで処理(therad): ' +text
@text = STRING_SYORI_T + text
elsif REGEXP_2 =~ text
# Thanks: >>202
# @text = 'goRuaで処理(board): ' +text
@text = STRING_SYORI_B + text
elsif REGEXP_SHITARABA_BOARD =~ text
@text = 'goRuaで処理(shitaraba board): ' +text
elsif REGEXP_SHITARABA_THREAD =~ text
@text = 'goRuaで処理(shitaraba thread): ' +text
else
# @text = '外部ブラウザで処理: ' +text
@text = STRING_SYORI_G + text
end
else
@text = text
end
super @text
# self.set_name 'UrlMenuItem'
self.set_name STRING_URLMENUITEM
self.signal_connect(STRING_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.gsub!(REGEXP_ANYTAG,'')
name=unquote(name)
# mailto があるなら Buttonにしてtooltipsを付ける
if mailto && mailto != ''
# mailto.gsub!(/<.+?>/,'')
mailto.gsub!(REGEXP_ANYTAG,'')
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(/
/i)
lines = body.split(REGEXP_C_BR)
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
# requisition = VVBOX.get_notebook.size_request
lines.each { |tmp_line|
# tmp_line.sub!(/\s+$/,'')
# 先頭に空白がはいってるのを削除
# tmp_line.sub!(/^ /,'')
tmp_line.sub!(REGEXP_INITIAL_SPACE,'')
tmp_line.sub!(REGEXP_LAST_SPACE,'')
# したらばに対してはよくないかも
# tmp_line.gsub!(/<.+?>/,'')
tmp_line.gsub!(REGEXP_ANYTAG,'')
tmp_line=unquote(tmp_line)
# 適当にさっぴかないとあふれる
tmp += TEXT_FONT.string_width(tmp_line)/ TEXT_WIDTH2
# tmp += TEXT_FONT.string_width(tmp_line)/ (requisition.width - 52)
# Thanks: M.Suzuki
strings = []
colors = []
base_color = COLOR_OTHERS
if REGEXP_CITE =~ tmp_line #引用
base_color = COLOR_CITE
# strings << $1
# colors << base_color
# tmp_line = $2
end
while true
case tmp_line
# when /(.*?)([>>〉]+(\d{1,4}-\d{1,4}))(.*)$/ #番号引用
when REGEXP_CITE_NUMBER_0
strings << $1
colors << base_color
strings << $2
colors << COLOR_CITE_NUMBER
url << $3
tmp_line = $4
# when /(.*?)([>>〉]+(\d{1,4}-))(.*)$/
when REGEXP_CITE_NUMBER_1
strings << $1
colors << base_color
strings << $2
colors << COLOR_CITE_NUMBER
url << $3
tmp_line = $4
# when /(.*?)([>>〉]+(\d{1,4}))(.*)$/
when REGEXP_CITE_NUMBER_2
strings << $1
colors << base_color
strings << $2
colors << COLOR_CITE_NUMBER
url << $3
tmp_line = $4
when REGEXP_URL # url
strings << $1
colors << base_color
strings << $2
colors << COLOR_URL
tmp_line = $3
host,port,path=parse_http_url($2)
if host
url << $2
end
else
strings << tmp_line
colors << base_color
break
end
end
while (str = strings.shift) != nil
text.insert nil,colors.shift,nil,str
end
text.insert nil,base_color,nil,"\n"
}
# text.set_usize TEXT_WIDTH,tmp*TEXT_FONT_HEIGHT
text.set_usize requisition.width - 32,tmp*TEXT_FONT_HEIGHT
# text.size_allocate(Gtk::Allocation.new(0,0,requisition.width - 32,tmp*TEXT_FONT_HEIGHT))
# メニューの生成
text.signal_connect(STRING_BUTTON_PRESS_EVENT){|w,e|
if e.instance_of?(Gdk::EventButton) && e.button==3
# $menu_popup = true
menu = Gtk::Menu.new
menu.signal_connect(STRING_BUTTON_PRESS_EVENT){
menu.popdown
}
item = Gtk::MenuItem.new(STRING_MENU)
menu.append item
# separator =
# menu.append separator
menu.append Gtk::MenuItem.new()
menu.add_editting_items(text)
menu.append Gtk::MenuItem.new()
url.each{ |t|
item = UrlMenuItem.new(t)
menu.append item
}
menu.popup(nil, nil, nil, e.button, e.time)
menu.show_all
w.signal_emit_stop_by_name(STRING_BUTTON_PRESS_EVENT)
end
true
}
end
# 将来使うかも
=begin
@name = name
@mailto= mailto
@date_id=date_id
@body=body
=end
end
end
# 投稿用Window
class PostWindow < Gtk::Window
include PostMessage2ch
def initialize(view_thread_box)
super Gtk::WINDOW_TOPLEVEL
self.set_wmclass('POST_WINDOW','goRua')
@view_thread_box = view_thread_box
@thread = view_thread_box.get_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,4,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
age_button = Gtk::Button.new('age')
sage_button = Gtk::Button.new('sage')
table.attach age_button,2,3,1,2,Gtk::SHRINK,Gtk::SHRINK
table.attach sage_button,3,4,1,2,Gtk::SHRINK,Gtk::SHRINK
age_button.signal_connect('clicked'){
@mailto_combo.entry.set_text('age')
}
sage_button.signal_connect('clicked'){
@mailto_combo.entry.set_text('sage')
}
@message = Gtk::Text.new
@message.set_name 'PostText'
@message.set_editable(true)
menu = Gtk::Menu.new
menu.signal_connect(STRING_BUTTON_PRESS_EVENT){
menu.popdown
}
menu.add_editting_items(@message)
menu.append Gtk::MenuItem.new() #separator
menu.add_facemark_items(@message)
menu.add_fileinsert_item(@message)
@message.signal_connect(STRING_BUTTON_PRESS_EVENT){|w,e|
if e.instance_of?(Gdk::EventButton) && e.button==3 then
menu.popup(nil, nil, nil, e.button, e.time)
menu.show_all
w.signal_emit_stop_by_name(STRING_BUTTON_PRESS_EVENT)
end
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_default_size $config['post_width'], $config['post_height']
self.show_all
cancel_button.signal_connect('clicked'){
self.destroy
}
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.destroy
# 失敗した際どうするか
# とりあえずなにもしない
if $config['reload_after_post']
sleep $config['wait_for_reload_after_post']
@view_thread_box.activate_reload_item
end
# 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 = nil
# @file = @thread.get_file
# こうしてもあまり早くならないかな?
self.set_file(1,@thread.get_file)
self.show
@notebook_label = nil
self.signal_connect_after(STRING_BUTTON_PRESS_EVENT){|w,e|
if e.instance_of?(Gdk::EventButton) && e.button==3
menu = Gtk::Menu.new
menu.signal_connect(STRING_BUTTON_PRESS_EVENT){
menu.popdown
}
item = Gtk::MenuItem.new(STRING_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(STRING_BUTTON_PRESS_EVENT){
self.set_start(@start-@num_of_message)
VVBOX.set_notebook_page(self)
}
next_item.signal_connect(STRING_BUTTON_PRESS_EVENT){
self.set_start(@start+@num_of_message)
VVBOX.set_notebook_page(self)
}
end
}
end
def set_notebook_label(label)
@notebook_label = label
end
def get_notebook_label
return @notebook_label
end
# 現在Threadのどこから閲覧しているか
def get_start
return @start
end
# 現在Thread中のレスを何個閲覧しているか
def get_num_of_message
return @num_of_message
end
# Thread全体のサイズ
def get_thread_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_thread_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(re=nil)
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
begin
@file.rewind
rescue IOError
$stderr.puts $!
end
if prev_title ==nil || prev_title== ''
# スレッドのタイトルがわからない場合
# タイトルをセット
begin
# (name,mailto,date_id,body,title)=replace_char(@lines[0]).split(/<>/)
(name,mailto,date_id,body,title)=replace_char(@lines[0]).split(REGEXP_BRAKET)
title.strip!
if title && title !=''
self.get_thread.set_title(title)
tmp = title+ " (#{self.get_thread_size})"
self.get_thread.set_title_and_size(tmp)
VVBOX.set_title(tmp)
VVBOX.show_all
VVBOX.write_sessions
end
# }
rescue
end
end
# Threadの描画
@sw.add_with_viewport vbox_view
# i=1 # 現在のレス番号
# j=0 # 描画したレスの数
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(/<>/)
(name,mailto,date_id,body,title)= replace_char(line.strip).split(REGEXP_BRAKET)
# 以下だど全部nilになることがあった
# (name,mailto,date_id,body,title)= replace_char(line.strip!).split(/<>/)
if re
if re =~ body
vbox_view.pack_start MessageFrame::new(i,name,mailto,date_id,body),false,false
end
else
vbox_view.pack_start MessageFrame::new(i,name,mailto,date_id,body),false,false
end
# @sw.show_all
i += 1
rescue
$stderr.print "#{i} におかしな情報がありました\n"
i += 1
end
# 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_thread_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_thread_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
if @file && !@file.closed?
@file.close
@file = nil
end
@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
if REGEXP_C_READ_CGI =~ line
next
end
@lines << line
@size+=1
end
@file.rewind
end
end
class ViewNotebook < Gtk::Notebook
def remove_page(num)
page = get_nth_page(num)
begin
page.get_thread.close_file
rescue NameError
end
super(num)
end
end
class UpdateBoardInfo2ch
include GetBoardInfo2ch
def update
host,port,path = parse_http_url($config['url_of_board_info'])
if host
body=get_board_info(host,port,path)
if body
info=parse_board_info_html(body)
end
end
if info
File.rename(BOARDINFO_FILE, BOARDINFO_FILE + '_backup')
open(BOARDINFO_FILE,'w') { |file|
info.each{ |e|
file.print NKF::nkf('-sxE',e)
}
}
File.rename(SESSIONS_FILE, SESSIONS_FILE + '_backup')
url_updator(BOARDINFO_FILE, BOARDINFO_FILE + '_backup',
SESSIONS_FILE, SESSIONS_FILE + '_backup')
File.rename(BOOKMARKS_FILE, BOOKMARKS_FILE + '_backup')
url_updator(BOARDINFO_FILE, BOARDINFO_FILE + '_backup',
BOOKMARKS_FILE, BOOKMARKS_FILE + '_backup')
else
$stderr.print "update失敗\n"
end
SVBOX.set_category(make_array_of_board_infos())
SVBOX.set_sw()
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
file_menu.add Gtk::MenuItem.new()
@sorting_item = Gtk::CheckMenuItem.new('スレタイでソート')
if $config['sort_items_by_title']
@sorting_item.activate
end
file_menu.add @sorting_item
file_menu.add Gtk::MenuItem.new()
@update_board_info_item = Gtk::MenuItem.new('Board Info ファイルの更新')
file_menu.add @update_board_info_item
@edit_bookmark_item = Gtk::MenuItem.new('Bookmark の編集')
file_menu.add @edit_bookmark_item
file_menu.add Gtk::MenuItem.new()
@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
@entry.set_editable(true)
# @entry_text_stack = []
if TAB_TYPE == 2
button = Gtk::Button.new
button.add Gtk::Pixmap.new(BATU_PIX, BATU_MASK)
button.signal_connect('clicked'){
page = @notebook.cur_page
if page
if page.child
num = @notebook.page_num(page.child)
@notebook.remove_page(num)
@sessions.delete_at(num)
self.write_sessions
self.set_item
end
end
}
hbox = Gtk::HBox.new
hbox.pack_start @entry
hbox.pack_start button,false,false,0
self.pack_start hbox,false,false,0
else
self.pack_start @entry,false,false,0
end
# ノートブック
# @notebook = Gtk::Notebook::new()
@notebook = ViewNotebook::new()
@notebook.set_usize VIEW_WIDTH2, HEIGHT3
# ? `requisition=': wrong # of arguments(1 for 2) (ArgumentError)
# @notebook.requisition= 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
if REGEXP_F =~ url
# url = $1 + '/test/read.cgi?' + 'bbs=' + $2 + '&key=' + $3
url = $1 + '/test/read.cgi/' + $3 + '/' + $4 +'/'
# elsif /^(.*)\/(.+?)\/dat\/(.+?)\.dat$/ =~ url
elsif REGEXP_G =~ 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
}
@update_board_info_item.signal_connect('activate'){
UpdateBoardInfo2ch.new.update
}
@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.dup
$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)
window = PostWindow.new(page.child)
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
}
@edit_bookmark_item.signal_connect('activate') {
fork do
tmp = $config['editor_command'].dup
tmp.sub!( /%s/, BASE_DIR + 'bookmarks')
exec tmp
end
}
@entry.signal_connect('activate') {|widget|
text = @entry.get_text
text.strip!
$entry_activate = true
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
if REGEXP_TTP =~ text
text = 'h' +text
end
url = ''
internal=false
catch(:tag) do
# if /\.(2ch|bbspink)\./=~ host
if REGEXP_0 =~ host
# if %r!^(.*/test/read\.cgi/.+?/[0-9]+?/).*! =~ text
# if %r!^((.*)/test/read\.cgi/(.+?)/([0-9]+)).*! =~ text
if REGEXP_3 =~ text
if VIA_DAT
url = $2 + '/' + $3 + '/dat/' + $4 + '.dat'
else
url = $1 + '/'
end
# elsif %r!^(.*)/test/read\.cgi\?(.*)! =~ text
elsif REGEXP_4 =~ text
base = $1
tmp = $3
# if /bbs=([^&]+)&.*key=([^&]+)/i =~ tmp
if REGEXP_H =~ tmp
bbs = $1
key = $2
# elsif /key=([^&]+)&.*bbs=([^&]+)/i =~ tmp
elsif REGEXP_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
elsif REGEXP_2 =~ 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('',url,nil,VIA_DAT)
pre_size=thread.load_file
if pre_size
if thread.get_file
VVBOX.set_thread(thread,pre_size)
end
end
return
elsif REGEXP_SHITARABA_BOARD =~ text
internal=true
url = 'http://www.shitaraba.com/bbs/' + $1 + '/'
board = BoardShitaraba.new(url,url)
SVBOX.set_kensaku_board(board)
throw :tag
elsif REGEXP_SHITARABA_THREAD =~ text
query = $1
if REGEXP_H =~ query
bbs = $1
key = $2
# elsif /key=([^&]+)&.*bbs=([^&]+)/i =~ tmp
elsif REGEXP_I =~ query
bbs = $2
key = $1
else
throw :tag
end
internal=true
url = 'http://www.shitaraba.com/bbs/' + bbs + '/dat/' + key + '.dat'
thread = ThreadShitaraba.new('',url,nil,true)
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!(/\'/,'\\\'')
text.gsub!(REGEXP_QUOTA,'\\\'')
=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+'\'')
nyo = tmp.split(/%u/,2)
tmp = nyo[0] + '\'' + text + '\'' + nyo[1]
# tmp.gsub!(/\&/,'\\\&')
# \\\だとだめだった
# tmp.gsub!(REGEXP_AND,'\\&')
tmp.untaint
exec(tmp)
end
end
return
end
end
if internal
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_thread_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
else
re = Regexp.new(text,true)
self.set_notebook_page(v_thread,re)
end
self.set_item
end
def get_check_new_item
return @check_new_item
end
def sort_items_by_title?
@sorting_item.active?
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,re=nil)
tmp =@notebook.page_num(v_thread)
if tmp == -1
v_thread.make_view(re)
tmp = v_thread.get_thread.get_title + " (#{v_thread.get_thread_size})"
hbox =create_tab_label(tmp,v_thread)
hbox.show_all
@notebook.append_page_menu(v_thread,hbox,Gtk::Label.new(tmp))
@sessions << v_thread.get_thread
self.write_sessions
if ! $init && SET_LAST_PAGE
# なぜかentryからいれると 最後のページに行かないな…
@notebook.set_page(-1)
end
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(re)
end
self.set_item
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
# 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'
v_thread.set_notebook_label(label)
# 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
when 2
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
#notebookのページが代わったときの処理
def page_switch(widget, page, page_num)
oldpage = @notebook.cur_page
if (page == oldpage)
return
end
v_thread=page.child
=begin
label = v_thread.get_notebook_label
style = label.get_style.copy
# style = Gtk::Style.new
# style.set_font(label.get_style.font)
style.set_font(Gdk::Font.fontset_load("-mona-gothic-medium-r-normal--12-*"))
style.set_fg(Gtk::STATE_NORMAL, 0, 65535, 0)
label.set_style(style)
label.show_all
=end
# @notebook.set_tab_label(v_thread, label)
self.set_title(v_thread.get_thread.get_title+ " (#{v_thread.get_thread_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
def unset_item
@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
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
self.unset_item
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_thread_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*$/,'')
@title = title.sub(REGEXP_SIZE_CUT,'')
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
if REGEXP_5 =~ @path
# dat版
@base_path = $1
@bbs = $2
# @key = $3.sub(/\.dat$/,'')
@key = $3.sub(REGEXP_DAT,'')
# elsif %r!^(/.*)/(.+?)/(.+?)/?$! =~ @path
elsif REGEXP_6 =~ @path
# read.cgi版
tmp = $1
@bbs = $2
# @key = $3.sub(/\.dat$/,'')
@key = $3.sub(REGEXP_DAT,'')
# @base_path = tmp.sub(/test\/read\.cgi$/,'')
@base_path = tmp.sub(REGEXP_J,'')
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 close_file
if @file && ! @file.closed?
@file.close
end
@file = nil
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がある場合は内部でrewindしてるだけだった.
# if @file && !@file.closed?
# @file.close
# end
@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
STATUS_BAR.pop(STATUS_BAR_ID_ERROR)
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_thread_size(size)
@size = size
end
def get_thread_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(/\//,'_')
@filename = BASE_DIR + 'subject/'+ @host + '_' + "#{@port}" + '_' + @path.gsub(REGEXP_SLASH,'_')
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)
#dupすべきところでdupしたはずなので以下でいいはず
@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') {
self.signal_handler_block @handler_id
# if @special
# else
unless @special
self.get_board_subject
end
self.expand
self.signal_handler_unblock @handler_id
}
@handler_id = self.signal_connect('deselect') {
}
@destroyed=false
self.signal_connect('destroy'){
@destroyed=true
}
end
def set_bookmarks_item
if @destroyed
return
end
expanded = self.expanded?
array = @board.get_thread_array
if array != []
subtree = Gtk::Tree.new
self.set_subtree subtree
# i=0
array.each{ |thread|
thread_item = ThreadTreeItem.new(thread,self,false)
subtree.append thread_item
thread_item.show_all
# i+=1
}
self.set_num_of_thread(array.size)
end
self.show_all
if expanded
#落ちる場合がある?
sleep 0.25
self.expand
end
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
# スレ一覧をソートするか
sort_items_by_title = VVBOX.sort_items_by_title?
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
if REGEXP_K =~ path
url += $1
path = $2
end
url += '/test/read.cgi' + path
end
# ここでsubtreeしちゃうと描画が大変
begin
i=0
if sort_items_by_title
threads = []
end
file.each{ |line|
# while line = file.gets
# p 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
if REGEXP_L =~ line
thread = $1
title = $2
# thread = Thread2ch.new(unquote($'),url,$`,@board.get_dat)
# if /\.dat$/ =~ thread
if REGEXP_DAT =~ thread
thread = Thread2ch.new(unquote(title),url,thread,@board.get_dat)
else
thread = thread + '.dat'
# title,num,sute,null = title.split(/<>/)
title,num,sute,null = title.split(REGEXP_BRAKET)
title = title + " (#{num})"
thread = ThreadShitaraba.new(unquote(title),url,thread,@board.get_dat)
end
if sort_items_by_title
threads.push(thread)
else
@board.append_thread thread
thread_item = ThreadTreeItem.new(thread,self)
subtree.append thread_item
thread_item.show
while Gtk.events_pending
Gtk.main_iteration
end
end
i+=1
end
# end
}
rescue Zlib::BufError => evar
p evar
$stderr.print "ZlibのBufError(要調査か?)\n"
end
if sort_items_by_title
threads.sort!{|x,y| x.get_title <=> y.get_title}
threads.each do |thread|
@board.append_thread thread
thread_item = ThreadTreeItem.new(thread,self)
subtree.append thread_item
thread_item.show
while Gtk.events_pending
Gtk.main_iteration
end
end
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,add_size=true)
@thread=thread
@board_item = board_item
if add_size
super(@thread.get_title_and_size)
else
super(@thread.get_title)
end
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') {
self.signal_handler_block @handler_id
pre_size=@thread.load_file
if pre_size
if @thread.get_file
VVBOX.set_thread(@thread,pre_size)
end
end
self.signal_handler_unblock @handler_id
# self.signal_emit_stop_by_name('select')
}
@handler_id = self.signal_connect('deselect') {
}
self.signal_connect(STRING_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(STRING_BUTTON_PRESS_EVENT){
menu.popdown
}
item = Gtk::MenuItem.new(STRING_MENU)
menu.append item
separator = Gtk::MenuItem.new()
menu.append separator
toziru_item = Gtk::MenuItem.new('閉じる')
menu.append toziru_item
toziru_item.signal_connect(STRING_BUTTON_PRESS_EVENT){|w,e|
@board_item.collapse
}
sakuzyo_item = Gtk::MenuItem.new('削除')
menu.append sakuzyo_item
sakuzyo_item.signal_connect(STRING_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
set_category(board_file_array)
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 != ''
re = Regexp.new(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
end
}
self.set_sw
end
def set_category(board_file_array)
$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).chop
case line
when REGEXP_BORAD_INFO_C
category = $1.strip
@category_array.push category
@board_hash[category] = []
if bookmarks_flag
$bookmarks_array << "C #{category}"
end
when REGEXP_BORAD_INFO_D
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 REGEXP_BORAD_INFO_U
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 REGEXP_BORAD_INFO_T
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 REGEXP_BORAD_INFO_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 REGEXP_BORAD_INFO_B
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(.+)/
when REGEXP_BORAD_INFO_A
board = Board2ch.new($2.strip,$1,true)
@board_hash[category] << board
if bookmarks_flag
$bookmarks_array << board
end
# when /^E\s+(\S+)\s(.+)/
when REGEXP_BORAD_INFO_E
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]
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.size_allocate(Gtk::Allocation.new(0,0,SELECT_WIDTH2, HEIGHT2))
sw.add_with_viewport @tree
@tree.clear_items 0,@tree_size
@tree_size=0
hash.each{ |board|
@tree_size+=1
item = 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 = 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
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}")
MAIN_WINDOW.set_wmclass('MAIN_WINDOW','goRua')
MAIN_WINDOW.set_default_size SELECT_WIDTH+VIEW_WIDTH,HEIGHT
MAIN_WINDOW.realize
BATU_PIX, BATU_MASK =
Gdk::Pixmap::create_from_xpm_d(MAIN_WINDOW.window, nil, BATU_XPM)
# Thanks : honeto@mail.goo.ne.jp さん
hpaned = Gtk::HPaned.new
VVBOX = ViewVBox.new()
BOARDINFO_FILE = BASE_DIR + 'board_info'
def make_array_of_board_infos()
file_array=[]
if File.exist?(BOOKMARKS_FILE)
bm_file = open(BOOKMARKS_FILE)
file_array << bm_file
end
if File.exist?(BOARDINFO_FILE)
file = open(BOARDINFO_FILE,'r')
file_array << file
else
$stderr.print "not found: ~/.goRua_2ch/board_info. see README.\n"
end
return file_array
end
file_array = make_array_of_board_infos()
SVBOX = SelectionVBox.new(file_array)
file_array.each { |board_file|
if board_file && ! board_file.closed?
board_file.close
end
}
# 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.show_all
# 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.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
if v_thread && !$entry_activate && notebook
begin
notebook.set_page(notebook.page_num(v_thread))
vadj = v_thread.get_sw.get_vadjustment
tmp = vadj.value
flag = true
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
# break
# false
flag = false
end
if flag
if tmp > vadj.upper - vadj.page_size
tmp = vadj.upper - vadj.page_size
end
vadj.set_value(tmp)
end
rescue ArgumentError
end
end
$entry_activate = false
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_SJIS_TO_EUC,line)
thread=nil
# line.strip!
line.chop!
if REGEXP_BORAD_INFO_S =~ line
thread = Thread2ch.new($2.strip,$1,nil,true)
elsif REGEXP_BORAD_INFO_T =~ line
thread = Thread2ch.new($2.strip,$1,nil,false)
elsif REGEXP_BORAD_INFO_U =~ 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