tabscroll 1.0.1 → 1.0.40

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 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: []