topinambour 1.0.11 → 1.0.12

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.
data/lib/preferences.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2016 Cedric LE MOIGNE, cedlemo@gmx.com
1
+ # Copyright 2016-2017 Cedric LE MOIGNE, cedlemo@gmx.com
2
2
  # This file is part of Topinambour.
3
3
  #
4
4
  # Topinambour is free software: you can redistribute it and/or modify
@@ -13,151 +13,196 @@
13
13
  #
14
14
  # You should have received a copy of the GNU General Public License
15
15
  # along with Topinambour. If not, see <http://www.gnu.org/licenses/>.
16
- require "gtksourceview3"
17
-
18
- module TopinambourPreferences
19
- def self.generate_dialog(parent)
20
- resource_file = "/com/github/cedlemo/topinambour/prefs-dialog.ui"
21
- builder = Gtk::Builder.new(:resource => resource_file)
22
- dialog = builder["Preferences_dialog"]
23
- dialog.transient_for = parent
24
- add_actions(builder, parent)
25
- connect_response(dialog, builder)
26
- dialog
27
- end
28
16
 
29
- def self.connect_response(dialog, builder)
30
- dialog.signal_connect "response" do |widget, response|
31
- case response
32
- when 1
33
- on_apply_response(widget, builder)
34
- else
35
- widget.destroy
17
+ class TopinambourPreferences < Gtk::Window
18
+ type_register
19
+ class << self
20
+ def init
21
+ resource_file = "/com/github/cedlemo/topinambour/prefs-dialog.ui"
22
+ set_template(:resource => resource_file)
23
+
24
+ %w(width_spin height_spin shell_entry audible_bell_switch
25
+ allow_bold_switch scroll_on_output_switch scroll_on_keystroke_switch
26
+ rewrap_on_resize_switch mouse_autohide_switch cursor_shape_sel
27
+ cursor_blink_mode_sel backspace_binding_sel delete_binding_sel
28
+ css_chooser_button use_custom_css_switch).each do |widget|
29
+ bind_template_child(widget)
36
30
  end
31
+
32
+ set_connect_func { |name| method(name) }
37
33
  end
38
- end
39
34
 
40
- def self.on_apply_response(widget, builder)
41
- props = get_all_properties(widget, builder)
42
- toplevel = widget.transient_for
43
- toplevel.application.update_css(props)
44
- widget.destroy
45
- end
35
+ private
46
36
 
47
- def self.get_all_properties(widget, builder)
48
- props = {}
49
- props.merge!(get_entry_value(builder))
50
- props.merge!(get_switch_values(builder))
51
- props.merge!(get_spin_values(widget))
52
- props.merge!(get_combo_values(builder))
53
- end
37
+ def on_width_spin_value_changed_cb(spin)
38
+ parent = spin.toplevel.transient_for
39
+ height = parent.application.settings["height"]
40
+ parent.resize(spin.value, height)
41
+ end
54
42
 
55
- def self.add_actions(builder, parent)
56
- %w(audible_bell allow_bold scroll_on_output
57
- scroll_on_keystroke rewrap_on_resize mouse_autohide).each do |prop_name|
58
- gen_switch_actions(prop_name, builder, parent)
43
+ def on_height_spin_value_changed_cb(spin)
44
+ parent = spin.toplevel.transient_for
45
+ width = parent.application.settings["width"]
46
+ parent.resize(width, spin.value)
59
47
  end
60
- gen_entry_actions("shell", builder, parent)
61
- %w(cursor_shape cursor_blink_mode backspace_binding
62
- delete_binding).each do |prop_name|
63
- gen_combobox_actions(prop_name, builder, parent)
48
+
49
+ def on_shell_entry_activate_cb(entry)
50
+ style_context = entry.style_context
51
+
52
+ if File.exist?(entry.text)
53
+ settings = entry.toplevel.settings
54
+ entry.set_icon_from_icon_name(:secondary, nil)
55
+ style_context.remove_class("error")
56
+ settings["default-shell"] = entry.text if settings
57
+ else
58
+ style_context.add_class("error")
59
+ entry.set_icon_from_icon_name(:secondary, "dialog-warning-symbolic")
60
+ end
61
+ end
62
+
63
+ def on_shell_entry_focus_out_event_cb(entry, _)
64
+ style_context = entry.style_context
65
+
66
+ if File.exist?(entry.text)
67
+ settings = entry.toplevel.settings
68
+ entry.set_icon_from_icon_name(:secondary, nil)
69
+ style_context.remove_class("error")
70
+ settings["default-shell"] = entry.text if settings
71
+ else
72
+ style_context.add_class("error")
73
+ entry.set_icon_from_icon_name(:secondary, "dialog-warning-symbolic")
74
+ end
75
+ end
76
+
77
+ def on_css_file_selected_cb(filechooser)
78
+ parent = filechooser.toplevel.transient_for
79
+ parent.application.settings["css-file"] = filechooser.filename
80
+ parent.application.reload_css_config
64
81
  end
