viewworkbook 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
Binary file
|
@@ -0,0 +1,126 @@
|
|
1
|
+
========================
|
2
|
+
Viewworkbook
|
3
|
+
========================
|
4
|
+
------------------------------------------------
|
5
|
+
view spreadsheet files in a terminal window
|
6
|
+
------------------------------------------------
|
7
|
+
|
8
|
+
SYNOPSIS
|
9
|
+
=============
|
10
|
+
viewworkbook <spreadsheet>
|
11
|
+
|
12
|
+
DESCRIPTION
|
13
|
+
=============
|
14
|
+
Viewworkbook lets you read spreadsheet-files in text-mode, in a terminal
|
15
|
+
window. It reproduces the tables which are contained in a spreadsheet file and
|
16
|
+
lets you navigate through the sheets. The visible part of a sheet is limited
|
17
|
+
laterally and horizontally by the terminal-size but can be scrolled. You can
|
18
|
+
adapt the width of table-columns, to render the table more readable and
|
19
|
+
finally, if you wish, save tables to a text-file. To the author of the
|
20
|
+
program, the utility serves to take a quick glance at spreadsheets that are
|
21
|
+
received via email, in the Mutt mail-client, which is itself a terminal
|
22
|
+
application.
|
23
|
+
|
24
|
+
The supported spreadsheet formats are those which are handled by the Ruby-gem
|
25
|
+
"**roo**" (February 2017):
|
26
|
+
|
27
|
+
- Microsoft™'s **xls** and **xlsx**,
|
28
|
+
|
29
|
+
- the OpenDocument spreadsheet **ods**
|
30
|
+
|
31
|
+
- and the SoftMaker™ spreadsheet formats **pmd** and **pmdx**.
|
32
|
+
|
33
|
+
Options
|
34
|
+
=============
|
35
|
+
Other than the path to the spreadsheet file, viewworkbook does currently not
|
36
|
+
interpret any command line arguments.
|
37
|
+
|
38
|
+
Menu commands
|
39
|
+
-------------------
|
40
|
+
A list of commands and their associated hotkeys (in parenthesis) is shown
|
41
|
+
below the current table:
|
42
|
+
|
43
|
+
+--+--------+--------+--------+--------+--------+--------+--------+
|
44
|
+
|11| 12.2 | July | 34000 | 5% | prior | 1957 | true |
|
45
|
+
+--+--------+--------+--------+--------+--------+--------+--------+
|
46
|
+
|12| 2.33 | July | 1234 | 12.2% | prior | 1966 | true |
|
47
|
+
+--+--------+--------+--------+--------+--------+--------+--------+
|
48
|
+
|13| 50.0 | August | 334566 | 12% | in | 1966 | false |
|
49
|
+
+==+========+========+========+========+========+========+========+
|
50
|
+
| | A| B| C| D| E| F| G|
|
51
|
+
+--+--------+--------+--------+--------+--------+--------+--------+
|
52
|
+
|
53
|
+
**value (v) * save to file (f) * sheet (s) * column (c) * up(i) * down(k) * left(j) * right(l) * quit (q) ***
|
54
|
+
|
55
|
+
When you enter one of the hotkeys, a command can be executed directly (like "q"
|
56
|
+
to terminate the program), a sub-menu may be shown (like with "c" which shall
|
57
|
+
give you control on column properties) or you are confronted with an input
|
58
|
+
invitation (like with "v", where you have to enter a cell-reference). Read on
|
59
|
+
for an explanation of each menu command.
|
60
|
+
|
61
|
+
value (v)
|
62
|
+
display the value from a cell. This is useful, when columns are not wide
|
63
|
+
enough to show complete values. After entering "**v**", you are
|
64
|
+
immediately invited to enter a cell reference. Cells are referenced in
|
65
|
+
the format "Column:Row", e.g. D:12 for the cell in column "D" and row
|
66
|
+
"12".
|
67
|
+
|
68
|
+
save to file (f)
|
69
|
+
Save the current or all tables to a text file. The command does initially
|
70
|
+
show two alternative sub-commands:
|
71
|
+
|
72
|
+
Current sheet (c) * all sheets (a)
|
73
|
+
Independently of your choice, in the next step you will have to enter the
|
74
|
+
path to the output file. You will be warned, if the file already exists
|
75
|
+
and may choose to either overwrite the existing file or name a different
|
76
|
+
one.
|
77
|
+
|
78
|
+
sheet (s)
|
79
|
+
Navigate to a different sheet in the current workbook. You may choose
|
80
|
+
between a sheet-number and the name of a sheet. If you want to indicate
|
81
|
+
the next sheet **by name**, the list of all available names is first
|
82
|
+
shown. You just type right away the one that you want.
|
83
|
+
|
84
|
+
column (c)
|
85
|
+
Alter the properties of the table-columns. At the time of this writing
|
86
|
+
(February 2017) the only property that can be changed, is the width of all
|
87
|
+
columns. When you enter "**c**" the subcommand column width (w) is shown.
|
88
|
+
After entering "**w**" you can enter the desired width in characters.
|
89
|
+
|
90
|
+
Arrows(i, j, k, l)
|
91
|
+
Navigate in a sheet that is too big to be displayed entirely at once.
|
92
|
+
These hotkeys correspond to the default navigation keys in the vi editor.
|
93
|
+
|
94
|
+
quit (q)
|
95
|
+
Enter "**q**" to quit the program at any moment except when an input
|
96
|
+
invitation is shown.
|
97
|
+
|
98
|
+
Hidden (menu-)commands
|
99
|
+
------------------------
|
100
|
+
There is currently only one such command available:
|
101
|
+
The **Escape-key** will interrupt an unfinished action and refresh the display.
|
102
|
+
|
103
|
+
Other Information
|
104
|
+
=================
|
105
|
+
|
106
|
+
Development and source code
|
107
|
+
Viewworkbook has been written in Ruby. As Ruby is an interpreted programming
|
108
|
+
language, the executable file and all those that it may refer to at one
|
109
|
+
point in time, are themselves the source-files of the current
|
110
|
+
program-version. You can open them in any text-editor to scrutinize the
|
111
|
+
source-code. If you have received the program as a Ruby-gem, you can also
|
112
|
+
decompress a copy of the gem-file with **tar -x**, then **tar -xzf**.
|
113
|
+
|
114
|
+
Bugs
|
115
|
+
Negative values are not always displayed, when very long (like 15 ciphers).
|
116
|
+
Enlarging the column-width further does not have an effect.
|
117
|
+
|
118
|
+
License
|
119
|
+
Viewworkbook is distributed under the conditions of the GNU General Public
|
120
|
+
License, version 3.
|
121
|
+
|
122
|
+
Author
|
123
|
+
Viewworkbook has been developed by Michael Uplawski
|
124
|
+
<michael.uplawski@uplawski.eu>
|
125
|
+
|
126
|
+
**Ω**
|
data/lib/action.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
|
3
|
+
=begin
|
4
|
+
/***************************************************************************
|
5
|
+
* Copyright © 2014, 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_relative 'logging'
|
25
|
+
|
26
|
+
# An Action is executed on a user's request.
|
27
|
+
# It has a name, an associated closure and hotkey.
|
28
|
+
class Action
|
29
|
+
include Logging
|
30
|
+
|
31
|
+
class ActionError < StandardError
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_accessor :name, :key, :proc, :global, :hidden
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
"[#<" << classname << ':' << hash << '@name="' << name << '", @key="' << key << '">'
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def initialize(options = {}, &b)
|
42
|
+
init_logger(STDOUT, Logger::INFO)
|
43
|
+
|
44
|
+
@name = options[:name]
|
45
|
+
@key = options[:key]
|
46
|
+
@proc = b if b
|
47
|
+
@global = options[:global]
|
48
|
+
@hidden = options[:hidden]
|
49
|
+
end
|
50
|
+
|
51
|
+
def call(*args)
|
52
|
+
unless @proc
|
53
|
+
raise ActionError.new((@name ? '' : 'Unnamed ') << 'action' << (@name ? (' ' << @name) : '') << ' called before a command was defined')
|
54
|
+
end
|
55
|
+
@proc.call(*args)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
/***************************************************************************
|
4
|
+
* Copyright (c) 2011, Michael Uplawski <michael.uplawski@uplawski.eu> *
|
5
|
+
* *
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
9
|
+
* (at your option) any later version. *
|
10
|
+
* *
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
14
|
+
* GNU General Public License for more details. *
|
15
|
+
* *
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
17
|
+
* along with this program; if not, write to the *
|
18
|
+
* Free Software Foundation, Inc., *
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
20
|
+
***************************************************************************/
|
21
|
+
|
22
|
+
A test and demo for the BusyIndicator.
|
23
|
+
This does not use the final BusyIndicator but directly creates
|
24
|
+
a Thread.
|
25
|
+
=end
|
26
|
+
require './color_output'
|
27
|
+
|
28
|
+
def clean_busy_indicator(thr, width, comment)
|
29
|
+
thr.terminate
|
30
|
+
thr.join
|
31
|
+
print ("\b" * width)
|
32
|
+
print ("%+#{width}s\n" %comment)
|
33
|
+
end
|
34
|
+
|
35
|
+
def busy_indicator(width)
|
36
|
+
tobj = Thread.new() do
|
37
|
+
loop do
|
38
|
+
%w"OOO ooo ___ ooo".each do |s|
|
39
|
+
print "%+#{width}s" %s
|
40
|
+
sleep 0.1
|
41
|
+
print ("\b" * width)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
# =======================
|
47
|
+
if($0 == __FILE__)
|
48
|
+
width = 20
|
49
|
+
2.times do
|
50
|
+
puts "I am busy, pse wait..."
|
51
|
+
thr = busy_indicator(width)
|
52
|
+
sleep 3
|
53
|
+
clean_busy_indicator(thr, width, "Okay")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
/***************************************************************************
|
4
|
+
* Copyright (c) 2011, Michael Uplawski <michael.uplawski@uplawski.eu> *
|
5
|
+
* *
|
6
|
+
* This program is free software; you can redistribute it and/or modify *
|
7
|
+
* it under the terms of the GNU General Public License as published by *
|
8
|
+
* the Free Software Foundation; either version 3 of the License, or *
|
9
|
+
* (at your option) any later version. *
|
10
|
+
* *
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
14
|
+
* GNU General Public License for more details. *
|
15
|
+
* *
|
16
|
+
* You should have received a copy of the GNU General Public License *
|
17
|
+
* along with this program; if not, write to the *
|
18
|
+
* Free Software Foundation, Inc., *
|
19
|
+
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
20
|
+
***************************************************************************/
|
21
|
+
|
22
|
+
=end
|
23
|
+
require_relative '../color_output'
|
24
|
+
|
25
|
+
#The BusyIndicator will show an "Animation" for the time of its execution.
|
26
|
+
#The main program will continue to run in its own thread and should stop
|
27
|
+
#the BusyIndicator, once that a process of longer duration has terminated.
|
28
|
+
class BusyIndicator
|
29
|
+
attr_writer :width
|
30
|
+
# Defines a busy_indicator of a width of 'width' characters.
|
31
|
+
# If 'start' is true, the text-animation is run immediately.
|
32
|
+
def initialize(start = true, width = nil)
|
33
|
+
@width = width && width >= 3 ? width : 3
|
34
|
+
@thr = busy_indicator(@width) if start
|
35
|
+
end
|
36
|
+
|
37
|
+
# Starts the text-animation, returns the thread.
|
38
|
+
def run()
|
39
|
+
@thr = busy_indicator(@width)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Stops the text-animation, terminates the thread.
|
43
|
+
# If comment is not null, it will be displayed in the end.
|
44
|
+
def stop(comment)
|
45
|
+
@thr.terminate
|
46
|
+
@thr.join
|
47
|
+
print ("\b" * @width)
|
48
|
+
print ("%+#{@width}s\n" %comment)
|
49
|
+
end
|
50
|
+
private
|
51
|
+
def busy_indicator(width)
|
52
|
+
tobj = Thread.new() do
|
53
|
+
print "working ... "
|
54
|
+
loop do
|
55
|
+
# %w"OOO ooo ___ ooo".each do |s|
|
56
|
+
# %w"000 OOO UUU VVV YYY TTT 777 >>> === --- ooo".each do |s|
|
57
|
+
%w"0OU OUV UVY VYT YT7 T7> 7>= >=- =-o -o0 o0O".each do |s|
|
58
|
+
print "%+#{width}s" %s
|
59
|
+
sleep 0.1
|
60
|
+
print ("\b" * width)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# =======================
|
67
|
+
# Example
|
68
|
+
# You can run this file directly from a command-line,
|
69
|
+
# like this
|
70
|
+
# user@mchine$: ruby busy_indicator.rb
|
71
|
+
#
|
72
|
+
# The program below will then start four (4)
|
73
|
+
# BusyIndicators and stop them again, one by one.
|
74
|
+
# The width of the "animation" is variated.
|
75
|
+
# In the loop, the BusyIndicator is started upon
|
76
|
+
# its creation, the other two instances are first
|
77
|
+
# created then 'run'.
|
78
|
+
if($0 == __FILE__)
|
79
|
+
width = 20
|
80
|
+
2.times do
|
81
|
+
puts "I am busy, pse wait..."
|
82
|
+
thr = BusyIndicator.new(true, width)
|
83
|
+
sleep 3
|
84
|
+
thr.stop("Okay")
|
85
|
+
end
|
86
|
+
bi = BusyIndicator.new(false)
|
87
|
+
bi.run
|
88
|
+
sleep 3
|
89
|
+
bi.stop("Stopped")
|
90
|
+
bi.width = 8
|
91
|
+
bi.run
|
92
|
+
sleep 3
|
93
|
+
bi.stop("stopped again")
|
94
|
+
end
|
95
|
+
|
data/lib/cell.rb
ADDED
@@ -0,0 +1,122 @@
|
|
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_relative 'logging'
|
25
|
+
require_relative 'row'
|
26
|
+
require_relative 'column'
|
27
|
+
|
28
|
+
# Objects of this class represent cells in a spreadsheet table
|
29
|
+
|
30
|
+
class Cell
|
31
|
+
@@DEF_HEIGHT=1
|
32
|
+
@@DEF_TYPE=Integer
|
33
|
+
@@split_pattern = nil
|
34
|
+
|
35
|
+
self.extend(Logging)
|
36
|
+
@@log = self.init_logger
|
37
|
+
|
38
|
+
def initialize(row = nil, col = nil, value = nil)
|
39
|
+
@log = @@log
|
40
|
+
@row = row if row
|
41
|
+
@col = col if col
|
42
|
+
@type_class = @@DEF_TYPE
|
43
|
+
@value = value
|
44
|
+
split_value
|
45
|
+
set_limits
|
46
|
+
@row.resize
|
47
|
+
@log.debug("cell.initialize, lines is #{@lines}, value is #{value}, ideal_width is #{@ideal_width}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_cell()
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def line(num = nil)
|
55
|
+
num && num < @lines.length ? @lines[num].to_s : ' ' if @lines && !@lines.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
object_id.to_s << "{" << @row.number.to_s << ":" << @col.number.to_s << ", " << @lines.to_s << ", " << @ideal_height.to_s << ", " << @ideal_width.to_s << " }"
|
60
|
+
end
|
61
|
+
|
62
|
+
def resize
|
63
|
+
@@split_pattern = nil
|
64
|
+
split_value
|
65
|
+
set_limits
|
66
|
+
@row.resize
|
67
|
+
end
|
68
|
+
|
69
|
+
def value=(value)
|
70
|
+
@@split_pattern = nil
|
71
|
+
@value = value
|
72
|
+
split_value
|
73
|
+
@log.debug('after split_value, lines is ' << @lines.to_s)
|
74
|
+
end
|
75
|
+
|
76
|
+
def col
|
77
|
+
@col.number
|
78
|
+
end
|
79
|
+
|
80
|
+
def row
|
81
|
+
@row.number
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_reader :ideal_height, :ideal_width, :value
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def split_value()
|
89
|
+
# The regex may be used in many cells. Define on class-level.
|
90
|
+
# And...
|
91
|
+
# This looks complicated because it is.
|
92
|
+
#
|
93
|
+
# until 18 february 2017
|
94
|
+
# @@split_pattern = Regexp.new('[\p{P}\p{M}]?\b.{1,%i}\b[\p{P}\p{M}]?' %(@col.width)) if !@@split_pattern
|
95
|
+
# since 18 february 2017
|
96
|
+
@@split_pattern = Regexp.new('\b(?:.{1,%i}(?:[\b\s\p{P}]?))' %(@col.width - 1)) if !@@split_pattern
|
97
|
+
|
98
|
+
@lines = @value.to_s.scan(@@split_pattern) # .collect {|match| match.strip}
|
99
|
+
@lines.reject! {|l| l.empty?}
|
100
|
+
set_limits
|
101
|
+
if @value.to_s.strip.length > @lines.join.strip.length
|
102
|
+
# @lines.insert(0, '...' )
|
103
|
+
if(! @lines.empty?)
|
104
|
+
@lines[@lines.length - 1] = '...'
|
105
|
+
else
|
106
|
+
@lines.insert(0, '...' )
|
107
|
+
end
|
108
|
+
end
|
109
|
+
@row.resize
|
110
|
+
end
|
111
|
+
|
112
|
+
def set_limits
|
113
|
+
if @lines && !@lines.empty?
|
114
|
+
@ideal_height = @lines.length
|
115
|
+
@ideal_width = @lines.max {|l1, l2 | l1.length <=> l2.length}.length if @lines && !@lines.empty?
|
116
|
+
else
|
117
|
+
@ideal_width ||= @col.width
|
118
|
+
@ideal_height ||= @@DEF_HEIGHT
|
119
|
+
@height ||= @ideal_height
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|