kentaroi-okayu 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +6 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/bin/okayu +6 -0
- data/lib/app.rb +25 -0
- data/lib/app_dir.rb +29 -0
- data/lib/client.rb +210 -0
- data/lib/controllers/listeners_controller.rb +18 -0
- data/lib/controllers/live_controller.rb +82 -0
- data/lib/controllers/visitors_controller.rb +19 -0
- data/lib/controllers.rb +3 -0
- data/lib/cookie_thief.rb +172 -0
- data/lib/db/migrate/000_create_lives.rb +14 -0
- data/lib/db/migrate/001_create_comments.rb +22 -0
- data/lib/db/migrate/002_create_visitors.rb +15 -0
- data/lib/db/migrate/003_create_listeners.rb +15 -0
- data/lib/db/migrate/004_create_communities.rb +13 -0
- data/lib/db/migrate/005_create_listeners_lives.rb +12 -0
- data/lib/db/migrate/006_create_lives_visitors.rb +13 -0
- data/lib/factory.rb +16 -0
- data/lib/models/comment.rb +22 -0
- data/lib/models/community.rb +3 -0
- data/lib/models/listener.rb +5 -0
- data/lib/models/live.rb +6 -0
- data/lib/models/visitor.rb +5 -0
- data/lib/models.rb +23 -0
- data/lib/okayu.rb +10 -0
- data/lib/player_status.rb +16 -0
- data/lib/sample_msg +2 -0
- data/lib/thread_info.rb +32 -0
- data/lib/user_info.rb +49 -0
- data/lib/views/app_frame.rb +89 -0
- data/lib/views/color_dialog.rb +56 -0
- data/lib/views/colors.rb +168 -0
- data/lib/views/live_panel.rb +335 -0
- data/lib/views/login_dialog.rb +80 -0
- data/lib/views/main_panel.rb +50 -0
- data/lib/views.rb +11 -0
- data/logged_in_page +893 -0
- data/login_error_page +83 -0
- data/okayu.gemspec +126 -0
- data/tcp_log +0 -0
- data/test/client_test.rb +61 -0
- data/test/cookie_thief_test.rb +36 -0
- data/test/ie_cookie.txt +63 -0
- data/test/okayu_test.rb +8 -0
- data/test/player_status_test.rb +61 -0
- data/test/raw_player_status +2 -0
- data/test/raw_player_status_community_only +3 -0
- data/test/raw_player_status_notlogged_in +2 -0
- data/test/test_helper.rb +11 -0
- data/test/thread_info_test.rb +11 -0
- metadata +193 -0
@@ -0,0 +1,335 @@
|
|
1
|
+
module Okayu
|
2
|
+
COLORIZE = 300
|
3
|
+
NAME = 301
|
4
|
+
|
5
|
+
class LiveListCtrl < Wx::ListCtrl
|
6
|
+
COLUMNS = { :no => 0,
|
7
|
+
:message => 1,
|
8
|
+
:mail => 2,
|
9
|
+
:time => 3,
|
10
|
+
:name => 4,
|
11
|
+
:id => 5,
|
12
|
+
:premium => 6,
|
13
|
+
:ngword => 7 }
|
14
|
+
attr_accessor :base_time
|
15
|
+
|
16
|
+
def initialize(parent)
|
17
|
+
super(parent, :id => Wx::ID_ANY, :style => Wx::LC_REPORT)
|
18
|
+
insert_column COLUMNS[:no], "No", Wx::LIST_FORMAT_LEFT, -1
|
19
|
+
insert_column COLUMNS[:message], "Message", Wx::LIST_FORMAT_LEFT, -1
|
20
|
+
insert_column COLUMNS[:mail], "Command", Wx::LIST_FORMAT_LEFT, -1
|
21
|
+
insert_column COLUMNS[:time], "Time", Wx::LIST_FORMAT_LEFT, -1
|
22
|
+
insert_column COLUMNS[:name], "Name", Wx::LIST_FORMAT_LEFT, -1
|
23
|
+
insert_column COLUMNS[:id], "Id", Wx::LIST_FORMAT_LEFT, -1
|
24
|
+
insert_column COLUMNS[:premium], "Premium", Wx::LIST_FORMAT_LEFT, -1
|
25
|
+
insert_column COLUMNS[:ngword], "NG word", Wx::LIST_FORMAT_LEFT, -1
|
26
|
+
load_column_width
|
27
|
+
|
28
|
+
evt_list_col_end_drag(Wx::ID_ANY){|event| on_column_resized(event)}
|
29
|
+
|
30
|
+
evt_list_item_right_click(get_id) {|event| on_list_item_right_click(event)}
|
31
|
+
evt_list_item_activated(Wx::ID_ANY) {|event| on_activated(event)}
|
32
|
+
evt_key_down(){|event| get_parent.on_key_down(event)}
|
33
|
+
|
34
|
+
evt_menu(COLORIZE){show_color_dialog}
|
35
|
+
evt_menu(NAME){show_name_dialog}
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_column_width
|
39
|
+
return unless UserInfo.column_width
|
40
|
+
set_column_width COLUMNS[:no], UserInfo.column_width[:no]
|
41
|
+
set_column_width COLUMNS[:message], UserInfo.column_width[:message]
|
42
|
+
set_column_width COLUMNS[:mail], UserInfo.column_width[:mail]
|
43
|
+
set_column_width COLUMNS[:time], UserInfo.column_width[:time]
|
44
|
+
set_column_width COLUMNS[:name], UserInfo.column_width[:name]
|
45
|
+
set_column_width COLUMNS[:id], UserInfo.column_width[:id]
|
46
|
+
set_column_width COLUMNS[:premium], UserInfo.column_width[:premium]
|
47
|
+
set_column_width COLUMNS[:ngword], UserInfo.column_width[:ngword]
|
48
|
+
end
|
49
|
+
|
50
|
+
def insert_comment comment
|
51
|
+
item = Wx::ListItem.new
|
52
|
+
item.set_data({:id => comment.id,
|
53
|
+
:visitor_id => comment.visitor_id,
|
54
|
+
:listener_id => comment.listener_id})
|
55
|
+
item.set_id 0
|
56
|
+
item.set_column COLUMNS[:no]
|
57
|
+
item.set_text comment.number.to_s
|
58
|
+
# item.set_text_colour Wx::GREEN
|
59
|
+
insert_item(item)
|
60
|
+
|
61
|
+
item.set_column COLUMNS[:message]
|
62
|
+
item.set_text comment.message
|
63
|
+
set_item(item)
|
64
|
+
|
65
|
+
item.set_column COLUMNS[:mail]
|
66
|
+
item.set_text comment.mail
|
67
|
+
set_item(item)
|
68
|
+
|
69
|
+
item.set_column COLUMNS[:time]
|
70
|
+
item.set_text Time.at(comment.commented_at - @base_time).strftime("%M:%S")
|
71
|
+
set_item(item)
|
72
|
+
|
73
|
+
if comment.listener
|
74
|
+
item.set_column COLUMNS[:name]
|
75
|
+
item.set_text comment.listener.name
|
76
|
+
set_item(item)
|
77
|
+
end
|
78
|
+
|
79
|
+
item.set_column COLUMNS[:id]
|
80
|
+
item.set_text comment.user_id || comment.visitor.user_id
|
81
|
+
set_item(item)
|
82
|
+
|
83
|
+
item.set_column COLUMNS[:premium]
|
84
|
+
if comment.owner
|
85
|
+
item.set_text '主'
|
86
|
+
elsif comment.premium
|
87
|
+
item.set_text 'P'
|
88
|
+
elsif comment.command
|
89
|
+
item.set_text 'System'
|
90
|
+
else
|
91
|
+
item.set_text ''
|
92
|
+
end
|
93
|
+
set_item(item)
|
94
|
+
|
95
|
+
if comment.listener
|
96
|
+
if color_index = comment.listener.color_index
|
97
|
+
set_color_to_an_item(0, COLORS[color_index])
|
98
|
+
end
|
99
|
+
elsif comment.visitor
|
100
|
+
if color_index = comment.visitor.color_index
|
101
|
+
set_color_to_an_item(0, COLORS[color_index])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_column_resized event
|
108
|
+
column = event.get_column
|
109
|
+
if UserInfo[:column_width]
|
110
|
+
UserInfo[:column_width][COLUMNS.index(column)] = get_column_width(column)
|
111
|
+
else
|
112
|
+
UserInfo[:column_width] = {
|
113
|
+
:no => get_column_width(COLUMNS[:no]),
|
114
|
+
:message => get_column_width(COLUMNS[:message]),
|
115
|
+
:mail => get_column_width(COLUMNS[:mail]),
|
116
|
+
:time => get_column_width(COLUMNS[:time]),
|
117
|
+
:name => get_column_width(COLUMNS[:name]),
|
118
|
+
:id => get_column_width(COLUMNS[:id]),
|
119
|
+
:premium => get_column_width(COLUMNS[:premium]),
|
120
|
+
:ngword => get_column_width(COLUMNS[:ngword])
|
121
|
+
}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def on_list_item_right_click event
|
126
|
+
menu = Wx::Menu.new
|
127
|
+
menu.append(COLORIZE, "色をつける(&C)", "")
|
128
|
+
menu.append(NAME, "名前をつける(&M)", "")
|
129
|
+
popup_menu(menu, event.get_point)
|
130
|
+
end
|
131
|
+
|
132
|
+
def show_color_dialog
|
133
|
+
dialog = ColorDialog.new(self)
|
134
|
+
color_index = dialog.show_modal
|
135
|
+
item_index = get_selections.first
|
136
|
+
if color_index >= 0 && color_index < COLORS.length
|
137
|
+
on_color_set item_index, color_index
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def show_name_dialog
|
142
|
+
dialog = Wx::TextEntryDialog.new(self, "名前を入力してください")
|
143
|
+
dialog.show_modal
|
144
|
+
item_index = get_selections.first
|
145
|
+
|
146
|
+
item = get_item(item_index)
|
147
|
+
item.set_text_colour(COLORS[WHITE][:color])
|
148
|
+
item.set_column COLUMNS[:name]
|
149
|
+
item.set_text(dialog.get_value)
|
150
|
+
set_item(item)
|
151
|
+
end
|
152
|
+
|
153
|
+
def is_colored? index
|
154
|
+
get_item_background_colour(index) != Wx::NULL_COLOUR &&
|
155
|
+
get_item_background_colour(index) != COLORS[WHITE][:color]
|
156
|
+
end
|
157
|
+
|
158
|
+
def on_activated event
|
159
|
+
item_index = event.get_index
|
160
|
+
if is_colored?(item_index)
|
161
|
+
on_color_unset item_index
|
162
|
+
else
|
163
|
+
on_color_set item_index
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def on_color_set index, color_index=nil
|
168
|
+
color_index = rand(COLORS.length) unless color_index
|
169
|
+
data = get_item_data(index)
|
170
|
+
return unless data
|
171
|
+
|
172
|
+
if data[:listener_id]
|
173
|
+
set_color({:listener_id => data[:listener_id]}, COLORS[color_index])
|
174
|
+
unless ListenersController.set_color_index :id => data[:listener_id],
|
175
|
+
:color_index => color_index
|
176
|
+
Wx::message_box('色設定の保存に失敗しました')
|
177
|
+
end
|
178
|
+
else
|
179
|
+
set_color({:visitor_id => data[:visitor_id]}, COLORS[color_index])
|
180
|
+
unless VisitorsController.set_color_index :id => data[:visitor_id],
|
181
|
+
:color_index => color_index
|
182
|
+
Wx::message_box('色設定の保存に失敗しました')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def on_color_unset index
|
188
|
+
data = get_item_data(index)
|
189
|
+
return unless data
|
190
|
+
|
191
|
+
if data[:listener_id]
|
192
|
+
set_color({:listener_id => data[:listener_id]}, COLORS[WHITE])
|
193
|
+
unless ListenersController.unset_color_index :id => data[:listener_id]
|
194
|
+
Wx::message_box('色設定の消去に失敗しました')
|
195
|
+
end
|
196
|
+
else
|
197
|
+
set_color({:visitor_id => data[:visitor_id]}, COLORS[WHITE])
|
198
|
+
unless VisitorsController.unset_color_index :id => data[:visitor_id]
|
199
|
+
Wx::message_box('色設定の消去に失敗しました')
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def set_color target, color
|
205
|
+
if target[:listener_id]
|
206
|
+
self.each do |index|
|
207
|
+
if get_item_data(index)[:listener_id] == target[:listener_id]
|
208
|
+
set_color_to_an_item(index, color)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
elsif target[:visitor_id]
|
212
|
+
self.each do |index|
|
213
|
+
if get_item_data(index)[:visitor_id] == target[:visitor_id]
|
214
|
+
set_color_to_an_item(index, color)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def set_color_to_an_item index, color
|
221
|
+
set_item_background_colour(index, color[:color])
|
222
|
+
if color[:white_text]
|
223
|
+
get_item(index).set_text_colour(COLORS[WHITE][:color])
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def on_name_set index, name
|
228
|
+
end
|
229
|
+
def on_name_unset index, name
|
230
|
+
end
|
231
|
+
def set_name target, name
|
232
|
+
end
|
233
|
+
def set_name_to_an_item index, name
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
class LivePanel < Wx::Panel
|
238
|
+
attr_reader :title
|
239
|
+
def initialize parent
|
240
|
+
super
|
241
|
+
@title = 'live'
|
242
|
+
@ready = false
|
243
|
+
|
244
|
+
@live_url = Wx::TextCtrl.new(self,
|
245
|
+
:id => Wx::ID_ANY,
|
246
|
+
:style => Wx::TE_PROCESS_ENTER)
|
247
|
+
@list = LiveListCtrl.new(self)
|
248
|
+
|
249
|
+
sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
250
|
+
sizer.add(@live_url, 0, Wx::ALL|Wx::EXPAND, 10)
|
251
|
+
sizer.add(@list, 1, Wx::ALL|Wx::EXPAND, 10)
|
252
|
+
set_sizer(sizer)
|
253
|
+
sizer.set_size_hints(self)
|
254
|
+
|
255
|
+
@live_url.set_focus
|
256
|
+
|
257
|
+
#@live_url.evt_key_down(){|event| on_key_down(event)}
|
258
|
+
evt_key_down(){|event| on_key_down(event)}
|
259
|
+
|
260
|
+
|
261
|
+
@live_url.evt_text_enter(@live_url.get_id) {|event| on_live_url_enter(event)}
|
262
|
+
end
|
263
|
+
|
264
|
+
def set_index index
|
265
|
+
@index = index
|
266
|
+
end
|
267
|
+
|
268
|
+
def on_new_url
|
269
|
+
@live_url.set_focus
|
270
|
+
@live_url.set_selection(-1, -1)
|
271
|
+
end
|
272
|
+
|
273
|
+
def on_live_url_enter event
|
274
|
+
close
|
275
|
+
|
276
|
+
if live_id = live_id_from_url(@live_url.get_value)
|
277
|
+
@ready = false
|
278
|
+
@live_controller = LiveController.new(live_id)
|
279
|
+
if @live_controller.valid?
|
280
|
+
@list.base_time = @live_controller.base_time
|
281
|
+
get_parent.set_page_text(@index, live_id)
|
282
|
+
|
283
|
+
@list.delete_all_items
|
284
|
+
@live_controller.comments.each do |comment|
|
285
|
+
@list.insert_comment(comment)
|
286
|
+
end
|
287
|
+
|
288
|
+
@ready = true
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def live_id_from_url url
|
294
|
+
if /^\d+$/ =~ url
|
295
|
+
"lv#{url}"
|
296
|
+
elsif /lv\d+/ =~ url
|
297
|
+
$&
|
298
|
+
else
|
299
|
+
nil
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def on_key_down event
|
304
|
+
case event.get_key_code
|
305
|
+
when 0x16E # Ctrl+PageUp
|
306
|
+
if event.control_down
|
307
|
+
get_parent.advance_selection false
|
308
|
+
end
|
309
|
+
when 0x16F # Ctrl+PageDown
|
310
|
+
if event.control_down
|
311
|
+
get_parent.advance_selection true
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def close
|
317
|
+
if @live_controller
|
318
|
+
@live_controller.close
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
def on_update
|
324
|
+
if @ready && @live_controller.live_now?
|
325
|
+
comments = @live_controller.new_comments
|
326
|
+
comments.each do |comment|
|
327
|
+
@list.insert_comment(comment)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Okayu
|
2
|
+
IE_BTN = 250
|
3
|
+
FIREFOX_BTN = 251
|
4
|
+
CHROME_BTN = 252
|
5
|
+
|
6
|
+
ID_PASS_BTN = 255
|
7
|
+
class LoginDialog < Wx::Dialog
|
8
|
+
def initialize parent
|
9
|
+
super(parent, Wx::ID_ANY, "ログイン")
|
10
|
+
@ie_btn = Wx::RadioButton.new(self, :id => IE_BTN, :label => 'Internet Explorer')
|
11
|
+
@firefox_btn = Wx::RadioButton.new(self, :id => FIREFOX_BTN, :label => 'Firefox / Iceweasel')
|
12
|
+
@chrome_btn = Wx::RadioButton.new(self, :id => CHROME_BTN, :label => 'Google Chrome')
|
13
|
+
@id_pass_btn = Wx::RadioButton.new(self, :id => ID_PASS_BTN, :label => 'Id && Password')
|
14
|
+
@mail = Wx::TextCtrl.new self,
|
15
|
+
:id => Wx::ID_ANY,
|
16
|
+
:value => UserInfo.mail || '',
|
17
|
+
:style => Wx::TE_PROCESS_ENTER
|
18
|
+
@password = Wx::TextCtrl.new self,
|
19
|
+
:id => Wx::ID_ANY,
|
20
|
+
:value => UserInfo.password || '',
|
21
|
+
:style => Wx::TE_PROCESS_ENTER | Wx::TE_PASSWORD
|
22
|
+
@ok_btn = Wx::Button.new(self, :id => Wx::ID_OK, :label => 'OK')
|
23
|
+
sizer = Wx::BoxSizer.new Wx::VERTICAL
|
24
|
+
sizer.add(@ie_btn, 0, Wx::ALL | Wx::EXPAND, 10)
|
25
|
+
sizer.add(@firefox_btn, 0, Wx::ALL | Wx::EXPAND, 10)
|
26
|
+
sizer.add(@chrome_btn, 0, Wx::ALL | Wx::EXPAND, 10)
|
27
|
+
sizer.add(@id_pass_btn, 0, Wx::ALL | Wx::EXPAND, 10)
|
28
|
+
sizer.add(@mail, 0, Wx::ALL | Wx::EXPAND, 10)
|
29
|
+
sizer.add(@password, 0, Wx::ALL | Wx::EXPAND, 10)
|
30
|
+
sizer.add(@ok_btn, 0, Wx::ALL | Wx::EXPAND, 10)
|
31
|
+
set_sizer(sizer)
|
32
|
+
sizer.set_size_hints(self)
|
33
|
+
sizer.fit(self)
|
34
|
+
|
35
|
+
case UserInfo.login
|
36
|
+
when :ie
|
37
|
+
@ie_btn.set_value true
|
38
|
+
when :firefox
|
39
|
+
@firefox_btn.set_value true
|
40
|
+
when :chrome
|
41
|
+
@chrome_btn.set_value true
|
42
|
+
when :id_pass
|
43
|
+
@id_pass_btn.set_value true
|
44
|
+
end
|
45
|
+
@ok_btn.set_focus
|
46
|
+
|
47
|
+
@mail.evt_text_enter(@mail.get_id){ on_enter }
|
48
|
+
@password.evt_text_enter(@password.get_id){ on_enter}
|
49
|
+
|
50
|
+
@ok_btn.evt_button(Wx::ID_OK){|event| on_ok(event)}
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_enter
|
54
|
+
UserInfo.login = :id_pass
|
55
|
+
UserInfo.mail = @mail.get_value
|
56
|
+
UserInfo.password = @password.get_value
|
57
|
+
Client.connect :mail => @mail.get_value, :password => @password.get_value
|
58
|
+
end_modal 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_ok event
|
62
|
+
if @ie_btn.get_value
|
63
|
+
UserInfo.login = :ie
|
64
|
+
Client.connect :cookie => IECookieThief.get(:host => '.nicovideo.jp', :name => 'user_session')
|
65
|
+
end_modal 1
|
66
|
+
elsif @firefox_btn.get_value
|
67
|
+
UserInfo.login = :firefox
|
68
|
+
Client.connect :cookie => FirefoxCookieThief.get(:host => '.nicovideo.jp', :name => 'user_session')
|
69
|
+
end_modal 1
|
70
|
+
elsif @firefox_btn.get_value
|
71
|
+
UserInfo.login = :chrome
|
72
|
+
Client.connect :cookie => ChromeCookieThief.get(:host => '.nicovideo.jp', :name => 'user_session')
|
73
|
+
end_modal 1
|
74
|
+
else
|
75
|
+
on_enter
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Okayu
|
2
|
+
class MainPanel < Wx::Panel
|
3
|
+
attr_reader :timer
|
4
|
+
def initialize(frame, x, y, w, h)
|
5
|
+
super(frame, -1, Wx::Point.new(x, y), Wx::Size.new(w, h))
|
6
|
+
@notebook = Wx::Notebook.new self,
|
7
|
+
:id =>ID_NOTEBOOK,
|
8
|
+
:pos => Wx::Point.new(x, y),
|
9
|
+
:size => Wx::Size.new(w, h)
|
10
|
+
# panel = LivePanel.new(@notebook)
|
11
|
+
# @notebook.add_page(panel, panel.title, TRUE)
|
12
|
+
create_tab
|
13
|
+
|
14
|
+
@timer = Wx::Timer.new(self, Wx::ID_ANY)
|
15
|
+
@timer.start
|
16
|
+
evt_timer(@timer.get_id) {|event| on_update}
|
17
|
+
|
18
|
+
evt_size{|event| on_size(event)}
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_update
|
22
|
+
@notebook.each_page do |page|
|
23
|
+
page.on_update
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_size event
|
28
|
+
if @notebook
|
29
|
+
size = get_client_size
|
30
|
+
@notebook.set_dimensions(2, 2, (size.get_width - 4), (size.get_height - 4))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_tab
|
35
|
+
panel = LivePanel.new(@notebook)
|
36
|
+
@notebook.add_page(panel, panel.title, TRUE)
|
37
|
+
@notebook.get_current_page.set_index(@notebook.get_selection)
|
38
|
+
end
|
39
|
+
|
40
|
+
def close_tab
|
41
|
+
@notebook.get_current_page.close
|
42
|
+
@notebook.delete_page(@notebook.get_selection)
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_current_page
|
46
|
+
@notebook.get_current_page
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|