65
- gen_spinbuttons_actions(builder, parent)
66
- end
67
82
 
68
- def self.gen_switch_actions(property_name, builder, parent)
69
- switch = builder["#{property_name}_switch"]
70
- switch.active = parent.notebook.current.term.send("#{property_name}?")
71
- switch.signal_connect "state-set" do |_widget, state|
72
- parent.notebook.send_to_all_terminals("#{property_name}=", state)
83
+ def on_custom_css_switch_state_set(switch, _state)
84
+ parent = switch.toplevel.transient_for
85
+ setting = "custom-css"
86
+ settings = parent.application.settings
87
+ settings[setting] = switch.active?
88
+ switch.toplevel.css_chooser_button.sensitive = settings[setting]
89
+ parent.application.reload_css_config
73
90
  false
74
91
  end
75
92
  end
76
93
 
77
- def self.gen_entry_actions(property_name, builder, parent)
78
- entry = builder["#{property_name}_entry"]
79
- entry.text = parent.shell
80
- entry.set_icon_from_icon_name(:secondary, "edit-clear")
81
- entry.signal_connect("activate") { |widget| parent.shell = widget.text }
94
+ attr_reader :settings
95
+ def initialize(parent)
96
+ super(:type => :toplevel)
97
+ set_transient_for(parent)
98
+ @parent = parent
82
99
 
83
- entry.signal_connect "icon-release" do |widget, position|
84
- if position == :secondary
85
- parent.shell = widget.text = parent.style_get_property(property_name)
86
- end
100
+ configure_headerbar
101
+
102
+ signal_connect "delete-event" do |widget|
103
+ widget.destroy
104
+ @parent.notebook.current.term.grab_focus
87
105
  end
106
+
107
+ @settings = @parent.application.settings
108
+
109
+ initialize_widgets
88
110
  end
89
111
 
90
- def self.gen_combobox_actions(property_name, builder, parent)
91
- combobox = builder["#{property_name}_sel"]
92
- id = parent.notebook.current.term.send("#{property_name}").nick + "_id"
93
- combobox.active_id = id
94
- combobox.signal_connect "changed" do |widget|
95
- value = widget.active_id.gsub(/_id/, "").to_sym
96
- parent.notebook.send_to_all_terminals("#{property_name}=", value)
97
- end
112
+ private
113
+
114
+ def initialize_widgets
115
+ initialize_use_custom_css_settings
116
+ bind_spin_buttons_with_settings
117
+ bind_switches_with_settings
118
+ bind_combo_boxes_with_settings
119
+
120
+ shell_entry.text = @settings["default-shell"]
121
+
122
+ css_chooser_button.current_folder = "#{ENV['HOME']}/.config/topinambour/"
123
+ css_chooser_button.filename = @parent.application.check_css_file_path || ""
98
124
  end
99
125
 
100
- def self.gen_spinbuttons_actions(builder, parent)
101
- width_spin = builder["width_spin"]
102
- height_spin = builder["height_spin"]
103
- width_spin.set_range(0, 2000)
104
- height_spin.set_range(0, 1000)
105
- width_spin.value, height_spin.value = parent.size
106
- gen_width_button_action(width_spin, parent)
107
- gen_height_button_action(height_spin, parent)
126
+ def bind_switches_with_settings
127
+ bind_switch_state_with_setting(allow_bold_switch, "allow-bold")
128
+ bind_switch_state_with_setting(audible_bell_switch, "audible-bell")
129
+ bind_switch_state_with_setting(scroll_on_output_switch, "scroll-on-output")
130
+ bind_switch_state_with_setting(scroll_on_keystroke_switch,
131
+ "scroll-on-keystroke")
132
+ bind_switch_state_with_setting(rewrap_on_resize_switch, "rewrap-on-resize")
133
+ bind_switch_state_with_setting(mouse_autohide_switch, "mouse-autohide")
108
134
  end
