vimamsa 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,177 @@
1
+
2
+ def gui_find_macro_update_callback(search_str = "")
3
+ puts "gui_find_macro_update_callback: #{search_str}"
4
+ heystack = $macro.named_macros
5
+ return [] if heystack.empty?
6
+ $macro_search_list = []
7
+ files = heystack.keys.sort.collect { |x| [x, 0] }
8
+
9
+ if (search_str.size > 1)
10
+ files = fuzzy_filter(search_str, heystack.keys, 40)
11
+ end
12
+ $macro_search_list = files
13
+ return files
14
+ end
15
+
16
+ def gui_find_macro_select_callback(search_str, idx)
17
+ puts "gui_find_macro_select_callback"
18
+ selected = $macro_search_list[idx]
19
+ m = $macro.named_macros[selected[0]].clone
20
+ puts "SELECTED MACRO:#{selected}, #{m}"
21
+ id = $macro.last_macro
22
+ $macro.recorded_macros[id] = m
23
+ $macro.run_macro(id)
24
+ end
25
+
26
+ class Macro
27
+ # attr_reader :recorded_macros, :recording, :named_macros
28
+ attr_accessor :recorded_macros, :recording, :named_macros, :last_macro
29
+
30
+ def initialize()
31
+ @recording = false
32
+ # @recorded_macros = {}
33
+ @current_recording = []
34
+ @current_name = nil
35
+ @last_macro = "a"
36
+
37
+ #TODO:
38
+ @recorded_macros = vma.marshal_load("macros", {})
39
+ @named_macros = vma.marshal_load("named_macros", {})
40
+ $hook.register(:shutdown, self.method("save"))
41
+ end
42
+
43
+ def save()
44
+ vma.marshal_save("macros", @recorded_macros)
45
+ vma.marshal_save("named_macros", @named_macros)
46
+ end
47
+
48
+ def gui_name_macro()
49
+ callback = self.method("name_macro")
50
+ # gui_one_input_action("Grep", "Search:", "grep", "grep_cur_buffer")
51
+ gui_one_input_action("Name last macro", "Name:", "Set", callback)
52
+ end
53
+
54
+ def find_macro_gui()
55
+ # Ripl.start :binding => binding
56
+
57
+ l = $macro.named_macros.keys.sort.collect { |x| [x, 0] }
58
+ $macro_search_list = l
59
+ $select_keys = ["h", "l", "f", "d", "s", "a", "g", "z"]
60
+
61
+ qt_select_update_window(l, $select_keys.collect { |x| x.upcase },
62
+ "gui_find_macro_select_callback",
63
+ "gui_find_macro_update_callback")
64
+ end
65
+
66
+ def name_macro(name, id = nil)
67
+ puts "NAME MACRO #{name}"
68
+ if id.nil?
69
+ id = @last_macro
70
+ end
71
+ @named_macros[name] = @recorded_macros[id].clone
72
+ end
73
+
74
+ def start_recording(name)
75
+ @recording = true
76
+ @current_name = name
77
+ @current_recording = []
78
+ message("Start recording macro [#{name}]")
79
+
80
+ # Returning false prevents from putting start_recording to start of macro
81
+ return false
82
+ end
83
+
84
+ def end_recording()
85
+ if @recording == true
86
+ @recorded_macros[@current_name] = @current_recording
87
+ @last_macro = @current_name
88
+ @current_name = @current_recording = nil
89
+ @recording = false
90
+ message("Stop recording macro [#{@last_macro}]")
91
+ else
92
+ message("Not recording macro")
93
+ end
94
+ end
95
+
96
+ def is_recording
97
+ return @recording
98
+ end
99
+
100
+ def record_action(eval_str)
101
+ if @recording
102
+ if eval_str == "repeat_last_action"
103
+ @current_recording << $command_history.last
104
+ else
105
+ @current_recording << eval_str
106
+ end
107
+ end
108
+ end
109
+
110
+ # Allow method to specify the macro action instead of recording from keyboard input
111
+ def overwrite_current_action(eval_str)
112
+ if @recording
113
+ @current_recording[-1] = eval_str
114
+ end
115
+ end
116
+
117
+ def run_last_macro
118
+ run_macro(@last_macro)
119
+ end
120
+
121
+ def run_macro(name)
122
+ if $macro.is_recording == true
123
+ message("Can't run a macro that runs a macro (recursion risk)")
124
+ return false
125
+ end
126
+ message("Start running macro [#{name}]")
127
+ if @recorded_macros.has_key?(name)
128
+ @last_macro = name
129
+ end
130
+ acts = @recorded_macros[name]
131
+ if acts.kind_of?(Array) and acts.any?
132
+ set_last_command({ method: $macro.method("run_macro"), params: [name] })
133
+ #
134
+ # Ripl.start :binding => binding
135
+ for a in acts
136
+ ret = exec_action(a)
137
+ puts ret
138
+ if ret == false
139
+ message("Error while running macro")
140
+ break
141
+ end
142
+ end
143
+ # eval_str = m.join(";")
144
+ # debug(eval_str)
145
+ # eval(eval_str)
146
+ end
147
+ buf.set_pos(buf.pos)
148
+ end
149
+
150
+ def save_macro(name)
151
+ m = @recorded_macros[name]
152
+ return if !(m.kind_of?(Array) and m.any?)
153
+ contents = m.join(";")
154
+ dot_dir = File.expand_path("~/.vimamsa")
155
+ Dir.mkdir(dot_dir) unless File.exist?(dot_dir)
156
+ save_fn = "#{dot_dir}/macro_#{name}.rb"
157
+
158
+ Thread.new {
159
+ File.open(save_fn, "w+") do |io|
160
+ #io.set_encoding(self.encoding)
161
+
162
+ begin
163
+ io.write(contents)
164
+ rescue Encoding::UndefinedConversionError => ex
165
+ # this might happen when trying to save UTF-8 as US-ASCII
166
+ # so just warn, try to save as UTF-8 instead.
167
+ warn("Saving as UTF-8 because of: #{ex.class}: #{ex}")
168
+ io.rewind
169
+
170
+ io.set_encoding(Encoding::UTF_8)
171
+ io.write(contents)
172
+ end
173
+ end
174
+ sleep 3 #TODO:remove
175
+ }
176
+ end
177
+ end
@@ -0,0 +1,71 @@
1
+ #scriptdir=File.expand_path(File.dirname(__FILE__))
2
+ $:.unshift File.dirname(__FILE__) + "/lib"
3
+
4
+ # require 'benchmark/ips'
5
+
6
+ # load "vendor/ver/lib/ver/vendor/textpow.rb"
7
+ # load "vendor/ver/lib/ver/syntax/detector.rb"
8
+ # load "vendor/ver/config/detect.rb"
9
+
10
+ require "differ"
11
+ module Differ
12
+ class Diff
13
+ def get_raw_array()
14
+ return @raw
15
+ end
16
+ end
17
+ end
18
+
19
+ Encoding.default_external = Encoding::UTF_8
20
+ Encoding.default_internal = Encoding::UTF_8
21
+
22
+ # Globals
23
+ $command_history = []
24
+ $clipboard = []
25
+ $register = Hash.new("")
26
+ $cnf = {}
27
+ $search_dirs = []
28
+ $errors = []
29
+
30
+ $cur_register = "a"
31
+ $input_char_call_func = nil
32
+ $debuginfo = {}
33
+
34
+ $jump_sequence = []
35
+
36
+ $debug = false
37
+ $experimental = false
38
+
39
+ # Return currently active buffer
40
+ def buf()
41
+ return $buffer
42
+ end
43
+
44
+ def bufs()
45
+ return $buffers
46
+ end
47
+
48
+ def buflist()
49
+ return $buffers
50
+ end
51
+
52
+ require "vimamsa/editor.rb"
53
+
54
+ # load "qt_funcs.rb"
55
+
56
+ $vma = Editor.new
57
+ def vma()
58
+ return $vma
59
+ end
60
+
61
+ # c_startup
62
+ # run_random_jump_test
63
+ # main_loop
64
+
65
+ # debug("END")
66
+
67
+
68
+
69
+
70
+
71
+
@@ -0,0 +1,1014 @@
1
+ require "rbvma/version"
2
+ require "gtk3"
3
+ require "gtksourceview3"
4
+ #require "gtksourceview4"
5
+ require "ripl"
6
+ require "fileutils"
7
+ require "pathname"
8
+ require "date"
9
+ require "ripl/multi_line"
10
+ require "json"
11
+
12
+ puts "INIT rbvma"
13
+
14
+ require "vimamsa/util"
15
+ # require "rbvma/rbvma"
16
+ require "vimamsa/main" #
17
+ require "vimamsa/key_binding_tree" #
18
+ require "vimamsa/actions" #
19
+ require "vimamsa/macro" #
20
+ require "vimamsa/buffer" #
21
+ require "vimamsa/debug" #
22
+ require "vimamsa/constants"
23
+ require "vimamsa/easy_jump"
24
+ require "vimamsa/hook"
25
+ require "vimamsa/search"
26
+ require "vimamsa/search_replace"
27
+ require "vimamsa/buffer_list"
28
+ require "vimamsa/file_finder"
29
+ require "vimamsa/hyper_plain_text"
30
+ require "vimamsa/ack"
31
+ require "vimamsa/encrypt"
32
+ require "vimamsa/file_manager"
33
+
34
+ # load "vendor/ver/lib/ver/vendor/textpow.rb"
35
+ # load "vendor/ver/lib/ver/syntax/detector.rb"
36
+ # load "vendor/ver/config/detect.rb"
37
+
38
+ $vma = Editor.new
39
+
40
+ def vma()
41
+ return $vma
42
+ end
43
+
44
+ $idle_scroll_to_mark = false
45
+
46
+ def idle_func
47
+ # puts "IDLEFUNC"
48
+ if $idle_scroll_to_mark
49
+ # Ripl.start :binding => binding
50
+ # $view.get_visible_rect
51
+ vr = $view.visible_rect
52
+
53
+ # iter = b.get_iter_at(:offset => i)
54
+
55
+ b = $view.buffer
56
+ iter = b.get_iter_at(:offset => b.cursor_position)
57
+ iterxy = $view.get_iter_location(iter)
58
+ # puts "ITERXY" + iterxy.inspect
59
+ # Ripl.start :binding => binding
60
+
61
+ intr = iterxy.intersect(vr)
62
+ if intr.nil?
63
+ $view.set_cursor_pos($view.buffer.cursor_position)
64
+ else
65
+ $idle_scroll_to_mark = false
66
+ end
67
+
68
+ sleep(0.1)
69
+ end
70
+ sleep(0.01)
71
+ return true
72
+ end
73
+
74
+ # qt_select_update_window(l, $select_keys.collect { |x| x.upcase },
75
+ # "gui_find_macro_select_callback",
76
+ # "gui_find_macro_update_callback")
77
+ class SelectUpdateWindow
78
+ COLUMN_JUMP_KEY = 0
79
+ COLUMN_DESCRIPTION = 1
80
+
81
+ def update_item_list(item_list)
82
+ # puts item_list.inspect
83
+ # Ripl.start :binding => binding
84
+ @model.clear
85
+ for item in item_list
86
+ iter = @model.append
87
+ v = ["", item[0]]
88
+ puts v.inspect
89
+ iter.set_values(v)
90
+ end
91
+
92
+ set_selected_row(0)
93
+ end
94
+
95
+ def set_selected_row(rownum)
96
+ rownum = 0 if rownum < 0
97
+ @selected_row = rownum
98
+
99
+ if @model.count > 0
100
+ path = Gtk::TreePath.new(@selected_row.to_s)
101
+ iter = @model.get_iter(path)
102
+ @tv.selection.select_iter(iter)
103
+ end
104
+ end
105
+
106
+ def initialize(main_window, item_list, jump_keys, select_callback, update_callback)
107
+ @window = Gtk::Window.new(:toplevel)
108
+ # @window.screen = main_window.screen
109
+ @window.title = "List Store"
110
+
111
+ @selected_row = 0
112
+
113
+ puts item_list.inspect
114
+ @update_callback = method(update_callback)
115
+ @select_callback = method(select_callback)
116
+ # puts @update_callback_m.call("").inspect
117
+
118
+ vbox = Gtk::Box.new(:vertical, 8)
119
+ vbox.margin = 8
120
+ @window.add(vbox)
121
+
122
+ @entry = Gtk::SearchEntry.new
123
+ @entry.width_chars = 45
124
+ container = Gtk::Box.new(:horizontal, 10)
125
+ # container.halign = :start
126
+ container.halign = :center
127
+ container.pack_start(@entry,
128
+ :expand => false, :fill => false, :padding => 0)
129
+
130
+ # create tree view
131
+ @model = Gtk::ListStore.new(String, String)
132
+ treeview = Gtk::TreeView.new(@model)
133
+ treeview.search_column = COLUMN_DESCRIPTION
134
+ @tv = treeview
135
+ # item_list = @update_callback.call("")
136
+ update_item_list(item_list)
137
+
138
+ # Ripl.start :binding => binding
139
+ @window.signal_connect("key-press-event") do |_widget, event|
140
+ # puts "KEYPRESS 1"
141
+ @entry.handle_event(event)
142
+ end
143
+
144
+ @entry.signal_connect("key_press_event") do |widget, event|
145
+ # puts "KEYPRESS 2"
146
+ if event.keyval == Gdk::Keyval::KEY_Down
147
+ puts "DOWN"
148
+ set_selected_row(@selected_row + 1)
149
+ # fixed = iter[COLUMN_FIXED]
150
+
151
+ true
152
+ elsif event.keyval == Gdk::Keyval::KEY_Up
153
+ set_selected_row(@selected_row - 1)
154
+ puts "UP"
155
+ true
156
+ elsif event.keyval == Gdk::Keyval::KEY_Return
157
+ path = Gtk::TreePath.new(@selected_row.to_s)
158
+ iter = @model.get_iter(path)
159
+ ret = iter[1]
160
+ @select_callback.call(ret, @selected_row)
161
+ @window.destroy
162
+ # puts iter[1].inspect
163
+ true
164
+ elsif event.keyval == Gdk::Keyval::KEY_Escape
165
+ @window.destroy
166
+ true
167
+ else
168
+ false
169
+ end
170
+ end
171
+
172
+ @entry.signal_connect("search-changed") do |widget|
173
+ puts "search changed: #{widget.text || ""}"
174
+ item_list = @update_callback.call(widget.text)
175
+ update_item_list(item_list)
176
+ # label.text = widget.text || ""
177
+ end
178
+ @entry.signal_connect("changed") { puts "[changed] " }
179
+ @entry.signal_connect("next-match") { puts "[next-match] " }
180
+
181
+ label = Gtk::Label.new(<<-EOF)
182
+
183
+ Search:
184
+ EOF
185
+ vbox.pack_start(label, :expand => false, :fill => false, :padding => 0)
186
+
187
+ vbox.pack_start(container, :expand => false, :fill => false, :padding => 0)
188
+ sw = Gtk::ScrolledWindow.new(nil, nil)
189
+ sw.shadow_type = :etched_in
190
+ sw.set_policy(:never, :automatic)
191
+ vbox.pack_start(sw, :expand => true, :fill => true, :padding => 0)
192
+
193
+ sw.add(treeview)
194
+
195
+ renderer = Gtk::CellRendererText.new
196
+ column = Gtk::TreeViewColumn.new("JMP",
197
+ renderer,
198
+ "text" => COLUMN_JUMP_KEY)
199
+ column.sort_column_id = COLUMN_JUMP_KEY
200
+ treeview.append_column(column)
201
+
202
+ renderer = Gtk::CellRendererText.new
203
+ column = Gtk::TreeViewColumn.new("Description",
204
+ renderer,
205
+ "text" => COLUMN_DESCRIPTION)
206
+ column.sort_column_id = COLUMN_DESCRIPTION
207
+ treeview.append_column(column)
208
+
209
+ @window.set_default_size(280, 500)
210
+ puts "SelectUpdateWindow"
211
+ end
212
+
213
+ def run
214
+ if !@window.visible?
215
+ @window.show_all
216
+ # add_spinner
217
+ else
218
+ @window.destroy
219
+ # GLib::Source.remove(@tiemout) unless @timeout.zero?
220
+ @timeout = 0
221
+ end
222
+ @window
223
+ end
224
+ end
225
+
226
+ def center_on_current_line()
227
+ b = $view.buffer
228
+ iter = b.get_iter_at(:offset => b.cursor_position)
229
+ within_margin = 0.0 #margin as a [0.0,0.5) fraction of screen size
230
+ use_align = true
231
+ xalign = 0.0 #0.0=top 1.0=bottom, 0.5=center
232
+ yalign = 0.5
233
+ $view.scroll_to_iter(iter, within_margin, use_align, xalign, yalign)
234
+ end
235
+
236
+ def qt_select_update_window(item_list, jump_keys, select_callback, update_callback)
237
+ $selup = SelectUpdateWindow.new(nil, item_list, jump_keys, select_callback, update_callback)
238
+ $selup.run
239
+ end
240
+
241
+ # ~/Drive/code/ruby-gnome/gtk3/sample/gtk-demo/search_entry2.rb
242
+ # ~/Drive/code/ruby-gnome/gtk3/sample/gtk-demo/list_store.rb
243
+
244
+ def qt_open_file_dialog(dirpath)
245
+ dialog = Gtk::FileChooserDialog.new(:title => "Open file",
246
+ :action => :open,
247
+ :buttons => [[Gtk::Stock::OPEN, :accept],
248
+ [Gtk::Stock::CANCEL, :cancel]])
249
+ dialog.set_current_folder(dirpath)
250
+
251
+ dialog.signal_connect("response") do |dialog, response_id|
252
+ if response_id == Gtk::ResponseType::ACCEPT
253
+ open_new_file(dialog.filename)
254
+ # puts "uri = #{dialog.uri}"
255
+ end
256
+ dialog.destroy
257
+ end
258
+ dialog.run
259
+ end
260
+
261
+ def qt_file_saveas(dirpath)
262
+ dialog = Gtk::FileChooserDialog.new(:title => "Save as",
263
+ :action => :save,
264
+ :buttons => [[Gtk::Stock::SAVE, :accept],
265
+ [Gtk::Stock::CANCEL, :cancel]])
266
+ dialog.set_current_folder(dirpath)
267
+ dialog.signal_connect("response") do |dialog, response_id|
268
+ if response_id == Gtk::ResponseType::ACCEPT
269
+ file_saveas(dialog.filename)
270
+ end
271
+ dialog.destroy
272
+ end
273
+
274
+ dialog.run
275
+ end
276
+
277
+ def qt_create_buffer(id)
278
+ puts "qt_create_buffer(#{id})"
279
+ buf1 = GtkSource::Buffer.new()
280
+ view = VSourceView.new()
281
+
282
+ view.set_highlight_current_line(true)
283
+ view.set_show_line_numbers(true)
284
+ view.set_buffer(buf1)
285
+
286
+ ssm = GtkSource::StyleSchemeManager.new
287
+ ssm.set_search_path(ssm.search_path << ppath("styles/"))
288
+ # sty = ssm.get_scheme("dark")
289
+ sty = ssm.get_scheme("molokai_edit")
290
+ # puts ssm.scheme_ids
291
+
292
+ view.buffer.highlight_matching_brackets = true
293
+ view.buffer.style_scheme = sty
294
+
295
+ provider = Gtk::CssProvider.new
296
+ provider.load(data: "textview { font-family: Monospace; font-size: 11pt; }")
297
+ # provider.load(data: "textview { font-family: Arial; font-size: 12pt; }")
298
+ view.style_context.add_provider(provider)
299
+ view.wrap_mode = :char
300
+
301
+ $vmag.buffers[id] = view
302
+ end
303
+
304
+ def gui_set_file_lang(id, lname)
305
+ view = $vmag.buffers[id]
306
+ lm = GtkSource::LanguageManager.new
307
+ lang = nil
308
+ lm.set_search_path(lm.search_path << ppath("lang/"))
309
+ lang = lm.get_language(lname)
310
+
311
+ view.buffer.language = lang
312
+ view.buffer.highlight_syntax = true
313
+ end
314
+
315
+ def qt_process_deltas
316
+ end
317
+
318
+ def qt_add_image(imgpath, pos)
319
+ end
320
+
321
+ def qt_process_deltas
322
+ end
323
+
324
+ def qt_process_events
325
+ end
326
+
327
+ def qt_select_window_close(arg = nil)
328
+ end
329
+
330
+ def set_window_title(str)
331
+ unimplemented
332
+ end
333
+
334
+ def render_text(tmpbuf, pos, selection_start, reset)
335
+ unimplemented
336
+ end
337
+
338
+ def qt_set_buffer_contents(id, txt)
339
+ # $vbuf.set_text(txt)
340
+ puts "qt_set_buffer_contents(#{id}, txt)"
341
+
342
+ $vmag.buffers[id].buffer.set_text(txt)
343
+ end
344
+
345
+ def qt_set_cursor_pos(id, pos)
346
+ $view.set_cursor_pos(pos)
347
+ # Ripl.start :binding => binding
348
+ end
349
+
350
+ def qt_set_selection_start(id, selection_start)
351
+ end
352
+
353
+ def qt_set_current_buffer(id)
354
+ view = $vmag.buffers[id]
355
+ puts "qt_set_current_buffer(#{id}), view=#{view}"
356
+ buf1 = view.buffer
357
+ $vmag.view = view
358
+ $vmag.buf1 = buf1
359
+ $view = view
360
+ $vbuf = buf1
361
+
362
+ $vmag.sw.remove($vmag.sw.child) if !$vmag.sw.child.nil?
363
+ $vmag.sw.add(view)
364
+
365
+ view.grab_focus
366
+ #view.set_focus(10)
367
+ view.set_cursor_visible(true)
368
+ #view.move_cursor(1, 1, false)
369
+ view.place_cursor_onscreen
370
+
371
+ #TODO:
372
+ # itr = view.buffer.get_iter_at(:offset => 0)
373
+ # view.buffer.place_cursor(itr)
374
+
375
+ wtitle = ""
376
+ wtitle = buf.fname if !buf.fname.nil?
377
+ $vmag.window.title = wtitle
378
+ $vmag.sw.show_all
379
+ end
380
+
381
+ def unimplemented
382
+ puts "unimplemented"
383
+ end
384
+
385
+ def center_where_cursor
386
+ unimplemented
387
+ end
388
+
389
+ def paste_system_clipboard()
390
+ # clipboard = $vmag.window.get_clipboard(Gdk::Selection::CLIPBOARD)
391
+ utf8_string = Gdk::Atom.intern("UTF8_STRING")
392
+ # x = clipboard.request_contents(utf8_string)
393
+
394
+ widget = Gtk::Invisible.new
395
+ clipboard = Gtk::Clipboard.get_default($vmag.window.display)
396
+ received_text = ""
397
+
398
+ target_string = Gdk::Selection::TARGET_STRING
399
+ ti = clipboard.request_contents(target_string)
400
+
401
+ # clipboard.request_contents(target_string) do |_clipboard, selection_data|
402
+ # received_text = selection_data.text
403
+ # puts "received_text=#{received_text}"
404
+ # end
405
+ if clipboard.wait_is_text_available?
406
+ received_text = clipboard.wait_for_text
407
+ end
408
+
409
+ if received_text != "" and !received_text.nil?
410
+ max_clipboard_items = 100
411
+ if received_text != $clipboard[-1]
412
+ #TODO: HACK
413
+ $paste_lines = false
414
+ end
415
+ $clipboard << received_text
416
+ # puts $clipboard[-1]
417
+ $clipboard = $clipboard[-([$clipboard.size, max_clipboard_items].min)..-1]
418
+ end
419
+ return received_text
420
+ end
421
+
422
+ def set_system_clipboard(arg)
423
+ # return if arg.class != String
424
+ # return if s.size < 1
425
+ # utf8_string = Gdk::Atom.intern("UTF8_STRING")
426
+ widget = Gtk::Invisible.new
427
+ clipboard = Gtk::Clipboard.get_default($vmag.window.display)
428
+ clipboard.text = arg
429
+ end
430
+
431
+ def get_visible_area()
432
+ view = $view
433
+ vr = view.visible_rect
434
+ startpos = view.get_iter_at_position_raw(vr.x, vr.y)[1].offset
435
+ endpos = view.get_iter_at_position_raw(vr.x + vr.width, vr.y + vr.height)[1].offset
436
+ return [startpos, endpos]
437
+ end
438
+
439
+ def page_up
440
+ $view.signal_emit("move-cursor", Gtk::MovementStep.new(:PAGES), -1, false)
441
+ return true
442
+ end
443
+
444
+ def page_down
445
+ $view.signal_emit("move-cursor", Gtk::MovementStep.new(:PAGES), 1, false)
446
+ return true
447
+ end
448
+
449
+ # module Rbvma
450
+ # # Your code goes here...
451
+ # def foo
452
+ # puts "BAR"
453
+ # end
454
+ # end
455
+ $debug = true
456
+
457
+ def scan_indexes(txt, regex)
458
+ # indexes = txt.enum_for(:scan, regex).map { Regexp.last_match.begin(0) + 1 }
459
+ indexes = txt.enum_for(:scan, regex).map { Regexp.last_match.begin(0) }
460
+ return indexes
461
+ end
462
+
463
+ $update_cursor = false
464
+
465
+ class VSourceView < GtkSource::View
466
+ def initialize(title = nil)
467
+ # super(:toplevel)
468
+ super()
469
+ puts "vsource init"
470
+ @last_keyval = nil
471
+ @last_event = [nil, nil]
472
+
473
+ signal_connect("key_press_event") do |widget, event|
474
+ handle_key_event(event, :key_press_event)
475
+ true
476
+ end
477
+
478
+ signal_connect("key_release_event") do |widget, event|
479
+ handle_key_event(event, :key_release_event)
480
+ true
481
+ end
482
+
483
+ signal_connect("move-cursor") do |widget, event|
484
+ $update_cursor = true
485
+ false
486
+ end
487
+
488
+ signal_connect "button-release-event" do |widget, event|
489
+ $buffer.set_pos(buffer.cursor_position)
490
+ false
491
+ end
492
+ @curpos_mark = nil
493
+ end
494
+
495
+ def handle_key_event(event, sig)
496
+ if $update_cursor
497
+ curpos = buffer.cursor_position
498
+ puts "MOVE CURSOR: #{curpos}"
499
+ buf.set_pos(curpos)
500
+ $update_cursor = false
501
+ end
502
+ puts $view.visible_rect.inspect
503
+
504
+ puts "key event"
505
+ puts event
506
+
507
+ key_name = event.string
508
+ if event.state.control_mask?
509
+ key_name = Gdk::Keyval.to_name(event.keyval)
510
+ # Gdk::Keyval.to_name()
511
+ end
512
+
513
+ keyval_trans = {}
514
+ keyval_trans[Gdk::Keyval::KEY_Control_L] = "ctrl"
515
+ keyval_trans[Gdk::Keyval::KEY_Control_R] = "ctrl"
516
+
517
+ keyval_trans[Gdk::Keyval::KEY_Escape] = "esc"
518
+
519
+ keyval_trans[Gdk::Keyval::KEY_Return] = "enter"
520
+ keyval_trans[Gdk::Keyval::KEY_ISO_Enter] = "enter"
521
+ keyval_trans[Gdk::Keyval::KEY_KP_Enter] = "enter"
522
+ keyval_trans[Gdk::Keyval::KEY_Alt_L] = "alt"
523
+ keyval_trans[Gdk::Keyval::KEY_Alt_R] = "alt"
524
+
525
+ keyval_trans[Gdk::Keyval::KEY_BackSpace] = "backspace"
526
+ keyval_trans[Gdk::Keyval::KEY_KP_Page_Down] = "pagedown"
527
+ keyval_trans[Gdk::Keyval::KEY_KP_Page_Up] = "pageup"
528
+ keyval_trans[Gdk::Keyval::KEY_Page_Down] = "pagedown"
529
+ keyval_trans[Gdk::Keyval::KEY_Page_Up] = "pageup"
530
+ keyval_trans[Gdk::Keyval::KEY_Left] = "left"
531
+ keyval_trans[Gdk::Keyval::KEY_Right] = "right"
532
+ keyval_trans[Gdk::Keyval::KEY_Down] = "down"
533
+ keyval_trans[Gdk::Keyval::KEY_Up] = "up"
534
+ keyval_trans[Gdk::Keyval::KEY_space] = "space"
535
+
536
+ keyval_trans[Gdk::Keyval::KEY_Shift_L] = "shift"
537
+ keyval_trans[Gdk::Keyval::KEY_Shift_R] = "shift"
538
+ keyval_trans[Gdk::Keyval::KEY_Tab] = "tab"
539
+
540
+ key_trans = {}
541
+ key_trans["\e"] = "esc"
542
+ tk = keyval_trans[event.keyval]
543
+ key_name = tk if !tk.nil?
544
+
545
+ key_str_parts = []
546
+ key_str_parts << "ctrl" if event.state.control_mask? and key_name != "ctrl"
547
+ key_str_parts << "alt" if event.state.mod1_mask? and key_name != "alt"
548
+
549
+ key_str_parts << key_name
550
+ key_str = key_str_parts.join("-")
551
+ keynfo = { :key_str => key_str, :key_name => key_name, :keyval => event.keyval }
552
+ puts keynfo.inspect
553
+ # $kbd.match_key_conf(key_str, nil, :key_press)
554
+ # puts "key_str=#{key_str} key_"
555
+
556
+ if key_str != "" # or prefixed_key_str != ""
557
+ if sig == :key_release_event and event.keyval == @last_keyval
558
+ $kbd.match_key_conf(key_str + "!", nil, :key_release)
559
+ @last_event = [event, :key_release]
560
+ elsif sig == :key_press_event
561
+ $kbd.match_key_conf(key_str, nil, :key_press)
562
+ @last_event = [event, key_str, :key_press]
563
+ end
564
+ @last_keyval = event.keyval #TODO: outside if?
565
+ end
566
+
567
+ handle_deltas
568
+
569
+ # set_focus(5)
570
+ # false
571
+
572
+ end
573
+
574
+ def pos_to_coord(i)
575
+ b = buffer
576
+ iter = b.get_iter_at(:offset => i)
577
+ iterxy = get_iter_location(iter)
578
+ winw = parent_window.width
579
+ view_width = visible_rect.width
580
+ gutter_width = winw - view_width
581
+
582
+ x = iterxy.x + gutter_width
583
+ y = iterxy.y
584
+
585
+ # buffer_to_window_coords(Gtk::TextWindowType::TEXT, iterxy.x, iterxy.y).inspect
586
+ # puts buffer_to_window_coords(Gtk::TextWindowType::TEXT, x, y).inspect
587
+ (x, y) = buffer_to_window_coords(Gtk::TextWindowType::TEXT, x, y)
588
+ # Ripl.start :binding => binding
589
+
590
+ return [x, y]
591
+ end
592
+
593
+ def handle_deltas()
594
+ any_change = false
595
+ while d = buf.deltas.shift
596
+ any_change = true
597
+ pos = d[0]
598
+ op = d[1]
599
+ num = d[2]
600
+ txt = d[3]
601
+ if op == DELETE
602
+ startiter = buffer.get_iter_at(:offset => pos)
603
+ enditer = buffer.get_iter_at(:offset => pos + num)
604
+ buffer.delete(startiter, enditer)
605
+ elsif op == INSERT
606
+ startiter = buffer.get_iter_at(:offset => pos)
607
+ buffer.insert(startiter, txt)
608
+ end
609
+ end
610
+ if any_change
611
+ qt_set_cursor_pos($buffer.id, $buffer.pos) #TODO: only when necessary
612
+ end
613
+
614
+ # sanity_check #TODO
615
+ end
616
+
617
+ def sanity_check()
618
+ a = buffer.text
619
+ b = buf.to_s
620
+ # puts "===================="
621
+ # puts a.lines[0..10].join()
622
+ # puts "===================="
623
+ # puts b.lines[0..10].join()
624
+ # puts "===================="
625
+ if a == b
626
+ puts "Buffers match"
627
+ else
628
+ puts "ERROR: Buffer's don't match."
629
+ end
630
+ end
631
+
632
+ def set_cursor_pos(pos)
633
+ # return
634
+ itr = buffer.get_iter_at(:offset => pos)
635
+ itr2 = buffer.get_iter_at(:offset => pos + 1)
636
+ buffer.place_cursor(itr)
637
+
638
+ # $view.signal_emit("extend-selection", Gtk::MovementStep.new(:PAGES), -1, false)
639
+
640
+ within_margin = 0.075 #margin as a [0.0,0.5) fraction of screen size
641
+ use_align = false
642
+ xalign = 0.5 #0.0=top 1.0=bottom, 0.5=center
643
+ yalign = 0.5
644
+
645
+ if @curpos_mark.nil?
646
+ @curpos_mark = buffer.create_mark("cursor", itr, false)
647
+ else
648
+ buffer.move_mark(@curpos_mark, itr)
649
+ end
650
+ scroll_to_mark(@curpos_mark, within_margin, use_align, xalign, yalign)
651
+ $idle_scroll_to_mark = true
652
+ ensure_cursor_visible
653
+
654
+ # scroll_to_iter(itr, within_margin, use_align, xalign, yalign)
655
+
656
+ # $view.signal_emit("extend-selection", Gtk::TextExtendSelection.new, itr,itr,itr2)
657
+ # Ripl.start :binding => binding
658
+ draw_cursor
659
+
660
+ return true
661
+ end
662
+
663
+ def cursor_visible_idle_func
664
+ puts "cursor_visible_idle_func"
665
+ # From https://picheta.me/articles/2013/08/gtk-plus--a-method-to-guarantee-scrolling.html
666
+ # vr = visible_rect
667
+
668
+ # b = $view.buffer
669
+ # iter = buffer.get_iter_at(:offset => buffer.cursor_position)
670
+ # iterxy = get_iter_location(iter)
671
+
672
+ sleep(0.01)
673
+ # intr = iterxy.intersect(vr)
674
+ if is_cursor_visible == false
675
+ # set_cursor_pos(buffer.cursor_position)
676
+
677
+ itr = buffer.get_iter_at(:offset => buffer.cursor_position)
678
+
679
+ within_margin = 0.075 #margin as a [0.0,0.5) fraction of screen size
680
+ use_align = false
681
+ xalign = 0.5 #0.0=top 1.0=bottom, 0.5=center
682
+ yalign = 0.5
683
+
684
+ scroll_to_iter(itr, within_margin, use_align, xalign, yalign)
685
+
686
+ # return true # Call this func again
687
+ else
688
+ return false # Don't call this idle func again
689
+ end
690
+ end
691
+
692
+ def is_cursor_visible
693
+ vr = visible_rect
694
+ iter = buffer.get_iter_at(:offset => buffer.cursor_position)
695
+ iterxy = get_iter_location(iter)
696
+ iterxy.width = 1 if iterxy.width == 0
697
+ iterxy.height = 1 if iterxy.height == 0
698
+
699
+ intr = iterxy.intersect(vr)
700
+ if intr.nil?
701
+ puts iterxy.inspect
702
+ puts vr.inspect
703
+ # Ripl.start :binding => binding
704
+
705
+ # exit!
706
+ return false
707
+ else
708
+ return true
709
+ end
710
+ end
711
+
712
+ def ensure_cursor_visible
713
+ if is_cursor_visible == false
714
+ Thread.new {
715
+ sleep 0.01
716
+ GLib::Idle.add(proc { cursor_visible_idle_func })
717
+ }
718
+ end
719
+ end
720
+
721
+ def draw_cursor
722
+ if is_command_mode
723
+ itr = buffer.get_iter_at(:offset => buf.pos)
724
+ itr2 = buffer.get_iter_at(:offset => buf.pos + 1)
725
+ $view.buffer.select_range(itr, itr2)
726
+ elsif buf.visual_mode?
727
+ puts "VISUAL MODE"
728
+ (_start, _end) = buf.get_visual_mode_range2
729
+ puts "#{_start}, #{_end}"
730
+ itr = buffer.get_iter_at(:offset => _start)
731
+ itr2 = buffer.get_iter_at(:offset => _end + 1)
732
+ $view.buffer.select_range(itr, itr2)
733
+ else # Insert mode
734
+ itr = buffer.get_iter_at(:offset => buf.pos)
735
+ $view.buffer.select_range(itr, itr)
736
+ puts "INSERT MODE"
737
+ end
738
+ end
739
+
740
+ # def quit
741
+ # destroy
742
+ # true
743
+ # end
744
+ end
745
+
746
+ class VMAg
747
+ attr_accessor :buffers, :sw, :view, :buf1, :window
748
+
749
+ VERSION = "1.0"
750
+
751
+ HEART = "♥"
752
+ RADIUS = 150
753
+ N_WORDS = 5
754
+ FONT = "Serif 18"
755
+ TEXT = "I ♥ GTK+"
756
+
757
+ def initialize()
758
+ @show_overlay = true
759
+ @da = nil
760
+ @buffers = {}
761
+ @view = nil
762
+ @buf1 = nil
763
+ end
764
+
765
+ def run
766
+ init_window
767
+ # init_rtext
768
+ Gtk.main
769
+ end
770
+
771
+ def start_overlay_draw()
772
+ @da = Gtk::Fixed.new
773
+ @overlay.add_overlay(@da)
774
+ @overlay.set_overlay_pass_through(@da, true)
775
+ end
776
+
777
+ def clear_overlay()
778
+ if @da != nil
779
+ @overlay.remove(@da)
780
+ end
781
+ end
782
+
783
+ def overlay_draw_text(text, textpos)
784
+ # puts "overlay_draw_text #{[x,y]}"
785
+ (x, y) = @view.pos_to_coord(textpos)
786
+ # puts "overlay_draw_text #{[x,y]}"
787
+ label = Gtk::Label.new("<span background='#00000088' foreground='#ff0000' weight='ultrabold'>#{text}</span>")
788
+ label.use_markup = true
789
+ @da.put(label, x, y)
790
+ end
791
+
792
+ def end_overlay_draw()
793
+ @da.show_all
794
+ end
795
+
796
+ def toggle_overlay
797
+ @show_overlay = @show_overlay ^ 1
798
+ if !@show_overlay
799
+ if @da != nil
800
+ @overlay.remove(@da)
801
+ end
802
+ return
803
+ else
804
+ @da = Gtk::Fixed.new
805
+ @overlay.add_overlay(@da)
806
+ @overlay.set_overlay_pass_through(@da, true)
807
+ end
808
+
809
+ (startpos, endpos) = get_visible_area
810
+ s = @view.buffer.text
811
+ wpos = s.enum_for(:scan, /\W(\w)/).map { Regexp.last_match.begin(0) + 1 }
812
+ wpos = wpos[0..130]
813
+
814
+ # vr = @view.visible_rect
815
+ # # gtk_text_view_get_line_at_y
816
+ # # gtk_text_view_get_iter_at_position
817
+ # gtk_text_view_get_iter_at_position(vr.
818
+ # istart = @view.get_iter_at_position(vr.x,vr.y)
819
+ # istart = @view.get_iter_at_y(vr.y)
820
+ # startpos = @view.get_iter_at_position_raw(vr.x,vr.y)[1].offset
821
+ # endpos = @view.get_iter_at_position_raw(vr.x+vr.width,vr.y+vr.height)[1].offset
822
+ # puts "startpos,endpos:#{[startpos, endpos]}"
823
+
824
+ da = @da
825
+ if false
826
+ da.signal_connect "draw" do |widget, cr|
827
+ cr.save
828
+ for pos in wpos
829
+ (x, y) = @view.pos_to_coord(pos)
830
+
831
+ layout = da.create_pango_layout("XY")
832
+ desc = Pango::FontDescription.new("sans bold 11")
833
+ layout.font_description = desc
834
+
835
+ cr.move_to(x, y)
836
+ # cr.move_to(gutter_width, 300)
837
+ cr.pango_layout_path(layout)
838
+
839
+ cr.set_source_rgb(1.0, 0.0, 0.0)
840
+ cr.fill_preserve
841
+ end
842
+ cr.restore
843
+ false # = draw other
844
+ # true # = Don't draw others
845
+ end
846
+ end
847
+
848
+ for pos in wpos
849
+ (x, y) = @view.pos_to_coord(pos)
850
+ # da.put(Gtk::Label.new("AB"), x, y)
851
+ label = Gtk::Label.new("<span background='#00000088' foreground='#ff0000' weight='ultrabold'>AB</span>")
852
+ label.use_markup = true
853
+ da.put(label, x, y)
854
+ end
855
+
856
+ # puts @view.pos_to_coord(300).inspect
857
+
858
+ @da.show_all
859
+ end
860
+
861
+ def init_keybindings
862
+ $kbd = KeyBindingTree.new()
863
+ $kbd.add_mode("C", :command)
864
+ $kbd.add_mode("I", :insert)
865
+ $kbd.add_mode("V", :visual)
866
+ $kbd.add_mode("M", :minibuffer)
867
+ $kbd.add_mode("R", :readchar)
868
+ $kbd.add_mode("B", :browse)
869
+ $kbd.set_default_mode(:command)
870
+ require "default_key_bindings"
871
+
872
+ $macro = Macro.new
873
+
874
+ # bindkey "VC j", "buf.move(FORWARD_LINE)"
875
+ bindkey "VC j", "puts('j_key_action')"
876
+ bindkey "VC ctrl-j", "puts('ctrl_j_key_action')"
877
+
878
+ bindkey "VC l", "buf.move(FORWARD_CHAR)"
879
+ bindkey "C x", "buf.delete(CURRENT_CHAR_FORWARD)"
880
+ # bindkey "C r <char>", "buf.replace_with_char(<char>)"
881
+ bindkey "I space", 'buf.insert_txt(" ")'
882
+
883
+ bindkey "VC l", "buf.move(FORWARD_CHAR)"
884
+ bindkey "VC j", "buf.move(FORWARD_LINE)"
885
+ bindkey "VC k", "buf.move(BACKWARD_LINE)"
886
+ bindkey "VC h", "buf.move(BACKWARD_CHAR)"
887
+ end
888
+
889
+ def handle_deltas()
890
+ while d = buf.deltas.shift
891
+ pos = d[0]
892
+ op = d[1]
893
+ num = d[2]
894
+ txt = d[3]
895
+ if op == DELETE
896
+ startiter = @buf1.get_iter_at(:offset => pos)
897
+ enditer = @buf1.get_iter_at(:offset => pos + num)
898
+ @buf1.delete(startiter, enditer)
899
+ elsif op == INSERT
900
+ startiter = @buf1.get_iter_at(:offset => pos)
901
+ @buf1.insert(startiter, txt)
902
+ end
903
+ end
904
+ end
905
+
906
+ def add_to_minibuf(msg)
907
+ startiter = @minibuf.buffer.get_iter_at(:offset => 0)
908
+ @minibuf.buffer.insert(startiter, "#{msg}\n")
909
+ @minibuf.signal_emit("move-cursor", Gtk::MovementStep.new(:PAGES), -1, false)
910
+ end
911
+
912
+ def init_minibuffer()
913
+ # Init minibuffer
914
+ sw = Gtk::ScrolledWindow.new
915
+ sw.set_policy(:automatic, :automatic)
916
+ overlay = Gtk::Overlay.new
917
+ overlay.add(sw)
918
+ @vpaned.pack2(overlay, :resize => false)
919
+ # overlay.set_size_request(-1, 50)
920
+ # $ovrl = overlay
921
+ # $ovrl.set_size_request(-1, 30)
922
+ $sw2 = sw
923
+ sw.set_size_request(-1, 12)
924
+
925
+ view = VSourceView.new()
926
+ view.set_highlight_current_line(false)
927
+ view.set_show_line_numbers(false)
928
+ # view.set_buffer(buf1)
929
+ ssm = GtkSource::StyleSchemeManager.new
930
+ ssm.set_search_path(ssm.search_path << ppath("styles/"))
931
+ sty = ssm.get_scheme("molokai_edit")
932
+ view.buffer.highlight_matching_brackets = false
933
+ view.buffer.style_scheme = sty
934
+ provider = Gtk::CssProvider.new
935
+ # provider.load(data: "textview { font-family: Monospace; font-size: 11pt; }")
936
+ provider.load(data: "textview { font-family: Arial; font-size: 10pt; color:#ff0000}")
937
+ view.style_context.add_provider(provider)
938
+ view.wrap_mode = :char
939
+ @minibuf = view
940
+ # Ripl.start :binding => binding
941
+ # startiter = view.buffer.get_iter_at(:offset => 0)
942
+ message("STARTUP")
943
+ sw.add(view)
944
+ end
945
+
946
+ def init_header_bar()
947
+ # @window = Gtk::Window.new(:toplevel)
948
+ # @window.screen = main_window.screen
949
+ # @window.set_default_size(600, 400)
950
+
951
+ header = Gtk::HeaderBar.new
952
+ header.show_close_button = true
953
+ header.title = "Welcome to Facebook - Log in, sign up or learn more"
954
+ header.has_subtitle = false
955
+
956
+ # icon = Gio::ThemedIcon.new("mail-send-receive-symbolic")
957
+ # icon = Gio::ThemedIcon.new("document-open-symbolic")
958
+ # icon = Gio::ThemedIcon.new("dialog-password")
959
+
960
+ #edit-redo edit-paste edit-find-replace edit-undo edit-find edit-cut edit-copy
961
+ #document-open document-save document-save-as document-properties document-new
962
+ button = Gtk::Button.new
963
+ icon = Gio::ThemedIcon.new("open-menu-symbolic")
964
+ image = Gtk::Image.new(:icon => icon, :size => :button)
965
+ button.add(image)
966
+ header.pack_end(button)
967
+
968
+ button = Gtk::Button.new
969
+ icon = Gio::ThemedIcon.new("document-revert-symbolic")
970
+ image = Gtk::Image.new(:icon => icon, :size => :button)
971
+ button.add(image)
972
+ header.pack_end(button)
973
+
974
+ box = Gtk::Box.new(:horizontal, 0)
975
+ box.style_context.add_class("linked")
976
+
977
+ button = Gtk::Button.new
978
+ image = Gtk::Image.new(:icon_name => "pan-start-symbolic", :size => :button)
979
+ button.add(image)
980
+ box.add(button)
981
+
982
+ button = Gtk::Button.new
983
+ image = Gtk::Image.new(:icon_name => "pan-end-symbolic", :size => :button)
984
+ button.add(image)
985
+ box.add(button)
986
+
987
+ header.pack_start(box)
988
+ @window.titlebar = header
989
+ @window.add(Gtk::TextView.new)
990
+ end
991
+
992
+ def init_window
993
+ @window = Gtk::Window.new(:toplevel)
994
+ @window.set_default_size(650, 850)
995
+ @window.title = "Multiple Views"
996
+ @window.show_all
997
+ # vpaned = Gtk::Paned.new(:horizontal)
998
+ @vpaned = Gtk::Paned.new(:vertical)
999
+ @window.add(@vpaned)
1000
+
1001
+ @sw = Gtk::ScrolledWindow.new
1002
+ @sw.set_policy(:automatic, :automatic)
1003
+ @overlay = Gtk::Overlay.new
1004
+ @overlay.add(@sw)
1005
+ @vpaned.pack1(@overlay, :resize => true)
1006
+
1007
+ init_minibuffer
1008
+ init_header_bar
1009
+
1010
+ @window.show_all
1011
+
1012
+ vma.start
1013
+ end
1014
+ end