viewworkbook 0.1.3
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 +7 -0
- data/bin/viewworkbook +31 -0
- data/doc/html/viewworkbook.html +523 -0
- data/doc/license.txt +674 -0
- data/doc/man/viewworkbook.1.gz +0 -0
- data/doc/rst/viewworkbook.rst +126 -0
- data/lib/action.rb +57 -0
- data/lib/busy_indicator/busy_function_test.rb +56 -0
- data/lib/busy_indicator/busy_indicator.rb +95 -0
- data/lib/cell.rb +122 -0
- data/lib/color_output.rb +43 -0
- data/lib/column.rb +97 -0
- data/lib/file_checking.rb +87 -0
- data/lib/log.conf +62 -0
- data/lib/logging.rb +195 -0
- data/lib/menu.rb +107 -0
- data/lib/row.rb +82 -0
- data/lib/scrollable.rb +392 -0
- data/lib/sheetdata.rb +96 -0
- data/lib/sheetinterface.rb +464 -0
- data/lib/translating.rb +89 -0
- data/lib/translations +26 -0
- data/lib/user_input.rb +52 -0
- data/lib/viewworkbook.rb +83 -0
- metadata +128 -0
data/lib/sheetdata.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
|
3
|
+
=begin
|
4
|
+
/*****************************************************************************
|
5
|
+
* Copyright © 2016-2016, Michael Uplawski <michael.uplawski@uplawski.eu> *
|
6
|
+
* *
|
7
|
+
* This program is free software; you can redistribute it and/or modify *
|
8
|
+
* it under the terms of the GNU General Public License as published by *
|
9
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
10
|
+
* (at your option) any later version. *
|
11
|
+
* *
|
12
|
+
* This program is distributed in the hope that it will be useful, *
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
15
|
+
* GNU General Public License for more details. *
|
16
|
+
* *
|
17
|
+
* You should have received a copy of the GNU General Public License *
|
18
|
+
* along with this program; if not, write to the *
|
19
|
+
* Free Software Foundation, Inc., *
|
20
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
21
|
+
*****************************************************************************/
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'roo'
|
25
|
+
require 'roo-xls'
|
26
|
+
require 'filemagic'
|
27
|
+
|
28
|
+
require_relative 'logging'
|
29
|
+
|
30
|
+
# Transforms a file into a Roo spreadsheet instance.
|
31
|
+
|
32
|
+
ODS_Magic = "OpenDocument Spreadsheet"
|
33
|
+
XLS_Magic = "Composite Document File V2 Document"
|
34
|
+
XLSX_Magic = "Microsoft OOXML"
|
35
|
+
|
36
|
+
ODS_MIME = "application/vnd.oasis.opendocument.spreadsheet"
|
37
|
+
XLS_MIME = "application/vnd.ms-excel"
|
38
|
+
XLSX_MIME = "application/octet-stream"
|
39
|
+
CSV_MIME = "text/plain"
|
40
|
+
|
41
|
+
class SheetData
|
42
|
+
self::extend(Logging)
|
43
|
+
@@log = init_logger(STDOUT, Logger::INFO)
|
44
|
+
|
45
|
+
@@file = nil
|
46
|
+
@@workbook = nil
|
47
|
+
|
48
|
+
def self::workbook(file)
|
49
|
+
@@file = file
|
50
|
+
if !@@workbook
|
51
|
+
begin
|
52
|
+
magic, mime = file_type
|
53
|
+
@@workbook = case mime
|
54
|
+
when ODS_MIME
|
55
|
+
if magic.match ODS_Magic
|
56
|
+
@@log.debug('ODS')
|
57
|
+
Roo::Spreadsheet::open(@@file, extension: :ods )
|
58
|
+
end
|
59
|
+
when XLS_MIME
|
60
|
+
if magic.match XLS_Magic
|
61
|
+
@@log.debug('XLS')
|
62
|
+
Roo::Spreadsheet::open(@@file, extension: :xls )
|
63
|
+
end
|
64
|
+
when XLSX_MIME
|
65
|
+
if magic.match XLSX_Magic
|
66
|
+
@@log.debug('XLSX')
|
67
|
+
Roo::Spreadsheet::open(@@file, extension: :xlsx )
|
68
|
+
end
|
69
|
+
when CSV_MIME
|
70
|
+
raise IOError.new('CSV is not yet supported, sorry')
|
71
|
+
else
|
72
|
+
raise IOError.new('Mime-Type is ' << mime << ' and Magic sais: ' << magic << ". Is this supposed to be a spreadsheet?")
|
73
|
+
end
|
74
|
+
rescue Exception => ex
|
75
|
+
msg = 'ERROR! File %s is not supported: %s' %[@@file, ex.message]
|
76
|
+
puts msg
|
77
|
+
@log.error yellow(msg)
|
78
|
+
exit false
|
79
|
+
end
|
80
|
+
|
81
|
+
return @@workbook
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
# derive the mime-type from the file
|
87
|
+
def self::file_type
|
88
|
+
fm = FileMagic.fm
|
89
|
+
file_magic = fm.file(@@file)
|
90
|
+
fm.flags = [:mime_type]
|
91
|
+
file_mime = fm.file(@@file)
|
92
|
+
@log.debug('File type is ' << file_magic << ', Mime-type is ' << file_mime)
|
93
|
+
return file_magic, file_mime
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
@@ -0,0 +1,464 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
|
3
|
+
=begin
|
4
|
+
/***************************************************************************
|
5
|
+
* Copyright © 2014 - 2017, Michael Uplawski <michael.uplawski@uplawski.eu> *
|
6
|
+
* *
|
7
|
+
* This program is free software; you can redistribute it and/or modify *
|
8
|
+
* it under the terms of the GNU General Public License as published by *
|
9
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
10
|
+
* (at your option) any later version. *
|
11
|
+
* *
|
12
|
+
* This program is distributed in the hope that it will be useful, *
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
15
|
+
* GNU General Public License for more details. *
|
16
|
+
* *
|
17
|
+
* You should have received a copy of the GNU General Public License *
|
18
|
+
* along with this program; if not, write to the *
|
19
|
+
* Free Software Foundation, Inc., *
|
20
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
21
|
+
***************************************************************************/
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'roo'
|
25
|
+
require 'io/wait'
|
26
|
+
require 'io/console'
|
27
|
+
|
28
|
+
require_relative 'logging'
|
29
|
+
require_relative 'scrollable'
|
30
|
+
require_relative 'row'
|
31
|
+
require_relative 'cell'
|
32
|
+
require_relative 'column'
|
33
|
+
require_relative 'color_output'
|
34
|
+
require_relative 'busy_indicator/busy_indicator'
|
35
|
+
require_relative 'user_input'
|
36
|
+
require_relative 'menu'
|
37
|
+
require_relative 'action'
|
38
|
+
require_relative 'translating'
|
39
|
+
|
40
|
+
# So this draws a table complete with the content of the cells from the
|
41
|
+
# currently selected spreadsheet.
|
42
|
+
# The really ingenuous stuff was provided by a helpful user on a newsgroup.
|
43
|
+
# Really... Usenet! Like in ... 2014!
|
44
|
+
|
45
|
+
#TODO: provide conversion for column designators to int vv, also for AA to ZZ !
|
46
|
+
|
47
|
+
class SheetInterface
|
48
|
+
include Logging
|
49
|
+
include Translating
|
50
|
+
|
51
|
+
class Status
|
52
|
+
def show
|
53
|
+
if(@message)
|
54
|
+
puts @message
|
55
|
+
@message.clear
|
56
|
+
end
|
57
|
+
end
|
58
|
+
attr_writer :message
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(wb, num)
|
62
|
+
init_logger(STDOUT, Logger::INFO)
|
63
|
+
@workbook = wb.sheet(num)
|
64
|
+
@menu = nil
|
65
|
+
@status = Status.new
|
66
|
+
construct
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
=begin
|
71
|
+
returns [width, height] in columns and lines of the current
|
72
|
+
terminal-window.
|
73
|
+
=end
|
74
|
+
def terminal_size
|
75
|
+
size = `stty size`.split.map { |x| x.to_i }.reverse
|
76
|
+
return size
|
77
|
+
end
|
78
|
+
# Asks the user to enter a number, then verifies ageinst eventual
|
79
|
+
# constraints.
|
80
|
+
def ask_number(question, &constraint)
|
81
|
+
num = nil
|
82
|
+
3.times do
|
83
|
+
print question.dup << " "
|
84
|
+
begin
|
85
|
+
num = STDIN.gets()
|
86
|
+
rescue Interrupt
|
87
|
+
return nil
|
88
|
+
end
|
89
|
+
num = num.to_i
|
90
|
+
if (constraint && !yield(num))
|
91
|
+
num = nil
|
92
|
+
end
|
93
|
+
return num if num
|
94
|
+
end
|
95
|
+
if !num
|
96
|
+
@status.message = "Inacceptable value, doing nothing."
|
97
|
+
return nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Shortcutting from “user inputs bull” to “program interrupts”
|
102
|
+
# without the need for the program to actually run havoc, first.
|
103
|
+
def ask_string(question, &constraint)
|
104
|
+
str = nil
|
105
|
+
until str
|
106
|
+
print question.dup << " "
|
107
|
+
begin
|
108
|
+
str = STDIN.gets().chomp!
|
109
|
+
rescue Interrupt
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
if("" == str || "\e" == str)
|
113
|
+
return nil
|
114
|
+
end
|
115
|
+
if(constraint && !yield(str))
|
116
|
+
str = nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
if !str
|
120
|
+
@status.message = "Inacceptable value, doing nothing."
|
121
|
+
end
|
122
|
+
return str
|
123
|
+
end
|
124
|
+
|
125
|
+
# Write content to a new file
|
126
|
+
def new_file()
|
127
|
+
file = nil
|
128
|
+
until file
|
129
|
+
print 'file path:' << ' '
|
130
|
+
begin
|
131
|
+
file = STDIN.gets().chomp!
|
132
|
+
rescue Interrupt
|
133
|
+
return nil
|
134
|
+
end
|
135
|
+
if(file && File::exist?(file) )
|
136
|
+
if(File::writable?(file) )
|
137
|
+
print "File #{file} exists! Do you want to overwrite its content, now (j/N)? "
|
138
|
+
answer = "%c" %wait_for_user
|
139
|
+
if('j' != answer.downcase)
|
140
|
+
file = nil
|
141
|
+
else
|
142
|
+
puts
|
143
|
+
end
|
144
|
+
else
|
145
|
+
puts "File #{file} is not writable! Please name a different file path"
|
146
|
+
file = nil
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
return file
|
152
|
+
end
|
153
|
+
|
154
|
+
def ask_cell_coord()
|
155
|
+
coord = nil
|
156
|
+
until coord
|
157
|
+
print 'Which cell (col:row)? '
|
158
|
+
begin
|
159
|
+
coord = STDIN.gets().chomp!
|
160
|
+
rescue Interrupt
|
161
|
+
return nil
|
162
|
+
end
|
163
|
+
if('' == coord || !coord.include?(':') || "\e" == coord)
|
164
|
+
return nil
|
165
|
+
else
|
166
|
+
col, row = coord.split(':')
|
167
|
+
row = row.to_i
|
168
|
+
col = col.strip
|
169
|
+
end
|
170
|
+
return [col, row]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def set_sheet(sheet)
|
175
|
+
current_sheet = @workbook.default_sheet
|
176
|
+
@workbook = @workbook.sheet(sheet)
|
177
|
+
columns = @workbook.last_column
|
178
|
+
if columns && columns > 0
|
179
|
+
@rows = nil
|
180
|
+
@columns = nil
|
181
|
+
else
|
182
|
+
@workbook = @workbook.sheet(current_sheet)
|
183
|
+
@status.message = red("Sheet #{sheet} contains no data!")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def open_sheet(designator = :number)
|
188
|
+
sn = nil
|
189
|
+
if designator == :number
|
190
|
+
sn = ask_number("Sheet number (max #{@workbook.sheets.size() - 1})?") do |num|
|
191
|
+
num < @workbook.sheets.size
|
192
|
+
end
|
193
|
+
else
|
194
|
+
sn = ask_string("Sheet name (#{@workbook.sheets.join(', ')})?") do |name|
|
195
|
+
@workbook.sheets.include?(name)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
if sn && !sn.to_s.empty?
|
200
|
+
@log.debug('calling workbook.sheet with ' << sn.to_s)
|
201
|
+
@log.debug('@workbook is of type ' << @workbook.class.name)
|
202
|
+
set_sheet(sn)
|
203
|
+
end
|
204
|
+
construct
|
205
|
+
end
|
206
|
+
|
207
|
+
# Adds concrete actions to the menu.
|
208
|
+
# Note the calls to ask_number and ask_string with constraints.
|
209
|
+
def define_actions(menu)
|
210
|
+
sm_Menu = Menu.new(:name => 'sheet', :key => 's')
|
211
|
+
sm_Menu.add_action(Action.new(:name => 'sheet number', :key => '#') {open_sheet(:number)})
|
212
|
+
sm_Menu.add_action(Action.new(:name => 'sheet name', :key => 'n') {open_sheet(:name)})
|
213
|
+
|
214
|
+
save_Menu = Menu.new(:name => 'save to file', :key => 'f')
|
215
|
+
save_Menu.add_action(Action.new(:name => 'current sheet', :key => 'c') do
|
216
|
+
file = new_file()
|
217
|
+
if(file)
|
218
|
+
File::open(file, 'w+') {|out| draw(out) }
|
219
|
+
@status.message = green('... done')
|
220
|
+
end
|
221
|
+
construct
|
222
|
+
end)
|
223
|
+
|
224
|
+
save_Menu.add_action(Action.new(:name => 'all sheets', :key => 'a') do
|
225
|
+
current_sheet = @workbook.default_sheet
|
226
|
+
@status.message = "\nATTN! If saving takes a long time, your spreadsheet may contain useless data or many empty cells. Check the visible width and height of your tables.\n"
|
227
|
+
file = new_file()
|
228
|
+
if(file)
|
229
|
+
bi = BusyIndicator.new(true)
|
230
|
+
begin
|
231
|
+
File::open(file, 'w+') do |out|
|
232
|
+
@workbook.sheets.each do |s|
|
233
|
+
@log.debug('writing sheet ' << s)
|
234
|
+
set_sheet(s)
|
235
|
+
construct(false)
|
236
|
+
draw(out)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
rescue Interrupt
|
240
|
+
puts bold(red("Interrupted by user. Bye!"))
|
241
|
+
exit true
|
242
|
+
end
|
243
|
+
bi.stop('... done')
|
244
|
+
end
|
245
|
+
set_sheet(current_sheet)
|
246
|
+
construct
|
247
|
+
end)
|
248
|
+
|
249
|
+
col_Menu = Menu.new(:name=>'column', :key => 'c')
|
250
|
+
col_Menu.add_action(Action.new(:name => 'column width', :key => 'w') do |num|
|
251
|
+
num = ask_number('column width (min. 4) ?'){|w| w > 3}
|
252
|
+
Column::col_width = num if num
|
253
|
+
construct
|
254
|
+
end)
|
255
|
+
|
256
|
+
menu.add_action(Action.new(:name => 'value', :key => 'v') do
|
257
|
+
cell_coord = ask_cell_coord
|
258
|
+
if(cell_coord && cell_coord.length == 2)
|
259
|
+
row = cell_coord[1] - 1
|
260
|
+
col = cell_coord[0].upcase.bytes[0] - 65
|
261
|
+
cell = nil
|
262
|
+
if(@rows[row] && @columns[col])
|
263
|
+
cell = @rows[row].cells.detect {|c| c.col == col}
|
264
|
+
if(cell)
|
265
|
+
@status.message = "#{cell_coord[0].upcase}:#{cell_coord[1]} is \"" << cell.value.to_s << "\" (%d)" %cell.value.to_s.length
|
266
|
+
else
|
267
|
+
@status.message = red('invalid cell')
|
268
|
+
end
|
269
|
+
else
|
270
|
+
@status.message = red(cell_coord.join(':') << ': out of range')
|
271
|
+
end
|
272
|
+
elsif(cell_coord && cell_coord.length > 0)
|
273
|
+
@status.message = red('invalid input')
|
274
|
+
end
|
275
|
+
construct
|
276
|
+
end)
|
277
|
+
|
278
|
+
menu.add_menu(save_Menu)
|
279
|
+
menu.add_menu(sm_Menu)
|
280
|
+
menu.add_menu(col_Menu)
|
281
|
+
menu.add_action(Action.new(:name => "⇧", :key => "i") {@table_view.up; refresh()})
|
282
|
+
menu.add_action(Action.new(:name => "⇩", :key => "k") {@table_view.down; refresh()})
|
283
|
+
menu.add_action(Action.new(:name => "⇦", :key => "j") {@table_view.left; refresh()})
|
284
|
+
menu.add_action(Action.new(:name => "⇨", :key => "l") {@table_view.right; refresh()})
|
285
|
+
menu.add_action(Action.new(:global => true, :hidden => true, :name => '', :key => "\e") {refresh})
|
286
|
+
menu.add_action(Action.new(:global => true, :name => 'quit', :key => 'q') {puts 'Bye ';exit 0})
|
287
|
+
end
|
288
|
+
# This is hard to describe.
|
289
|
+
# The function basically draws a table on screen.
|
290
|
+
def draw(out = STDOUT)
|
291
|
+
if(@rows && !@rows.empty?)
|
292
|
+
# Welcome to hell.
|
293
|
+
on_screen = STDOUT == out
|
294
|
+
col_w = Column::col_width
|
295
|
+
num_c = @rows[0].cells.length()
|
296
|
+
num_r = @rows.length
|
297
|
+
ch = nil
|
298
|
+
hline = nil
|
299
|
+
#-----
|
300
|
+
table_view = ''
|
301
|
+
#-----
|
302
|
+
|
303
|
+
table_view << @workbook.default_sheet << "\n"
|
304
|
+
#out.puts @workbook.default_sheet
|
305
|
+
table_view << ('┌' << '─' * num_r.to_s.length << '┬' << (('─' * col_w) << '┬' ) * (num_c -1) ) << (('─' * col_w) << '┐' ) << "\n"
|
306
|
+
# out.puts ('┌' << '─' * num_r.to_s.length << '┬' << (('─' * col_w) << '┬' ) * (num_c -1) ) << (('─' * col_w) << '┐' )
|
307
|
+
lh = 0
|
308
|
+
@rows.each do |row|
|
309
|
+
|
310
|
+
if(row == @rows.first)
|
311
|
+
ch = 'A'
|
312
|
+
table_view << "│%#{num_r.to_s.length}s" %" "
|
313
|
+
#out.print "│%#{num_r.to_s.length}s" %" "
|
314
|
+
table_view << '│'
|
315
|
+
# out.print '│'
|
316
|
+
row.each do |c|
|
317
|
+
head = "%#{col_w}s" %ch
|
318
|
+
head = bold(head) if(on_screen)
|
319
|
+
# out.print(head << "|")
|
320
|
+
table_view << (head << "│")
|
321
|
+
ch = ch.next
|
322
|
+
end
|
323
|
+
# out.puts
|
324
|
+
table_view << "\n"
|
325
|
+
hline = ('├' << ('─' * (num_r.to_s.length) ) <<'┼' << (('─' * col_w) << '┼' ) * (num_c - 1) ) << (('─' * col_w) << '┤')if !hline
|
326
|
+
table_view << hline << "\n"
|
327
|
+
# out.puts hline
|
328
|
+
end
|
329
|
+
lh += 1
|
330
|
+
table_view << "│"
|
331
|
+
# out.print "│"
|
332
|
+
line_head = "%#{num_r.to_s.length}s" %lh.to_s
|
333
|
+
line_head = bold(line_head) if on_screen
|
334
|
+
table_view << line_head
|
335
|
+
# out.print line_head
|
336
|
+
|
337
|
+
# out.print "│" << bold("%#{num_r.to_s.length}s" %line_head)
|
338
|
+
|
339
|
+
row.height.times do |li|
|
340
|
+
table_view << "│%#{num_r.to_s.length}s" %" " if li > 0
|
341
|
+
# out.print "│%#{num_r.to_s.length}s" %" " if li > 0
|
342
|
+
table_view << '│'
|
343
|
+
# out.print '│'
|
344
|
+
row.each do |cell|
|
345
|
+
line = cell.line(li)
|
346
|
+
content = "%#{col_w}s│" %line
|
347
|
+
if(on_screen && line && line.end_with?('...'))
|
348
|
+
content = red(content)
|
349
|
+
end
|
350
|
+
table_view << content
|
351
|
+
# out.print content
|
352
|
+
end
|
353
|
+
table_view << "\n"
|
354
|
+
# out.puts
|
355
|
+
end
|
356
|
+
hlline = ('│' << (('─' * col_w) << '│' ) * num_c ) if !hline
|
357
|
+
|
358
|
+
table_view << hline << "\n"
|
359
|
+
# out.puts hline
|
360
|
+
|
361
|
+
# Some of the following code may go to a function as it repeats the
|
362
|
+
# procedure from the beginning of the table. I am currently unwilling to
|
363
|
+
# do it, because all the rest must stay so verbose that another
|
364
|
+
# function-call at this position cannot contribute to a better
|
365
|
+
# understanding of this ... table-generating MESS!!
|
366
|
+
if(row == @rows.last)
|
367
|
+
ch = 'A'
|
368
|
+
table_view << "│%#{num_r.to_s.length}s" %" "
|
369
|
+
# out.print "│%#{num_r.to_s.length}s" %" "
|
370
|
+
table_view << '│'
|
371
|
+
# out.print '│'
|
372
|
+
row.each do |c|
|
373
|
+
head = "%#{col_w}s" %ch
|
374
|
+
head = bold(head) if on_screen
|
375
|
+
table_view << ( head << '│')
|
376
|
+
# out.print( head << '│')
|
377
|
+
ch = ch.next
|
378
|
+
end
|
379
|
+
table_view << "\n"
|
380
|
+
# out.puts
|
381
|
+
hline = ('└' << ('─' * (num_r.to_s.length) ) << '┴' << (('─' * col_w) << '┴' ) * (num_c - 1) ) << (('─' * col_w) << '┘' )
|
382
|
+
|
383
|
+
table_view << hline << "\n"
|
384
|
+
# out.puts hline
|
385
|
+
end
|
386
|
+
|
387
|
+
end
|
388
|
+
=begin
|
389
|
+
table_width = hline.length
|
390
|
+
tw = terminal_size[0]
|
391
|
+
if(on_screen && table_width > tw)
|
392
|
+
@status.message = red("ATTN! Terminal is to narrow to display the whole table (#{tw} characters for #{table_width}). Diminish column-width!")
|
393
|
+
end
|
394
|
+
=end
|
395
|
+
return table_view
|
396
|
+
else
|
397
|
+
return nil
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
# The function which is called to recreate the view.
|
402
|
+
# It prepares the table-generation by transforming the Roo-instance to
|
403
|
+
# objects of the classes Cell, Row and Column, then calls draw(), above and
|
404
|
+
# adds the status- and menu-lines. The menu waits for user input (blocking)
|
405
|
+
# before construct() returns, no other thread or “event-loop” involved there.
|
406
|
+
def construct(on_screen = true)
|
407
|
+
puts "... reading spreadsheet data (%s), PSE wait" %[@workbook.default_sheet]
|
408
|
+
bi = BusyIndicator.new()
|
409
|
+
if(!@rows || !@columns)
|
410
|
+
|
411
|
+
@rows = Array.new if !@rows
|
412
|
+
@columns = Array.new if !@columns
|
413
|
+
# create for each column
|
414
|
+
num_columns = @workbook.last_column
|
415
|
+
if(num_columns && num_columns > 0)
|
416
|
+
num_columns.times do |column|
|
417
|
+
# ... a column, then
|
418
|
+
@columns[column] = Column.new(column) if !@columns[column]
|
419
|
+
cur_col = @columns[column]
|
420
|
+
|
421
|
+
# ... create for each row in each column
|
422
|
+
(@workbook.last_row).times do |row|
|
423
|
+
# ... a row then
|
424
|
+
@rows[row] = Row.new(row) if !@rows[row]
|
425
|
+
cur_row = @rows[row]
|
426
|
+
# ... create for each cell in each row
|
427
|
+
cur_cell = cur_row.cells.detect {|cell| column == cell.col }
|
428
|
+
if !cur_cell
|
429
|
+
# ... a cell
|
430
|
+
cell = @workbook.cell(row + 1, column + 1)
|
431
|
+
cur_cell = Cell.new(cur_row, cur_col, cell.to_s)
|
432
|
+
cur_row << cur_cell
|
433
|
+
cur_col << cur_cell
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
bi.stop("\n")
|
441
|
+
if(on_screen)
|
442
|
+
@table_view = draw()
|
443
|
+
if @table_view
|
444
|
+
@table_view.extend(Scrollable)
|
445
|
+
@table_view.fixed_rows = 4
|
446
|
+
@table_view.fixed_cols = 4
|
447
|
+
refresh()
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def refresh()
|
453
|
+
@table_view.width=terminal_size[0] - 1
|
454
|
+
@table_view.height=terminal_size[1]
|
455
|
+
# @table_view.box = true
|
456
|
+
@table_view.show
|
457
|
+
@status.show
|
458
|
+
if !@menu
|
459
|
+
@menu = Menu.new(:name => 'main')
|
460
|
+
define_actions(@menu)
|
461
|
+
end
|
462
|
+
@menu.call
|
463
|
+
end
|
464
|
+
end
|