109
135
 
110
- def self.gen_width_button_action(spin_widget, parent)
111
- spin_widget.signal_connect "value-changed" do |widget|
112
- _, h = parent.size
113
- parent.resize(widget.value, h)
114
- w, _h = parent.size
115
- widget.value = w if widget.value + 1 < w
116
- end
136
+ def bind_combo_boxes_with_settings
137
+ bind_combo_box_with_setting(cursor_shape_sel, "cursor-shape")
138
+ bind_combo_box_with_setting(cursor_blink_mode_sel, "cursor-blink-mode")
139
+ bind_combo_box_with_setting(backspace_binding_sel, "backspace-binding")
140
+ bind_combo_box_with_setting(delete_binding_sel, "delete-binding")
117
141
  end
118
142
 
119
- def self.gen_height_button_action(spin_widget, parent)
120
- spin_widget.signal_connect "value-changed" do |widget|
121
- w, _h = parent.size
122
- parent.resize(w, widget.value)
123
- _, h = parent.size
124
- widget.value = h if widget.value + 1 < h
125
- end
143
+ def bind_spin_buttons_with_settings
144
+ bind_spin_button_with_setting(width_spin, "width")
145
+ bind_spin_button_with_setting(height_spin, "height")
126
146
  end
127
147
 
128
- def self.get_entry_value(builder)
129
- text = builder["shell_entry"].text
130
- { "-TopinambourWindow-shell" => text }
148
+ def configure_headerbar
149
+ headerbar = Gtk::HeaderBar.new
150
+ headerbar.title = "Topinambour Preferences"
151
+ headerbar.show_close_button = true
152
+ set_titlebar(headerbar)
131
153
  end
132
154
 
133
- def self.get_switch_values(builder)
134
- props = {}
135
- %w(audible_bell allow_bold scroll_on_output
136
- scroll_on_keystroke rewrap_on_resize mouse_autohide).each do |prop_name|
137
- switch = builder["#{prop_name}_switch"]
138
- name = prop_name.tr("_", "-")
139
- props["-TopinambourTerminal-#{name}"] = switch.active?
155
+ def initialize_use_custom_css_settings
156
+ setting = "custom-css"
157
+ switch = use_custom_css_switch
158
+ switch.active = @settings[setting]
159
+ css_chooser_button.sensitive = @settings[setting]
160
+ end
161
+
162
+ def set_switch_to_initial_state(_switch, setting)
163
+ state = @settings[setting]
164
+ m = "#{setting.tr('-', '_')}="
165
+ @parent.notebook.each do |tab|
166
+ tab.term.send(m, state)
140
167
  end
141
- props
142
168
  end
143
169
 
144
- def self.get_combo_values(builder)
145
- props = {}
146
- %w(cursor_shape cursor_blink_mode backspace_binding
147
- delete_binding).each do |prop_name|
148
- combobox = builder["#{prop_name}_sel"]
149
- value = combobox.active_id.gsub(/_id\z/, "")
150
- name = prop_name.tr("_", "-")
151
- props["-TopinambourTerminal-#{name}"] = value.to_sym
170
+ def bind_switch_state_with_setting(switch, setting)
171
+ set_switch_to_initial_state(switch, setting)
172
+ @settings.bind(setting,
173
+ switch,
174
+ "active",
175
+ Gio::SettingsBindFlags::DEFAULT)
176
+ switch.signal_connect "state-set" do |_switch, state|
177
+ m = "#{setting.tr('-', '_')}="
178
+ @parent.notebook.each { |tab| tab.term.send(m, state) }
179
+ false
180
+ end
181
+ end
182
+
183
+ def set_combo_to_initial_state(_combo_box, setting)
184
+ active = @settings[setting]
185
+ m = "#{setting.tr('-', '_')}="
186
+ @parent.notebook.each { |tab| tab.term.send(m, active) }
187
+ end
188
+
189
+ def bind_combo_box_with_setting(combo_box, setting)
190
+ set_combo_to_initial_state(combo_box, setting)
191
+ @settings.bind(setting,
192
+ combo_box,
193
+ "active",
194
+ Gio::SettingsBindFlags::DEFAULT)
195
+ combo_box.signal_connect "changed" do
196
+ m = "#{setting.tr('-', '_')}="
197
+ @parent.notebook.each { |tab| tab.term.send(m, combo_box.active) }
198
+ false
152
199
  end
