viewworkbook 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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
+ **Ω**
@@ -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
+
@@ -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