topinambour 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|