153
- props
154
200
  end
155
201
 
156
- def self.get_spin_values(widget)
157
- props = {}
158
- w, h = widget.transient_for.size
159
- props["-TopinambourWindow-width"] = w
160
- props["-TopinambourWindow-height"] = h
161
- props
202
+ def bind_spin_button_with_setting(spin_button, setting)
203
+ @settings.bind(setting,
204
+ spin_button,
205
+ "value",
206
+ Gio::SettingsBindFlags::DEFAULT)
162
207
  end
163
208
  end
data/lib/shortcuts.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2016 Cedric LE MOIGNE, cedlemo@gmx.com
1
+ # Copyright 2016-2017 Cedric LE MOIGNE, cedlemo@gmx.com
2
2
  # This file is part of Topinambour.
3
3
  #
4
4
  # Topinambour is free software: you can redistribute it and/or modify
@@ -22,7 +22,6 @@ module TopinambourShortcuts
22
22
  end
23
23
 
24
24
  def self.handle_simple(event, window)
25
- overlay_mode = window.in_overlay_mode?
26
25
  case event.keyval
27
26
  when Gdk::Keyval::KEY_Escape # escape from overlay mode
28
27
  if window.in_overlay_mode?
@@ -62,31 +61,20 @@ module TopinambourShortcuts
62
61
  when Gdk::Keyval::KEY_slash
63
62
  window.show_searchbar
64
63
  true
65
- when Gdk::Keyval::KEY_Up
66
- window.toggle_shrink
64
+ when Gdk::Keyval::KEY_Page_Up
65
+ window.opacity = window.opacity + 0.05
66
+ true
67
+ when Gdk::Keyval::KEY_Page_Down
68
+ window.opacity = window.opacity - 0.05
67
69
  true
68
70
  end
69
71
  end
70
72
 
71
73
  def self.handle_key_press(window, event)
72
- keyval = event.keyval
73
74
  if ctrl_shift?(event)
74
75
  handle_ctrl_shift(event, window)
75
76
  else
76
77
  handle_simple(event, window)
77
78
  end
78
79
  end
79
-
80
- def self.handle_scroll(window, event)
81
- if ctrl_shift?(event)
82
- case event.direction
83
- when Gdk::ScrollDirection::UP
84
- window.opacity = window.opacity + 0.05
85
- true
86
- when Gdk::ScrollDirection::DOWN
87
- window.opacity = window.opacity - 0.05
88
- true
89
- end
90
- end
91
- end
92
80
  end
data/lib/terminal.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2015-2016 Cedric LE MOIGNE, cedlemo@gmx.com
1
+ # Copyright 2015-2017 Cedric LE MOIGNE, cedlemo@gmx.com
2
2
  # This file is part of Topinambour.
3
3
  #
4
4
  # Topinambour is free software: you can redistribute it and/or modify
@@ -19,7 +19,7 @@
19
19
 
20
20
  class TopinambourTabTerm < Gtk::Box
21
21
  attr_reader :term
22
- def initialize(command_string, working_dir=nil)
22
+ def initialize(command_string, working_dir = nil)
23
23
  super(:horizontal, 0)
24
24
  set_name("topinambour-tab-term")
25
25
  @term = TopinambourTerminal.new(command_string, working_dir)
@@ -31,26 +31,24 @@ class TopinambourTabTerm < Gtk::Box
31
31
  end
32
32
  ##
33
33
  # The default vte terminal customized
34
- class TopinambourTerminal
34
+ class TopinambourTerminal < Vte::Terminal
35
35
  attr_reader :pid, :menu, :regexes, :last_match
36
- attr_accessor :preview, :colors, :custom_title
36
+ attr_accessor :preview, :custom_title
37
37
 
38
38
  ##
39
39
  # Create a new TopinambourTerminal instance that runs command_string
40
40
  def initialize(command_string, working_dir = nil)
41
41
  super()
42
- # TODO: make a begin/rescue like in glib2-2.2.4/sample/shell.rb
43
- command_array = GLib::Shell.parse(command_string)
44
- @pid = spawn(:argv => command_array,
45
- :working_directory => working_dir,
46
- :spawn_flags => GLib::Spawn::SEARCH_PATH)
42
+
43
+ command_array = parse_command(command_string)
44
+ rescued_spawn(command_array, working_dir)
47
45
 
48
46
  signal_connect "child-exited" do |widget|
49
47
  tabterm = widget.parent
