Files
meow/lib/meow/ui.rb
T
2026-09-17 01:08:16 +02:00

171 lines
5.7 KiB
Ruby

module MeowClient
class UI
def initialize(program)
@program = program
@profiles = ProfileRepository.new(program)
end
def main
loop do
action, profile = connection_manager
case action
when :connect
connect(profile)
when :new
edit_profile(nil)
when :edit
edit_profile(profile)
when :delete
delete_profile(profile)
else
break
end
end
end
private
def connection_manager
profiles = @profiles.all
labels = profiles.map do |profile|
security = profile.tls ? _("TLS") : _("plain")
_("%{name}, %{host}, port %{port}, %{security}") % {
:name => profile.name,
:host => profile.host,
:port => profile.port,
:security => security
}
end
list = ListBox.new(
labels,
:header => _("Connections"),
:empty_label => _("No connection profiles. Choose New to create one.")
)
connect_button = Button.new(_("Connect"))
new_button = Button.new(_("New"))
edit_button = Button.new(_("Edit"))
delete_button = Button.new(_("Delete"))
exit_button = Button.new(_("Exit"))
form = Form.new([list, connect_button, new_button, edit_button, delete_button, exit_button], :quiet => true)
form.accept_button = connect_button
form.cancel_button = exit_button
result = nil
choose = proc do |action|
if profiles.empty? && [:connect, :edit, :delete].include?(action)
alert(_("Create a connection profile first."))
else
result = [action, profiles[list.index]]
form.resume
end
end
connect_button.on(:press) { choose.call(:connect) }
new_button.on(:press) do
result = [:new, nil]
form.resume
end
edit_button.on(:press) { choose.call(:edit) }
delete_button.on(:press) { choose.call(:delete) }
exit_button.on(:press) do
result = [:exit, nil]
form.resume
end
form.wait
result || [:exit, nil]
end
def edit_profile(existing)
profile = existing || @profiles.build
name = EditBox.new(_("Profile name"), :text => profile.name)
host = EditBox.new(_("Server host"), :text => profile.host)
port = EditBox.new(_("Port"), :type => EditBox::Flags::Numbers, :text => profile.port.to_s)
tls = CheckBox.new(_("Use TLS"), :checked => profile.tls)
auto_read = CheckBox.new(_("Read incoming text automatically"), :checked => profile.auto_read)
encoding_names = ProfileRepository::ENCODINGS.keys
encoding_index = [ProfileRepository::ENCODINGS.values.index(profile.encoding) || 0, 0].max
encoding = ChoiceListBox.new(
[[_("Character encoding"), encoding_names, encoding_index]],
:header => _("Connection options")
)
save = Button.new(_("Save"))
cancel = Button.new(_("Cancel"))
form = Form.new([name, host, port, tls, auto_read, encoding, save, cancel], :quiet => true)
form.accept_button = save
form.cancel_button = cancel
saved = false
save.on(:press) do
candidate = @profiles.build(
:id => profile.id,
:name => name.text,
:host => host.text,
:port => port.text,
:tls => tls.checked,
:encoding => ProfileRepository::ENCODINGS[encoding_names[encoding.value(0)]],
:auto_read => auto_read.checked
)
errors = @profiles.validate(candidate)
if errors.empty?
@profiles.save(candidate)
saved = true
form.resume
else
alert(errors.join("\n"))
end
end
cancel.on(:press) { form.resume }
form.wait
speak(_("Connection profile saved.")) if saved
rescue Exception => error
Log.error("Meow profile editor failed: #{error.class}: #{error.message}") if defined?(Log)
alert(_("Could not save the connection profile: %{message}") % {:message => error.message})
end
def delete_profile(profile)
return if profile == nil
return unless confirm(_("Delete connection %{name}?") % {:name => profile.name})
@profiles.delete(profile.id)
speak(_("Connection profile deleted."))
rescue Exception => error
alert(_("Could not delete the connection profile: %{message}") % {:message => error.message})
end
def connect(profile)
transport = begin
EltenAPI::Tasks.run(
:title => _("Connecting to %{name}") % {:name => profile.name},
:timeout => 20
) do |_progress, token|
token.raise_if_cancelled!
Transport.connect(profile)
end
rescue EltenAPI::Tasks::Cancelled
speak(_("Connection cancelled."))
return
rescue EltenAPI::Tasks::TimedOut
alert(_("The connection attempt timed out."))
return
rescue SocketError, SystemCallError, IOError, OpenSSL::SSL::SSLError, EltenAPI::TLS::Error => error
alert(_("Could not connect: %{message}") % {:message => error.message})
return
rescue Exception => error
Log.error("Meow connection failed: #{error.class}: #{error.message}") if defined?(Log)
alert(_("Could not connect: %{message}") % {:message => error.message})
return
end
@program.manage(transport)
begin
Session.new(@program, profile, transport).run
rescue Exception => error
Log.error("Meow session failed: #{error.class}: #{error.message}") if defined?(Log)
alert(_("The session closed because of an error: %{message}") % {:message => error.message})
ensure
transport.close
@program.release(transport, :close => false)
end
end
end
end