ncumbra 0.1.0 → 0.1.1
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/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
|