topinambour 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/topinambour +2 -2
- data/lib/application.rb +47 -61
- data/lib/color_selector.rb +4 -2
- data/lib/css_editor.rb +1 -1
- data/lib/css_handler.rb +139 -24
- data/lib/terminal.rb +38 -29
- data/lib/terminal_regex.rb +100 -0
- data/lib/window.rb +4 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebb72186258606bea34685031717fda37dd0e0c3
|
4
|
+
data.tar.gz: 5f9126dbeb67655424c77ab18a5ccbee00a61c85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdf0c7271408c88ec111d7fe5a05571736ef9251bf6a1a5dbde356ce9c3325850a6f5e7162edad4c0ce170e6cf5c59880878cde87d4d902c3b92e766797a7055
|
7
|
+
data.tar.gz: cb63168c754ccf4501d3a833e60602a8ebdd3b3ec04769fad5ef891eb7e892bb4f634149745e5e79646a534b92229b0abcb42c745d7bba1a187a5b22a55faf12
|
data/bin/topinambour
CHANGED
@@ -28,7 +28,7 @@ CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
|
|
28
28
|
ROOT_PATH = File.expand_path("#{CURRENT_PATH}/..")
|
29
29
|
LIB_PATH = "#{CURRENT_PATH}/../lib"
|
30
30
|
DATA_PATH = "#{CURRENT_PATH}/../data"
|
31
|
-
CONFIG_DIR = File.expand_path("#{ENV['HOME']}/.config/
|
31
|
+
CONFIG_DIR = File.expand_path("#{ENV['HOME']}/.config/topinambour")
|
32
32
|
USR_CSS = "#{CONFIG_DIR}/topinambour.css"
|
33
33
|
USR_LIB_PATH = "#{CONFIG_DIR}/lib"
|
34
34
|
gresource_bin = "/tmp/topinambour.gresource"
|
@@ -46,7 +46,7 @@ end
|
|
46
46
|
resource = Gio::Resource.load(gresource_bin)
|
47
47
|
Gio::Resources.register(resource)
|
48
48
|
# Load default libraries !!WARNING!! loading order matters
|
49
|
-
%w(actions style_properties application terminal notebook color_selector font_selector
|
49
|
+
%w(actions style_properties css_handler application terminal_regex terminal notebook color_selector font_selector
|
50
50
|
terminal_chooser headerbar shortcuts window resize_message css_editor).each do |l|
|
51
51
|
if File.exist?("#{USR_LIB_PATH}/#{l}.rb")
|
52
52
|
require "#{USR_LIB_PATH}/#{l}.rb"
|
data/lib/application.rb
CHANGED
@@ -15,14 +15,16 @@
|
|
15
15
|
# along with Topinambour. If not, see <http://www.gnu.org/licenses/>.
|
16
16
|
|
17
17
|
class TopinambourApplication < Gtk::Application
|
18
|
-
attr_reader :provider
|
18
|
+
attr_reader :provider, :css_file, :css_content # :css_content really needed ?
|
19
19
|
def initialize
|
20
20
|
super("com.github.cedlemo.topinambour", :non_unique)
|
21
21
|
|
22
22
|
signal_connect "startup" do |application|
|
23
23
|
load_css_config
|
24
24
|
screen = Gdk::Display.default.default_screen
|
25
|
-
Gtk::StyleContext.add_provider_for_screen(screen,
|
25
|
+
Gtk::StyleContext.add_provider_for_screen(screen,
|
26
|
+
@provider,
|
27
|
+
Gtk::StyleProvider::PRIORITY_USER)
|
26
28
|
TopinambourActions.add_actions_to(application)
|
27
29
|
load_menu_ui_in(application)
|
28
30
|
end
|
@@ -35,88 +37,68 @@ class TopinambourApplication < Gtk::Application
|
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
38
|
-
def update_css(new_props
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
40
|
+
def update_css(new_props)
|
41
|
+
props = []
|
42
|
+
new_props.each do |prop|
|
43
|
+
props << {:name => prop[0], :value => prop[1]}
|
44
|
+
end
|
45
|
+
new_css = CssHandler.update_css(@css_file, props)
|
46
|
+
replace_old_conf_with(new_css)
|
47
|
+
|
48
|
+
begin
|
49
|
+
load_custom_css_config
|
50
|
+
rescue
|
51
|
+
puts "Bad css file using default css"
|
52
|
+
load_default_css_config
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
55
56
|
private
|
56
57
|
|
58
|
+
def replace_old_conf_with(new_conf)
|
59
|
+
if File.exist?(USR_CSS)
|
60
|
+
new_name = "#{USR_CSS}_#{Time.new.strftime('%Y-%m-%d-%H-%M-%S')}.backup"
|
61
|
+
FileUtils.mv(USR_CSS, new_name)
|
62
|
+
end
|
63
|
+
check_and_create_if_no_config_dir
|
64
|
+
File.open(USR_CSS, "w") do |file|
|
65
|
+
file.puts new_conf
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
57
69
|
def load_menu_ui_in(application)
|
58
70
|
builder = Gtk::Builder.new(:resource => "/com/github/cedlemo/topinambour/app-menu.ui")
|
59
71
|
app_menu = builder["appmenu"]
|
60
72
|
application.app_menu = app_menu
|
61
73
|
end
|
62
74
|
|
75
|
+
def load_default_css_config
|
76
|
+
@css_content = Gio::Resources.lookup_data("/com/github/cedlemo/topinambour/topinambour.css", 0)
|
77
|
+
@css_file = "#{DATA_PATH}/topinambour.css"
|
78
|
+
@provider.load(:data => @css_content)
|
79
|
+
end
|
80
|
+
|
81
|
+
def load_custom_css_config
|
82
|
+
@css_content = File.open(USR_CSS, "r").read
|
83
|
+
@css_file = USR_CSS
|
84
|
+
@provider.load(:data => @css_content)
|
85
|
+
end
|
86
|
+
|
63
87
|
def load_css_config
|
64
88
|
@provider = Gtk::CssProvider.new
|
65
|
-
default_css = Gio::Resources.lookup_data("/com/github/cedlemo/topinambour/topinambour.css", 0)
|
66
89
|
if File.exist?(USR_CSS)
|
67
90
|
begin
|
68
|
-
|
91
|
+
load_custom_css_config
|
69
92
|
rescue
|
70
93
|
puts "Bad css file using default css"
|
71
|
-
|
94
|
+
load_default_css_config
|
72
95
|
end
|
73
96
|
else
|
74
97
|
puts "No custom CSS, using default css"
|
75
|
-
|
98
|
+
load_default_css_config
|
76
99
|
end
|
77
100
|
end
|
78
101
|
|
79
|
-
def load_css_to_tree
|
80
|
-
engine = Sass::Engine.new(@provider.to_s, :syntax => :scss)
|
81
|
-
engine.to_tree
|
82
|
-
end
|
83
|
-
|
84
|
-
def update_css_properties
|
85
|
-
modified_sass = change_existing_properties
|
86
|
-
sass_to_add = @props.empty? ? "" : add_new_css_properties
|
87
|
-
Sass::Engine.new(sass_to_add + modified_sass, :syntax => :sass).render
|
88
|
-
end
|
89
|
-
|
90
|
-
def add_new_css_properties
|
91
|
-
new_sass = "*"
|
92
|
-
@props.each do |k, v|
|
93
|
-
new_sass += "\n #{k}: #{v}"
|
94
|
-
end
|
95
|
-
new_sass + "\n"
|
96
|
-
end
|
97
|
-
|
98
|
-
def change_existing_properties
|
99
|
-
keys_found = []
|
100
|
-
tree = load_css_to_tree
|
101
|
-
# we search for properties that are already configured
|
102
|
-
tree.children.each do |node|
|
103
|
-
node.each do |prop|
|
104
|
-
next if prop.class != Sass::Tree::PropNode
|
105
|
-
name = prop.name[0]
|
106
|
-
next unless @props[name]
|
107
|
-
keys_found << name unless keys_found.include?(name)
|
108
|
-
if @props[name] != prop.value.value
|
109
|
-
value_object = prop.value.value.class.new(@props[name])
|
110
|
-
prop.value = Sass::Script::Tree::Literal.new(value_object)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
keys_found.each do |k|
|
115
|
-
@props.delete(k)
|
116
|
-
end
|
117
|
-
tree.to_sass
|
118
|
-
end
|
119
|
-
|
120
102
|
def css_properties
|
121
103
|
@props = {}
|
122
104
|
return @props if windows[0].notebook.current.class == TopinambourTerminal
|
@@ -128,4 +110,8 @@ class TopinambourApplication < Gtk::Application
|
|
128
110
|
@props["-TopinambourTerminal-font"] = DEFAULT_TERMINAL_FONT
|
129
111
|
@props["-TopinambourWindow-shell"] = "\'/usr/bin/fish\'"
|
130
112
|
end
|
113
|
+
|
114
|
+
def check_and_create_if_no_config_dir
|
115
|
+
Dir.mkdir(CONFIG_DIR) unless Dir.exist?(CONFIG_DIR)
|
116
|
+
end
|
131
117
|
end
|
data/lib/color_selector.rb
CHANGED
@@ -37,7 +37,7 @@ class TopinambourColorSelector < Gtk::Box
|
|
37
37
|
private
|
38
38
|
|
39
39
|
def initialize_default_colors
|
40
|
-
@default_colors = @window.notebook.current.
|
40
|
+
@default_colors = @window.notebook.current.css_colors
|
41
41
|
@colors = @default_colors.dup
|
42
42
|
end
|
43
43
|
|
@@ -67,7 +67,9 @@ class TopinambourColorSelector < Gtk::Box
|
|
67
67
|
button.signal_connect "clicked" do |widget|
|
68
68
|
new_props = {}
|
69
69
|
TERMINAL_COLOR_NAMES.each_with_index do |c, i|
|
70
|
-
|
70
|
+
if @colors[i] != @default_colors[i]
|
71
|
+
new_props["-TopinambourTerminal-#{c}"] = @colors[i].to_s
|
72
|
+
end
|
71
73
|
end
|
72
74
|
apply_new_css_properties(widget.toplevel, new_props)
|
73
75
|
button.toplevel.exit_overlay_mode
|
data/lib/css_editor.rb
CHANGED
@@ -147,7 +147,7 @@ class TopinambourCssEditor < Gtk::Grid
|
|
147
147
|
def reload_terminal_custom_css_properties
|
148
148
|
@window.notebook.each do |tab|
|
149
149
|
next unless tab.class == TopinambourTerminal
|
150
|
-
colors = tab.
|
150
|
+
colors = tab.css_colors unless colors
|
151
151
|
tab.colors = colors
|
152
152
|
tab.apply_colors
|
153
153
|
end
|
data/lib/css_handler.rb
CHANGED
@@ -1,6 +1,26 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Copyright 2016 Cédric LE MOIGNE, cedlemo@gmx.com
|
2
|
+
# This file is part of Topinambour.
|
3
|
+
#
|
4
|
+
# Topinambour is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# any later version.
|
8
|
+
#
|
9
|
+
# Topinambour is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with Topinambour. If not, see <http://www.gnu.org/licenses/>.
|
3
16
|
|
17
|
+
##
|
18
|
+
# This module contains all the methods needed to read, parse
|
19
|
+
# and modify css file.
|
20
|
+
# Sass is used to read, parse and get informations about
|
21
|
+
# css selectors or properties
|
22
|
+
# The modifying or addition of css properties are done
|
23
|
+
# with ruby strings facilities
|
4
24
|
module CssHandler
|
5
25
|
def self.css_file?(filename)
|
6
26
|
filename =~ /^.*\.css$/ ? true : false
|
@@ -38,6 +58,10 @@ module CssHandler
|
|
38
58
|
end
|
39
59
|
end
|
40
60
|
|
61
|
+
def self.reload_engine(engine, css_content)
|
62
|
+
Sass::Engine.new(css_content, :syntax => engine.options[:syntax])
|
63
|
+
end
|
64
|
+
|
41
65
|
def self.color_property?(value)
|
42
66
|
parsed_color = nil
|
43
67
|
begin
|
@@ -50,11 +74,11 @@ module CssHandler
|
|
50
74
|
|
51
75
|
def self.property_to_css_instructions(name, value)
|
52
76
|
if value.class == String && color_property?(value)
|
53
|
-
"#{name}: #{value}
|
77
|
+
"#{name}: #{value}"
|
54
78
|
elsif value.class == String
|
55
|
-
|
79
|
+
%(#{name}: "#{value}")
|
56
80
|
else
|
57
|
-
"#{name}: #{value}
|
81
|
+
"#{name}: #{value}"
|
58
82
|
end
|
59
83
|
end
|
60
84
|
|
@@ -69,7 +93,7 @@ module CssHandler
|
|
69
93
|
end
|
70
94
|
props
|
71
95
|
end
|
72
|
-
|
96
|
+
|
73
97
|
def self.prop_position(prop)
|
74
98
|
source_range = prop.source_range
|
75
99
|
[source_range.start_pos, source_range.end_pos]
|
@@ -79,43 +103,134 @@ module CssHandler
|
|
79
103
|
tree.children.each do |node|
|
80
104
|
node.each do |prop|
|
81
105
|
next if prop.class != Sass::Tree::PropNode
|
106
|
+
next if prop.class == Sass::Tree::CommentNode
|
82
107
|
return true if prop.name[0] == name
|
83
108
|
end
|
84
109
|
end
|
85
110
|
false
|
86
111
|
end
|
87
112
|
|
88
|
-
def self.
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
113
|
+
def self.modify_in_ranges(line, line_number, start_range, end_range, value)
|
114
|
+
if line_number < start_range.line || line_number > end_range.line
|
115
|
+
line
|
116
|
+
else
|
117
|
+
tmp = ""
|
118
|
+
if line_number == start_range.line
|
119
|
+
tmp += line[0..(start_range.offset - 2)] + value
|
120
|
+
end
|
121
|
+
tmp += line[(end_range.offset - 1)..-1] if line_number == end_range.line
|
122
|
+
tmp
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.modify_property_value(file_content, property, value)
|
127
|
+
start_range = property.value_source_range.start_pos
|
128
|
+
end_range = property.value_source_range.end_pos
|
129
|
+
tmp = ""
|
130
|
+
line_number = 1
|
131
|
+
file_content.each_line do |line|
|
132
|
+
tmp += modify_in_ranges(line, line_number, start_range, end_range, value)
|
133
|
+
line_number += 1
|
134
|
+
end
|
135
|
+
tmp
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.modify_each_property_values(css_content, engine, prop)
|
139
|
+
engine = engine
|
140
|
+
tree = engine.to_tree
|
141
|
+
props = props_with_name(tree, prop[:name])
|
142
|
+
new_css = css_content
|
143
|
+
(0..(props.size - 1)).each do |i|
|
144
|
+
new_css = modify_property_value(new_css, props[i], prop[:value])
|
145
|
+
engine = reload_engine(engine, new_css)
|
146
|
+
tree = engine.to_tree
|
147
|
+
props = props_with_name(tree, prop[:name])
|
148
|
+
end
|
149
|
+
new_css
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.append_new_property_after_line(line, prop, indent)
|
153
|
+
tmp = line.gsub(/\;[^\;]*$/, ";\n")
|
154
|
+
new_prop = property_to_css_instructions(prop[:name], prop[:value])
|
155
|
+
tmp += (indent + new_prop + (Regexp.last_match(0) || ";\n"))
|
156
|
+
tmp
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.last_child_which_is_not_comment(selector)
|
160
|
+
son = nil
|
161
|
+
selector.children.each do |child|
|
162
|
+
son = child unless child.class == Sass::Tree::CommentNode
|
163
|
+
end
|
164
|
+
son
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.compute_position_to_append(selector, element)
|
168
|
+
indent = last_line = nil
|
169
|
+
if element
|
170
|
+
indent = " " * (element.name_source_range.start_pos.offset - 1) || ""
|
171
|
+
last_line = element.value_source_range.end_pos.line
|
172
|
+
else # If we don 't have any property in selector, use sel offset
|
173
|
+
indent = " " * (selector.source_range.start_pos.offset - 1) || ""
|
174
|
+
last_line = selector.source_range.start_pos.line
|
175
|
+
end
|
176
|
+
[indent, last_line]
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.append_property_in_universal_selector(css_content, engine, prop)
|
180
|
+
last_selector = selectors_with_name(engine.to_tree, "*").last
|
181
|
+
last_prop = last_child_which_is_not_comment(last_selector)
|
182
|
+
indent, last_line = compute_position_to_append(last_selector, last_prop)
|
183
|
+
tmp = ""
|
184
|
+
line_number = 1
|
185
|
+
css_content.each_line do |line|
|
186
|
+
if last_line == line_number
|
187
|
+
tmp += append_new_property_after_line(line, prop, indent)
|
188
|
+
else
|
189
|
+
tmp += line
|
190
|
+
end
|
191
|
+
line_number += 1
|
192
|
+
end
|
193
|
+
tmp
|
94
194
|
end
|
95
195
|
|
96
|
-
def self.
|
97
|
-
|
98
|
-
|
196
|
+
def self.rule_node_name_is?(node, name)
|
197
|
+
if node.rule.include?(name)
|
198
|
+
true
|
199
|
+
elsif node.parsed_rules.to_s == name
|
200
|
+
true
|
201
|
+
elsif node.resolved_rules == name
|
202
|
+
true
|
203
|
+
else
|
204
|
+
false
|
205
|
+
end
|
99
206
|
end
|
100
207
|
|
101
208
|
def self.selectors_with_name(tree, sel_name)
|
102
209
|
selectors = []
|
103
210
|
tree.children.each do |node|
|
104
211
|
next unless node.class == Sass::Tree::RuleNode
|
105
|
-
selectors << node
|
212
|
+
selectors << node if rule_node_name_is?(node, sel_name)
|
106
213
|
end
|
107
214
|
selectors
|
108
|
-
end
|
109
|
-
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.update_css_with_new_property(content, engine, prop)
|
218
|
+
if property_defined?(engine.to_tree, prop[:name])
|
219
|
+
modify_each_property_values(content, engine, prop)
|
220
|
+
else
|
221
|
+
append_property_in_universal_selector(content, engine, prop)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
110
225
|
def self.update_css(filename, properties)
|
111
226
|
engine = to_engine(filename)
|
112
|
-
|
227
|
+
new_css = nil
|
228
|
+
content = File.open(filename, "r").read
|
113
229
|
properties.each do |prop|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
append_property_in_universal_selector(filename, tree, prop)
|
118
|
-
end
|
230
|
+
new_css = update_css_with_new_property(content, engine, prop)
|
231
|
+
engine = reload_engine(engine, new_css)
|
232
|
+
content = new_css
|
119
233
|
end
|
234
|
+
new_css
|
120
235
|
end
|
121
236
|
end
|
data/lib/terminal.rb
CHANGED
@@ -49,13 +49,8 @@ class TopinambourTerminal < Vte::Terminal
|
|
49
49
|
signal_connect "child-exited" do |widget|
|
50
50
|
notebook = widget.parent
|
51
51
|
current_page = notebook.page_num(widget)
|
52
|
-
|
53
|
-
|
54
|
-
notebook.get_nth_page(notebook.page).grab_focus
|
55
|
-
else
|
56
|
-
notebook.remove_page(current_page)
|
57
|
-
notebook.toplevel.application.quit
|
58
|
-
end
|
52
|
+
notebook.remove_page(current_page)
|
53
|
+
notebook.toplevel.application.quit unless notebook.n_pages >= 1
|
59
54
|
end
|
60
55
|
|
61
56
|
signal_connect "window-title-changed" do |widget|
|
@@ -72,24 +67,20 @@ class TopinambourTerminal < Vte::Terminal
|
|
72
67
|
|
73
68
|
builder = Gtk::Builder.new(:resource => "/com/github/cedlemo/topinambour/terminal-menu.ui")
|
74
69
|
@menu = Gtk::Popover.new(self, builder["termmenu"])
|
75
|
-
|
70
|
+
|
76
71
|
signal_connect "button-press-event" do |widget, event|
|
77
72
|
if event.type == Gdk::EventType::BUTTON_PRESS &&
|
78
73
|
event.button == Gdk::BUTTON_SECONDARY
|
79
|
-
|
80
|
-
x, y = event.window.coords_to_parent(event.x,
|
81
|
-
event.y)
|
82
|
-
rect = Gdk::Rectangle.new(x - allocation.x,
|
83
|
-
y - allocation.y,
|
84
|
-
1,
|
85
|
-
1)
|
86
|
-
widget.menu.set_pointing_to(rect)
|
87
|
-
widget.menu.show
|
74
|
+
display_copy_past_menu(widget, event)
|
88
75
|
true
|
76
|
+
elsif event.button == Gdk::BUTTON_PRIMARY
|
77
|
+
manage_regex_on_click(widget, event)
|
78
|
+
false # let false so that it doesn't block the event
|
89
79
|
else
|
90
80
|
false
|
91
81
|
end
|
92
82
|
end
|
83
|
+
|
93
84
|
configure
|
94
85
|
end
|
95
86
|
|
@@ -97,20 +88,17 @@ class TopinambourTerminal < Vte::Terminal
|
|
97
88
|
File.readlink("/proc/#{@pid}/cwd")
|
98
89
|
end
|
99
90
|
|
100
|
-
def
|
101
|
-
background = nil
|
102
|
-
foreground = nil
|
91
|
+
def css_colors
|
103
92
|
colors = []
|
104
93
|
background = parse_css_color(TERMINAL_COLOR_NAMES[0].to_s)
|
105
94
|
foreground = parse_css_color(TERMINAL_COLOR_NAMES[1].to_s)
|
106
95
|
TERMINAL_COLOR_NAMES[2..-1].each do |c|
|
107
|
-
|
108
|
-
colors << color
|
96
|
+
colors << parse_css_color(c.to_s)
|
109
97
|
end
|
110
98
|
[background, foreground] + colors
|
111
99
|
end
|
112
100
|
|
113
|
-
def
|
101
|
+
def css_font
|
114
102
|
font = style_get_property("font")
|
115
103
|
unless font
|
116
104
|
font = Pango::FontDescription.new(DEFAULT_TERMINAL_FONT)
|
@@ -134,15 +122,36 @@ class TopinambourTerminal < Vte::Terminal
|
|
134
122
|
def configure
|
135
123
|
set_rewrap_on_resize(true)
|
136
124
|
set_scrollback_lines(-1)
|
137
|
-
@colors =
|
138
|
-
set_font(
|
125
|
+
@colors = css_colors
|
126
|
+
set_font(css_font)
|
139
127
|
apply_colors
|
140
|
-
|
128
|
+
add_matches
|
141
129
|
end
|
142
130
|
|
143
131
|
def add_matches
|
144
|
-
|
145
|
-
|
132
|
+
@REGEXES = [:REGEX_URL_AS_IS, :REGEX_URL_FILE, :REGEX_URL_HTTP,
|
133
|
+
:REGEX_URL_VOIP, :REGEX_EMAIL, :REGEX_NEWS_MAN]
|
134
|
+
@REGEXES.each do |name|
|
135
|
+
regex_name = TopinambourRegex.const_get(name)
|
136
|
+
flags = [GLib::RegexCompileFlags::OPTIMIZE,
|
137
|
+
GLib::RegexCompileFlags::MULTILINE]
|
138
|
+
regex = GLib::Regex.new(regex_name, :compile_options => flags)
|
139
|
+
match_add_gregex(regex, 0)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def display_copy_past_menu(widget, event)
|
144
|
+
x, y = event.window.coords_to_parent(event.x,
|
145
|
+
event.y)
|
146
|
+
rect = Gdk::Rectangle.new(x - allocation.x,
|
147
|
+
y - allocation.y,
|
148
|
+
1,
|
149
|
+
1)
|
150
|
+
widget.menu.set_pointing_to(rect)
|
151
|
+
widget.menu.show
|
152
|
+
end
|
153
|
+
|
154
|
+
def manage_regex_on_click(widget, event)
|
155
|
+
#puts match_check_event(event).inspect
|
146
156
|
end
|
147
157
|
end
|
148
|
-
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Copyright 2016 Cédric LE MOIGNE, cedlemo@gmx.com
|
2
|
+
# This file is part of Topinambour.
|
3
|
+
#
|
4
|
+
# Topinambour is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# any later version.
|
8
|
+
#
|
9
|
+
# Topinambour is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with Topinambour. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
# Stolen from https://github.com/GNOME/gnome-terminal/blob/master/src/terminal-regex.h
|
18
|
+
module TopinambourRegex
|
19
|
+
SCHEME = "(?ix: news | telnet | nntp | https? | ftps? | sftp | webcal )"
|
20
|
+
USERCHARS = "-+.[:alnum:]"
|
21
|
+
# Nonempty username, e.g. "john.smith"
|
22
|
+
USER = "[#{USERCHARS}]+"
|
23
|
+
PASSCHARS_CLASS = '[-[:alnum:]\\Q,?;.:/!%$^*&~\"#\'\\E]'
|
24
|
+
# Optional colon-prefixed password. I guess empty password should be allowed, right? E.g. ":secret", ":", "" */
|
25
|
+
PASS = "(?x: :#{PASSCHARS_CLASS}* )?"
|
26
|
+
# Optional at-terminated username (with perhaps a password too), e.g. "joe@", "pete:secret@", "" */
|
27
|
+
USERPASS = "(?:#{USER}#{PASS}@)?"
|
28
|
+
# S4: IPv4 segment (number between 0 and 255) with lookahead at the end so that we don't match "25" in the string "256".
|
29
|
+
# The lookahead could go to the last segment of IPv4 only but this construct allows nicer unittesting. */
|
30
|
+
S4_DEF = "(?(DEFINE)(?<S4>(?x: (?: [0-9] | [1-9][0-9] | 1[0-9]{2} | 2[0-4][0-9] | 25[0-5] ) (?! [0-9] ) )))"
|
31
|
+
# IPV4: Decimal IPv4, e.g. "1.2.3.4", with lookahead (implemented in S4) at the end so that we don't match "192.168.1.123" in the string "192.168.1.1234". */
|
32
|
+
IPV4_DEF = "#{S4_DEF}(?(DEFINE)(?<IPV4>(?x: (?: (?&S4) \\. ){3} (?&S4) )))"
|
33
|
+
# IPv6, including embedded IPv4, e.g. "::1", "dead:beef::1.2.3.4".
|
34
|
+
# Lookahead for the next char not being a dot or digit, so it doesn't get stuck matching "dead:beef::1" in "dead:beef::1.2.3.4".
|
35
|
+
# This is not required since the surrounding brackets would trigger backtracking, but it allows nicer unittesting.
|
36
|
+
# TODO: more strict check (right number of colons, etc.)
|
37
|
+
# TODO: add zone_id: RFC 4007 section 11, RFC 6874 */
|
38
|
+
# S6: IPv6 segment, S6C: IPv6 segment followed by a comma, CS6: comma followed by an IPv6 segment */
|
39
|
+
S6_DEF = "(?(DEFINE)(?<S6>[[:xdigit:]]{1,4})(?<CS6>:(?&S6))(?<S6C>(?&S6):))"
|
40
|
+
# No :: shorthand */
|
41
|
+
IPV6_FULL = "(?x: (?&S6C){7} (?&S6) )"
|
42
|
+
# Begins with :: */
|
43
|
+
IPV6_LEFT = "(?x: : (?&CS6){1,7} )"
|
44
|
+
# :: somewhere in the middle - use negative lookahead to make sure there aren't too many colons in total */
|
45
|
+
IPV6_MID = "(?x: (?! (?: [[:xdigit:]]*: ){8} ) (?&S6C){1,6} (?&CS6){1,6} )"
|
46
|
+
# Ends with :: */
|
47
|
+
IPV6_RIGHT = "(?x: (?&S6C){1,7} : )"
|
48
|
+
# Is "::" and nothing more */
|
49
|
+
IPV6_NULL = "(?x: :: )"
|
50
|
+
# The same ones for IPv4-embedded notation, without the actual IPv4 part */
|
51
|
+
IPV6V4_FULL = "(?x: (?&S6C){6} )"
|
52
|
+
IPV6V4_LEFT = "(?x: :: (?&S6C){0,5} )" # includes "::<ipv4>" */
|
53
|
+
IPV6V4_MID = "(?x: (?! (?: [[:xdigit:]]*: ){7} ) (?&S6C){1,4} (?&CS6){1,4} ) :"
|
54
|
+
IPV6V4_RIGHT= "(?x: (?&S6C){1,5} : )"
|
55
|
+
# IPV6: An IPv6 address (possibly with an embedded IPv4).
|
56
|
+
# This macro defines both IPV4 and IPV6, since the latter one requires the former. */
|
57
|
+
IP_DEF = "#{IPV4_DEF}#{S6_DEF}(?(DEFINE)(?<IPV6>(?x: (?: #{IPV6_NULL} | #{IPV6_LEFT} | #{IPV6_MID} | #{IPV6_RIGHT}" +
|
58
|
+
" | #{IPV6_FULL} | (?: #{IPV6V4_FULL} | #{IPV6V4_LEFT} | #{IPV6V4_MID} | #{IPV6V4_RIGHT}" +
|
59
|
+
" ) (?&IPV4) ) (?! [.:[:xdigit:]] ) )))"
|
60
|
+
# Either an alphanumeric character or dash; or if [negative lookahead] not ASCII
|
61
|
+
# then any graphical Unicode character.
|
62
|
+
# A segment can consist entirely of numbers.
|
63
|
+
# (Note: PCRE doesn't support character class subtraction/intersection.) */
|
64
|
+
HOSTNAMESEGMENTCHARS_CLASS = "(?x: [-[:alnum:]] | (?! [[:ascii:]] ) [[:graph:]] )"
|
65
|
+
# A hostname of at least 1 component. The last component cannot be entirely numbers.
|
66
|
+
# E.g. "foo", "example.com", "1234.com", but not "foo.123" */
|
67
|
+
HOSTNAME1 = "(?x: (?: #{HOSTNAMESEGMENTCHARS_CLASS}+ \\. )* " + "#{HOSTNAMESEGMENTCHARS_CLASS}* (?! [0-9] ) #{HOSTNAMESEGMENTCHARS_CLASS}+ )"
|
68
|
+
# A hostname of at least 2 components. The last component cannot be entirely numbers.
|
69
|
+
# E.g. "example.com", "1234.com", but not "1234.56" */
|
70
|
+
HOSTNAME2 = "(?x: (?: #{HOSTNAMESEGMENTCHARS_CLASS}+ \\.)+ #{HOSTNAME1} )"
|
71
|
+
# For URL: Hostname, IPv4, or bracket-enclosed IPv6, e.g. "example.com", "1.2.3.4", "[::1]" */
|
72
|
+
URL_HOST = "(?x: #{HOSTNAME1} | (?&IPV4) | \\[ (?&IPV6) \\] )"
|
73
|
+
# For e-mail: Hostname of at least two segments, or bracket-enclosed IPv4 or IPv6, e.g. "example.com", "[1.2.3.4]", "[::1]".
|
74
|
+
# Technically an e-mail with a single-component hostname might be valid on a local network,
|
75
|
+
# but let's avoid tons of false positives (e.g. in a typical shell prompt). */
|
76
|
+
EMAIL_HOST = "(?x: #{HOSTNAME2} | \\[ (?: (?&IPV4) | (?&IPV6) ) \\] )"
|
77
|
+
# Number between 1 and 65535, with lookahead at the end so that we don't match "6789" in the string "67890",
|
78
|
+
# and in turn we don't eventually match "http://host:6789" in "http://host:67890". */
|
79
|
+
N_1_65535 = "(?x: (?: [1-9][0-9]{0,3} | [1-5][0-9]{4} | 6[0-4][0-9]{3} | 65[0-4][0-9]{2} | 655[0-2][0-9] | 6553[0-5] ) (?! [0-9] ) )"
|
80
|
+
# Optional colon-prefixed port, e.g. ":1080", "" */
|
81
|
+
PORT = "(?x: \\:#{N_1_65535} )?"
|
82
|
+
PATHCHARS_CLASS = "[-[:alnum:]\\Q_$.+!*,:;@&=?/~#|%\\E]"
|
83
|
+
# Chars not to end a URL */
|
84
|
+
PATHNONTERM_CLASS = "[\\Q.!,?\\E]"
|
85
|
+
# Lookbehind at the end, so that the last character (if we matched a character at all) is not from PATHTERM_CLASS */
|
86
|
+
URLPATH = "(?x: /#{PATHCHARS_CLASS}* (?<! #{PATHNONTERM_CLASS} ) )?"
|
87
|
+
VOIP_PATH = "(?x: [;?]#{PATHCHARS_CLASS}* (?<! #{PATHNONTERM_CLASS} ) )?"
|
88
|
+
# Now let's put these fragments together */
|
89
|
+
DEFS = IP_DEF
|
90
|
+
#REGEX_URL_AS_IS = "#{SCHEME}://#{USERPASS}#{URL_HOST}#{PORT}#{URLPATH}"
|
91
|
+
REGEX_URL_AS_IS = "#{DEFS}#{SCHEME}#://#{USERPASS}#{URL_HOST}#{PORT}#{URLPATH}"
|
92
|
+
# TODO: also support file:/etc/passwd */
|
93
|
+
REGEX_URL_FILE = "#{DEFS}(?ix: file:/ (?: / (?: #{HOSTNAME1} )? / )? (?! / ) )(?x: #{PATHCHARS_CLASS}+ (?<! #{PATHNONTERM_CLASS} ) )?"
|
94
|
+
# Lookbehind so that we don't catch "abc.www.foo.bar", bug 739757.
|
95
|
+
# Lookahead for www/ftp for convenience (so that we can reuse HOSTNAME1). */
|
96
|
+
REGEX_URL_HTTP = "#{DEFS}(?<!(?:#{HOSTNAMESEGMENTCHARS_CLASS}|[.]))(?=(?i:www|ftp))#{HOSTNAME1}#{PORT}#{URLPATH}"
|
97
|
+
REGEX_URL_VOIP = "#{DEFS}(?i:h323:|sips?:)#{USERPASS}#{URL_HOST}#{PORT}#{VOIP_PATH}"
|
98
|
+
REGEX_EMAIL = "#{DEFS}(?i:mailto:)?#{USER}@#{EMAIL_HOST}"
|
99
|
+
REGEX_NEWS_MAN = "(?i:news:|man:|info:)[-[:alnum:]\\Q^_{|}~!\"#$%&'()*+,./;:=?`\\E]+"
|
100
|
+
end
|
data/lib/window.rb
CHANGED
@@ -63,6 +63,7 @@ class TopinambourWindow < Gtk::ApplicationWindow
|
|
63
63
|
@notebook.append_page(terminal, Gtk::Label.new)
|
64
64
|
@notebook.set_tab_reorderable(terminal, true)
|
65
65
|
@notebook.set_page(@notebook.n_pages - 1)
|
66
|
+
@notebook.current.grab_focus
|
66
67
|
end
|
67
68
|
|
68
69
|
def quit_gracefully
|
@@ -78,11 +79,13 @@ class TopinambourWindow < Gtk::ApplicationWindow
|
|
78
79
|
def show_prev_tab
|
79
80
|
exit_overlay_mode
|
80
81
|
@notebook.cycle_prev_page
|
82
|
+
@notebook.current.grab_focus
|
81
83
|
end
|
82
84
|
|
83
85
|
def show_next_tab
|
84
86
|
exit_overlay_mode
|
85
87
|
@notebook.cycle_next_page
|
88
|
+
@notebook.current.grab_focus
|
86
89
|
end
|
87
90
|
|
88
91
|
def show_terminal_chooser
|
@@ -191,6 +194,7 @@ class TopinambourWindow < Gtk::ApplicationWindow
|
|
191
194
|
def toggle_overlay(klass)
|
192
195
|
if in_overlay_mode? && @overlay.children[1].class == klass
|
193
196
|
exit_overlay_mode
|
197
|
+
@notebook.current.grab_focus
|
194
198
|
else
|
195
199
|
exit_overlay_mode
|
196
200
|
add_overlay(klass.new(self))
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: topinambour
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cédric LE MOIGNE
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: vte3
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- lib/style_properties.rb
|
114
114
|
- lib/terminal.rb
|
115
115
|
- lib/terminal_chooser.rb
|
116
|
+
- lib/terminal_regex.rb
|
116
117
|
- lib/window.rb
|
117
118
|
homepage: https://github.com/cedlemo/topinambour
|
118
119
|
licenses:
|