Skip to content

Commit 5d44c8b

Browse files
committed
Merge pull request #11 from Expez/ui
Merge initial UI roughout
2 parents f14e2e3 + fbbb674 commit 5d44c8b

17 files changed

+399
-41
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.rbx

.rvmrc

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
77
# Only full ruby name is supported here, for short names use:
88
# echo "rvm use 1.9.3" > .rvmrc
9-
environment_id="ruby-1.9.3-p374@hatter"
9+
environment_id="rbx-head@hatter"
1010

1111
# Uncomment the following lines if you want to verify rvm version per project
1212
# rvmrc_rvm_version="1.18.3 (stable)" # 1.10.1 seams as a safe start
@@ -46,3 +46,6 @@ fi
4646
# then
4747
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
4848
# fi
49+
50+
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
51+
export RUBYLIB="$DIR/lib"

Gemfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ group :development, :test do
55
gem 'pry'
66
gem 'rake'
77
gem 'mail'
8-
gem 'rbcurse'
8+
gem 'rb_termbox'
99
gem 'configtoolkit'
1010
end
1111

Gemfile.lock

+3-13
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@ GEM
1414
diff-lcs (1.1.3)
1515
ffi (1.3.1)
1616
ffi (1.3.1-x86-mingw32)
17-
ffi-locale (1.0.1)
18-
ffi (>= 1.0.9)
19-
ffi-ncurses (0.4.0)
20-
ffi (>= 1.0.9)
21-
ffi-locale (>= 1.0.0)
2217
i18n (0.6.1)
2318
mail (2.5.3)
2419
i18n (>= 0.4.0)
@@ -37,13 +32,8 @@ GEM
3732
slop (~> 3.4)
3833
win32console (~> 1.3)
3934
rake (10.0.3)
40-
rbcurse (1.5.2)
41-
rbcurse-core (>= 0.0)
42-
rbcurse-extras (>= 0.0)
43-
rbcurse-core (0.0.3)
44-
ffi-ncurses (>= 0.4.0)
45-
rbcurse-extras (0.0.0)
46-
rbcurse-core (>= 0.0.3)
35+
rb_termbox (0.1.0)
36+
ffi
4737
relative (1.0.3)
4838
rspec (2.12.0)
4939
rspec-core (~> 2.12.0)
@@ -69,5 +59,5 @@ DEPENDENCIES
6959
mail
7060
pry
7161
rake
72-
rbcurse
62+
rb_termbox
7363
rspec

.hatterrc renamed to hatterrc

+6
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ maildir = /path/to/maildir
88

99
# The format of the maildir, e.g maildir or mbox
1010
maildir_format = maildir
11+
12+
# Hatter relies on Termbox for the terminal UI
13+
termbox_library_path = /usr/lib/libtermbox.so
14+
15+
# All the colors i Hatter can be customized:
16+
colors = {foreground => green, background => black}

lib/configuration.rb

+14-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
require 'configtoolkit'
22
require 'configtoolkit/keyvaluereader'
3+
require 'relative'
34

45
class Configuration < ConfigToolkit::BaseConfig
56

6-
# Reads a configuration from file.
7-
#
8-
# @param [String] configPath the path to a configuration file.
9-
#
10-
# @return [Configuration] with values loaded from file.
11-
def self.from_file configPath
12-
reader = ConfigToolkit::KeyValueReader.new configPath
7+
include Singleton
8+
9+
CONFIG_FILE = File.expand_path_relative_to_caller("../hatterrc")
10+
11+
def initialize(config_file = CONFIG_FILE)
12+
reader = ConfigToolkit::KeyValueReader.new config_file
1313
load reader
1414
end
1515

16+
class Colors < ConfigToolkit::BaseConfig
17+
add_required_param(:foreground, String)
18+
add_required_param(:background, String)
19+
end
20+
1621
add_required_param(:maildir, String)
1722
add_required_param(:maildir_format, String)
23+
add_required_param(:termbox_library_path, String)
24+
add_required_param(:colors, Colors)
1825
end

lib/hatter.rb

+8
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
1+
require 'terminal'
12

3+
terminal = Terminal.new
4+
begin
5+
terminal.draw
6+
sleep(10)
7+
ensure
8+
terminal.shutdown
9+
end

