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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2f7932d9b9a4f36c2f645f4d26e55d9b52a994b7
4
- data.tar.gz: e7866495e480919ef65ec5b5db1b390736def36c
3
+ metadata.gz: ebb72186258606bea34685031717fda37dd0e0c3
4
+ data.tar.gz: 5f9126dbeb67655424c77ab18a5ccbee00a61c85
5
5
  SHA512:
6
- metadata.gz: 72feed7bf6215d8cfd3cf937ec0e0b0124300327b4b2b2656d33d9e5feec097661a41b8ef03b929d45e6b602321772676492b0aa2c7333ba0f5aee511ea620b4
7
- data.tar.gz: d1bb51e53e5bccab717f675eedc4f2f3cae0cdcfe3b64f5ab8f650acd42cbb6cab4724b9d86e3fecd45d0434589dd5ecd1d5e8a9ef6e05b0f1086920e8968e65
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/Topinambour")
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, @provider, Gtk::StyleProvider::PRIORITY_USER)
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 = nil)
39
- css_properties
40
- @props.merge!(new_props) if new_props
41
- css = update_css_properties
42
- merged_css = Sass::Engine.new(css, :syntax => :scss).render
43
- if File.exist?(USR_CSS)
44
- FileUtils.mv(USR_CSS, "#{USR_CSS}_#{Time.new.strftime('%Y-%m-%d-%H-%M-%S')}.backup")
45
- File.open(USR_CSS, "w") do |file|
46
- file.puts merged_css
47
- end
48
- else
49
- File.open(USR_CSS, "w") do |file|
50
- file.puts merged_css
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
- @provider.load(:path => USR_CSS)
91
+ load_custom_css_config
69
92
  rescue
70
93
  puts "Bad css file using default css"
71
- @provider.load(:data => default_css)
94
+ load_default_css_config
72
95
  end
73
96
  else
74
97
  puts "No custom CSS, using default css"
75
- @provider.load(:data => default_css)
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
@@ -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.get_css_colors
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
- new_props["-TopinambourTerminal-#{c}"] = @colors[i].to_s
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.get_css_colors unless colors
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
- require "sass"
2
- require "gtk3" # to remove
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};\n"
77
+ "#{name}: #{value}"
54
78
  elsif value.class == String
55
- "#{name}: \"#{value}\";\n"
79
+ %(#{name}: "#{value}")
56
80
  else
57
- "#{name}: #{value};\n"
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.modify_property(filename, tree, prop)
89
- # Get the property line/pos via the tree
90
- # modify the existing prop in the file
91
- # create a temp file
92
- # copy initial file and modify the part we want
93
- # replace initial file with temp file
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.append_property_in_universal_selector(filename, tree, prop)
97
- # Get the universal selector line/pos via the tree
98
- # append our property at the end of this selector
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
- tree = engine.to_tree
227
+ new_css = nil
228
+ content = File.open(filename, "r").read
113
229
  properties.each do |prop|
114
- if property_defined?(tree, prop[:name])
115
- modify_property(filename, tree, prop)
116
- else
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
- if notebook.n_pages > 1
53
- notebook.remove_page(current_page)
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 get_css_colors
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
- color = parse_css_color(c.to_s)
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 get_css_font
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 = get_css_colors
138
- set_font(get_css_font)
125
+ @colors = css_colors
126
+ set_font(css_font)
139
127
  apply_colors
140
- #add_matches
128
+ add_matches
141
129
  end
142
130
 
143
131
  def add_matches
144
- puts methods.grep(/rege/)
145
- match_add_gregex("cedlemo")
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.2
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-14 00:00:00.000000000 Z
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: