todo-curses 0.0.3 → 0.0.4
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 +4 -4
- data/.gitignore +1 -0
- data/CONTRIBUTING.md +34 -0
- data/Gemfile +0 -5
- data/Gemfile.lock +28 -9
- data/README.md +120 -0
- data/README.rdoc +10 -92
- data/Rakefile +65 -28
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/todo-curses +37 -40
- data/images/todo-curses-demo.gif +0 -0
- data/lib/TodoCurses.rb +16 -0
- data/lib/{todo-txt → TodoCurses}/list.rb +21 -20
- data/lib/{todo-txt → TodoCurses}/logger.rb +2 -2
- data/lib/{todo-txt → TodoCurses}/task.rb +20 -20
- data/lib/TodoCurses/version.rb +3 -0
- data/lib/TodoCurses/view.rb +261 -0
- data/todo-curses.gemspec +26 -19
- metadata +148 -34
- data/features/step_definitions/todo_steps.rb +0 -6
- data/features/support/env.rb +0 -15
- data/features/todo.feature +0 -8
- data/lib/todo-txt.rb +0 -7
- data/lib/todo.rb +0 -6
- data/lib/todo/todo_viewer.rb +0 -283
- data/lib/todo/version.rb +0 -3
- data/reset.sh +0 -2
- data/run.sh +0 -2
- data/test/default_test.rb +0 -14
- data/test/test_helper.rb +0 -9
- data/todo.rdoc +0 -5
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "TodoCurses"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/bin/todo-curses
CHANGED
@@ -1,47 +1,44 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
c.action do |global_options,options,args|
|
17
|
-
if args.size != 1 then
|
18
|
-
printf("usage: #{$0} file\n");
|
19
|
-
exit
|
20
|
-
end
|
21
|
-
|
22
|
-
TodoViewer.new(ARGV[0])
|
3
|
+
require 'optparse'
|
4
|
+
require 'methadone'
|
5
|
+
require 'TodoCurses'
|
6
|
+
|
7
|
+
class App
|
8
|
+
include Methadone::Main
|
9
|
+
include Methadone::CLILogging
|
10
|
+
include TodoCurses
|
11
|
+
|
12
|
+
main do |todo_path|
|
13
|
+
# your program code here
|
14
|
+
# You can access CLI options via
|
15
|
+
# the options Hash
|
23
16
|
end
|
24
|
-
end
|
25
17
|
|
26
|
-
|
27
|
-
# Pre logic here
|
28
|
-
# Return true to proceed; false to abort and not call the
|
29
|
-
# chosen command
|
30
|
-
# Use skips_pre before a command to skip this block
|
31
|
-
# on that command only
|
32
|
-
true
|
33
|
-
end
|
18
|
+
# supplemental methods here
|
34
19
|
|
35
|
-
|
36
|
-
# Post logic here
|
37
|
-
# Use skips_post before a command to skip this
|
38
|
-
# block on that command only
|
39
|
-
end
|
20
|
+
# Declare command-line interface here
|
40
21
|
|
41
|
-
|
42
|
-
#
|
43
|
-
#
|
44
|
-
|
45
|
-
|
22
|
+
# description "one line description of your app"
|
23
|
+
#
|
24
|
+
# Accept flags via:
|
25
|
+
# on("--flag VAL","Some flag")
|
26
|
+
# options[flag] will contain VAL
|
27
|
+
#
|
28
|
+
# Specify switches via:
|
29
|
+
# on("--[no-]switch","Some switch")
|
30
|
+
#
|
31
|
+
# Or, just call OptionParser methods on opts
|
32
|
+
#
|
33
|
+
# Require an argument
|
34
|
+
# arg :some_arg
|
35
|
+
#
|
36
|
+
# # Make an argument optional
|
37
|
+
# arg :optional_arg, :optional
|
46
38
|
|
47
|
-
|
39
|
+
version TodoCurses::VERSION
|
40
|
+
|
41
|
+
use_log_level_option :toggle_debug_on_signal => 'USR1'
|
42
|
+
|
43
|
+
go!
|
44
|
+
end
|
Binary file
|
data/lib/TodoCurses.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'TodoCurses/list.rb'
|
2
|
+
require 'TodoCurses/logger.rb'
|
3
|
+
require 'TodoCurses/view.rb'
|
4
|
+
require 'TodoCurses/version.rb'
|
5
|
+
require 'TodoCurses/task.rb'
|
6
|
+
|
7
|
+
module TodoCurses
|
8
|
+
include Logger
|
9
|
+
|
10
|
+
if ARGV.size != 1
|
11
|
+
printf("usage: #{$PROGRAM_NAME} file\n")
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
|
15
|
+
View.new ARGV[0]
|
16
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
module
|
1
|
+
module TodoCurses
|
2
|
+
# Handles todo.txt list files
|
2
3
|
class List < Array
|
3
4
|
# Initializes a Todo List object with a path to the corresponding todo.txt
|
4
5
|
# file. For example, if your todo.txt file is located at:
|
@@ -7,7 +8,7 @@ module Todo
|
|
7
8
|
#
|
8
9
|
# You would initialize this object like:
|
9
10
|
#
|
10
|
-
# list =
|
11
|
+
# list = TodoCurses::List.new "/home/sam/Dropbox/todo/todo-txt"
|
11
12
|
#
|
12
13
|
# Alternately, you can initialize this object with an array of strings or
|
13
14
|
# tasks. If the array is of strings, the strings will be converted into
|
@@ -17,10 +18,10 @@ module Todo
|
|
17
18
|
#
|
18
19
|
# array = Array.new
|
19
20
|
# array.push "(A) A string task!"
|
20
|
-
# array.push
|
21
|
+
# array.push TodoCurses::Task.new("(A) An actual task!")
|
21
22
|
#
|
22
|
-
# list =
|
23
|
-
def initialize
|
23
|
+
# list = TodoCurses::List.new array
|
24
|
+
def initialize(list)
|
24
25
|
if list.is_a? Array
|
25
26
|
# No file path was given.
|
26
27
|
@path = nil
|
@@ -29,25 +30,25 @@ module Todo
|
|
29
30
|
list.each do |task|
|
30
31
|
# If it's a string, make a new task out of it.
|
31
32
|
if task.is_a? String
|
32
|
-
|
33
|
+
push TodoCurses::Task.new task
|
33
34
|
# If it's a task, just add it.
|
34
|
-
elsif task.is_a?
|
35
|
-
|
35
|
+
elsif task.is_a? TodoCurses::Task
|
36
|
+
push task
|
36
37
|
end
|
37
38
|
end
|
38
39
|
elsif list.is_a? String
|
39
40
|
@path = list
|
40
41
|
|
41
|
-
# Read in lines from file, create
|
42
|
+
# Read in lines from file, create TodoCurses::Tasks out of them and push them
|
42
43
|
# onto self.
|
43
44
|
File.open(list) do |file|
|
44
|
-
file.each_line { |line|
|
45
|
+
file.each_line { |line| push TodoCurses::Task.new line }
|
45
46
|
end
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
50
|
# The path to the todo.txt file that you supplied when you created the
|
50
|
-
#
|
51
|
+
# TodoCurses::List object.
|
51
52
|
def path
|
52
53
|
@path
|
53
54
|
end
|
@@ -56,32 +57,32 @@ module Todo
|
|
56
57
|
#
|
57
58
|
# Example:
|
58
59
|
#
|
59
|
-
# list =
|
60
|
+
# list = TodoCurses::List.new "/path/to/list"
|
60
61
|
# list.by_priority "A" #=> Will be a new list with only priority A tasks
|
61
|
-
def by_priority
|
62
|
-
|
62
|
+
def by_priority(priority)
|
63
|
+
TodoCurses::List.new select { |task| task.priority == priority }
|
63
64
|
end
|
64
65
|
|
65
66
|
# Filters the list by context and returns a new list.
|
66
67
|
#
|
67
68
|
# Example:
|
68
69
|
#
|
69
|
-
# list =
|
70
|
+
# list = TodoCurses::List.new "/path/to/list"
|
70
71
|
# list.by_context "@context" #=> Will be a new list with only tasks
|
71
72
|
# containing "@context"
|
72
|
-
def by_context
|
73
|
-
|
73
|
+
def by_context(context)
|
74
|
+
TodoCurses::List.new select { |task| task.contexts.include? context }
|
74
75
|
end
|
75
76
|
|
76
77
|
# Filters the list by project and returns a new list.
|
77
78
|
#
|
78
79
|
# Example:
|
79
80
|
#
|
80
|
-
# list =
|
81
|
+
# list = TodoCurses::List.new "/path/to/list"
|
81
82
|
# list.by_project "+project" #=> Will be a new list with only tasks
|
82
83
|
# containing "+project"
|
83
|
-
def by_project
|
84
|
-
|
84
|
+
def by_project(project)
|
85
|
+
TodoCurses::List.new select { |task| task.projects.include? project }
|
85
86
|
end
|
86
87
|
end
|
87
88
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'date'
|
2
2
|
|
3
|
-
module
|
3
|
+
module TodoCurses
|
4
4
|
class Task
|
5
5
|
include Comparable
|
6
|
-
include
|
6
|
+
include TodoCurses::Logger
|
7
7
|
|
8
8
|
# The regular expression used to match contexts.
|
9
9
|
def self.contexts_regex
|
@@ -40,7 +40,7 @@ module Todo
|
|
40
40
|
#
|
41
41
|
# Example:
|
42
42
|
#
|
43
|
-
# task =
|
43
|
+
# task = TodoCurses::Task.new("(A) A high priority task!")
|
44
44
|
def initialize task
|
45
45
|
@orig = task
|
46
46
|
@completed_on = get_completed_date #orig.scan(self.class.done_regex)[1] ||= nil
|
@@ -54,7 +54,7 @@ module Todo
|
|
54
54
|
#
|
55
55
|
# Example:
|
56
56
|
#
|
57
|
-
# task =
|
57
|
+
# task = TodoCurses::Task.new "(A) @context +project Hello!"
|
58
58
|
# task.orig #=> "(A) @context +project Hello!"
|
59
59
|
attr_reader :orig
|
60
60
|
|
@@ -62,7 +62,7 @@ module Todo
|
|
62
62
|
#
|
63
63
|
# Example:
|
64
64
|
#
|
65
|
-
# task =
|
65
|
+
# task = TodoCurses::Task.new "(A) 2012-03-04 Task."
|
66
66
|
# task.created_on
|
67
67
|
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
68
68
|
#
|
@@ -75,7 +75,7 @@ module Todo
|
|
75
75
|
#
|
76
76
|
# Example:
|
77
77
|
#
|
78
|
-
# task =
|
78
|
+
# task = TodoCurses::Task.new "x 2012-03-04 Task."
|
79
79
|
# task.completed_on
|
80
80
|
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
81
81
|
#
|
@@ -88,7 +88,7 @@ module Todo
|
|
88
88
|
#
|
89
89
|
# Example:
|
90
90
|
#
|
91
|
-
# task =
|
91
|
+
# task = TodoCurses::Task.new "(A) This is a task. due:2012-03-04"
|
92
92
|
# task.due_on
|
93
93
|
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
94
94
|
#
|
@@ -101,10 +101,10 @@ module Todo
|
|
101
101
|
#
|
102
102
|
# Example:
|
103
103
|
#
|
104
|
-
# task =
|
104
|
+
# task = TodoCurses::Task.new "(A) Some task."
|
105
105
|
# task.priority #=> "A"
|
106
106
|
#
|
107
|
-
# task =
|
107
|
+
# task = TodoCurses::Task.new "Some task."
|
108
108
|
# task.priority #=> nil
|
109
109
|
attr_reader :priority
|
110
110
|
|
@@ -129,7 +129,7 @@ module Todo
|
|
129
129
|
#
|
130
130
|
# Example:
|
131
131
|
#
|
132
|
-
# task =
|
132
|
+
# task = TodoCurses::Task.new "(A) @test Testing!"
|
133
133
|
# task.text #=> "Testing!"
|
134
134
|
def text
|
135
135
|
@text ||= orig.
|
@@ -146,7 +146,7 @@ module Todo
|
|
146
146
|
#
|
147
147
|
# Example:
|
148
148
|
#
|
149
|
-
# task =
|
149
|
+
# task = TodoCurses::Task.new "(A) 2012-03-04 Task."
|
150
150
|
# task.date
|
151
151
|
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
152
152
|
#
|
@@ -165,7 +165,7 @@ module Todo
|
|
165
165
|
#
|
166
166
|
# Example:
|
167
167
|
#
|
168
|
-
# task =
|
168
|
+
# task = TodoCurses::Task.new("This task is overdue! due:#{Date.today - 1}")
|
169
169
|
# task.overdue?
|
170
170
|
# #=> true
|
171
171
|
def overdue?
|
@@ -177,11 +177,11 @@ module Todo
|
|
177
177
|
#
|
178
178
|
# Example:
|
179
179
|
#
|
180
|
-
# task =
|
180
|
+
# task = TodoCurses::Task.new "x 2012-12-08 Task."
|
181
181
|
# task.done?
|
182
182
|
# #=> true
|
183
183
|
#
|
184
|
-
# task =
|
184
|
+
# task = TodoCurses::Task.new "Task."
|
185
185
|
# task.done?
|
186
186
|
# #=> false
|
187
187
|
def done?
|
@@ -192,7 +192,7 @@ module Todo
|
|
192
192
|
#
|
193
193
|
# Example:
|
194
194
|
#
|
195
|
-
# task =
|
195
|
+
# task = TodoCurses::Task.new "2012-12-08 Task."
|
196
196
|
# task.done?
|
197
197
|
# #=> false
|
198
198
|
#
|
@@ -212,7 +212,7 @@ module Todo
|
|
212
212
|
#
|
213
213
|
# Example:
|
214
214
|
#
|
215
|
-
# task =
|
215
|
+
# task = TodoCurses::Task.new "x 2012-12-08 2012-03-04 Task."
|
216
216
|
# task.done?
|
217
217
|
# #=> true
|
218
218
|
#
|
@@ -232,7 +232,7 @@ module Todo
|
|
232
232
|
#
|
233
233
|
# Example:
|
234
234
|
#
|
235
|
-
# task =
|
235
|
+
# task = TodoCurses::Task.new "x 2012-12-08 Task."
|
236
236
|
# task.done?
|
237
237
|
# #=> true
|
238
238
|
#
|
@@ -251,7 +251,7 @@ module Todo
|
|
251
251
|
#
|
252
252
|
# Example:
|
253
253
|
#
|
254
|
-
# task =
|
254
|
+
# task = TodoCurses::Task.new "(A) 2012-12-08 Task"
|
255
255
|
# task.to_s
|
256
256
|
# #=> "(A) 2012-12-08 Task"
|
257
257
|
def to_s
|
@@ -268,8 +268,8 @@ module Todo
|
|
268
268
|
#
|
269
269
|
# Example:
|
270
270
|
#
|
271
|
-
# task1 =
|
272
|
-
# task2 =
|
271
|
+
# task1 = TodoCurses::Task.new "(A) Priority A."
|
272
|
+
# task2 = TodoCurses::Task.new "(B) Priority B."
|
273
273
|
#
|
274
274
|
# task1 > task2
|
275
275
|
# # => true
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'ncursesw'
|
2
|
+
|
3
|
+
module TodoCurses
|
4
|
+
include Ncurses
|
5
|
+
|
6
|
+
# A curses based todo.txt file viewer
|
7
|
+
class View
|
8
|
+
# Run the ncurses application
|
9
|
+
def interact
|
10
|
+
while true
|
11
|
+
result = true
|
12
|
+
c = Ncurses.getch
|
13
|
+
case c
|
14
|
+
when 'j'.ord # Move down
|
15
|
+
result = scroll_down
|
16
|
+
when 'k'.ord # Move up
|
17
|
+
result = scroll_up
|
18
|
+
when 'J'.ord # Decrease priority
|
19
|
+
result = priority_down
|
20
|
+
when 'K'.ord # Increase priority
|
21
|
+
result = priority_up
|
22
|
+
when 'x'.ord # Toggle complete
|
23
|
+
toggle_item_completion
|
24
|
+
when 'n'.ord # New item
|
25
|
+
new_item
|
26
|
+
when 'h'.ord # Scroll to top
|
27
|
+
while scroll_up
|
28
|
+
end
|
29
|
+
when 'l'.ord # Scroll to bottom
|
30
|
+
while scroll_down
|
31
|
+
end
|
32
|
+
when 'q'.ord
|
33
|
+
break
|
34
|
+
else
|
35
|
+
@screen.mvprintw(0, 0, "[unknown key `#{Ncurses.keyname(c)}'=#{c}] ")
|
36
|
+
end
|
37
|
+
Ncurses.beep unless result
|
38
|
+
end
|
39
|
+
clean_done_tasks
|
40
|
+
close_ncurses
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Create a new fileviewer, and view the file.
|
46
|
+
def initialize(filename)
|
47
|
+
init_curses
|
48
|
+
load_file(filename)
|
49
|
+
interact
|
50
|
+
end
|
51
|
+
|
52
|
+
# Perform the curses setup
|
53
|
+
def init_curses
|
54
|
+
@screen = Ncurses.initscr
|
55
|
+
Ncurses.nonl
|
56
|
+
Ncurses.cbreak
|
57
|
+
Ncurses.noecho
|
58
|
+
@screen.scrollok(true)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Loads the given file as a todo.txt array. Sets the view to the top
|
62
|
+
# and redraws the list view.
|
63
|
+
# @param filename [String] path to the text file to be loaded
|
64
|
+
def load_file(filename)
|
65
|
+
@done_file = File.dirname(filename) + '/done.txt'
|
66
|
+
@list = TodoCurses::List.new filename
|
67
|
+
@list.sort! { |x, y| y <=> x } # Reverse sort
|
68
|
+
items = []
|
69
|
+
last_priority = nil
|
70
|
+
last_selection = @menu.current_item.user_object if @menu
|
71
|
+
current_selection = nil
|
72
|
+
|
73
|
+
# Build the menu item list
|
74
|
+
@list.each do |item|
|
75
|
+
# Insert dividers on priority change
|
76
|
+
if item.priority != last_priority
|
77
|
+
divider_priority = item.priority.nil? ? 'N/A' : item.priority.to_s
|
78
|
+
divider = Ncurses::Menu::ITEM.new(divider_priority, '')
|
79
|
+
items << divider
|
80
|
+
last_priority = item.priority
|
81
|
+
end
|
82
|
+
|
83
|
+
# Build the todo menu item
|
84
|
+
menu_item = Ncurses::Menu::ITEM.new(item.to_s, '') # name, description
|
85
|
+
menu_item.user_object = item
|
86
|
+
items << menu_item
|
87
|
+
|
88
|
+
# Set the current selection
|
89
|
+
current_selection = menu_item if item.to_s == last_selection.to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
# Build the final menu object
|
93
|
+
@menu = Ncurses::Menu::MENU.new items
|
94
|
+
@menu.set_menu_win(@screen)
|
95
|
+
@menu.set_menu_sub(@screen.derwin(@screen.getmaxx, @screen.getmaxy, 0, 0))
|
96
|
+
@menu.set_menu_format(@screen.getmaxy, 1)
|
97
|
+
|
98
|
+
# Set dividers to non-interactive
|
99
|
+
@menu.menu_items.select { |i| i.user_object.nil? }.each do |divider|
|
100
|
+
divider.item_opts_off Ncurses::Menu::O_SELECTABLE
|
101
|
+
end
|
102
|
+
|
103
|
+
# Show the menu
|
104
|
+
@screen.clear
|
105
|
+
@menu.post_menu
|
106
|
+
|
107
|
+
# Set selection position
|
108
|
+
@menu.set_current_item current_selection if current_selection
|
109
|
+
@menu.menu_driver(
|
110
|
+
Ncurses::Menu::REQ_DOWN_ITEM) if @menu.current_item.user_object.nil?
|
111
|
+
|
112
|
+
# Refresh
|
113
|
+
@screen.refresh
|
114
|
+
end
|
115
|
+
|
116
|
+
# Moves the current selection's priority up by one unless it is at Z.
|
117
|
+
def priority_up
|
118
|
+
item = @menu.current_item.user_object
|
119
|
+
item.priority_inc
|
120
|
+
save_list
|
121
|
+
end
|
122
|
+
|
123
|
+
# Moves the current selection's priority down by one unless it is at A.
|
124
|
+
def priority_down
|
125
|
+
item = @menu.current_item.user_object
|
126
|
+
item.priority_dec
|
127
|
+
save_list
|
128
|
+
end
|
129
|
+
|
130
|
+
# Scroll the display up by one line
|
131
|
+
# @return [Boolean] true if the action completed successfully.
|
132
|
+
def scroll_up
|
133
|
+
# Move to the next item if it's not the first in the list
|
134
|
+
unless @menu.menu_items[0].user_object.nil? &&
|
135
|
+
@menu.current_item.item_index < 2
|
136
|
+
result = @menu.menu_driver(Ncurses::Menu::REQ_UP_ITEM)
|
137
|
+
end
|
138
|
+
# Move to the next item if it's not a divider
|
139
|
+
result = @menu.menu_driver(
|
140
|
+
Ncurses::Menu::REQ_UP_ITEM) unless @menu.current_item.user_object
|
141
|
+
return true if result == Ncurses::Menu::E_OK
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
# Scroll the display down by one line
|
146
|
+
# @return [Boolean] true if the action completed successfully.
|
147
|
+
def scroll_down
|
148
|
+
result = @menu.menu_driver(Ncurses::Menu::REQ_DOWN_ITEM)
|
149
|
+
result = @menu.menu_driver(
|
150
|
+
Ncurses::Menu::REQ_DOWN_ITEM) unless @menu.current_item.user_object
|
151
|
+
return true if result == Ncurses::Menu::E_OK
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
# Collects a new todo item from the user and saves
|
156
|
+
# it to the text file.
|
157
|
+
def new_item
|
158
|
+
field = Ncurses::Form::FIELD.new(1, @screen.getmaxx - 1, 2, 1, 0, 0)
|
159
|
+
field.set_field_back(Ncurses::A_UNDERLINE)
|
160
|
+
fields = [field]
|
161
|
+
my_form = Ncurses::Form::FORM.new(fields)
|
162
|
+
my_form.user_object = 'My identifier'
|
163
|
+
|
164
|
+
# Calculate the area required for the form
|
165
|
+
rows = []
|
166
|
+
cols = []
|
167
|
+
my_form.scale_form(rows, cols)
|
168
|
+
|
169
|
+
# Create the window to be associated with the form
|
170
|
+
my_form_win = Ncurses::WINDOW.new(rows[0] + 3, cols[0] + 14, 1, 1)
|
171
|
+
my_form_win.keypad(TRUE)
|
172
|
+
|
173
|
+
# Set main window and sub window
|
174
|
+
my_form.set_form_win(my_form_win)
|
175
|
+
my_form.set_form_sub(my_form_win.derwin(rows[0], cols[0], 2, 12))
|
176
|
+
|
177
|
+
my_form.post_form
|
178
|
+
|
179
|
+
# Print field types
|
180
|
+
my_form_win.mvaddstr(4, 2, 'New item')
|
181
|
+
my_form_win.wrefresh
|
182
|
+
|
183
|
+
Ncurses::stdscr.refresh
|
184
|
+
|
185
|
+
new_item_text = capture_text_field_input(my_form_win, my_form, field)
|
186
|
+
|
187
|
+
# Save results
|
188
|
+
save_new_item(new_item_text)
|
189
|
+
|
190
|
+
# Clean up
|
191
|
+
my_form.unpost_form
|
192
|
+
my_form.free_form
|
193
|
+
|
194
|
+
field.free_field
|
195
|
+
# fields.each {|f| f.free_field}
|
196
|
+
end
|
197
|
+
|
198
|
+
# Adds a new item to the list and saves the file
|
199
|
+
# @param task [String] the task to be added
|
200
|
+
# @return [TodoCurses::List] the updated list
|
201
|
+
def save_new_item(task)
|
202
|
+
@list << TodoCurses::Task.new(task)
|
203
|
+
save_list
|
204
|
+
@list
|
205
|
+
end
|
206
|
+
|
207
|
+
# Saves the current state of the list. Overrides the current file.
|
208
|
+
# Reloads the newly saved file.
|
209
|
+
def save_list
|
210
|
+
File.open(@list.path, 'w') { |file| file << @list.join("\n") }
|
211
|
+
load_file @list.path
|
212
|
+
end
|
213
|
+
|
214
|
+
# Marks the currently selected menu item as complete and saves the list.
|
215
|
+
def toggle_item_completion
|
216
|
+
@menu.current_item.user_object.toggle!
|
217
|
+
save_list
|
218
|
+
end
|
219
|
+
|
220
|
+
# Saves done tasks to done.txt and removes them from todo.txt
|
221
|
+
def clean_done_tasks
|
222
|
+
done_tasks = @list.select { |task| !task.completed_on.nil? }
|
223
|
+
File.open(@done_file, 'a') do |file|
|
224
|
+
file << "\n"
|
225
|
+
file << done_tasks.join("\n")
|
226
|
+
end
|
227
|
+
remaining_tasks = @list.select { |task| task.completed_on.nil? }
|
228
|
+
File.open(@list.path, 'w') { |file| file << remaining_tasks.join("\n") }
|
229
|
+
end
|
230
|
+
|
231
|
+
# put the screen back in its normal state
|
232
|
+
def close_ncurses
|
233
|
+
Ncurses.echo
|
234
|
+
Ncurses.nocbreak
|
235
|
+
Ncurses.nl
|
236
|
+
Ncurses.endwin
|
237
|
+
end
|
238
|
+
|
239
|
+
# Captures text input into a form and returns the resulting string.
|
240
|
+
# @param window [Window] the form window
|
241
|
+
# @param form [FORM] the form to be captured
|
242
|
+
# @param field [FIELD] the form to be captured
|
243
|
+
# @return [String] the captured input
|
244
|
+
def capture_text_field_input(window, form, field)
|
245
|
+
while (ch = window.getch) != 13 # return is ascii 13
|
246
|
+
case ch
|
247
|
+
when Ncurses::KEY_LEFT
|
248
|
+
form.form_driver Ncurses::Form::REQ_PREV_CHAR
|
249
|
+
when Ncurses::KEY_RIGHT
|
250
|
+
form.form_driver Ncurses::Form::REQ_NEXT_CHAR
|
251
|
+
when Ncurses::KEY_BACKSPACE
|
252
|
+
form.form_driver Ncurses::Form::REQ_DEL_PREV
|
253
|
+
else
|
254
|
+
form.form_driver ch # If this is a normal character, it gets Printed
|
255
|
+
end
|
256
|
+
end
|
257
|
+
form.form_driver Ncurses::Form::REQ_NEXT_FIELD # Request next to set 0 buffer in field
|
258
|
+
Ncurses::Form.field_buffer(field, 0)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|