lib/mailbox/maildir_mailbox.rb

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'mail'
2+
3+
class MaildirMailbox
4+
5+
def initialize(maildir_path)
6+
@maildir_path = maildir_path
7+
@maildir_path = "spec/fixtures/maildir"
8+
end
9+
10+
def mail(where={label: nil, folder: nil, from: nil, to: nil, date: nil})
11+
Mail.read(@maildir_path + "/INBOX/cur/TRAIN_00001.eml")
12+
end
13+
14+
def most_recent
15+
mail
16+
end
17+
18+
def labels
19+
["work", "cat pictures", "news"]
20+
end
21+
22+
def folders
23+
%w{ inbox archived spam trash }
24+
end
25+
end

lib/terminal.rb

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require 'termbox'
2+
require 'views/mail_view'
3+
require 'views/label_view'
4+
require 'views/folder_view'
5+
6+
module Termbox
7+
TB_INPUT_ESCAPE = 1
8+
TB_INPUT_ALT = 2
9+
end
10+
11+
class Terminal
12+
13+
def initialize
14+
initialize_termbox
15+
initialize_views
16+
end
17+
18+
def draw
19+
Termbox.tb_clear
20+
@views.each {|view| view.draw}
21+
Termbox.tb_present
22+
end
23+
24+
def shutdown
25+
Termbox.tb_shutdown
26+
end
27+
28+
private
29+
30+
def initialize_views
31+
folder_view_location = {x0: 0, y0: 0, x1: 19, y1: 40}
32+
label_view_location = {x0: 0, y0: 41, x1: 19, y1: 79}
33+
mail_view_location = {x0: 20, y0: 0, x1: 79, y1: 79}
34+
@views = [MailView.new(mail_view_location),
35+
LabelView.new(label_view_location),
36+
FolderView.new(folder_view_location)]
37+
end
38+
39+
def initialize_termbox
40+
libtermbox_path = Configuration.instance.termbox_library_path
41+
Termbox.initialize_library libtermbox_path
42+
unless Termbox.tb_init
43+
raise "Failed failed to start UI"
44+
end
45+
@event = Termbox::Event.new
46+
Termbox.tb_select_input_mode Termbox::TB_INPUT_ESCAPE
47+
end
48+
49+
end

lib/views/folder_view.rb

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class FolderView < View
2+
3+
def draw
4+
draw_folders
5+
draw_horizontal_line @end_y
6+
end
7+
8+
private
9+
10+
def draw_folders
11+
x = @start_x
12+
y = @start_y
13+
y = draw_text("Folders:", x, y)
14+
@mailbox.folders.each do |folder|
15+
y = draw_text folder, x, y + 1
16+
end
17+
end
18+
end

lib/views/label_view.rb

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class LabelView < View
2+
3+
def draw
4+
draw_labels
5+
end
6+
7+
private
8+
9+
def draw_labels
10+
x = @start_x
11+
y = @start_y
12+
y = draw_text("Labels:", x, y)
13+
@mailbox.labels.each do |label|
14+
y = draw_text label, x, y + 1
15+
end
16+
end
17+
end

lib/views/mail_view.rb

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
require 'views/view'
2+
require 'termbox'
3+
4+
class MailView < View
5+
6+
def initialize(location)
7+
super
8+
@mail = @mailbox.most_recent
9+
end
10+
11+
def draw
12+
draw_borders
13+
draw_mail
14+
end
15+
16+
private
17+
18+
def draw_borders
19+
draw_horizontal_line @start_y
20+
draw_horizontal_line @end_y
21+
draw_vertical_line @start_x
22+
draw_vertical_line @end_x
23+
end
24+
25+
def draw_mail
26+
from, = @mail.from
27+
subject = @mail.subject
28+
text = @mail.body.decoded
29+
x = @start_x + @Border_width + 1
30+
y = @start_y + @Border_width + 1
31+
32+
y = draw_text(("From: " + from), x, y) + 1
33+
y = draw_text(("Subject: " + subject), x, y + 1) + 1
34+
draw_text text, x, y + 1
35+
end
36+
37+
end

