topinambour 1.0.11 → 1.0.12

Sign up to get free protection for your applications and to get access to all the features.
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)