tabscroll 1.0.1 → 1.0.40

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6540d5dc6c362a98bacc11b57de848c5394d30b5
4
- data.tar.gz: 452e31c625187a882eb1e9b5962bee8632def569
3
+ metadata.gz: ee12d935ef76ceb70ff47c224fb0b9eb03a670b1
4
+ data.tar.gz: 4d9f7d721e110bd9105329e6524d7a332577f535
5
5
  SHA512:
6
- metadata.gz: ee8c16c7694cb01bb2285170a023c8451ffe97f954bcf1f78966304c80aa3b873d65b76c79ac7bd431429d68e0cf16c67c07a983c819962000ac09b6f1a16872
7
- data.tar.gz: bd24ff15e9523237f04e1cd6fa84cde656ef01398d011993f21d316f44f185d42ccdc7878ad2b4561d0391b9d9c2e193618ec67bb84df7d4f7e190176c119a8f
6
+ metadata.gz: f63aa0639af372c80a70742876933614d22f9596761f0d3c199b0bb1e23881515c7227eccdc5a4b8088de6765003d5d25646d7defefc89488489dc93524e475a
7
+ data.tar.gz: 8954f7d1eb656939bb70c249470de5dd2ca17c9e6690dc135c71cc6f2466dd646cfde1ff71c5f9cc8f5e6360001cc5c96a225e1589a805a2005d5c1ad37319e2
@@ -0,0 +1,5 @@
1
+ # Guitar tabs on my current directory
2
+ *.txt
3
+
4
+ # The final gem
5
+ *.gem
@@ -1,5 +1,12 @@
1
1
  <!-- My awesome changelog -->
2
2
 
3
+ ## 1.0.40 (Oct 10, 2013)
4
+
5
+ BUG FIXES:
6
+
7
+ * Fixed bad `require_relative` at executable and `gemspec`.
8
+ * Skipped bad gems upload `1.0.2` and `1.0.3`.
9
+
3
10
  ## 1.0.1 (Oct 10, 2013)
4
11
 
5
12
  IMPROVEMENTS:
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # tabscroll - Neat guitar tab scroller.
2
2
 
