rbpad 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +48 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/rbpad +3 -0
- data/lib/rbpad/blockmatch.rb +136 -0
- data/lib/rbpad/config/actions_menubar.xml +166 -0
- data/lib/rbpad/config/actions_menubar_template.xml +126 -0
- data/lib/rbpad/config/actions_menubar_toggle.xml +27 -0
- data/lib/rbpad/config/find_next.xpm +196 -0
- data/lib/rbpad/config/find_prev.xpm +196 -0
- data/lib/rbpad/config/menu.xml +63 -0
- data/lib/rbpad/config/messages.xml +64 -0
- data/lib/rbpad/config/platform.xml +41 -0
- data/lib/rbpad/config/run.xpm +44 -0
- data/lib/rbpad/config/sourceview.xml +132 -0
- data/lib/rbpad/config.rb +216 -0
- data/lib/rbpad/editor.rb +141 -0
- data/lib/rbpad/error_jp.rb +176 -0
- data/lib/rbpad/input.rb +27 -0
- data/lib/rbpad/output.rb +51 -0
- data/lib/rbpad/pad.rb +656 -0
- data/lib/rbpad/page.rb +257 -0
- data/lib/rbpad/util.rb +30 -0
- data/lib/rbpad/version.rb +3 -0
- data/lib/rbpad.rb +32 -0
- data/rbpad.gemspec +43 -0
- data/rbpad3_en.png +0 -0
- data/rbpad3_jp.png +0 -0
- metadata +95 -0
data/lib/rbpad/output.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
require 'gtk3'
|
3
|
+
|
4
|
+
|
5
|
+
module Rbpad
|
6
|
+
|
7
|
+
class ConsoleOutput < Gtk::ScrolledWindow
|
8
|
+
def initialize(conf)
|
9
|
+
super()
|
10
|
+
@view = Gtk::TextView.new
|
11
|
+
@view.override_font(Pango::FontDescription.new("#{conf.platform[:font][:output]}"))
|
12
|
+
@view.left_margin = 6
|
13
|
+
@view.pixels_above_lines = 1
|
14
|
+
@view.pixels_below_lines = 1
|
15
|
+
@view.accepts_tab = false # 'TAB' is used for moving focus
|
16
|
+
@view.buffer.create_tag('info', {foreground: 'gray'})
|
17
|
+
@view.buffer.create_tag('result', {weight: 600}) # SEMI-BOLD
|
18
|
+
@view.buffer.create_tag('error_jp', {foreground: 'red'})
|
19
|
+
@view.set_editable(false)
|
20
|
+
self.add(@view)
|
21
|
+
end
|
22
|
+
|
23
|
+
# line feed other than at the beginning of a line
|
24
|
+
def newline
|
25
|
+
@view.buffer.insert(@view.buffer.end_iter, "\n") if @view.buffer.end_iter.line_offset != 0
|
26
|
+
end
|
27
|
+
|
28
|
+
# insert text at bottom line
|
29
|
+
def add_tail(text, tag = nil)
|
30
|
+
@view.buffer.insert(@view.buffer.end_iter, text.scrub)
|
31
|
+
unless tag == nil # format by specified tag
|
32
|
+
iter_s = @view.buffer.get_iter_at(offset: @view.buffer.end_iter.offset - text.size)
|
33
|
+
iter_e = @view.buffer.end_iter
|
34
|
+
@view.buffer.apply_tag(tag, iter_s, iter_e)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# scroll to bottom line
|
39
|
+
def scroll_tail
|
40
|
+
mark = @view.buffer.create_mark(nil, @view.buffer.end_iter, true)
|
41
|
+
@view.scroll_to_mark(mark, 0, false, 0, 1) # ('scroll_to_iter' does not work)
|
42
|
+
end
|
43
|
+
|
44
|
+
# clear
|
45
|
+
def clear
|
46
|
+
@view.buffer.delete(@view.buffer.start_iter, @view.buffer.end_iter)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
data/lib/rbpad/pad.rb
ADDED
@@ -0,0 +1,656 @@
|
|
1
|
+
|
2
|
+
require 'gtk3'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'win32ole'
|
7
|
+
rescue LoadError => e
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rbpad/output'
|
11
|
+
require 'rbpad/input'
|
12
|
+
require 'rbpad/editor'
|
13
|
+
require 'rbpad/config'
|
14
|
+
require 'rbpad/util'
|
15
|
+
require 'rbpad/error_jp'
|
16
|
+
require 'rbpad/version'
|
17
|
+
|
18
|
+
|
19
|
+
module Rbpad
|
20
|
+
|
21
|
+
class Pad < Gtk::Window
|
22
|
+
DEFAULT_W = 800
|
23
|
+
DEFAULT_H = 650
|
24
|
+
def initialize(lang = :jp, w = DEFAULT_W, h = DEFAULT_H)
|
25
|
+
super("rbpad (#{VERSION})")
|
26
|
+
if w <= 0 and h <= 0
|
27
|
+
self.set_size_request(DEFAULT_W, DEFAULT_H)
|
28
|
+
self.maximize
|
29
|
+
else
|
30
|
+
self.set_size_request(w, h)
|
31
|
+
end
|
32
|
+
|
33
|
+
@lang = lang # language(:jp/:en)
|
34
|
+
@drb_portno = 49323 # port number for DRb
|
35
|
+
@q = Queue.new # queue for Inter-thread communication
|
36
|
+
@last_dir = nil # directory of last opened or saved
|
37
|
+
|
38
|
+
@conf = Config.new(
|
39
|
+
binding, # to create Proc instance in `class Pad` context
|
40
|
+
Utility.get_os,
|
41
|
+
@lang
|
42
|
+
)
|
43
|
+
|
44
|
+
# menu
|
45
|
+
@uimanager = Gtk::UIManager.new # UIManager
|
46
|
+
_set_menu # set menu
|
47
|
+
self.add_accel_group(@uimanager.accel_group) # enable to use shortcut keys
|
48
|
+
|
49
|
+
# console for output
|
50
|
+
@console_output = ConsoleOutput.new(@conf)
|
51
|
+
frame_output = Gtk::Frame.new(@conf.messages[:frame_output])
|
52
|
+
frame_output.add(@console_output)
|
53
|
+
|
54
|
+
# console for input
|
55
|
+
@console_input = ConsoleInput.new(@conf, @drb_portno)
|
56
|
+
frame_input = Gtk::Frame.new(@conf.messages[:frame_input])
|
57
|
+
frame_input.add(@console_input)
|
58
|
+
|
59
|
+
# statu area
|
60
|
+
status_area = Gtk::Label.new
|
61
|
+
status_area.set_alignment(0, 0.5)
|
62
|
+
status_area.override_font(Pango::FontDescription.new("#{@conf.platform[:font][:status]}"))
|
63
|
+
frame_status = Gtk::Frame.new
|
64
|
+
frame_status.add(status_area)
|
65
|
+
|
66
|
+
# editor
|
67
|
+
@editor = Editor.new(@conf, status_area)
|
68
|
+
@editor.set_draw_spaces(@uimanager.get_widget("/MenuBar/option/drawspaces").active?)
|
69
|
+
@editor.set_block_match(@uimanager.get_widget("/MenuBar/option/blockmatch").active?)
|
70
|
+
|
71
|
+
# vertical pane for edtor and output console
|
72
|
+
vpaned = Gtk::Paned.new(:vertical)
|
73
|
+
vpaned.pack1(@editor, resize: true, shrink: false)
|
74
|
+
vpaned.pack2(frame_output.set_shadow_type(:etched_in), resize: true, shrink: false)
|
75
|
+
vpaned.position = self.size[1] * 0.55 # <-- 300
|
76
|
+
|
77
|
+
# horizontal box for input console and status area
|
78
|
+
hbox = Gtk::Box.new(:horizontal)
|
79
|
+
hbox.pack_start(frame_input.set_shadow_type(:etched_in), expand: true, fill: true)
|
80
|
+
hbox.pack_start(frame_status.set_shadow_type(:etched_in), expand: false, fill: false)
|
81
|
+
frame_status.set_size_request(200, -1)
|
82
|
+
|
83
|
+
# toolbar
|
84
|
+
hbox_tool = Gtk::Box.new(:horizontal, 0)
|
85
|
+
hbox_tool.pack_start(_create_toolbox1, expand: false, fill: false, padding: 0)
|
86
|
+
hbox_tool.pack_end(_create_toolbox2, expand: false, fill: false, padding: 10)
|
87
|
+
|
88
|
+
# container
|
89
|
+
vbox_all = Gtk::Box.new(:vertical, 0)
|
90
|
+
vbox_all.pack_start(@uimanager.get_widget("/MenuBar"), expand: false, fill: true, padding: 0)
|
91
|
+
vbox_all.pack_start(hbox_tool, expand: false, fill: true, padding: 0)
|
92
|
+
vbox_all.pack_start(vpaned, expand: true, fill: true, padding: 0)
|
93
|
+
vbox_all.pack_start(hbox, expand: false, fill: false, padding: 0)
|
94
|
+
|
95
|
+
# window
|
96
|
+
@editor.page_focus # set focus to page of editor
|
97
|
+
self.add(vbox_all)
|
98
|
+
self.set_window_position(:center)
|
99
|
+
self.show_all
|
100
|
+
|
101
|
+
# handler
|
102
|
+
self.signal_connect(:delete_event) do
|
103
|
+
puts "clicked [x]"
|
104
|
+
_close_page_all # in case of being returned 'true', signal of ':destroy' is not happend
|
105
|
+
end
|
106
|
+
|
107
|
+
self.signal_connect(:destroy) do
|
108
|
+
# _quit
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# set menu
|
113
|
+
private def _set_menu
|
114
|
+
menu = @conf.menu
|
115
|
+
actions_menubar = @conf.actions_menubar
|
116
|
+
actions_toggle = @conf.actions_menubar_toggle
|
117
|
+
|
118
|
+
menubar_group = Gtk::ActionGroup.new("menubar_group")
|
119
|
+
menubar_group.add_actions(actions_menubar)
|
120
|
+
@uimanager.insert_action_group(menubar_group, 0)
|
121
|
+
|
122
|
+
toggle_group = Gtk::ActionGroup.new("toggle_group")
|
123
|
+
toggle_group.add_toggle_actions(actions_toggle)
|
124
|
+
@uimanager.insert_action_group(toggle_group, 0)
|
125
|
+
|
126
|
+
@uimanager.add_ui(menu)
|
127
|
+
|
128
|
+
@uimanager.action_groups.each do |ag|
|
129
|
+
ag.actions.each do |a|
|
130
|
+
a.hide_if_empty = false # if true, couldn't show empty node
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
@uimanager.get_action("/MenuBar/file/kill").sensitive = false # disable 'kill(terminate)' item
|
135
|
+
end
|
136
|
+
|
137
|
+
# toolbox for toolbutton
|
138
|
+
def _create_toolbox1
|
139
|
+
toolbar = Gtk::Toolbar.new
|
140
|
+
f = "#{File.expand_path(File.dirname(__FILE__))}/config/run.xpm"
|
141
|
+
@toolitem_run = Gtk::ToolButton.new(icon_widget: Gtk::Image.new(file: f), label: @conf.messages[:exec])
|
142
|
+
toolbar.insert(@toolitem_run, 0)
|
143
|
+
toolbar.style = Gtk::ToolbarStyle::BOTH
|
144
|
+
@toolitem_run.signal_connect(:clicked) {
|
145
|
+
_exec
|
146
|
+
}
|
147
|
+
|
148
|
+
toolbar
|
149
|
+
end
|
150
|
+
|
151
|
+
# toolbox for finding function
|
152
|
+
def _create_toolbox2
|
153
|
+
boxf = Gtk::Box.new(:vertical)
|
154
|
+
box = Gtk::Box.new(:horizontal)
|
155
|
+
|
156
|
+
f = "#{File.expand_path(File.dirname(__FILE__))}/config/find_next.xpm"
|
157
|
+
item = Gtk::ToolButton.new(icon_widget: Gtk::Image.new(file: f))
|
158
|
+
#item.set_icon_name("go-down")
|
159
|
+
box.pack_end(item)
|
160
|
+
item.signal_connect(:clicked) {
|
161
|
+
_find_and_select(@word, :forward)
|
162
|
+
}
|
163
|
+
|
164
|
+
f = "#{File.expand_path(File.dirname(__FILE__))}/config/find_prev.xpm"
|
165
|
+
item = Gtk::ToolButton.new(icon_widget: Gtk::Image.new(file: f))
|
166
|
+
#item.set_icon_name("go-up")
|
167
|
+
box.pack_end(item)
|
168
|
+
item.signal_connect(:clicked) {
|
169
|
+
_find_and_select(@word, :backward)
|
170
|
+
}
|
171
|
+
|
172
|
+
entry = Gtk::Entry.new
|
173
|
+
entry.set_size_request(0, 0)
|
174
|
+
entry.override_font(Pango::FontDescription.new("#{@conf.platform[:font][:find]}"))
|
175
|
+
box.pack_end(entry, fill: false, expand: false, padding: 5) # want to be minimum ...?
|
176
|
+
entry.signal_connect(:changed) {
|
177
|
+
@word = entry.text
|
178
|
+
}
|
179
|
+
entry.signal_connect("activate") {
|
180
|
+
_find_and_select(@word, :forward)
|
181
|
+
}
|
182
|
+
item = Gtk::Label.new(@conf.messages[:find])
|
183
|
+
box.pack_end(item, fill: false, expand: false, padding: 5) # want to be minimum ...?
|
184
|
+
|
185
|
+
boxf.pack_start(Gtk::Label.new(''), fill: false, expand: false)
|
186
|
+
boxf.pack_start(box, fill: false, expand: false)
|
187
|
+
boxf.pack_start(Gtk::Label.new(''), fill: false, expand: false)
|
188
|
+
|
189
|
+
boxf
|
190
|
+
end
|
191
|
+
|
192
|
+
# execute
|
193
|
+
private def _exec
|
194
|
+
@thread = Thread.start do
|
195
|
+
# disabled/enable menu
|
196
|
+
@toolitem_run.sensitive = false
|
197
|
+
@uimanager.get_action("/MenuBar/file/run").sensitive = false
|
198
|
+
@uimanager.get_action("/MenuBar/file/kill").sensitive = true
|
199
|
+
|
200
|
+
begin
|
201
|
+
dirname, basename, tabname, status = @editor.get_page_properties
|
202
|
+
if dirname and Dir.exist?(dirname)
|
203
|
+
# in case of already existing the file in directory
|
204
|
+
run_dirname = dirname # run at there directory
|
205
|
+
run_filename = @editor.save_tmp(dirname)
|
206
|
+
else
|
207
|
+
tmp_dirname = Dir.mktmpdir(["ruby_", "_tmp"])
|
208
|
+
run_dirname = tmp_dirname # run at temporary directory
|
209
|
+
run_filename = @editor.save_tmp(tmp_dirname)
|
210
|
+
end
|
211
|
+
|
212
|
+
# save temporary file for DRb in directory for executing
|
213
|
+
required_filename = _save_required_file(run_dirname)
|
214
|
+
|
215
|
+
#puts "tmp_dirname : #{tmp_dirname}"
|
216
|
+
#puts "run_dirname : #{run_dirname}"
|
217
|
+
#puts "run_filename : #{run_filename}"
|
218
|
+
#puts "required_filename : #{required_filename}"
|
219
|
+
|
220
|
+
# create executing command
|
221
|
+
# cmd = %Q{ruby -E UTF-8 -r #{required_filename} -C #{run_dirname} #{run_filename}} # -C (couldn't run about directory named by Japanese, above Ruby 2.2)
|
222
|
+
# cmd = %Q{cd /d "#{run_dirname}" & ruby -E UTF-8 -r "#{required_filename}" #{run_filename}} # (couldn't delete temporary directory when process has killed)
|
223
|
+
cmd = %Q{ruby -E UTF-8 -r "#{required_filename}" #{run_filename}}
|
224
|
+
puts cmd
|
225
|
+
|
226
|
+
# starting message
|
227
|
+
@console_output.add_tail("> start: #{tabname} (#{Time.now.strftime('%H:%M:%S')})\n", "info") # insert to bottom line
|
228
|
+
@console_output.scroll_tail # scroll till bottom line
|
229
|
+
|
230
|
+
# move current directory while running, and go back when finished
|
231
|
+
current_dir = Dir.pwd
|
232
|
+
Dir.chdir(run_dirname)
|
233
|
+
|
234
|
+
# run
|
235
|
+
jpmsg = nil
|
236
|
+
@q << io = IO.popen(cmd, err: [:child, :out]) # merge stderr to stdout
|
237
|
+
io.each do |line|
|
238
|
+
line.gsub!(run_filename, tabname) # replace temporary file name to tab name for output
|
239
|
+
line.gsub!("\x03\n", "") # correspond the 'print' command which has no line-feed (delete line-feed including "\x03")
|
240
|
+
@console_output.add_tail(line, "result") # insert to bottom line
|
241
|
+
@console_output.scroll_tail # scroll till bottom line
|
242
|
+
if @uimanager.get_action("/MenuBar/option/errorjp") and
|
243
|
+
@uimanager.get_action("/MenuBar/option/errorjp").active?
|
244
|
+
jpmsg = Error_jp.get_msg(line) if line =~ /^#{tabname}/ or line =~ /\(.*Error\)$/
|
245
|
+
end
|
246
|
+
end
|
247
|
+
if jpmsg
|
248
|
+
@console_output.add_tail(jpmsg, "error_jp") # insert to bottom line
|
249
|
+
@console_output.scroll_tail # scroll till bottom line
|
250
|
+
end
|
251
|
+
|
252
|
+
ensure
|
253
|
+
# close IO
|
254
|
+
io.close
|
255
|
+
|
256
|
+
# go back to the original directory
|
257
|
+
Dir.chdir(current_dir)
|
258
|
+
|
259
|
+
# ending message
|
260
|
+
@console_output.newline # insert line-feed when not head of line
|
261
|
+
@console_output.add_tail("> end : #{tabname} (#{Time.now.strftime('%H:%M:%S')})\n\n", "info") # insert to bottom line
|
262
|
+
@console_output.scroll_tail # scroll till bottom line
|
263
|
+
|
264
|
+
# delete temporary directory including files in it
|
265
|
+
FileUtils.remove_entry_secure "#{run_dirname}/#{run_filename}"
|
266
|
+
FileUtils.remove_entry_secure required_filename
|
267
|
+
puts "Thread Terminate... #{tmp_dirname}"
|
268
|
+
FileUtils.remove_entry_secure tmp_dirname if tmp_dirname # couldn't delete current directory if stay here
|
269
|
+
puts "Thread Terminate..."
|
270
|
+
|
271
|
+
# clear the queue
|
272
|
+
@q.clear
|
273
|
+
end
|
274
|
+
|
275
|
+
# enabled/disable menu
|
276
|
+
@toolitem_run.sensitive = true
|
277
|
+
@uimanager.get_action("/MenuBar/file/run").sensitive = true
|
278
|
+
@uimanager.get_action("/MenuBar/file/kill").sensitive = false
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# terminate
|
283
|
+
private def _kill
|
284
|
+
pid = (@q.empty? ? -1 : @q.pop.pid)
|
285
|
+
if pid > 0 and @thread
|
286
|
+
p pid
|
287
|
+
p @thread
|
288
|
+
puts "kill tprocess => " + pid.to_s
|
289
|
+
if @os == :windows
|
290
|
+
system("taskkill /f /pid #{pid}") # terminate the process (Windows)
|
291
|
+
else
|
292
|
+
Process.kill(:KILL, pid) # terminate the process (not Windows)
|
293
|
+
end
|
294
|
+
|
295
|
+
@thread.kill # kill thread
|
296
|
+
@thread = nil
|
297
|
+
|
298
|
+
# enabled/disable menu
|
299
|
+
@toolitem_run.sensitive = true
|
300
|
+
@uimanager.get_action("/MenuBar/file/run").sensitive = true
|
301
|
+
@uimanager.get_action("/MenuBar/file/kill").sensitive = false
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# code for DRb server to emulate STDIN (to be required when starting-up)
|
306
|
+
private def _save_required_file(tmp_dirname)
|
307
|
+
# $stdin <-- (druby) -- $in
|
308
|
+
script = <<~"EOS" # exclude indent
|
309
|
+
require 'drb/drb'
|
310
|
+
require 'drb/acl'
|
311
|
+
$stdout.sync = true
|
312
|
+
$stderr.sync = true
|
313
|
+
$stdin, $in = IO.pipe
|
314
|
+
module Kernel
|
315
|
+
alias_method :__print__, :print
|
316
|
+
def print(*args)
|
317
|
+
__print__(*args, "\\x03\\n") # correspond the 'print' command which has no line-feed
|
318
|
+
end
|
319
|
+
end
|
320
|
+
list = %w[
|
321
|
+
deny all
|
322
|
+
allow 127.0.0.1
|
323
|
+
]
|
324
|
+
# -- <DRb::DRbConnError: An existing connection was forcibly closed by the remote host.> if use 'localhost'
|
325
|
+
DRb.install_acl(ACL.new(list, ACL::DENY_ALLOW))
|
326
|
+
begin
|
327
|
+
DRb.start_service("druby://127.0.0.1:#{@drb_portno}", $in, safe_level: 1)
|
328
|
+
rescue
|
329
|
+
end
|
330
|
+
EOS
|
331
|
+
basename = "rq_#{Utility.get_uniqname}.rb"
|
332
|
+
tmpfilepath = "#{tmp_dirname}/#{basename}"
|
333
|
+
File.open(tmpfilepath, "w") do |fp|
|
334
|
+
fp.puts script
|
335
|
+
end
|
336
|
+
return tmpfilepath # path of temporary file
|
337
|
+
end
|
338
|
+
|
339
|
+
# show properties
|
340
|
+
private def _info
|
341
|
+
dirname, basename, tabname, status = @editor.get_page_properties
|
342
|
+
# show
|
343
|
+
@console_output.add_tail("[#{tabname}]\n")
|
344
|
+
@console_output.add_tail(" #{@conf.messages[:info_stat]} : #{status}\n")
|
345
|
+
@console_output.add_tail(" #{@conf.messages[:info_dir]} : #{dirname}\n")
|
346
|
+
@console_output.add_tail(" #{@conf.messages[:info_base]} : #{basename}\n\n")
|
347
|
+
@console_output.scroll_tail # scroll till bottom line
|
348
|
+
end
|
349
|
+
|
350
|
+
# show version of Ruby
|
351
|
+
private def _ruby_ver
|
352
|
+
thread = Thread.start do
|
353
|
+
IO.popen("ruby -v", err: [:child, :out]) do |pipe|
|
354
|
+
pipe.each do |line|
|
355
|
+
@console_output.add_tail(line) # insert to bottom line
|
356
|
+
end
|
357
|
+
end
|
358
|
+
#@console_output.add_tail("\n") # insert to bottom line (empty-line)
|
359
|
+
@console_output.scroll_tail # scroll till bottom line
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# open file
|
364
|
+
private def _open
|
365
|
+
dialog = Gtk::FileChooserDialog.new(
|
366
|
+
title: @conf.messages[:dialog_open],
|
367
|
+
parent: self,
|
368
|
+
action: :open,
|
369
|
+
back: nil,
|
370
|
+
buttons: [[Gtk::Stock::OK, :accept],
|
371
|
+
[Gtk::Stock::CANCEL, :cancel]]
|
372
|
+
)
|
373
|
+
# set current directory
|
374
|
+
if @last_dir
|
375
|
+
# last specified directory
|
376
|
+
dialog.current_folder = @last_dir
|
377
|
+
else
|
378
|
+
# desktop
|
379
|
+
if ENV.has_key?("HOME") and Dir.exists?(ENV["HOME"] + "/Desktop")
|
380
|
+
dialog.current_folder = ENV["HOME"] + "/Desktop"
|
381
|
+
elsif ENV.has_key?("USERPROFILE") and Dir.exists?(ENV["USERPROFILE"] + "/Desktop")
|
382
|
+
dialog.current_folder = ENV["USERPROFILE"] + "/Desktop"
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# set filters
|
387
|
+
filter1 = Gtk::FileFilter.new
|
388
|
+
filter2 = Gtk::FileFilter.new
|
389
|
+
filter1.name = "Ruby Program Files (*.rb)"
|
390
|
+
filter2.name = "All Files (*.*)"
|
391
|
+
filter1.add_pattern('*.rb')
|
392
|
+
filter2.add_pattern('*')
|
393
|
+
dialog.add_filter(filter1) # default is first added filter
|
394
|
+
dialog.add_filter(filter2)
|
395
|
+
|
396
|
+
while true
|
397
|
+
filename = nil
|
398
|
+
res = dialog.run
|
399
|
+
if res == :accept
|
400
|
+
filename = dialog.filename.gsub('\\', '/') # /foo/bar/zzz
|
401
|
+
end
|
402
|
+
|
403
|
+
# close dialog when canceled
|
404
|
+
break if filename == nil
|
405
|
+
|
406
|
+
if File.extname(filename) != ".lnk"
|
407
|
+
puts "filename #{filename}"
|
408
|
+
if @editor.load(filename) # load the file from specified directory
|
409
|
+
@last_dir = File.dirname(filename)
|
410
|
+
end
|
411
|
+
break
|
412
|
+
else
|
413
|
+
begin
|
414
|
+
# Windows only
|
415
|
+
sh = WIN32OLE.new('WScript.Shell')
|
416
|
+
lnkfile = sh.CreateShortcut(filename) # create WshShortcut object from *.lnk file (WIN32OLE)
|
417
|
+
if FileTest.directory?(lnkfile.TargetPath)
|
418
|
+
dialog.current_folder = lnkfile.TargetPath # open linked directory if link to directory (show dialog again)
|
419
|
+
elsif FileTest.readable_real?(lnkfile.TargetPath)
|
420
|
+
@editor.load(lnkfile.TargetPath) # load the linked file from specified directory
|
421
|
+
@last_dir = File.dirname(lnkfile.TargetPath)
|
422
|
+
break
|
423
|
+
end
|
424
|
+
rescue
|
425
|
+
break
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
dialog.destroy
|
430
|
+
end
|
431
|
+
|
432
|
+
# save file
|
433
|
+
private def _save
|
434
|
+
# do nothing if status is :EMPTY or :SAVED
|
435
|
+
# overwrite silently if 'dirname' is existing directory
|
436
|
+
# otherwise, call '_save_as'
|
437
|
+
dirname, basename, tabname, status = @editor.get_page_properties
|
438
|
+
puts "status #{status} basename #{basename} dirname #{dirname}"
|
439
|
+
return if status == :EMPTY
|
440
|
+
return if status == :SAVED
|
441
|
+
if dirname and Dir.exist?(dirname) and basename
|
442
|
+
@editor.save("#{dirname}/#{basename}") # save the file where specified directory
|
443
|
+
else
|
444
|
+
_save_as # show dialog for save as
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
# save file as
|
449
|
+
private def _save_as
|
450
|
+
dialog = Gtk::FileChooserDialog.new(
|
451
|
+
title: @conf.messages[:dialog_saveas],
|
452
|
+
parent: self,
|
453
|
+
action: :save,
|
454
|
+
back: nil,
|
455
|
+
buttons: [[Gtk::Stock::OK, :accept],
|
456
|
+
[Gtk::Stock::CANCEL, :cancel]]
|
457
|
+
)
|
458
|
+
# set current directory
|
459
|
+
if @last_dir
|
460
|
+
# last specified directory
|
461
|
+
dialog.current_folder = @last_dir
|
462
|
+
else
|
463
|
+
# desktop
|
464
|
+
if ENV.has_key?("HOME") and Dir.exists?(ENV["HOME"] + "/Desktop")
|
465
|
+
dialog.current_folder = ENV["HOME"] + "/Desktop"
|
466
|
+
elsif ENV.has_key?("USERPROFILE") and Dir.exists?(ENV["USERPROFILE"] + "/Desktop")
|
467
|
+
dialog.current_folder = ENV["USERPROFILE"] + "/Desktop"
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
# confirm whether overwrite, when already exist file which has same name
|
472
|
+
dialog.do_overwrite_confirmation = true
|
473
|
+
dialog.signal_connect("confirm_overwrite") do |fc|
|
474
|
+
#puts "confirm #{dialog.uri}"
|
475
|
+
:confirm
|
476
|
+
end
|
477
|
+
|
478
|
+
# set filters
|
479
|
+
filter1 = Gtk::FileFilter.new
|
480
|
+
filter2 = Gtk::FileFilter.new
|
481
|
+
filter1.name = "Ruby Program Files (*.rb)"
|
482
|
+
filter2.name = "All Files (*.*)"
|
483
|
+
filter1.add_pattern('*.rb')
|
484
|
+
filter2.add_pattern('*')
|
485
|
+
dialog.add_filter(filter1) # default is first added filter
|
486
|
+
dialog.add_filter(filter2)
|
487
|
+
|
488
|
+
# res = dialog.run
|
489
|
+
# if res == :accept
|
490
|
+
# puts dialog.filename
|
491
|
+
# filename = dialog.filename.gsub('\\', '/') # /foo/bar/zzz
|
492
|
+
# @editor.save(filename) # save the file where specified directory
|
493
|
+
# end
|
494
|
+
|
495
|
+
while true
|
496
|
+
filename = nil
|
497
|
+
res = dialog.run
|
498
|
+
if res == :accept
|
499
|
+
filename = dialog.filename.gsub('\\', '/') # /foo/bar/zzz
|
500
|
+
end
|
501
|
+
|
502
|
+
# close dialog when canceled
|
503
|
+
break if filename == nil
|
504
|
+
|
505
|
+
if File.extname(filename) != ".lnk"
|
506
|
+
puts "filename #{filename}"
|
507
|
+
@editor.save(filename) # save the file where specified directory
|
508
|
+
@last_dir = File.dirname(filename)
|
509
|
+
break
|
510
|
+
else
|
511
|
+
begin
|
512
|
+
# Windows only
|
513
|
+
sh = WIN32OLE.new('WScript.Shell')
|
514
|
+
lnkfile = sh.CreateShortcut(filename) # create WshShortcut object from *.lnk file (WIN32OLE)
|
515
|
+
if FileTest.directory?(lnkfile.TargetPath)
|
516
|
+
dialog.current_folder = lnkfile.TargetPath # open linked directory if link to directory (show dialog again)
|
517
|
+
elsif FileTest.readable_real?(lnkfile.TargetPath)
|
518
|
+
@editor.save(lnkfile.TargetPath) # save the linked file where specified directory
|
519
|
+
@last_dir = File.dirname(lnkfile.TargetPath)
|
520
|
+
break
|
521
|
+
end
|
522
|
+
rescue
|
523
|
+
break
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
dialog.destroy
|
529
|
+
end
|
530
|
+
|
531
|
+
# confirmation dialog (Yes/No/Cancel)
|
532
|
+
private def _draw_confirm_dialog(title, labeltext, parent)
|
533
|
+
dialog = Gtk::Dialog.new(title: title, parent: parent, flags: :modal)
|
534
|
+
|
535
|
+
dialog.child.pack_start(Gtk::Label.new(labeltext), expand: true, fill: true, padding: 30)
|
536
|
+
|
537
|
+
dialog.add_button(Gtk::Stock::YES, :yes)
|
538
|
+
dialog.add_button(Gtk::Stock::NO, :no)
|
539
|
+
dialog.add_button(Gtk::Stock::CANCEL, :cancel)
|
540
|
+
dialog.default_response = Gtk::ResponseType::CANCEL # set default (= CANCEL button)
|
541
|
+
dialog.show_all
|
542
|
+
|
543
|
+
res = dialog.run
|
544
|
+
dialog.destroy
|
545
|
+
return res
|
546
|
+
end
|
547
|
+
|
548
|
+
# open the references
|
549
|
+
private def _startcmd(uri)
|
550
|
+
if uri =~ /^http/
|
551
|
+
cmd = "#{@conf.platform[:command][:browse]} #{uri}"
|
552
|
+
else
|
553
|
+
# decide the relative path based on the place of script when local files
|
554
|
+
cmd = "#{@conf.platform[:command][:browse]} #{File.expand_path(File.dirname(__FILE__))}/#{uri}"
|
555
|
+
end
|
556
|
+
|
557
|
+
Thread.start do
|
558
|
+
p cmd
|
559
|
+
system(cmd) # open by default application
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
# append page
|
564
|
+
private def _new
|
565
|
+
@editor.append
|
566
|
+
@editor.page_focus # set focus to current page in editor
|
567
|
+
end
|
568
|
+
|
569
|
+
# close
|
570
|
+
private def _close
|
571
|
+
_close_page
|
572
|
+
if @editor.n_pages <= 0 # quit if all pages are closed
|
573
|
+
_kill
|
574
|
+
_quit
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
# close all pages
|
579
|
+
private def _close_page_all
|
580
|
+
until @editor.n_pages <= 0
|
581
|
+
return true if _close_page == :CANCEL_CLOSE # if true, not to be destroyed when call from delete_event
|
582
|
+
end
|
583
|
+
_kill
|
584
|
+
_quit
|
585
|
+
true # if true, not to be destroyed when call from delete_event
|
586
|
+
end
|
587
|
+
|
588
|
+
# quit
|
589
|
+
private def _quit
|
590
|
+
# -- pending the following codes for persistance
|
591
|
+
# options = {}
|
592
|
+
# @conf.option_ids.each do |o|
|
593
|
+
# if @uimanager.get_widget("/MenuBar/option/#{o}")
|
594
|
+
# options[o.to_sym] = @uimanager.get_widget("/MenuBar/option/#{o}").active?
|
595
|
+
# end
|
596
|
+
# end
|
597
|
+
# @conf.persist(options)
|
598
|
+
Gtk.main_quit
|
599
|
+
end
|
600
|
+
|
601
|
+
# close page
|
602
|
+
private def _close_page
|
603
|
+
dirname, basename, tabname, status = @editor.get_page_properties
|
604
|
+
if status == :UNSAVED
|
605
|
+
# show dialog when not saved
|
606
|
+
res = _draw_confirm_dialog("rbpad", " #{tabname} #{@conf.messages[:confirm_save]}", self)
|
607
|
+
if res == :yes
|
608
|
+
if dirname and basename
|
609
|
+
_save
|
610
|
+
else
|
611
|
+
_save_as
|
612
|
+
end
|
613
|
+
@editor.close
|
614
|
+
elsif res == :no
|
615
|
+
@editor.close
|
616
|
+
else
|
617
|
+
return :CANCEL_CLOSE
|
618
|
+
end
|
619
|
+
else
|
620
|
+
# close as it is, when saved or empty
|
621
|
+
@editor.close
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
###
|
626
|
+
private def _undo ; @editor.undo ; end
|
627
|
+
private def _redo ; @editor.redo ; end
|
628
|
+
private def _cut ; @editor.cut ; end
|
629
|
+
private def _copy ; @editor.copy ; end
|
630
|
+
private def _paste ; @editor.paste ; end
|
631
|
+
private def _select_all ; @editor.select_all ; end
|
632
|
+
private def _clear_output ; @console_output.clear ; end
|
633
|
+
|
634
|
+
private def _find_and_select(word, direction)
|
635
|
+
unless @editor.find_and_select(word, direction)
|
636
|
+
@console_output.add_tail("'#{word}' #{@conf.messages[:not_found]}\n") # insert to bottom line
|
637
|
+
@console_output.scroll_tail # scroll till bottom line
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
private def _drawspaces(ag, action)
|
642
|
+
@editor.set_draw_spaces(action.active?)
|
643
|
+
end
|
644
|
+
|
645
|
+
private def _blockmatch(ag, action)
|
646
|
+
@editor.set_block_match(action.active?)
|
647
|
+
end
|
648
|
+
|
649
|
+
# insert block to new page (for tamplates)
|
650
|
+
private def _load_block(statement)
|
651
|
+
@editor.load_block(statement)
|
652
|
+
end
|
653
|
+
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|