ncumbra 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG +14 -0
- data/README.md +21 -6
- data/examples/data/atp_matches_2017.csv +2887 -0
- data/examples/data/atp_matches_2018.csv +460 -0
- data/examples/data/import.sh +13 -0
- data/examples/data/schema.txt +51 -0
- data/examples/ex1.rb +2 -0
- data/examples/ex2.rb +1 -0
- data/examples/ex5.rb +1 -1
- data/examples/exbox.rb +3 -2
- data/examples/exm1.rb +1 -2
- data/examples/extab2.rb +170 -0
- data/examples/extab3.rb +306 -0
- data/examples/extabular.rb +123 -0
- data/examples/tasks.csv +88 -0
- data/lib/umbra/box.rb +38 -21
- data/lib/umbra/checkbox.rb +2 -2
- data/lib/umbra/dialog.rb +1 -2
- data/lib/umbra/eventhandler.rb +52 -4
- data/lib/umbra/field.rb +7 -3
- data/lib/umbra/form.rb +119 -55
- data/lib/umbra/keymappinghandler.rb +4 -4
- data/lib/umbra/label.rb +31 -17
- data/lib/umbra/labeledfield.rb +7 -8
- data/lib/umbra/listbox.rb +114 -341
- data/lib/umbra/messagebox.rb +93 -42
- data/lib/umbra/multiline.rb +476 -0
- data/lib/umbra/pad.rb +20 -14
- data/lib/umbra/radiobutton.rb +2 -2
- data/lib/umbra/table.rb +260 -0
- data/lib/umbra/tabular.rb +431 -0
- data/lib/umbra/textbox.rb +36 -223
- data/lib/umbra/togglebutton.rb +3 -5
- data/lib/umbra/version.rb +1 -1
- data/lib/umbra/widget.rb +72 -13
- data/lib/umbra/window.rb +59 -15
- data/lib/umbra.rb +82 -11
- data/umbra.gemspec +2 -2
- metadata +18 -4
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
sqlite3 tennis.sqlite <<!
|
4
|
+
.mode csv
|
5
|
+
.import atp_matches_2018.csv matches
|
6
|
+
!
|
7
|
+
|
8
|
+
sqlite3 tennis.sqlite <<!
|
9
|
+
DELETE from matches WHERE tourney_id = 'tourney_id';
|
10
|
+
!
|
11
|
+
sqlite3 tennis.sqlite <<!
|
12
|
+
select count(1) from matches;
|
13
|
+
!
|
@@ -0,0 +1,51 @@
|
|
1
|
+
CREATE TABLE matches(
|
2
|
+
"tourney_id" TEXT,
|
3
|
+
"tourney_name" TEXT,
|
4
|
+
"surface" TEXT,
|
5
|
+
"draw_size" TEXT,
|
6
|
+
"tourney_level" TEXT,
|
7
|
+
"tourney_date" TEXT,
|
8
|
+
"match_num" TEXT,
|
9
|
+
"winner_id" TEXT,
|
10
|
+
"winner_seed" TEXT,
|
11
|
+
"winner_entry" TEXT,
|
12
|
+
"winner_name" TEXT,
|
13
|
+
"winner_hand" TEXT,
|
14
|
+
"winner_ht" TEXT,
|
15
|
+
"winner_ioc" TEXT,
|
16
|
+
"winner_age" TEXT,
|
17
|
+
"winner_rank" TEXT,
|
18
|
+
"winner_rank_points" TEXT,
|
19
|
+
"loser_id" TEXT,
|
20
|
+
"loser_seed" TEXT,
|
21
|
+
"loser_entry" TEXT,
|
22
|
+
"loser_name" TEXT,
|
23
|
+
"loser_hand" TEXT,
|
24
|
+
"loser_ht" TEXT,
|
25
|
+
"loser_ioc" TEXT,
|
26
|
+
"loser_age" TEXT,
|
27
|
+
"loser_rank" TEXT,
|
28
|
+
"loser_rank_points" TEXT,
|
29
|
+
"score" TEXT,
|
30
|
+
"best_of" TEXT,
|
31
|
+
"round" TEXT,
|
32
|
+
"minutes" TEXT,
|
33
|
+
"w_ace" TEXT,
|
34
|
+
"w_df" TEXT,
|
35
|
+
"w_svpt" TEXT,
|
36
|
+
"w_1stIn" TEXT,
|
37
|
+
"w_1stWon" TEXT,
|
38
|
+
"w_2ndWon" TEXT,
|
39
|
+
"w_SvGms" TEXT,
|
40
|
+
"w_bpSaved" TEXT,
|
41
|
+
"w_bpFaced" TEXT,
|
42
|
+
"l_ace" TEXT,
|
43
|
+
"l_df" TEXT,
|
44
|
+
"l_svpt" TEXT,
|
45
|
+
"l_1stIn" TEXT,
|
46
|
+
"l_1stWon" TEXT,
|
47
|
+
"l_2ndWon" TEXT,
|
48
|
+
"l_SvGms" TEXT,
|
49
|
+
"l_bpSaved" TEXT,
|
50
|
+
"l_bpFaced" TEXT
|
51
|
+
);
|
data/examples/ex1.rb
CHANGED
data/examples/ex2.rb
CHANGED
data/examples/ex5.rb
CHANGED
data/examples/exbox.rb
CHANGED
@@ -45,7 +45,8 @@ begin
|
|
45
45
|
# check with long lines
|
46
46
|
catch(:close) do
|
47
47
|
form = Form.new win
|
48
|
-
box = Box.new row: 2,col: 2, height: 24, width: 80, title: "A box", justify: :left
|
48
|
+
#box = Box.new row: 2,col: 2, height: 24, width: 80, title: "A box", justify: :left
|
49
|
+
box = Box.new row: 1,col: 2, height: -3, width: 80, title: "A box", justify: :left
|
49
50
|
win.printstring(3,1,"Just testing that listbox is correctly positioned")
|
50
51
|
#lb = Listbox.new list: alist, row: 4, col: 2, width: 70, height: 18
|
51
52
|
lb = Listbox.new list: alist
|
@@ -55,7 +56,7 @@ begin
|
|
55
56
|
win.printstring(box.row+1,0,"XX")
|
56
57
|
win.printstring(box.row+1,lb.col+lb.width,"XX")
|
57
58
|
win.printstring(box.row+box.height,1,"This prints below the listbox")
|
58
|
-
brow = box.row+box.height+
|
59
|
+
brow = box.row+box.height+2
|
59
60
|
tb = ToggleButton.new onvalue: "Border", offvalue: "No Border", row: brow, col: 10, value: true
|
60
61
|
ab = Button.new text: "Processes" , row: brow, col: 30
|
61
62
|
logb = Button.new text: "LogFile" , row: brow, col: 50
|
data/examples/exm1.rb
CHANGED
@@ -39,7 +39,7 @@ def _alert_fields str
|
|
39
39
|
add Field.new name:"name", default: "Rahul", width: 25, color_pair: CP_CYAN, attr: REVERSE
|
40
40
|
add LabeledField.new label:"Age:", name:"age", text:"25", col: 15, color_pair: CP_CYAN, attr: REVERSE
|
41
41
|
# unfortunately this exceeds since width only takes field into account not label
|
42
|
-
add LabeledField.new label:"Address:", name:"address", width:50 , col: 15, color_pair: CP_CYAN, attr: REVERSE
|
42
|
+
add LabeledField.new label:"Address:", name:"address", width:50 , maxlen: 70, col: 15, color_pair: CP_CYAN, attr: REVERSE
|
43
43
|
end
|
44
44
|
mb.run
|
45
45
|
end
|
@@ -92,7 +92,6 @@ begin
|
|
92
92
|
b4.command do
|
93
93
|
ret = confirm "Do you wish to go?"
|
94
94
|
message_label.text = "You selected #{ret}"
|
95
|
-
#message_label.touch
|
96
95
|
end
|
97
96
|
form.add_widget message_label, b1, b2, b3, b4
|
98
97
|
form.pack
|
data/examples/extab2.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# ----------------------------------------------------------------------------- #
|
3
|
+
# File: extab2.rb
|
4
|
+
# Description:
|
5
|
+
# Author: j kepler http://github.com/mare-imbrium/canis/
|
6
|
+
# Date: 2018-05-06 - 11:20
|
7
|
+
# License: MIT
|
8
|
+
# Last update: 2018-05-22 23:05
|
9
|
+
# ----------------------------------------------------------------------------- #
|
10
|
+
# extab2.rb Copyright (C) 2018 j kepler
|
11
|
+
require 'umbra'
|
12
|
+
require 'umbra/label'
|
13
|
+
require 'umbra/tabular'
|
14
|
+
require 'umbra/listbox'
|
15
|
+
require 'umbra/textbox'
|
16
|
+
require 'umbra/box'
|
17
|
+
require 'umbra/table'
|
18
|
+
|
19
|
+
def startup
|
20
|
+
require 'date'
|
21
|
+
|
22
|
+
$log = create_logger "v.log"
|
23
|
+
#$log.level = Logger::DEBUG
|
24
|
+
today = Date.today
|
25
|
+
$log.info "Started demo table on #{today}"
|
26
|
+
end
|
27
|
+
def statusline win, str, col = 0
|
28
|
+
win.printstring( FFI::NCurses.LINES-1, col, str, 5)
|
29
|
+
end
|
30
|
+
begin
|
31
|
+
include Umbra
|
32
|
+
init_curses
|
33
|
+
startup
|
34
|
+
win = Window.new
|
35
|
+
statusline(win, " "*(win.width-0), 0)
|
36
|
+
statusline(win, "Press C-q to quit #{win.height}:#{win.width}", 20)
|
37
|
+
title = Label.new( :text => "Demo of Table", :row => 0, :col => 0 , :width => FFI::NCurses.COLS-1,
|
38
|
+
:justify => :center, :color_pair => 0)
|
39
|
+
|
40
|
+
form = Form.new win
|
41
|
+
form.add_widget title
|
42
|
+
|
43
|
+
box = Box.new row: 2, col: 0, width: 15, height: 7
|
44
|
+
=begin
|
45
|
+
t = Tabular.new(['a', 'b'], [1, 2], [3, 4], [5,6])
|
46
|
+
lb = Listbox.new list: t.render
|
47
|
+
=end
|
48
|
+
#table = Table.new(['a', 'b'], [1, 2], [3, 4], [5,6])
|
49
|
+
table = Table.new(columns: ['a', 'b'], data: [[1, 2], [3, 4], [5,6]])
|
50
|
+
box.fill table
|
51
|
+
box1 = Box.new row: box.row + box.height + 0, col: box.col, width: box.width, height: 7
|
52
|
+
table1 = Table.new columns: ['a', 'b']
|
53
|
+
table1 << [8, 6]
|
54
|
+
table1 << [1, 2]
|
55
|
+
table1 << [3, 4]
|
56
|
+
table1 << [4, 6]
|
57
|
+
table1 << [2, 6]
|
58
|
+
table1.y = '| '
|
59
|
+
table1.x = '+-'
|
60
|
+
#lb1 = Listbox.new list: t.render
|
61
|
+
#table1.render
|
62
|
+
box1.fill table1
|
63
|
+
|
64
|
+
#file = File.expand_path("examples/tasks.csv", __FILE__)
|
65
|
+
file = File.expand_path("examples/tasks.csv")
|
66
|
+
lines = File.open(file,'r').readlines
|
67
|
+
heads = %w[ id sta type prio title ]
|
68
|
+
t = Table.new do |t|
|
69
|
+
t.headings = heads
|
70
|
+
t.y = ' '
|
71
|
+
lines.each { |e| t.add_row e.chomp.split '|' }
|
72
|
+
end
|
73
|
+
t.tabular.use_separator = false
|
74
|
+
# testing out coloring different rows in different colors depending on status
|
75
|
+
def t.color_of_row index, state
|
76
|
+
arr = super
|
77
|
+
if index == 0 ## header
|
78
|
+
arr = @header_color_pair || [ CP_MAGENTA, REVERSE ]
|
79
|
+
elsif data[index-1][1] == "clo"
|
80
|
+
arr = [ CP_BLUE, NORMAL ]
|
81
|
+
elsif self.data[index-1][1] == "ope"
|
82
|
+
arr = [ CP_YELLOW, BOLD ]
|
83
|
+
end
|
84
|
+
arr
|
85
|
+
end
|
86
|
+
#t.column_width 4, 15 ## checking truncate of long data
|
87
|
+
|
88
|
+
#t.render
|
89
|
+
box2 = Box.new title: "tasks.csv", row: box.row , col: box.col + box.width+1, width: FFI::NCurses.COLS-box.width-1, height: FFI::NCurses.LINES-1-box.row
|
90
|
+
#lb2 = Listbox.new list: t
|
91
|
+
|
92
|
+
table2 = t
|
93
|
+
#box2.fill table2
|
94
|
+
|
95
|
+
r = `ls -l`
|
96
|
+
res = r.split("\n")
|
97
|
+
|
98
|
+
table3 = Table.new do
|
99
|
+
self.headings = 'User', 'Size', 'Mon', 'Date', 'Time', 'File'
|
100
|
+
self.header_color_pair = [ CP_CYAN, REVERSE ]
|
101
|
+
res.each { |e|
|
102
|
+
cols = e.split
|
103
|
+
next if cols.count < 6
|
104
|
+
cols = cols[3..-1]
|
105
|
+
cols = cols[0..5] if cols.count > 6
|
106
|
+
add_row cols
|
107
|
+
}
|
108
|
+
column_width 1, 10
|
109
|
+
#column_width 5, 10 ## testing truncate
|
110
|
+
column_align 1, :right
|
111
|
+
column_align 3, :right
|
112
|
+
end
|
113
|
+
|
114
|
+
# testing out coloring different columns in different color
|
115
|
+
## NOTE that as we scroll the colors will change for a column
|
116
|
+
## We don't know which columns have come in since scrolling will cut off some columns.
|
117
|
+
## We need to access pcol to find out which the starting column is, so which know which columns
|
118
|
+
## we actually have.
|
119
|
+
def table3.print_row(win, row, col, str, index, state)
|
120
|
+
f = str.split('|')
|
121
|
+
attrib = NORMAL
|
122
|
+
attrib = BOLD if index == 0
|
123
|
+
attrib = REVERSE if state == :HIGHLIGHTED
|
124
|
+
f.each_with_index {|c, ix|
|
125
|
+
win.printstring(row, col, c, ix+1, attrib)
|
126
|
+
col += c.length+1
|
127
|
+
}
|
128
|
+
|
129
|
+
end
|
130
|
+
#table3.render
|
131
|
+
#t.y = '| '
|
132
|
+
#t.x = '+-'
|
133
|
+
#tb = Textbox.new list: t.render
|
134
|
+
box2.add table2, table3
|
135
|
+
|
136
|
+
form.add_widget box, table
|
137
|
+
form.add_widget box1, table1
|
138
|
+
form.add_widget box2, table2, table3
|
139
|
+
|
140
|
+
|
141
|
+
form.pack
|
142
|
+
form.repaint
|
143
|
+
win.wrefresh
|
144
|
+
|
145
|
+
y = x = 1
|
146
|
+
main_loop(form) do |ch|
|
147
|
+
statusline(win, "Pressed #{ch} on ", 70)
|
148
|
+
form.handle_key ch
|
149
|
+
win.wrefresh
|
150
|
+
end
|
151
|
+
=begin
|
152
|
+
while (ch = win.getkey) != FFI::NCurses::KEY_CTRL_Q
|
153
|
+
next if ch == -1
|
154
|
+
form.handle_key ch
|
155
|
+
#statusline(win, "Pressed #{ch} on ", 70)
|
156
|
+
win.wrefresh
|
157
|
+
end
|
158
|
+
=end
|
159
|
+
|
160
|
+
rescue Object => e
|
161
|
+
@window.destroy if @window
|
162
|
+
FFI::NCurses.endwin
|
163
|
+
puts e
|
164
|
+
puts e.backtrace.join("\n")
|
165
|
+
ensure
|
166
|
+
@window.destroy if @window
|
167
|
+
$log.close if $log
|
168
|
+
FFI::NCurses.endwin
|
169
|
+
puts
|
170
|
+
end
|
data/examples/extab3.rb
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# ----------------------------------------------------------------------------- #
|
3
|
+
# File: extab3.rb
|
4
|
+
# Description: Example of using a Table with an sqlite3 resultset.
|
5
|
+
# Author: j kepler http://github.com/mare-imbrium/umbra/
|
6
|
+
# Date: 2018-05-11
|
7
|
+
# License: MIT
|
8
|
+
# Last update: 2018-05-23 08:34
|
9
|
+
# ----------------------------------------------------------------------------- #
|
10
|
+
# extab3.rb Copyright (C) 2018 j kepler
|
11
|
+
require 'umbra'
|
12
|
+
require 'umbra/label'
|
13
|
+
require 'umbra/tabular'
|
14
|
+
require 'umbra/box'
|
15
|
+
require 'umbra/table'
|
16
|
+
require 'sqlite3'
|
17
|
+
|
18
|
+
def startup
|
19
|
+
require 'logger'
|
20
|
+
require 'date'
|
21
|
+
|
22
|
+
path = File.join(ENV["LOGDIR"] || "./" ,"v.log")
|
23
|
+
file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT)
|
24
|
+
$log = Logger.new(path)
|
25
|
+
$log.level = Logger::DEBUG
|
26
|
+
today = Date.today
|
27
|
+
$log.info "Started demo table #{$0} on #{today}"
|
28
|
+
$log.info "RETURN is #{curses::KEY_RETURN} "
|
29
|
+
$log.info "ENTER is #{curses::KEY_ENTER} "
|
30
|
+
path = File.dirname(__FILE__)
|
31
|
+
$log.info "PATH: #{path}"
|
32
|
+
FFI::NCurses.init_pair(10, FFI::NCurses::BLACK, FFI::NCurses::GREEN) # statusline
|
33
|
+
@file = "#{path}/data/tennis.sqlite"
|
34
|
+
@db = SQLite3::Database.new(@file)
|
35
|
+
@tablename = "matches"
|
36
|
+
$ncurses_timeout = -1
|
37
|
+
end
|
38
|
+
def ORIGstatusline win, str, col = 0
|
39
|
+
win.printstring( FFI::NCurses.LINES-1, col, str, 10)
|
40
|
+
end
|
41
|
+
def oldstatusline win, str, column = 1
|
42
|
+
# LINES-2 prints on second last line so that box can be seen
|
43
|
+
win.printstring( FFI::NCurses.LINES-1, 0, " "*(win.width), 6, REVERSE)
|
44
|
+
# printing fields in two alternating colors so easier to see
|
45
|
+
str.split("|").each_with_index {|s,ix|
|
46
|
+
_color = 6
|
47
|
+
_color = 5 if ix%2==0
|
48
|
+
win.printstring( FFI::NCurses.LINES-1, column, s, _color, REVERSE)
|
49
|
+
column += s.length+1
|
50
|
+
}
|
51
|
+
end
|
52
|
+
def statusline win, str, column = 1
|
53
|
+
@status.text = str
|
54
|
+
end # }}}
|
55
|
+
def fetch_data db, sql # {{{
|
56
|
+
$log.debug "SQL: #{sql} "
|
57
|
+
columns, *rows = db.execute2(sql)
|
58
|
+
content = rows
|
59
|
+
return nil if content.nil? or content[0].nil?
|
60
|
+
datatypes = content[0].types #if @datatypes.nil?
|
61
|
+
return content, columns, datatypes
|
62
|
+
end # }}}
|
63
|
+
def view_details(table)
|
64
|
+
id = table.current_id
|
65
|
+
|
66
|
+
res = %x{ sqlite3 #{@file} -line "select * from #{@tablename} where rowid = '#{id}'"}
|
67
|
+
if res and !res.empty?
|
68
|
+
res = res.split("\n")
|
69
|
+
view res
|
70
|
+
else
|
71
|
+
alert("No row for #{id} in #{@tablename}")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
def get_full_row_as_hash(table)
|
75
|
+
id = table.current_id
|
76
|
+
data, columns, datatypes = fetch_data(@db, "select * from #{@tablename} where rowid = '#{id}'")
|
77
|
+
hash = columns.zip(data.first).to_h
|
78
|
+
#$log.debug " HASH == #{hash}"
|
79
|
+
return hash
|
80
|
+
end
|
81
|
+
def filter_popup lb # {{{
|
82
|
+
## present user with a popup allowing him to select range of rating title, range of year, status etc
|
83
|
+
## for querying.
|
84
|
+
#rowdata = fetch_data(@db, "select title, actors, director, imdbrating, language, genre , metascore, imdbvotes from #{@tablename} WHERE rowid = #{id}")
|
85
|
+
data = []
|
86
|
+
_columns = ["tourney_name", "winner_name", "loser_name", "surface", "Year >", "Year <", "round", "level", "best_of"]
|
87
|
+
_columns.each {|c| data << "" }
|
88
|
+
_datatypes = ["text","text","text","char(10)","char(4)","char(4)","char(5)", "char(1)", "char(1)"]
|
89
|
+
ret, array = generic_edit "Filter", _columns, data, _datatypes
|
90
|
+
if ret == 0
|
91
|
+
# okay pressed
|
92
|
+
## query database on values entered TODO
|
93
|
+
_filter_table(lb, _columns, array)
|
94
|
+
end
|
95
|
+
end # }}}
|
96
|
+
def _filter_table(lb, columns, fields)
|
97
|
+
cols = []
|
98
|
+
bind_vars = []
|
99
|
+
sql = "#{@query}"
|
100
|
+
fields.each_with_index { |f, ix|
|
101
|
+
next if f.text == ""
|
102
|
+
c = columns[ix].downcase
|
103
|
+
if c == "round"
|
104
|
+
cols << " round = ? "
|
105
|
+
bind_vars << "#{f.text}"
|
106
|
+
elsif c == "year >"
|
107
|
+
cols << " tourney_date >= ? "
|
108
|
+
bind_vars << "#{f.text}"
|
109
|
+
elsif c == "year <"
|
110
|
+
cols << " tourney_date <= ? "
|
111
|
+
bind_vars << "#{f.text}"
|
112
|
+
elsif c == "level"
|
113
|
+
cols << " tourney_level = ? "
|
114
|
+
bind_vars << "#{f.text}"
|
115
|
+
elsif c == "best_of"
|
116
|
+
cols << " best_of = ? "
|
117
|
+
bind_vars << "#{f.text}"
|
118
|
+
else
|
119
|
+
cols << columns[ix] + " LIKE ? "
|
120
|
+
bind_vars << "%#{f.text}%"
|
121
|
+
end
|
122
|
+
}
|
123
|
+
if !cols.empty?
|
124
|
+
sql += " WHERE " + cols.join(" AND ")
|
125
|
+
end
|
126
|
+
$log.debug " SQL: #{sql} "
|
127
|
+
$log.debug " ibv: #{bind_vars.join ', '} "
|
128
|
+
alist = @db.execute( sql, bind_vars)
|
129
|
+
if alist #and !alist.empty?
|
130
|
+
#lb.list = alist ## results in error, FIXME
|
131
|
+
lb.data = alist
|
132
|
+
else
|
133
|
+
#alert "No rows returned. Check your query"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
## generic edit messagebox
|
137
|
+
## @param array of column labels
|
138
|
+
## @param array of data to edit, or blank
|
139
|
+
## @param array of sqlite3 datatypes (real int text char(9) date)
|
140
|
+
## @return offset of button pressed and array of LabeledFields
|
141
|
+
## 0 = ok, 1 = cancel.
|
142
|
+
## Use element.text() to get edited value of each element of array
|
143
|
+
def generic_edit _title, _columns, data, _datatypes
|
144
|
+
require 'umbra/messagebox'
|
145
|
+
require 'umbra/labeledfield'
|
146
|
+
|
147
|
+
array = [] # array of labeledfield we will create, and return
|
148
|
+
mb = MessageBox.new title: _title, width: 80 do
|
149
|
+
data.each_with_index do |r, ix|
|
150
|
+
dt = _datatypes[ix]
|
151
|
+
_w, _ml = calc_width_from_datatype( dt )
|
152
|
+
$log.debug " MESSAGE_BOX::: #{_columns[ix]} ==> #{_datatypes[ix]} "
|
153
|
+
|
154
|
+
x = LabeledField.new label: _columns[ix], name: _columns[ix], text: r, col: 20, width: _w, maxlen: _ml, color_pair: CP_CYAN, attr: REVERSE
|
155
|
+
|
156
|
+
$log.debug " MESSAGE_BOX::: after create #{_columns[ix]} ==> #{_datatypes[ix]} "
|
157
|
+
array << x
|
158
|
+
add x
|
159
|
+
$log.debug " MESSAGE_BOX::: after add #{_columns[ix]} ==> #{_datatypes[ix]} "
|
160
|
+
end
|
161
|
+
end
|
162
|
+
ret = mb.run
|
163
|
+
return ret, array
|
164
|
+
end
|
165
|
+
## Given an sqlite3 datatype, returns width and maximum length.
|
166
|
+
## Used in messageboxes.
|
167
|
+
def calc_width_from_datatype dt
|
168
|
+
if dt == "text"
|
169
|
+
_w = 50
|
170
|
+
_ml = 100
|
171
|
+
elsif dt == "date"
|
172
|
+
_w = 12
|
173
|
+
_ml = 20
|
174
|
+
elsif dt == "real"
|
175
|
+
_w = 10
|
176
|
+
_ml = 10
|
177
|
+
elsif dt == "int"
|
178
|
+
_w = 8
|
179
|
+
_ml = 8
|
180
|
+
elsif dt.index("char")
|
181
|
+
a = dt.match(/(\d+)/)
|
182
|
+
_w = a.captures().first.to_i
|
183
|
+
_ml = _w
|
184
|
+
else
|
185
|
+
_w = _ml = 30
|
186
|
+
end
|
187
|
+
return _w, _ml
|
188
|
+
end
|
189
|
+
begin
|
190
|
+
include Umbra
|
191
|
+
init_curses
|
192
|
+
startup
|
193
|
+
win = Window.new
|
194
|
+
# by making this is label with -1 as row, it will recalc and redraw when window height is changed.
|
195
|
+
@status = Label .new( text: "Press C-q to quit", row: -1, col: 0, width: -1 )
|
196
|
+
def @status.print_label(win, row, col, format, value, _color, _attr)
|
197
|
+
column = col
|
198
|
+
win.printstring( row, 0, " "*(win.width), 6, REVERSE)
|
199
|
+
value.split("|").each_with_index {|s,ix|
|
200
|
+
_color = 6
|
201
|
+
_color = 5 if ix%2==0
|
202
|
+
win.printstring( row, column, s, _color, REVERSE)
|
203
|
+
column += s.length+1
|
204
|
+
}
|
205
|
+
end
|
206
|
+
#statusline(win, " "*(win.width-0), 0)
|
207
|
+
#statusline(win, "Press C-q to quit #{win.height}:#{win.width}", 20)
|
208
|
+
#title = Label.new( :text => "Tennis Query", :row => 0, :col => 0 , :width => FFI::NCurses.COLS-1,
|
209
|
+
title = Label.new( :text => "Tennis Query", :row => 0, :col => 0 , :width => -1,
|
210
|
+
:justify => :center, :color_pair => 0, :attr => REVERSE)
|
211
|
+
|
212
|
+
#win.title "Tennis Query", 3, REVERSE
|
213
|
+
form = Form.new win
|
214
|
+
form.add_widget title, @status
|
215
|
+
|
216
|
+
## -1 here suggests that this widget should extend till the end, less one.
|
217
|
+
## This is not absolute, it is relative to row or col.
|
218
|
+
box = Box.new row: 1, col: 0, width: -1, height: -2
|
219
|
+
#box = Box.new row: 1, col: 0, width: -1, height: FFI::NCurses.LINES-2
|
220
|
+
#form.position_below(box, label)
|
221
|
+
#box.below(title)
|
222
|
+
#box.expand_right(0).expand_down(-1)
|
223
|
+
#box.expand_down(-1)
|
224
|
+
@query = "SELECT rowid, tourney_id, tourney_name, tourney_date, winner_name, loser_name, score, round FROM matches "
|
225
|
+
@order_by = "ORDER BY tourney_date, tourney_name, match_num"
|
226
|
+
data, columns, datatypes = fetch_data(@db, "#{@query} #{@order_by}")
|
227
|
+
|
228
|
+
table = Table.new(columns: columns, data: data) do |tt|
|
229
|
+
tt.column_hidden(0, true)
|
230
|
+
tt.column_hidden(1, true)
|
231
|
+
tt.column_width(6, 24)
|
232
|
+
end
|
233
|
+
#table.column_hide(0)
|
234
|
+
#table.column_hide(1)
|
235
|
+
#table.column_width(6, 24)
|
236
|
+
box.title = "#{table.row_count} rows"
|
237
|
+
box.fill table
|
238
|
+
tabular = table.tabular
|
239
|
+
# this is one way of hiding a column if we don't want to use the hide option.
|
240
|
+
# But we have to supply formatstring, and take care of separator and headings too.
|
241
|
+
#def tabular.convert_value_to_text row, format_string, index
|
242
|
+
#"%-5.5s |%-10.10s |%-12.12s |%-12.12s |%-27.27s |%-27.27s |%-24.24s |%-5.5s" % row
|
243
|
+
#"%-12.12s |%-12.12s |%-27.27s |%-27.27s |%-24.24s |%-5.5s" % row[2..-1]
|
244
|
+
#end
|
245
|
+
#def table._format_value line, index, state
|
246
|
+
#end
|
247
|
+
def table.color_of_data_row index, state, data_index
|
248
|
+
arr = _format_color index, state
|
249
|
+
if index == 0 ## header
|
250
|
+
arr = @header_color_pair || [ CP_MAGENTA, REVERSE ]
|
251
|
+
elsif data[data_index][-1] == "SF"
|
252
|
+
arr = [ CP_WHITE, BOLD | arr[1] ]
|
253
|
+
elsif data[data_index][-1] == "F"
|
254
|
+
arr = [ CP_YELLOW, BOLD | arr[1]]
|
255
|
+
end
|
256
|
+
|
257
|
+
arr
|
258
|
+
end
|
259
|
+
table.bind_event(:CHANGED) { |obj| box.title = "#{obj.row_count} rows"; }
|
260
|
+
table.bind_event(:PRESS) { |obj| alert( " Pressed on #{obj.current_index} :: #{obj.curpos} ") }
|
261
|
+
table.command do |ix|
|
262
|
+
#rowid = table.current_id
|
263
|
+
data = table.current_row_as_array
|
264
|
+
if data
|
265
|
+
#t_id = data[1]
|
266
|
+
#t_name = data[2]
|
267
|
+
hash = table.current_row_as_hash
|
268
|
+
score = hash['score']
|
269
|
+
h1 = get_full_row_as_hash(table)
|
270
|
+
surface = h1['surface']
|
271
|
+
draw = h1['draw_size']
|
272
|
+
level = h1['tourney_level']
|
273
|
+
#statusline(win, "#{rowid} | #{t_id} | #{t_name} ")
|
274
|
+
statusline(win, data[0..4].join("| ") + " | #{score} | #{surface} | #{draw} | #{level} " )
|
275
|
+
end
|
276
|
+
end
|
277
|
+
table.bind_key( 'v', 'view details') { view_details(table) }
|
278
|
+
table.bind_key( ?\C-s, 'filter popup') { filter_popup(table) }
|
279
|
+
|
280
|
+
|
281
|
+
form.add_widget box, table
|
282
|
+
|
283
|
+
|
284
|
+
form.pack
|
285
|
+
form.repaint
|
286
|
+
form.select_first_field
|
287
|
+
win.wrefresh
|
288
|
+
|
289
|
+
y = x = 1
|
290
|
+
while (ch = win.getkey) != FFI::NCurses::KEY_CTRL_Q
|
291
|
+
next if ch == -1
|
292
|
+
form.handle_key ch
|
293
|
+
#statusline(win, "Pressed #{ch} on ", 70)
|
294
|
+
win.wrefresh
|
295
|
+
end
|
296
|
+
|
297
|
+
rescue Object => e
|
298
|
+
@window.destroy if @window
|
299
|
+
FFI::NCurses.endwin
|
300
|
+
puts e
|
301
|
+
puts e.backtrace.join("\n")
|
302
|
+
ensure
|
303
|
+
@window.destroy if @window
|
304
|
+
FFI::NCurses.endwin
|
305
|
+
puts
|
306
|
+
end
|