3
+ <!-- gem badge (ignore this if you're in text mode) -->
4
+ [![Gem Version](https://badge.fury.io/rb/tabscroll.png)](http://badge.fury.io/rb/tabscroll)
5
+
3
6
  `tabscroll` is a (Ruby-powered) guitar tab scroller on the terminal.
4
7
 
5
8
  It supports forward and backwards auto-scrolling and currently
@@ -0,0 +1,14 @@
1
+ # Adding lib directory to load path
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $:.unshift lib unless $:.include? lib
4
+
5
+ require 'tabscroll/version'
6
+
7
+ task :build do
8
+ system 'gem build tabscroll.gemspec'
9
+ end
10
+
11
+ task :release => :build do
12
+ system "gem push tabscroll-#{TabScroll::VERSION}.gem"
13
+ end
14
+
@@ -3,8 +3,8 @@
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
+ require 'tabscroll'
7
+ require 'tabscroll/settings'
8
8
 
9
9
  # class TabScroll expects this hash, watch out!
10
10
  $settings = {}
@@ -0,0 +1,101 @@
1
+ # The main executable file.
2
+
3
+ require 'tabscroll/engine'
4
+ require 'tabscroll/screen'
5
+ require 'tabscroll/track'
6
+ require 'tabscroll/popup'
7
+ require 'tabscroll/version'
8
+
9
+ # The main program.
10
+ #
11
+ # Note that we expect a global hash `$settings`.
12
+ # It contains settings from the commandline argument parser
13
+ # (Settings class).
14
+ module TabScroll
15
+
16
+ # Executes the whole program, loading contents in `filename`.
17
+ def self.run filename
18
+ @engine = Engine.new
19
+ if not @engine
20
+ puts 'Failed to start curses!'
21
+ exit 1337
22
+ end
23
+ @engine.timeout 10
24
+
25
+ win = Screen.new(0, 1, @engine.width, @engine.height - 2)
26
+ track = Track.new win
27
+ track.load filename
28
+ track.auto_scroll true
29
+
30
+ titlebar = Screen.new(0, 0, @engine.width, 1)
31
+ statusbar = Screen.new(0, (@engine.height - 1), @engine.width, 1)
32
+
33
+ bars_hidden = false
34
+ finished = false
35
+ while not finished
36
+
37
+ # WHY DOES GETCH CLEARS UP THE WHOLE SCREEN?
38
+ # IT DOESNT MAKE ANY SENSE
39
+ c = @engine.getchar
40
+ case c
41
+ when 'e'
42
+ track.end
43
+ when 'a'
44
+ track.begin
45
+ when 'h'
46
+ show_help_window
47
+ when 'o'
48
+ bars_hidden = (bars_hidden ? false : true)
49
+ Curses::clear
50
+ when '<'
51
+ track.scroll -5
52
+ when '>'
53
+ track.scroll 5
54
+ when Curses::KEY_LEFT
55
+ track.speed -= 1
56
+ when Curses::KEY_RIGHT
57
+ track.speed += 1
58
+ when Curses::KEY_DOWN, Curses::KEY_UP
59
+ track.speed = 0
60
+ when 'q'
61
+ $enigne.exit
62
+ return nil
63
+ end
64
+
65
+ track.update
66
+ track.show
67
+ if not bars_hidden
68
+ titlebar.mvaddstr(0, 0, "#{filename} (#{track.percent_completed}%) ", Engine::Colors[:cyan])
69
+ titlebar.mvaddstr_right(0, " Speed: #{track.speed}", Engine::Colors[:cyan])
70
+
71
+ statusbar.mvaddstr_left(0, "tabscroll v#{VERSION} - press `h` for help", Engine::Colors[:green])
72
+ end
73
+ end
74
+
75
+ @engine.exit
76
+ return nil
77
+ end
78
+
79
+ # Displays a help window that waits for a keypress.
80
+ def self.show_help_window
81
+ title = 'Help'
82
+ text = <<END_OF_TEXT
83
+ q quit
84
+ h help/go back
85
+ left/right auto-scroll left/right
86
+ up/down stop auto-scrolling
87
+ </> step scroll left/right
88
+ o toggle status/title bars
89
+
90
+
91
+ tabscroll v#{VERSION}
92
+ homepage alexdantas.net/projects/tabscroll
93
+ author Alexandre Dantas <eu@alexdantas.net>
94
+ END_OF_TEXT
95
+
96
+ pop = Popup.new(title, text)
97
+ will_quit = pop.show
98
+ quit if will_quit
99
+ end
100
+ end
101
+
@@ -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,64 @@
1
+
2
+ require 'tabscroll/screen'
3
+
4
+ # A simple centralized popup on the terminal.
5
+ class Popup < Screen
6
+
7
+ # Creates a Popup with `title` and inner `text`.
8
+ #
9
+ # It resizes to contain the whole text plus a 1x1 border
10
+ # around itself.
11
+ def initialize(title, text)
12
+ @title = title
13
+ @text = []
14
+ text.each_line do |line|
15
+ @text += [line.chomp]
16
+ end
17
+
18
+ max_width = title.length
19
+ max_height = 1
20
+
21
+ @text.each do |line|
22
+ max_width = line.length if line.length > max_width
23
+ max_height += 1
24
+ end
25
+
26
+ max_width += 2 # left-right borders
27
+ max_height += 1 # down border
28
+
29
+ x = Curses::cols/2 - max_width/2
30
+ y = Curses::lines/2 - max_height/2
31
+
32
+ super(x, y, max_width, max_height)
33
+ self.background ' '
34
+ self.box
35
+
36
+ self.mvaddstr_center(0, title, Engine::Colors[:cyan])
37
+
38
+ y = 1
39
+ @text.each do |line|
40
+ self.mvaddstr(1, y, line)
41
+ y += 1
42
+ end
43
+ end
44
+
45
+ # Makes the Popup appear on the screen and wait for any key.
46
+ # When it exits, clears the screen erasing itself.
47
+ def show
48
+ finished = false
49
+ while not finished
50
+ c = Curses::getch
51
+ case c
52
+ when 'q'
53
+ return true
54
+ when 'h'
55
+ finished = true
56
+ end
57
+ end
58
+
59
+ Curses::stdscr.clear
60
+ Curses::stdscr.refresh
61
+ return false
62
+ end
63
+ end
64
+
@@ -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,65 @@
1
+
2
+ require 'optparse'
3
+ require 'tabscroll'
4
+
5
+ # Global configurations of the program, along with a commandline
6
+ # argument parser.
7
+ #
8
+ # Contains the program's specific configuration rules.
9
+ class Settings
10
+
11
+ # Creates a configuration, with default values.
12
+ def initialize
13
+ @settings = {}
14
+ @settings[:filename] = nil
15
+ end
16
+
17
+ # Sets options based on commandline arguments `args`.
18
+ # It should be `ARGV`.
19
+ def parse args
20
+
21
+ opts = OptionParser.new do |parser|
22
+ parser.banner = "Usage: tabscroll [options] filename"
23
+
24
+ # Make output beautiful
25
+ parser.separator ""
26
+ parser.separator "Specific options:"
27
+
28
+ parser.on_tail("-h", "--help", "Show this message") do
29
+ puts parser
30
+ exit
31
+ end
32
+
33
+ parser.on_tail("--version", "Show version") do
34
+ puts "tabscroll v#{TabScroll::VERSION}"
35
+ exit
36
+ end
37
+ end
38
+ opts.parse! args
39
+
40
+ # After parsing all '--args' and '-a' we will check if
41
+ # the user has provided a filename.
42
+ #
43
+ # The first argument without a leading '-' will be
44
+ # considered.
45
+ args.each do |arg|
46
+ if arg =~ /^[^-]/
47
+ @settings[:filename] = arg
48
+ break
49
+ end
50
+ end
51
+
52
+ if not @settings[:filename]
53
+ puts opts
54
+ exit 666
55
+ end
56
+
57
+ return @settings
58
+ end
59
+
60
+ # Returns a specific setting previously set.
61
+ def [] name
62
+ return @settings[name]
63
+ end
64
+ end
65
+
@@ -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
+ # Creates the timer, doing nothing else.
13
+ def initialize
14
+ @is_running = false
15
+ @is_paused = false
16
+ end
17
+
18
+ # Starts counting.
19
+ def start
20
+ return if @is_running
21
+
22
+ @start_time = Time.now
23
+ @stop_time = 0.0
24
+ @paused_time = 0.0
25
+ @is_running = true
26
+ @is_paused = false
27
+ end
28
+
29
+ # Stops counting.
30
+ def stop
31
+ return if not @is_running
32
+
33
+ @stop_time = Time.now
34
+ @is_running = false
35
+ @is_paused = false
36
+ end
37
+
38
+ def restart
39
+ self.stop
40
+ self.start
41
+ end
42
+
43
+ def pause
44
+ return if not @is_running or @is_paused
45
+
46
+ @paused_time = (Time.now - @start_time)
47
+ @is_running = false
48
+ @is_paused = true
49
+ end
50
+
51
+ def unpause
52
+ return if not @is_paused or @is_running
53
+
54
+ @start_time = (Time.now - @paused_time)
55
+ @is_running = true
56
+ @is_paused = false
57
+ end
58
+
59
+ def running?
60
+ @is_running
61
+ end
62
+
63
+ def paused?
64
+ @is_paused
65
+ end
66
+
67
+ # Returns the current delta in seconds (float).
68
+ def delta
69
+ if @is_running
70
+ return (Time.now.to_f - @start_time.to_f)
71
+ end
72
+
73
+ return @paused_time.to_f if @is_paused
74
+
75
+ return @start_time if @start_time == 0 # Something's wrong
76
+
77
+ return (@stop_time.to_f - @start_time.to_f)
78
+ end
79
+
80
+ # Converts the timer's delta to a formatted string.
81
+ def to_s
82
+ min = (self.delta / 60).to_i
83
+ sec = (self.delta).to_i
84
+ msec = (self.delta * 100).to_i
85
+
86
+ "#{min}:#{sec}:#{msec}"
87
+ end
88
+ end
89
+
@@ -0,0 +1,208 @@
1
+
2
+ require 'tabscroll/screen'
3
+ require 'tabscroll/timer'
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
+
10
+ # Any line on the file that starts with this char is completely
11
+ # ignored when processed.
12
+ # Useful for making use of tabs we currently can't parse.
13
+ COMMENT_CHAR = '#'
14
+
15
+ attr_reader :screen, :percent_completed
16
+ attr_accessor :speed
17
+
18
+ # Creates a Track that will be shown on `screen`.
19
+ # See Screen.
20
+ def initialize(screen)
21
+ @offset = 0
22
+ @timer = Timer.new
23
+ @timer.start
24
+ @speed = 0
25
+ @screen = screen
26
+ @percent_completed = 0
27
+
28
+ @raw_track = []
29
+ @raw_track[0] = ""
30
+ @raw_track[1] = ""
31
+ @raw_track[2] = ""
32
+ @raw_track[3] = ""
33
+ @raw_track[4] = ""
34
+ @raw_track[5] = ""
35
+ @raw_track[6] = ""
36
+ end
37
+
38
+ # Loads and parses +filename+'s contents into Track.
39
+ def load filename
40
+ if not File.exist? filename
41
+ raise "Error: File '#{filename}' doesn't exist!"
42
+ end
43
+ if not File.file? filename
44
+ raise "Error: '#{filename}' is not a file!"
45
+ end
46
+
47
+ file = File.new filename
48
+
49
+ # The thing here is there's no way I can know in
50
+ # advance how many lines the tab track will have.
51
+ #
52
+ # People put lots of strange things on them like
53
+ # timing, comments, etecetera.
54
+ #
55
+ # So I will read all non-blank lines, creating a
56
+ # counter. Then I will use it to display the track
57
+ # onscreen.
58
+ #
59
+ # I will also make every line have the same width
60
+ # as of the biggest one.
61
+
62
+ # Any tab line MUST have EITHER ---1---9--| OR |---3----0
63
+ tab_line = /[-[:alnum:]]\||\|[-[:alnum:]]/
64
+
65
+ # Duration of each note only has those chars.
66
+ # So we look for anything BUT these chars.
67
+ not_duration_line = /[^WHQESTX \.]/
68
+
69
+ count = 0
70
+ max_width = 0
71
+
72
+ file.readlines.each do |line|
73
+ next if line[0] == COMMENT_CHAR
74
+
75
+ line.chomp!
76
+ if line.empty?
77
+
78
+ # Making sure everything will have the same width
79
+ @raw_track.each_with_index do |t, i|
80
+ if t.length < max_width
81
+ @raw_track[i] += (' ' * (max_width - t.length))
82
+ end
83
+ end
84
+
85
+ count = 0
86
+ max_width = 0
87
+
88
+ # Lines must be EITHER a tab_line OR a duration_line.
89
+ # not not duration line means that
90
+ # (I should find a better way of expressing myself on regexes)
91
+ elsif (line =~ tab_line) or (not line =~ not_duration_line)
92
+ @raw_track[count] += line
93
+
94
+ if @raw_track[count].length > max_width
95
+ max_width = @raw_track[count].length
96
+ end
97
+
98
+ count += 1
99
+
100
+ end # Ignoring any other kind of line
101
+
102
+ if count > 7
103
+ raise "Error: Invalid format on '#{filename}'"
104
+ end
105
+
106
+ end
107
+ end
108
+
109
+ # Prints the track on the screen, along with string indicators
110
+ # on the left.
111
+ #
112
+ # It is shown at the vertical center of the provided Screen,
113
+ # spanning it's whole width.
114
+ def show
115
+ x = 1
116
+ y = (@screen.height/2) - (@raw_track.size/2)
117
+
118
+ # This both prints EADGBE and clears the whole screen,
119
+ # printing spaces where the track was.
120
+ #
121
+ # Also, if we have only 5 tracks, we leave the sixth
122
+ # indicator out of the screen.
123
+ if @raw_track.last =~ /^ *$/
124
+ @screen.mvaddstr(0, y, "E" + (' ' * (@screen.width - 1)))
125
+ @screen.mvaddstr(0, y + 1, "B" + (' ' * (@screen.width - 1)))
126
+ @screen.mvaddstr(0, y + 2, "G" + (' ' * (@screen.width - 1)))
127
+ @screen.mvaddstr(0, y + 3, "D" + (' ' * (@screen.width - 1)))
128
+ @screen.mvaddstr(0, y + 4, "A" + (' ' * (@screen.width - 1)))
129
+ @screen.mvaddstr(0, y + 5, "E" + (' ' * (@screen.width - 1)))
130
+ else
131
+ @screen.mvaddstr(0, y, ' ' * @screen.width)
132
+ @screen.mvaddstr(0, y + 1, "E" + (' ' * (@screen.width - 1)))
133
+ @screen.mvaddstr(0, y + 2, "B" + (' ' * (@screen.width - 1)))
134
+ @screen.mvaddstr(0, y + 3, "G" + (' ' * (@screen.width - 1)))
135
+ @screen.mvaddstr(0, y + 4, "D" + (' ' * (@screen.width - 1)))
136
+ @screen.mvaddstr(0, y + 5, "A" + (' ' * (@screen.width - 1)))
137
+ @screen.mvaddstr(0, y + 6, "E" + (' ' * (@screen.width - 1)))
138
+ end
139
+
140
+ (0...@raw_track.size).each do |i|
141
+ str = @raw_track[i]
142
+ str = str[@offset..(@offset + @screen.width - 2)]
143
+ @screen.mvaddstr(x, y + i, str)
144
+ end
145
+ end
146
+
147
+ # Scrolls the guitar tab by `n`.
148
+ #
149
+ # * If `n` is positive, scroll forward.
150
+ # * If `n` is negative, scroll backward.
151
+ def scroll n
152
+ @offset += n
153
+
154
+ left_limit = 0
155
+ right_limit = (@raw_track[0].length - @screen.width + 1).abs
156
+
157
+ if @offset < left_limit then @offset = left_limit end
158
+ if @offset > right_limit then @offset = right_limit end
159
+ end
160
+
161
+ # Goes to the beginning of the Track.
162
+ def begin
163
+ @offset = 0
164
+ @speed = 0
165
+ end
166
+
167
+ # Goes to the end of the Track.
168
+ def end
169
+ @offset = (@raw_track[0].length - @screen.width + 1).abs
170
+ @speed = 0
171
+ end
172
+
173
+ # Turns on/off Track's auto scroll functionality.
174
+ #
175
+ # Note that it won't work anyways if you don't keep calling
176
+ # +update+ method.
177
+ def auto_scroll option
178
+ if option == true
179
+ @timer.start if not @timer.running?
180
+ else
181
+ @timer.stop
182
+ end
183
+ end
184
+
185
+ # Updates Track's auto scroll functionality.
186
+ def update
187
+ return if not @timer.running?
188
+
189
+ current_completed = @offset + @screen.width - 1
190
+ @percent_completed = ((100.0 * current_completed)/@raw_track[0].length).ceil
191
+
192
+ if @timer.running? and @speed != 0
193
+ if @timer.delta > (1/(@speed*0.5)).abs
194
+ if @speed > 0
195
+ self.scroll 1
196
+ self.show
197
+ else
198
+ self.scroll -1
199
+ self.show
200
+ end
201
+
202
+ @timer.restart
203
+ end
204
+ end
205
+ end
206
+
207
+ end
208
+
@@ -0,0 +1,3 @@
1
+ module TabScroll
2
+ VERSION = '1.0.40'
3
+ end
@@ -0,0 +1,32 @@
1
+ # Adding lib directory to load path
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $:.unshift lib unless $:.include? lib
4
+
5
+ require 'tabscroll/version'
6
+ require 'date'
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = 'tabscroll'
10
+ s.version = TabScroll::VERSION
11
+ s.summary = "Guitar tab scroller on the terminal"
12
+ s.date = "#{Date.today.year}-#{Date.today.month}-#{Date.today.day}"
13
+ s.description = <<END_OF_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.
17
+ END_OF_DESCRIPTION
18
+ s.authors = ["Alexandre Dantas"]
19
+ s.email = ["eu@alexdantas.net"]
20
+ s.homepage = 'http://www.alexdantas.net/projects/tabscroll'
21
+ s.license = "GPL-3.0"
22
+
23
+ # Including everything that's on `git`
24
+ s.files = `git ls-files`.split($/)
25
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
26
+ s.require_paths = ['lib']
27
+
28
+ s.metadata = { 'github' => 'http://www.github.com/alexdantas/tabscroll' }
29
+
30
+ s.add_development_dependency "rake"
31
+ end
32
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tabscroll
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.40
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Dantas
@@ -9,7 +9,21 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2013-10-10 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: |
14
28
  Scrolls a textual guitar tab on the terminal.
15
29
  It supports forward and backwards auto-scrolling and currently
@@ -21,10 +35,21 @@ executables:
21
35
  extensions: []
22
36
  extra_rdoc_files: []
23
37
  files:
24
- - bin/tabscroll
38
+ - ".gitignore"
39
+ - CHANGELOG.md
25
40
  - LICENSE
26
41
  - README.md
27
- - CHANGELOG.md
42
+ - Rakefile
43
+ - bin/tabscroll
44
+ - lib/tabscroll.rb
45
+ - lib/tabscroll/engine.rb
46
+ - lib/tabscroll/popup.rb
47
+ - lib/tabscroll/screen.rb
48
+ - lib/tabscroll/settings.rb
49
+ - lib/tabscroll/timer.rb
50
+ - lib/tabscroll/track.rb
51
+ - lib/tabscroll/version.rb
52
+ - tabscroll.gemspec
28
53
  homepage: http://www.alexdantas.net/projects/tabscroll
29
54
  licenses:
30
55
  - GPL-3.0
@@ -36,12 +61,12 @@ require_paths:
36
61
  - lib
37
62
  required_ruby_version: !ruby/object:Gem::Requirement
38
63
  requirements:
39
- - - '>='
64
+ - - ">="
40
65
  - !ruby/object:Gem::Version
41
66
  version: '0'
42
67
  required_rubygems_version: !ruby/object:Gem::Requirement
43
68
  requirements:
44
- - - '>='
69
+ - - ">="
45
70
  - !ruby/object:Gem::Version
46
71
  version: '0'
47
72
  requirements: []