50
48
  notebook = tabterm.parent
51
49
  current_page = notebook.page_num(tabterm)
52
50
  notebook.remove_page(current_page)
53
- notebook.toplevel.application.quit unless notebook.n_pages >= 1
51
+ @application.quit unless notebook.n_pages >= 1
54
52
  end
55
53
 
56
54
  signal_connect "window-title-changed" do |widget|
@@ -59,8 +57,71 @@ class TopinambourTerminal
59
57
  when_terminal_title_change if notebook && notebook.current.term == self
60
58
  end
61
59
 
62
- builder = Gtk::Builder.new(:resource =>
63
- "/com/github/cedlemo/topinambour/terminal-menu.ui")
60
+ add_popup_menu
61
+ configure
62
+ end
63
+
64
+ def pid_dir
65
+ File.readlink("/proc/#{@pid}/cwd")
66
+ end
67
+
68
+ def terminal_title
69
+ @custom_title.class == String ? @custom_title : window_title.to_s
70
+ end
71
+
72
+ def load_settings
73
+ colors
74
+ set_colors(@colors[0], @colors[1], @colors[2..-1])
75
+ set_font(font)
76
+ end
77
+
78
+ def colors
79
+ colors_strings = application.settings["colorscheme"]
80
+ @colors = colors_strings.map { |c| Gdk::RGBA.parse(c) }
81
+ @colors
82
+ end
83
+
84
+ def font
85
+ font_str = application.settings["font"]
86
+ @font = Pango::FontDescription.new(font_str)
87
+ end
88
+
89
+ def colors=(colors)
90
+ set_colors(colors[0], colors[1], colors[2..-1])
91
+ end
92
+
93
+ def font=(font_str)
94
+ application.settings["font"] = Pango::FontDescription.new(font_str)
95
+ set_font(font)
96
+ @font = font
97
+ end
98
+
99
+ def application
100
+ @application = self.parent.toplevel.application unless @application
101
+ @application
102
+ end
103
+
104
+ private
105
+
106
+ def parse_command(command_string)
107
+ GLib::Shell.parse(command_string)
108
+ rescue GLib::ShellError => e
109
+ STDERR.puts "domain = #{e.domain}"
110
+ STDERR.puts "code = #{e.code}"
111
+ STDERR.puts "message = #{e.message}"
112
+ end
113
+
114
+ def rescued_spawn(command_array, working_dir)
115
+ @pid = spawn(:argv => command_array,
116
+ :working_directory => working_dir,
117
+ :spawn_flags => GLib::Spawn::SEARCH_PATH)
118
+ rescue => e
119
+ STDERR.puts e.message
120
+ end
121
+
122
+ def add_popup_menu
123
+ ui = "/com/github/cedlemo/topinambour/terminal-menu.ui"
124
+ builder = Gtk::Builder.new(:resource => ui)
64
125
  @menu = Gtk::Popover.new(self, builder["termmenu"])
65
126
 
66
127
  signal_connect "button-press-event" do |widget, event|
@@ -76,28 +137,12 @@ class TopinambourTerminal
76
137
  false
77
138
  end
78
139
  end
79
-
80
- configure
81
- end
82
-
83
- def pid_dir
84
- File.readlink("/proc/#{@pid}/cwd")
85
- end
86
-
87
- def terminal_title
88
- @custom_title.class == String ? @custom_title : window_title.to_s
89
140
  end
90
141
 
91
- private
92
-
93
142
  def configure
94
143
  set_rewrap_on_resize(true)
95
144
  set_scrollback_lines(-1)
96
145
  search_set_wrap_around(true)
97
- @colors = css_colors
98
- set_font(css_font)
99
- apply_colors
100
- load_properties
101
146
  add_matches
102
147
  end
103
148
 
@@ -149,11 +194,9 @@ class TopinambourTerminal
149
194
  end
150
195
 
151
196
  def launch_default_for_regex_match(match, regex_type)
152
- begin
153
- Gio::AppInfo.launch_default_for_uri(match)
154
- rescue => e
155
- puts "error : #{e.message}\n\tfor match: #{match} of type :#{regex_type}"
156
- end
197
+ Gio::AppInfo.launch_default_for_uri(match)
198
+ rescue => e
199
+ puts "error : #{e.message}\n\tfor match: #{match} of type :#{regex_type}"
157
200
  end
158
201
 
159
202
  def launch_color_visualizer(color_name)