lib/views/view.rb

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
require 'configuration'
2+
require 'termbox'
3+
4+
class View
5+
6+
attr_reader :colors, :mailbox
7+
8+
def initialize(location)
9+
@Border_width = 1
10+
@start_x = location[:x0]
11+
@start_y = location[:y0]
12+
@end_x = location[:x1]
13+
@end_y = location[:y1]
14+
set_mailbox
15+
set_colors
16+
end
17+
18+
def draw_text(text, x, y)
19+
words = text.split(/\s+/)
20+
words.each do |word|
21+
if x + word.length + 1 >= @end_x
22+
y += 1
23+
x = @start_x + @Border_width + 1
24+
redo
25+
elsif y >= @end_y
26+
return y
27+
end
28+
word.each_char do |c|
29+
Termbox.tb_change_cell(x, y, c.ord, @colors[:bg], @colors[:fg])
30+
x += 1
31+
end
32+
Termbox.tb_change_cell(x, y, " ".ord, @colors[:bg], @colors[:fg])
33+
x += 1
34+
end
35+
return y
36+
end
37+
38+
def draw_horizontal_line(at)
39+
@start_x.upto(@end_x) do |x|
40+
Termbox.tb_change_cell(x, at, " ".ord, @colors[:bg], @colors[:fg])
41+
end
42+
end
43+
44+
def draw_vertical_line(at)
45+
@start_y.upto(@end_y) do |y|
46+
Termbox.tb_change_cell(at, y, " ".ord, @colors[:bg], @colors[:fg])
47+
end
48+
end
49+
50+
private
51+
52+
# Reads the maildir format from config file. Requires the class and sets
53+
# @mailbox to mailbox/<maildir-format>Mailbox
54+
def set_mailbox
55+
config = Configuration.instance
56+
maildir_format = config.maildir_format
57+
mailbox_class = maildir_format.capitalize + "Mailbox"
58+
class_dir = File.expand_path('../../', __FILE__) + "/mailbox"
59+
mailbox_file = File.join class_dir, maildir_format + "_mailbox"
60+
require mailbox_file
61+
maildir_path = config.maildir
62+
@mailbox = self.class.const_get(mailbox_class).new maildir_path
63+
end
64+
65+
def set_colors
66+
colors = Configuration.instance.colors
67+
prefix = 'Termbox::Colors[:'
68+
suffix = ']'
69+
fg = prefix + colors.foreground + suffix
70+
bg = prefix + colors.background + suffix
71+
@colors = {fg: (eval fg), bg: (eval bg)}
72+
end
73+
74+
end

spec/configuration_spec.rb

+20-19
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,37 @@
55
describe 'Configuration' do
66

77
before :all do
8+
CONFIG_FILE = File.expand_path_relative_to_caller("../hatterrc")
89
@maildir_path = "/path/to/maildir"
9-
CONFIG_FILE = File.expand_path_relative_to_caller("../.hatterrc")
1010
@maildir_format = "maildir"
11-
@config = Configuration.new() do |config|
12-
config.maildir = @maildir_path
13-
config.maildir_format = @maildir_format
14-
end
11+
@termbox_library_path = "/usr/lib/libtermbox.so"
1512
end
1613

17-
it "contains all the required configuration settings" do
18-
@config.maildir?.should be_true
19-
@config.maildir_format?.should be_true
14+
it "barfs when required values are missing" do
15+
config = File.read CONFIG_FILE
16+
config.gsub!(/maildir/, 'asdf')
17+
File.open("invalid_config", "w") {|file| file.write config}
18+
expect {Configuration.instance "invalid_config"}.to raise_error
19+
FileUtils::rm("invalid_config")
2020
end
2121

22-
it "the settings have the correct values" do
23-
@config.maildir.should eq @maildir_path
24-
@config.maildir_format.should eq @maildir_format
22+
it "reads the configuration values from file" do
23+
config = Configuration.instance
24+
config.maildir.should eq @maildir_path
25+
config.maildir_format.should eq @maildir_format
2526
end
2627

27-
it "reads the configuration values from file" do
28-
config = Configuration.from_file CONFIG_FILE
28+
it "the settings have the correct values" do
29+
config = Configuration.instance
2930
config.maildir.should eq @maildir_path
3031
config.maildir_format.should eq @maildir_format
32+
config.termbox_library_path.should eq @termbox_library_path
3133
end
3234

33-
it "barfs when required values are missing" do
34-
config = File.read CONFIG_FILE
35-
config.gsub!(/maildir/, 'asdf')
36-
File.open("invalid_config", "w") {|file| file.write config}
37-
expect {Configuration.from_file("invalid_config")}.to raise_error
38-
FileUtils::rm("invalid_config")
35+
it "contains a nested config with colors" do
36+
config = Configuration.instance
37+
config.colors.foreground.should eq "green"
38+
config.colors.foreground.should eq "green"
3939
end
40+
4041
end

0 commit comments

Comments
 (0)