tabscroll 0.0.1
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 +7 -0
- data/bin/tabscroll +72 -0
- data/lib/tabscroll/engine.rb +95 -0
- data/lib/tabscroll/popup.rb +60 -0
- data/lib/tabscroll/screen.rb +125 -0
- data/lib/tabscroll/timer.rb +89 -0
- data/lib/tabscroll/track.rb +203 -0
- data/lib/tabscroll.rb +40 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a07da9616f2254f2fd9bd14e11a5e8f327c132df
|
4
|
+
data.tar.gz: 854189ab82e34c155233f7b5b9b8ac0768105761
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 00977e49cde6a2ccf4666510585224a82fdd4efbc0d38b9e3f38008eced4f969d0d63b0b693285bea776feb6aa2589be7decd1ebba1346365c1ede6e80df4e86
|
7
|
+
data.tar.gz: d1a0f44480d736aa8f1c9a7b2f171d873d79b3d66c9a4d715b85eabee982552606c9d1aaa23cba8e37795b0136a7a11a6086f15b7be804154d5e68a437187ef6
|
data/bin/tabscroll
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# The main `tabscroll` executable
|
4
|
+
|
5
|
+
require 'tabscroll'
|
6
|
+
|
7
|
+
begin
|
8
|
+
filename = ARGV[0]
|
9
|
+
if not filename
|
10
|
+
puts "Usage:"
|
11
|
+
puts " $ #{PROGRAM_NAME} (filename)"
|
12
|
+
exit 666
|
13
|
+
end
|
14
|
+
|
15
|
+
$engine = Engine.new
|
16
|
+
if not $engine
|
17
|
+
puts 'Failed to start curses!'
|
18
|
+
exit 1337
|
19
|
+
end
|
20
|
+
$engine.timeout 10
|
21
|
+
|
22
|
+
win = Screen.new(0, 1, $engine.width, $engine.height - 2)
|
23
|
+
track = Track.new win
|
24
|
+
track.load filename
|
25
|
+
track.auto_scroll true
|
26
|
+
|
27
|
+
titlebar = Screen.new(0, 0, $engine.width, 1)
|
28
|
+
statusbar = Screen.new(0, ($engine.height - 1), $engine.width, 1)
|
29
|
+
|
30
|
+
bars_hidden = false
|
31
|
+
finished = false
|
32
|
+
while not finished
|
33
|
+
# WHY DOES GETCH CLEARS UP THE WHOLE SCREEN?
|
34
|
+
# IT DOESNT MAKE ANY SENSE
|
35
|
+
c = $engine.getchar
|
36
|
+
case c
|
37
|
+
when 'e'
|
38
|
+
track.end
|
39
|
+
when 'a'
|
40
|
+
track.begin
|
41
|
+
when 'h'
|
42
|
+
show_help_window
|
43
|
+
when 'o'
|
44
|
+
bars_hidden = (bars_hidden ? false : true)
|
45
|
+
Curses::clear
|
46
|
+
when '<'
|
47
|
+
track.scroll -5
|
48
|
+
when '>'
|
49
|
+
track.scroll 5
|
50
|
+
when Curses::KEY_LEFT
|
51
|
+
track.speed -= 1
|
52
|
+
when Curses::KEY_RIGHT
|
53
|
+
track.speed += 1
|
54
|
+
when Curses::KEY_DOWN, Curses::KEY_UP
|
55
|
+
track.speed = 0
|
56
|
+
when 'q'
|
57
|
+
quit
|
58
|
+
end
|
59
|
+
|
60
|
+
track.update
|
61
|
+
track.show
|
62
|
+
if not bars_hidden
|
63
|
+
titlebar.mvaddstr(0, 0, "#{filename} (#{track.percent_completed}%) ", Engine::Colors[:cyan])
|
64
|
+
titlebar.mvaddstr_right(0, " Speed: #{track.speed}", Engine::Colors[:cyan])
|
65
|
+
|
66
|
+
statusbar.mvaddstr_left(0, "#{PROGRAM_NAME} v#{PROGRAM_VERSION} - press `h` for help", Engine::Colors[:green])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
quit
|
71
|
+
end
|
72
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
require 'curses'
|
3
|
+
|
4
|
+
# The main interface with Curses.
|
5
|
+
#
|
6
|
+
# This acts as a middleman, abstracting away curses' details.
|
7
|
+
class Engine
|
8
|
+
|
9
|
+
# All possible colors.
|
10
|
+
Colors = {
|
11
|
+
:black => 1,
|
12
|
+
:white => 2,
|
13
|
+
:red => 3,
|
14
|
+
:yellow => 4,
|
15
|
+
:magenta => 5,
|
16
|
+
:blue => 6,
|
17
|
+
:green => 7,
|
18
|
+
:cyan => 8
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
# Initializes Ncurses with minimal +width+ and +height+.
|
22
|
+
#
|
23
|
+
def initialize(min_width=nil, min_height=nil)
|
24
|
+
@has_colors = nil
|
25
|
+
|
26
|
+
@screen = Curses::init_screen
|
27
|
+
return nil if not @screen
|
28
|
+
|
29
|
+
if min_width and min_height
|
30
|
+
cur_width = @screen.maxx
|
31
|
+
cur_height = @screen.maxy
|
32
|
+
|
33
|
+
if cur_width < @width or cur_height < @height
|
34
|
+
self.exit
|
35
|
+
$stderr << "Error: Screen size too small (#{cur_width}x#{cur_height})\n"
|
36
|
+
$stderr << "Please resize your terminal to at least #{@width}x#{@height}\n"
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@has_colors = Curses.has_colors?
|
42
|
+
if @has_colors
|
43
|
+
Curses.start_color
|
44
|
+
Curses.use_default_colors # will use default background
|
45
|
+
|
46
|
+
# Initializes: constant foreground bg
|
47
|
+
Curses.init_pair(Colors[:white], Curses::COLOR_BLACK, -1)
|
48
|
+
Curses.init_pair(Colors[:blue], Curses::COLOR_BLUE, -1)
|
49
|
+
Curses.init_pair(Colors[:red], Curses::COLOR_RED, -1)
|
50
|
+
Curses.init_pair(Colors[:green], Curses::COLOR_GREEN, -1)
|
51
|
+
Curses.init_pair(Colors[:magenta], Curses::COLOR_MAGENTA, -1)
|
52
|
+
Curses.init_pair(Colors[:yellow], Curses::COLOR_YELLOW, -1)
|
53
|
+
Curses.init_pair(Colors[:cyan], Curses::COLOR_CYAN, -1)
|
54
|
+
end
|
55
|
+
|
56
|
+
Curses::cbreak
|
57
|
+
Curses::curs_set 0
|
58
|
+
Curses::noecho
|
59
|
+
Curses::nonl
|
60
|
+
Curses::stdscr.keypad = true # extra keys
|
61
|
+
end
|
62
|
+
|
63
|
+
def width
|
64
|
+
return Curses::cols
|
65
|
+
end
|
66
|
+
|
67
|
+
def height
|
68
|
+
return Curses::lines
|
69
|
+
end
|
70
|
+
|
71
|
+
def exit
|
72
|
+
Curses::refresh
|
73
|
+
Curses::close_screen
|
74
|
+
end
|
75
|
+
|
76
|
+
def set_color color
|
77
|
+
if @has_colors
|
78
|
+
@screen.attron Curses::color_pair(color)
|
79
|
+
return self
|
80
|
+
else
|
81
|
+
return nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def getchar
|
86
|
+
return Curses::getch
|
87
|
+
end
|
88
|
+
|
89
|
+
# +timeout+ says how many milliseconds we wait for a key to be
|
90
|
+
# pressed.
|
91
|
+
def timeout timeout
|
92
|
+
Curses::timeout = timeout
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
require_relative 'screen.rb'
|
3
|
+
|
4
|
+
# A simple centralized popup.
|
5
|
+
#
|
6
|
+
# It resizes as you place the text on it.
|
7
|
+
class Popup < Screen
|
8
|
+
|
9
|
+
def initialize(title, text)
|
10
|
+
@title = title
|
11
|
+
@text = []
|
12
|
+
text.each_line do |line|
|
13
|
+
@text += [line.chomp]
|
14
|
+
end
|
15
|
+
|
16
|
+
max_width = title.length
|
17
|
+
max_height = 1
|
18
|
+
|
19
|
+
@text.each do |line|
|
20
|
+
max_width = line.length if line.length > max_width
|
21
|
+
max_height += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
max_width += 2 # left-right borders
|
25
|
+
max_height += 1 # down border
|
26
|
+
|
27
|
+
x = Curses::cols/2 - max_width/2
|
28
|
+
y = Curses::lines/2 - max_height/2
|
29
|
+
|
30
|
+
super(x, y, max_width, max_height)
|
31
|
+
self.background ' '
|
32
|
+
self.box
|
33
|
+
|
34
|
+
self.mvaddstr_center(0, title, Engine::Colors[:cyan])
|
35
|
+
|
36
|
+
y = 1
|
37
|
+
@text.each do |line|
|
38
|
+
self.mvaddstr(1, y, line)
|
39
|
+
y += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def show
|
44
|
+
finished = false
|
45
|
+
while not finished
|
46
|
+
c = Curses::getch
|
47
|
+
case c
|
48
|
+
when 'q'
|
49
|
+
return true
|
50
|
+
when 'h'
|
51
|
+
finished = true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Curses::stdscr.clear
|
56
|
+
Curses::stdscr.refresh
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,125 @@
|
|
1
|
+
|
2
|
+
require 'curses'
|
3
|
+
|
4
|
+
# A segment of the terminal screen.
|
5
|
+
#
|
6
|
+
# BUG WARNING HACK FUCK
|
7
|
+
# Whenever I use @win.attrset/@win.setpos/@win.addch
|
8
|
+
# it doesn't work at all.
|
9
|
+
# Apparently, when I do this, Curses::getch clears up
|
10
|
+
# the entire screen.
|
11
|
+
#
|
12
|
+
# DO NOT DO THIS
|
13
|
+
#
|
14
|
+
class Screen
|
15
|
+
attr_reader :width, :height
|
16
|
+
|
17
|
+
# Creates a Screen at `x` `y` `w` `h`.
|
18
|
+
def initialize(x, y, w, h)
|
19
|
+
@win = Curses::Window.new(h, w, y, x)
|
20
|
+
@width = w
|
21
|
+
@height = h
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sets the current color of the Screen.
|
25
|
+
def set_color color
|
26
|
+
Curses::attrset(Curses::color_pair color)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Executes a block of code encapsulated within a color on/off.
|
30
|
+
# Note that the color can be overrided.
|
31
|
+
def with_color(color=nil)
|
32
|
+
Curses::attron(Curses::color_pair color) if color
|
33
|
+
yield
|
34
|
+
Curses::attroff(Curses::color_pair color) if color
|
35
|
+
end
|
36
|
+
|
37
|
+
# Puts a character +c+ on (+x+, +y+) with optional +color+.
|
38
|
+
def mvaddch(x, y, c, color=nil)
|
39
|
+
return if x < 0 or x >= @width
|
40
|
+
return if y < 0 or y >= @height
|
41
|
+
|
42
|
+
self.with_color color do
|
43
|
+
Curses::setpos(@win.begy + y, @win.begx + x)
|
44
|
+
Curses::addch c
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Puts a string +str+ on (+x+, +y+) with optional +color+.
|
49
|
+
def mvaddstr(x, y, str, color=nil)
|
50
|
+
return if x < 0 or x >= @width
|
51
|
+
return if y < 0 or y >= @height
|
52
|
+
|
53
|
+
self.with_color color do
|
54
|
+
# @win.setpos(@win.begy + y, @win.begx + x)
|
55
|
+
# @win.addstr str
|
56
|
+
Curses::setpos(@win.begy + y, @win.begx + x)
|
57
|
+
Curses::addstr str
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Puts a string +str+ centered on +y+ with optional +color+.
|
62
|
+
def mvaddstr_center(y, str, color=nil)
|
63
|
+
x = (@width/2) - (str.length/2)
|
64
|
+
self.mvaddstr(x, y, str, color)
|
65
|
+
end
|
66
|
+
|
67
|
+
def mvaddstr_left(y, str, color=nil)
|
68
|
+
self.mvaddstr(0, y, str, color)
|
69
|
+
end
|
70
|
+
|
71
|
+
def mvaddstr_right(y, str, color=nil)
|
72
|
+
x = @width - str.length
|
73
|
+
self.mvaddstr(x, y, str, color)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Erases all of the Screen's contents
|
77
|
+
def clear
|
78
|
+
@win.clear
|
79
|
+
end
|
80
|
+
|
81
|
+
# Commits the changes on the Screen.
|
82
|
+
def refresh
|
83
|
+
@win.refresh
|
84
|
+
end
|
85
|
+
|
86
|
+
# Moves window so that the upper-left corner is at `x` `y`.
|
87
|
+
def move(x, y)
|
88
|
+
@win.move(y, x)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Resizes window to +width+ and +h+eight.
|
92
|
+
def resize(w, h)
|
93
|
+
@win.resize(h, w)
|
94
|
+
@width = w
|
95
|
+
@height = h
|
96
|
+
end
|
97
|
+
|
98
|
+
# Set block/nonblocking reads for window.
|
99
|
+
#
|
100
|
+
# * If `delay` is negative, blocking read is used.
|
101
|
+
# * If `delay` is zero, nonblocking read is used.
|
102
|
+
# * If `delay` is positive, waits for `delay` milliseconds and
|
103
|
+
# returns ERR of no input.
|
104
|
+
def timeout(delay=-1)
|
105
|
+
@win.timeout = delay
|
106
|
+
end
|
107
|
+
|
108
|
+
def background char
|
109
|
+
@win.bkgd char
|
110
|
+
@win.refresh
|
111
|
+
end
|
112
|
+
|
113
|
+
# Sets the Screen border.
|
114
|
+
#
|
115
|
+
# * If all arguments are set, that's ok.
|
116
|
+
# * If only the first 2 arguments are set, they are the vertical
|
117
|
+
# and horizontal chars.
|
118
|
+
#
|
119
|
+
def box(horizontal=0, vertical=0)
|
120
|
+
@win.box(horizontal, vertical)
|
121
|
+
@win.refresh
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# A simple timer that counts in seconds.
|
2
|
+
#
|
3
|
+
# Usage:
|
4
|
+
# timer = Timer.new
|
5
|
+
# timer.start
|
6
|
+
# ...
|
7
|
+
# if timer.delta > 0.5 # half a second
|
8
|
+
# ...
|
9
|
+
#
|
10
|
+
class Timer
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@is_running = false
|
14
|
+
@is_paused = false
|
15
|
+
end
|
16
|
+
|
17
|
+
# Starts counting.
|
18
|
+
def start
|
19
|
+
return if @is_running
|
20
|
+
|
21
|
+
@start_time = Time.now
|
22
|
+
@stop_time = 0.0
|
23
|
+
@paused_time = 0.0
|
24
|
+
@is_running = true
|
25
|
+
@is_paused = false
|
26
|
+
end
|
27
|
+
|
28
|
+
# Stops counting.
|
29
|
+
def stop
|
30
|
+
return if not @is_running
|
31
|
+
|
32
|
+
@stop_time = Time.now
|
33
|
+
@is_running = false
|
34
|
+
@is_paused = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def restart
|
38
|
+
self.stop
|
39
|
+
self.start
|
40
|
+
end
|
41
|
+
|
42
|
+
def pause
|
43
|
+
return if not @is_running or @is_paused
|
44
|
+
|
45
|
+
@paused_time = (Time.now - @start_time)
|
46
|
+
@is_running = false
|
47
|
+
@is_paused = true
|
48
|
+
end
|
49
|
+
|
50
|
+
def unpause
|
51
|
+
return if not @is_paused or @is_running
|
52
|
+
|
53
|
+
@start_time = (Time.now - @paused_time)
|
54
|
+
@is_running = true
|
55
|
+
@is_paused = false
|
56
|
+
end
|
57
|
+
|
58
|
+
def running?
|
59
|
+
@is_running
|
60
|
+
end
|
61
|
+
|
62
|
+
def paused?
|
63
|
+
@is_paused
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the current delta in seconds (float).
|
67
|
+
def delta
|
68
|
+
if @is_running
|
69
|
+
return (Time.now.to_f - @start_time.to_f)
|
70
|
+
end
|
71
|
+
|
72
|
+
return @paused_time.to_f if @is_paused
|
73
|
+
|
74
|
+
return @start_time if @start_time == 0 # Something's wrong
|
75
|
+
|
76
|
+
return (@stop_time.to_f - @start_time.to_f)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Converts the timer's delta to a formatted string.
|
80
|
+
def to_s
|
81
|
+
min = (self.delta / 60).to_i
|
82
|
+
sec = (self.delta).to_i
|
83
|
+
msec = (self.delta * 100).to_i
|
84
|
+
|
85
|
+
"#{min}:#{sec}:#{msec}"
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,203 @@
|
|
1
|
+
|
2
|
+
require_relative 'screen.rb'
|
3
|
+
require_relative 'timer.rb'
|
4
|
+
|
5
|
+
# A full guitar tab, as shown on the screen.
|
6
|
+
# Note that it depends on an already-existing window to exist.
|
7
|
+
#
|
8
|
+
class Track
|
9
|
+
COMMENT_CHAR = '#'
|
10
|
+
attr_reader :screen, :percent_completed
|
11
|
+
attr_accessor :speed
|
12
|
+
|
13
|
+
# Creates a Track that will be shown on `screen`.
|
14
|
+
# See Screen.
|
15
|
+
def initialize(screen)
|
16
|
+
@offset = 0
|
17
|
+
@timer = Timer.new
|
18
|
+
@timer.start
|
19
|
+
@speed = 0
|
20
|
+
@screen = screen
|
21
|
+
@percent_completed = 0
|
22
|
+
|
23
|
+
@raw_track = []
|
24
|
+
@raw_track[0] = ""
|
25
|
+
@raw_track[1] = ""
|
26
|
+
@raw_track[2] = ""
|
27
|
+
@raw_track[3] = ""
|
28
|
+
@raw_track[4] = ""
|
29
|
+
@raw_track[5] = ""
|
30
|
+
@raw_track[6] = ""
|
31
|
+
end
|
32
|
+
|
33
|
+
# Loads and parses +filename+'s contents into Track.
|
34
|
+
def load filename
|
35
|
+
if not File.exist? filename
|
36
|
+
raise "Error: File '#{filename}' doesn't exist!"
|
37
|
+
end
|
38
|
+
if not File.file? filename
|
39
|
+
raise "Error: '#{filename}' is not a file!"
|
40
|
+
end
|
41
|
+
|
42
|
+
file = File.new filename
|
43
|
+
|
44
|
+
# The thing here is there's no way I can know in
|
45
|
+
# advance how many lines the tab track will have.
|
46
|
+
#
|
47
|
+
# People put lots of strange things on them like
|
48
|
+
# timing, comments, etecetera.
|
49
|
+
#
|
50
|
+
# So I will read all non-blank lines, creating a
|
51
|
+
# counter. Then I will use it to display the track
|
52
|
+
# onscreen.
|
53
|
+
#
|
54
|
+
# I will also make every line have the same width
|
55
|
+
# as of the biggest one.
|
56
|
+
|
57
|
+
# Any tab line MUST have EITHER ---1---9--| OR |---3----0
|
58
|
+
tab_line = /[-[:alnum:]]\||\|[-[:alnum:]]/
|
59
|
+
|
60
|
+
# Duration of each note only has those chars.
|
61
|
+
# So we look for anything BUT these chars.
|
62
|
+
not_duration_line = /[^WHQESTX \.]/
|
63
|
+
|
64
|
+
count = 0
|
65
|
+
max_width = 0
|
66
|
+
|
67
|
+
file.readlines.each do |line|
|
68
|
+
next if line[0] == COMMENT_CHAR
|
69
|
+
|
70
|
+
line.chomp!
|
71
|
+
if line.empty?
|
72
|
+
|
73
|
+
# Making sure everything will have the same width
|
74
|
+
@raw_track.each_with_index do |t, i|
|
75
|
+
if t.length < max_width
|
76
|
+
@raw_track[i] += (' ' * (max_width - t.length))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
count = 0
|
81
|
+
max_width = 0
|
82
|
+
|
83
|
+
# Lines must be EITHER a tab_line OR a duration_line.
|
84
|
+
# not not duration line means that
|
85
|
+
# (I should find a better way of expressing myself on regexes)
|
86
|
+
elsif (line =~ tab_line) or (not line =~ not_duration_line)
|
87
|
+
@raw_track[count] += line
|
88
|
+
|
89
|
+
if @raw_track[count].length > max_width
|
90
|
+
max_width = @raw_track[count].length
|
91
|
+
end
|
92
|
+
|
93
|
+
count += 1
|
94
|
+
|
95
|
+
end # Ignoring any other kind of line
|
96
|
+
|
97
|
+
if count > 7
|
98
|
+
raise "Error: Invalid format on '#{filename}'"
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Prints the track on the screen, along with string indicators
|
105
|
+
# on the left.
|
106
|
+
#
|
107
|
+
# It is shown at the vertical center of the provided Screen,
|
108
|
+
# spanning it's whole width.
|
109
|
+
def show
|
110
|
+
x = 1
|
111
|
+
y = (@screen.height/2) - (@raw_track.size/2)
|
112
|
+
|
113
|
+
# This both prints EADGBE and clears the whole screen,
|
114
|
+
# printing spaces where the track was.
|
115
|
+
#
|
116
|
+
# Also, if we have only 5 tracks, we leave the sixth
|
117
|
+
# indicator out of the screen.
|
118
|
+
if not @raw_track[6] =~ /[:blank:]/
|
119
|
+
@screen.mvaddstr(0, y, "E" + (' ' * (@screen.width - 1)))
|
120
|
+
@screen.mvaddstr(0, y + 1, "B" + (' ' * (@screen.width - 1)))
|
121
|
+
@screen.mvaddstr(0, y + 2, "G" + (' ' * (@screen.width - 1)))
|
122
|
+
@screen.mvaddstr(0, y + 3, "D" + (' ' * (@screen.width - 1)))
|
123
|
+
@screen.mvaddstr(0, y + 4, "A" + (' ' * (@screen.width - 1)))
|
124
|
+
@screen.mvaddstr(0, y + 5, "E" + (' ' * (@screen.width - 1)))
|
125
|
+
else
|
126
|
+
@screen.mvaddstr(0, y, ' ' * @screen.width)
|
127
|
+
@screen.mvaddstr(0, y + 1, "E" + (' ' * (@screen.width - 1)))
|
128
|
+
@screen.mvaddstr(0, y + 2, "B" + (' ' * (@screen.width - 1)))
|
129
|
+
@screen.mvaddstr(0, y + 3, "G" + (' ' * (@screen.width - 1)))
|
130
|
+
@screen.mvaddstr(0, y + 4, "D" + (' ' * (@screen.width - 1)))
|
131
|
+
@screen.mvaddstr(0, y + 5, "A" + (' ' * (@screen.width - 1)))
|
132
|
+
@screen.mvaddstr(0, y + 6, "E" + (' ' * (@screen.width - 1)))
|
133
|
+
end
|
134
|
+
|
135
|
+
(0...@raw_track.size).each do |i|
|
136
|
+
str = @raw_track[i]
|
137
|
+
str = str[@offset..(@offset + @screen.width - 2)]
|
138
|
+
@screen.mvaddstr(x, y + i, str)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Scrolls the guitar tab by `n`.
|
143
|
+
#
|
144
|
+
# * If `n` is positive, scroll forward.
|
145
|
+
# * If `n` is negative, scroll backward.
|
146
|
+
def scroll n
|
147
|
+
@offset += n
|
148
|
+
|
149
|
+
left_limit = 0
|
150
|
+
right_limit = (@raw_track[0].length - @screen.width + 1).abs
|
151
|
+
|
152
|
+
if @offset < left_limit then @offset = left_limit end
|
153
|
+
if @offset > right_limit then @offset = right_limit end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Goes to the beginning of the Track.
|
157
|
+
def begin
|
158
|
+
@offset = 0
|
159
|
+
@speed = 0
|
160
|
+
end
|
161
|
+
|
162
|
+
# Goes to the end of the Track.
|
163
|
+
def end
|
164
|
+
@offset = (@raw_track[0].length - @screen.width + 1).abs
|
165
|
+
@speed = 0
|
166
|
+
end
|
167
|
+
|
168
|
+
# Turns on/off Track's auto scroll functionality.
|
169
|
+
#
|
170
|
+
# Note that it won't work anyways if you don't keep calling
|
171
|
+
# +update+ method.
|
172
|
+
def auto_scroll option
|
173
|
+
if option == true
|
174
|
+
@timer.start if not @timer.running?
|
175
|
+
else
|
176
|
+
@timer.stop
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Updates Track's auto scroll functionality.
|
181
|
+
def update
|
182
|
+
return if not @timer.running?
|
183
|
+
|
184
|
+
current_completed = @offset + @screen.width - 1
|
185
|
+
@percent_completed = ((100.0 * current_completed)/@raw_track[0].length).ceil
|
186
|
+
|
187
|
+
if @timer.running? and @speed != 0
|
188
|
+
if @timer.delta > (1/(@speed*0.5)).abs
|
189
|
+
if @speed > 0
|
190
|
+
self.scroll 1
|
191
|
+
self.show
|
192
|
+
else
|
193
|
+
self.scroll -1
|
194
|
+
self.show
|
195
|
+
end
|
196
|
+
|
197
|
+
@timer.restart
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
data/lib/tabscroll.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# The main executable file.
|
2
|
+
|
3
|
+
require_relative 'tabscroll/engine'
|
4
|
+
require_relative 'tabscroll/screen'
|
5
|
+
require_relative 'tabscroll/track'
|
6
|
+
require_relative 'tabscroll/popup'
|
7
|
+
|
8
|
+
# Global vars
|
9
|
+
$engine = nil
|
10
|
+
PROGRAM_NAME = "tabscroll"
|
11
|
+
PROGRAM_VERSION = "0.0.1"
|
12
|
+
|
13
|
+
# Terminates program's execution normally, finishing the engine.
|
14
|
+
def quit
|
15
|
+
$engine.exit
|
16
|
+
exit 0
|
17
|
+
end
|
18
|
+
|
19
|
+
# Displays a help window, waiting for a keypress.
|
20
|
+
def show_help_window
|
21
|
+
title = 'Help'
|
22
|
+
text = <<END_OF_TEXT
|
23
|
+
q quit
|
24
|
+
h help/go back
|
25
|
+
left/right auto-scroll left/right
|
26
|
+
up/down stop auto-scrolling
|
27
|
+
</> step scroll left/right
|
28
|
+
o toggle status/title bars
|
29
|
+
|
30
|
+
|
31
|
+
#{PROGRAM_NAME} v#{PROGRAM_VERSION}
|
32
|
+
homepage alexdantas.net/projects/tabscroll
|
33
|
+
author Alexandre Dantas <eu@alexdantas.net>
|
34
|
+
END_OF_TEXT
|
35
|
+
|
36
|
+
pop = Popup.new(title, text)
|
37
|
+
will_quit = pop.show
|
38
|
+
quit if will_quit
|
39
|
+
end
|
40
|
+
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tabscroll
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexandre Dantas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-13 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Scrolls a textual guitar tab on the terminal.
|
14
|
+
email:
|
15
|
+
- eu@alexdantas.net
|
16
|
+
executables:
|
17
|
+
- tabscroll
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/tabscroll.rb
|
22
|
+
- lib/tabscroll/engine.rb
|
23
|
+
- lib/tabscroll/screen.rb
|
24
|
+
- lib/tabscroll/track.rb
|
25
|
+
- lib/tabscroll/popup.rb
|
26
|
+
- lib/tabscroll/timer.rb
|
27
|
+
- bin/tabscroll
|
28
|
+
homepage: http://www.alexdantas.net/projects/tabscroll
|
29
|
+
licenses:
|
30
|
+
- GPL-3.0
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.1.7
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Guitar tab scroller on the terminal
|
52
|
+
test_files: []
|