tabscroll 0.0.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/LICENSE +674 -0
- data/README.md +73 -0
- data/bin/tabscroll +9 -63
- metadata +11 -10
- data/lib/tabscroll.rb +0 -40
- data/lib/tabscroll/engine.rb +0 -95
- data/lib/tabscroll/popup.rb +0 -60
- data/lib/tabscroll/screen.rb +0 -125
- data/lib/tabscroll/timer.rb +0 -89
- data/lib/tabscroll/track.rb +0 -203
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# tabscroll - Neat guitar tab scroller.
|
2
|
+
|
3
|
+
`tabscroll` is a (Ruby-powered) guitar tab scroller on the terminal.
|
4
|
+
|
5
|
+
It supports forward and backwards auto-scrolling and currently
|
6
|
+
reads a nice range of guitar tabs.
|
7
|
+
|
8
|
+
## Usage and Screenshots
|
9
|
+
|
10
|
+
$ tabscroll my-guitar-tab.txt
|
11
|
+
|
12
|
+
Running on a small terminal
|
13
|
+
![big screen](http://www.alexdantas.net/projects/tabscroll/screenshots/screen1.png "Small Screen")
|
14
|
+
|
15
|
+
The help window on a big screen
|
16
|
+
![big screen](http://www.alexdantas.net/projects/tabscroll/screenshots/screen2.png "Big Screen")
|
17
|
+
|
18
|
+
## Guitar tab file format and troubleshooting
|
19
|
+
|
20
|
+
`tabscroll` only works with textual guitar tabs, not chords with lyrics.
|
21
|
+
|
22
|
+
It attempts to read any kind of tab format (although it works better with tabs
|
23
|
+
exported from _Guitar Pro_). For now it requires you to split each tab row with
|
24
|
+
a blank line. For example, this would work:
|
25
|
+
|
26
|
+
E S S S S S S Q S S S S E S S S S S S E S S S S S S
|
27
|
+
--0--------0------0----L---0-1-|--0----------0h-1-0----------0h-1-|
|
28
|
+
--1----------0h-1-1----L-1-----|--1----1----------1----1----------|
|
29
|
+
------------------0----L-------|--2--2-----2------0--0-----0------|
|
30
|
+
-----0-2----------2----L-------|--3------3--------2------2--------|
|
31
|
+
--3------3--------3----L-------|----------------------------------|
|
32
|
+
-------------------------------|----------------------------------|
|
33
|
+
|
34
|
+
E S S S S S S E S S S S S S E S S S S S S E S S S S S S
|
35
|
+
--0----3---0-----5--3---0------0-|--L----------0h-1-0----------3p-1-|
|
36
|
+
--1----------1-3-0--------3p-0---|--1----1----------1----1----------|
|
37
|
+
--2--2---2-------0----0----------|--2--2-----2------0--0-----0------|
|
38
|
+
---------------------------------|--3------3--------2------2--------|
|
39
|
+
--0------------------------------|----------------------------------|
|
40
|
+
-----------------3------3--------|----------------------------------|
|
41
|
+
|
42
|
+
But this wouldn't:
|
43
|
+
|
44
|
+
--0--------0------0----L---0-1-|--0----------0h-1-0----------0h-1-|
|
45
|
+
--1----------0h-1-1----L-1-----|--1----1----------1----1----------|
|
46
|
+
------------------0----L-------|--2--2-----2------0--0-----0------|
|
47
|
+
-----0-2----------2----L-------|--3------3--------2------2--------|
|
48
|
+
--3------3--------3----L-------|----------------------------------|
|
49
|
+
-------------------------------|----------------------------------|
|
50
|
+
--0----3---0-----5--3---0------0-|--L----------0h-1-0----------3p-1-|
|
51
|
+
--1----------1-3-0--------3p-0---|--1----1----------1----1----------|
|
52
|
+
--2--2---2-------0----0----------|--2--2-----2------0--0-----0------|
|
53
|
+
---------------------------------|--3------3--------2------2--------|
|
54
|
+
--0------------------------------|----------------------------------|
|
55
|
+
-----------------3------3--------|----------------------------------|
|
56
|
+
|
57
|
+
So if something bad happens and the program crashes, edit the tab making sure to
|
58
|
+
keep a consistent tab format (6 in 6 lines, or 5 in 5) each with a blank line
|
59
|
+
inbetween.
|
60
|
+
|
61
|
+
At worse, please comment (add `#` at the start of) any line that's not the tab
|
62
|
+
(you know, author info, instructions, etc). Sorry for the inconvenience, I plan
|
63
|
+
to improve a lot the detection of tabs on the future.
|
64
|
+
|
65
|
+
## Contact
|
66
|
+
|
67
|
+
Hi, I'm Alexandre Dantas! Thanks for having interest in this project. Please
|
68
|
+
take the time to visit any of the links below.
|
69
|
+
|
70
|
+
* `tabscroll` homepage: http://www.alexdantas.net/projects/tabscroll
|
71
|
+
* Contact: `eu @ alexdantas.net`
|
72
|
+
* My homepage: http://www.alexdantas.net
|
73
|
+
|
data/bin/tabscroll
CHANGED
@@ -2,71 +2,17 @@
|
|
2
2
|
#
|
3
3
|
# The main `tabscroll` executable
|
4
4
|
|
5
|
-
|
5
|
+
# Is this considered ugly?
|
6
|
+
require_relative '../lib/tabscroll'
|
7
|
+
require_relative '../lib/tabscroll/settings'
|
6
8
|
|
7
|
-
|
8
|
-
|
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)
|
9
|
+
# class TabScroll expects this hash, watch out!
|
10
|
+
$settings = {}
|
29
11
|
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
12
|
+
begin
|
13
|
+
$settings = Settings.new
|
14
|
+
$settings.parse ARGV
|
69
15
|
|
70
|
-
|
16
|
+
TabScroll::run $settings[:filename]
|
71
17
|
end
|
72
18
|
|
metadata
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tabscroll
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexandre Dantas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: |
|
14
|
+
Scrolls a textual guitar tab on the terminal.
|
15
|
+
It supports forward and backwards auto-scrolling and currently
|
16
|
+
reads a nice range of guitar tabs.
|
14
17
|
email:
|
15
18
|
- eu@alexdantas.net
|
16
19
|
executables:
|
@@ -18,17 +21,15 @@ executables:
|
|
18
21
|
extensions: []
|
19
22
|
extra_rdoc_files: []
|
20
23
|
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
24
|
- bin/tabscroll
|
25
|
+
- LICENSE
|
26
|
+
- README.md
|
27
|
+
- CHANGELOG.md
|
28
28
|
homepage: http://www.alexdantas.net/projects/tabscroll
|
29
29
|
licenses:
|
30
30
|
- GPL-3.0
|
31
|
-
metadata:
|
31
|
+
metadata:
|
32
|
+
github: http://www.github.com/alexdantas/tabscroll
|
32
33
|
post_install_message:
|
33
34
|
rdoc_options: []
|
34
35
|
require_paths:
|
data/lib/tabscroll.rb
DELETED
@@ -1,40 +0,0 @@
|
|
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
|
-
|
data/lib/tabscroll/engine.rb
DELETED
@@ -1,95 +0,0 @@
|
|
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
|
-
|
data/lib/tabscroll/popup.rb
DELETED
@@ -1,60 +0,0 @@
|
|
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
|
-
|
data/lib/tabscroll/screen.rb
DELETED
@@ -1,125 +0,0 @@
|
|
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
|
-
|