accutronic 0.1.7
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.
- data/LICENSE.txt +201 -0
- data/bin/accu-encrypt +4 -0
- data/lib/accu-encrypt.rb +823 -0
- data/lib/accu-file.rb +198 -0
- data/lib/accu-window.rb +756 -0
- metadata +93 -0
data/lib/accu-file.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# Simple library which provides a simple
|
5
|
+
# command line "File Manager" interface.
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# Author:: Luke Spangler
|
9
|
+
# Email:: m27frogy.roblox@gmail.com
|
10
|
+
|
11
|
+
require "abc"
|
12
|
+
require "accu-window"
|
13
|
+
|
14
|
+
# Primary module which contains
|
15
|
+
# the methods.
|
16
|
+
module FileLibrary
|
17
|
+
|
18
|
+
# Enables completion in the file
|
19
|
+
# manager by taking in a directory
|
20
|
+
# and input text and returning
|
21
|
+
# a matching file or the original
|
22
|
+
# text if no match is found.
|
23
|
+
def self.Completion(dir,text)
|
24
|
+
#--
|
25
|
+
result = text
|
26
|
+
range = (0)..(text.length-1)
|
27
|
+
Dir.foreach(dir) { |file|
|
28
|
+
#puts file.slice(range), text
|
29
|
+
if file.slice(range) == text then
|
30
|
+
result = file
|
31
|
+
break
|
32
|
+
end
|
33
|
+
}
|
34
|
+
return result
|
35
|
+
#++
|
36
|
+
end
|
37
|
+
|
38
|
+
# Selects a file using a simple command-line
|
39
|
+
# navigation system.
|
40
|
+
def self.SelectFile()
|
41
|
+
#--
|
42
|
+
origDir = Dir.pwd
|
43
|
+
currentDirectory = ""
|
44
|
+
selection = ""
|
45
|
+
request = ""
|
46
|
+
while selection == "" do
|
47
|
+
currentDirectory = Dir.pwd
|
48
|
+
puts "---------\nPWD: " + currentDirectory + "\n---------"
|
49
|
+
Dir.foreach(currentDirectory) { |file|
|
50
|
+
if File.directory? file then
|
51
|
+
puts "|" + file + "| - dir"
|
52
|
+
else
|
53
|
+
puts "|" + file + "| - file"
|
54
|
+
end
|
55
|
+
}
|
56
|
+
puts "---------"
|
57
|
+
request = gets
|
58
|
+
request.slice!(-1)
|
59
|
+
while not (File.exists? request) do
|
60
|
+
puts "Invalid: |" + request + "|"
|
61
|
+
puts "Please enter a valid file name or directory."
|
62
|
+
request = gets
|
63
|
+
request.slice!(-1)
|
64
|
+
if not File.exists? request then
|
65
|
+
request = self.Completion(currentDirectory,request)
|
66
|
+
if File.exists? request then
|
67
|
+
if not File.directory? request then
|
68
|
+
puts "Is this file correct: |" + request + "| |Y N| "
|
69
|
+
else
|
70
|
+
puts "Is this directory correct: |" + request + "| |Y N| "
|
71
|
+
end
|
72
|
+
if WindowTerminal.getchr.downcase != "y" then
|
73
|
+
request = ""
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
if File.directory? request then
|
79
|
+
Dir.chdir(request)
|
80
|
+
else
|
81
|
+
selection = request
|
82
|
+
end
|
83
|
+
end
|
84
|
+
selection = Dir.pwd + "/" + selection
|
85
|
+
puts "File selected: |" + selection + "|"
|
86
|
+
return selection
|
87
|
+
#++
|
88
|
+
end
|
89
|
+
|
90
|
+
# An implementation of SelectFile
|
91
|
+
# using WindowTerminal.
|
92
|
+
def self.SelectFile_With_Windows(manager=WindowTerminal::WindowManager.new)
|
93
|
+
#--
|
94
|
+
# Declarations
|
95
|
+
origDir = Dir.pwd
|
96
|
+
currentDirectory = ""
|
97
|
+
selection = ""
|
98
|
+
request = ""
|
99
|
+
# Define window.
|
100
|
+
window = WindowTerminal::Window.new(WindowTerminal::Orientation.new,"Browse for file.")
|
101
|
+
text = WindowTerminal::WrappedText.new(WindowTerminal::Orientation.new(-1,0),"",5,:preserve)
|
102
|
+
text2 = WindowTerminal::ColoredText.new(WindowTerminal::Orientation.new(1,0),"",5)
|
103
|
+
window.add_objects text2,text
|
104
|
+
num = manager.new_page window
|
105
|
+
manager.display_page num
|
106
|
+
# Standard loop.
|
107
|
+
while selection == "" do
|
108
|
+
text2.set_text ""
|
109
|
+
currentDirectory = Dir.pwd
|
110
|
+
string = ""
|
111
|
+
string << "---------\nPWD: " + currentDirectory + "\n---------\n"
|
112
|
+
Dir.foreach(currentDirectory) { |file|
|
113
|
+
if File.directory? file then
|
114
|
+
string << "|" + file + "| dir\n"
|
115
|
+
else
|
116
|
+
string << "|" + file + "| file\n"
|
117
|
+
end
|
118
|
+
}
|
119
|
+
string << "---------\n"
|
120
|
+
string = string.split("\n")
|
121
|
+
range = 0..(string.length-1)
|
122
|
+
text.set_text string[range].join("\n")
|
123
|
+
WindowTerminal.screen_render
|
124
|
+
char = WindowTerminal.getchr().downcase()
|
125
|
+
while (char == "w") or (char == "s") do
|
126
|
+
if char == "w" then
|
127
|
+
start = range.begin - 1
|
128
|
+
start = 0 if start < 0
|
129
|
+
ending = range.end
|
130
|
+
range = start..ending
|
131
|
+
else
|
132
|
+
start = range.begin + 1
|
133
|
+
start = range.end if start > range.end
|
134
|
+
ending = range.end
|
135
|
+
range = start..ending
|
136
|
+
end
|
137
|
+
text.set_text string[range].join("\n")
|
138
|
+
WindowTerminal.screen_render
|
139
|
+
char = WindowTerminal.getchr().downcase()
|
140
|
+
end
|
141
|
+
#puts string
|
142
|
+
text2.set_text "File: "
|
143
|
+
WindowTerminal.screen_render
|
144
|
+
request = ""
|
145
|
+
while not (File.exists? request) do
|
146
|
+
text2.set_text "Invalid: |" + request + "| Please enter a valid file name or directory." if request != ""
|
147
|
+
WindowTerminal.screen_render
|
148
|
+
request = WindowTerminal.getchrs { |char,full|
|
149
|
+
text2.set_text "File: " + full
|
150
|
+
#WindowTerminal.screen_render
|
151
|
+
}
|
152
|
+
if not File.exists? request then
|
153
|
+
request = self.Completion(currentDirectory,request)
|
154
|
+
if File.exists? request then
|
155
|
+
if not File.directory? request then
|
156
|
+
text2.set_text "Is this file correct: |" + request + "| |Y N| "
|
157
|
+
else
|
158
|
+
text2.set_text "Is this directory correct: |" + request + "| |Y N| "
|
159
|
+
end
|
160
|
+
WindowTerminal.screen_render
|
161
|
+
if WindowTerminal.getchr.downcase != "y" then
|
162
|
+
request = ""
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
if File.directory? request then
|
168
|
+
Dir.chdir(request)
|
169
|
+
else
|
170
|
+
selection = request
|
171
|
+
end
|
172
|
+
end
|
173
|
+
selection = Dir.pwd + "/" + selection
|
174
|
+
text2.set_text ""
|
175
|
+
text.set_text "File selected: |" + selection + "|"
|
176
|
+
WindowTerminal.screen_render
|
177
|
+
WindowTerminal.getchr()
|
178
|
+
# Cleanup window.
|
179
|
+
manager.remove_page num
|
180
|
+
# Return
|
181
|
+
return selection
|
182
|
+
#++
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Copyright 2014 Luke Spangler
|
187
|
+
#
|
188
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
189
|
+
# you may not use this file except in compliance with the License.
|
190
|
+
# You may obtain a copy of the License at
|
191
|
+
#
|
192
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
193
|
+
#
|
194
|
+
# Unless required by applicable law or agreed to in writing, software
|
195
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
196
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
197
|
+
# See the License for the specific language governing permissions and
|
198
|
+
# limitations under the License.
|
data/lib/accu-window.rb
ADDED
@@ -0,0 +1,756 @@
|
|
1
|
+
# This is a library which uses some hacky references
|
2
|
+
# together with lots of math and loops to split
|
3
|
+
# a terminal into windows, via the module WindowTerminal.
|
4
|
+
# There are also several smaller classes declared
|
5
|
+
# which include:
|
6
|
+
#
|
7
|
+
# Orientation:: A class for orienting objects.
|
8
|
+
# Text:: A class for printing text in a window, based on an Orientation object.
|
9
|
+
# ColoredText:: A subclass of Text which adds compatibility for colored text.
|
10
|
+
# WrappedText:: A subclass of Text which allows text to be wrapped into a window.
|
11
|
+
# ColoredWrappedText:: A subclass of WrappedText which adds support for colored text.
|
12
|
+
# Line:: A class which creates a horizontal line across a window.
|
13
|
+
# Window:: The main class which handles the rendering of basic windows and their objects in a terminal emulator.
|
14
|
+
# WindowManager:: A class which allows simple(r) handling and rendering of windows and their objects.
|
15
|
+
#
|
16
|
+
#
|
17
|
+
# Author:: Luke Spangler
|
18
|
+
# Email:: m27frogy.roblox@gmail.com
|
19
|
+
|
20
|
+
require "highline/import"
|
21
|
+
|
22
|
+
# The primary module in-which the majority
|
23
|
+
# of classes and methods are bundled.
|
24
|
+
module WindowTerminal
|
25
|
+
TIOCGNWINSZ = 0x5413
|
26
|
+
STDOUT_HANDLE = 0xFFFFFFF5
|
27
|
+
@@windows = []
|
28
|
+
|
29
|
+
# A class which stores the Orientation
|
30
|
+
# of an object.
|
31
|
+
class Orientation
|
32
|
+
attr_reader :x,:y
|
33
|
+
|
34
|
+
# Initializes an Orienation object.
|
35
|
+
def initialize(x=0,y=0)
|
36
|
+
if x != -1 and x != 0 and x != 1 then
|
37
|
+
x = 0
|
38
|
+
end
|
39
|
+
if y != -1 and y != 0 and y != 1 then
|
40
|
+
y = 0
|
41
|
+
end
|
42
|
+
@x = x
|
43
|
+
@y = y
|
44
|
+
end
|
45
|
+
|
46
|
+
# Makes the to_s return a more
|
47
|
+
# "bootiful" string.
|
48
|
+
def to_s
|
49
|
+
"#{@x}|#{@y}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Aclass for use with the Window object
|
54
|
+
# in rendering strings with Orientation.
|
55
|
+
class Text
|
56
|
+
attr_reader :orientation,:text, :y
|
57
|
+
|
58
|
+
# Initializes a Text object.
|
59
|
+
def initialize(orientation,text,y)
|
60
|
+
raise "Orientation must be passed!" if not orientation.is_a? Orientation
|
61
|
+
raise "String must be passed!" if not text.is_a? String
|
62
|
+
@orientation = orientation
|
63
|
+
@text = text
|
64
|
+
@y = y
|
65
|
+
end
|
66
|
+
|
67
|
+
# Allows the @text instance variable
|
68
|
+
# to be mutable.
|
69
|
+
def set_text(text)
|
70
|
+
@text = text
|
71
|
+
end
|
72
|
+
|
73
|
+
# Gets the length of the objects string
|
74
|
+
# while ignoring the character 27.
|
75
|
+
def get_length(text=@text)
|
76
|
+
#--
|
77
|
+
length = 0
|
78
|
+
text.each_char {|char|
|
79
|
+
if char.ord != 27 then
|
80
|
+
length += 1
|
81
|
+
end
|
82
|
+
}
|
83
|
+
#puts length
|
84
|
+
return length
|
85
|
+
#++
|
86
|
+
end
|
87
|
+
|
88
|
+
# Renders a line of text.
|
89
|
+
def render_line(line,width,*args)
|
90
|
+
#--
|
91
|
+
line = line.dup
|
92
|
+
if get_length() == @text.length and get_length.even? then
|
93
|
+
@text << " "
|
94
|
+
end
|
95
|
+
if args[0] == nil then
|
96
|
+
args[0] = 0
|
97
|
+
end
|
98
|
+
start = args[0]
|
99
|
+
if @orientation.x == 0 then
|
100
|
+
length = get_length()
|
101
|
+
real_length = @text.length
|
102
|
+
difference = (length - real_length).abs
|
103
|
+
# Calculate centering.
|
104
|
+
centered_width = (get_length() / 2).floor
|
105
|
+
centered_length = ((width - 1) / 2).floor
|
106
|
+
|
107
|
+
range = (centered_length-centered_width)..(centered_length+centered_width)
|
108
|
+
# Add stretch space for colored text.
|
109
|
+
if difference > 0 then
|
110
|
+
char_existing = line[range.begin]
|
111
|
+
append_string = ""
|
112
|
+
#puts length,real_length
|
113
|
+
(difference+(difference*2.5).ceil).times {|v|
|
114
|
+
append_string << char_existing
|
115
|
+
}
|
116
|
+
#puts line[(range.begin)..(real_length -1)]
|
117
|
+
line = line[0..range.begin] + append_string + line[(range.begin)..(width)]
|
118
|
+
range = (range.begin+difference)..(range.end+difference)
|
119
|
+
end
|
120
|
+
line[range] = @text
|
121
|
+
if get_length(line) > width then
|
122
|
+
#puts "wut"
|
123
|
+
# RAIG GLITCH AHX AHOGWHGOHWOGHWOGHGH
|
124
|
+
line[get_length(line)-start-1] = ""
|
125
|
+
end
|
126
|
+
elsif @orientation.x == -1 then
|
127
|
+
length = get_length()
|
128
|
+
real_length = @text.length
|
129
|
+
difference = (length - real_length).abs
|
130
|
+
#puts difference
|
131
|
+
range = (start)..(length+1)
|
132
|
+
#puts range
|
133
|
+
# Add stretch space for colored text.
|
134
|
+
if difference > 0 then
|
135
|
+
char_existing = line[range.begin]
|
136
|
+
append_string = ""
|
137
|
+
puts length,real_length
|
138
|
+
(difference+(difference*2.5).ceil).times {|v|
|
139
|
+
append_string << char_existing
|
140
|
+
}
|
141
|
+
#puts line[(range.begin)..(real_length -1)]
|
142
|
+
puts "|" + line[0..range.begin-1] + "|"
|
143
|
+
line = line[0..(range.begin-1)] + append_string + line[(range.begin)..(width)]
|
144
|
+
#range = (range.begin+difference)..(range.end+difference)
|
145
|
+
end
|
146
|
+
line[range] = @text
|
147
|
+
elsif @orientation.x == 1 then
|
148
|
+
length = get_length()
|
149
|
+
real_length = @text.length
|
150
|
+
max_length = width - start - 1
|
151
|
+
difference = (length - real_length).abs
|
152
|
+
#puts difference
|
153
|
+
range = (max_length - length + 1)..(max_length)
|
154
|
+
#puts range
|
155
|
+
# Add stretch space for colored text.
|
156
|
+
if difference > 0 then
|
157
|
+
char_existing = line[range.begin]
|
158
|
+
append_string = ""
|
159
|
+
puts length,real_length
|
160
|
+
(difference+(difference*3).ceil).times {|v|
|
161
|
+
append_string << char_existing
|
162
|
+
}
|
163
|
+
puts "|" + line[(range.end+1)..(range.end + start)] + "|"
|
164
|
+
#puts (length/2.5).ceil
|
165
|
+
line = line[0..(range.begin)] + append_string + line[(range.end-((length/2)-2).ceil)..(range.end + start)]
|
166
|
+
range = (range.begin+difference*3.5)..(range.end)
|
167
|
+
end
|
168
|
+
line[range] = @text
|
169
|
+
end
|
170
|
+
return line
|
171
|
+
#++
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# A subclass of Text for colored window text.
|
176
|
+
class ColoredText < Text
|
177
|
+
#--
|
178
|
+
#say(%{Here's some <%= color('dark red text', RED) %>.})
|
179
|
+
#++
|
180
|
+
|
181
|
+
# Initializes a ColoredText object.
|
182
|
+
def initialize(orientation,text,y,color=:green)
|
183
|
+
if WindowTerminal.os == :linux then
|
184
|
+
if color == :green then
|
185
|
+
text = "\e[#{32}m#{text}\e[0m"
|
186
|
+
elsif color == :red then
|
187
|
+
text = "\e[#{31}m#{text}\e[0m"
|
188
|
+
elsif color == :yellow then
|
189
|
+
text = "\e[#{33}m#{text}\e[0m"
|
190
|
+
elsif color == :pink then
|
191
|
+
text = "\e[#{35}m#{text}\e[0m"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
super(orientation,text,y)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# A subclass of Text which allows for
|
199
|
+
# wrapped text within a Window.
|
200
|
+
class WrappedText < Text
|
201
|
+
undef :render_line
|
202
|
+
|
203
|
+
# Initializes a WrappedText object.
|
204
|
+
def initialize(orientation,text,y,mode=:destroy)
|
205
|
+
@mode = mode
|
206
|
+
super(orientation,text,y)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Renders the WrappedText object
|
210
|
+
# based on passed lines and
|
211
|
+
# returns the modified lines.
|
212
|
+
def render(lines,cols,rows,padding=2)
|
213
|
+
#--
|
214
|
+
lines = lines.dup
|
215
|
+
words = wrap(@text,rows-padding*2)
|
216
|
+
if words.length > (cols - @y) then
|
217
|
+
words.slice!((cols - @y + 1)..(words.length-1))
|
218
|
+
end
|
219
|
+
range = (@y - 1)..(cols-1)
|
220
|
+
sum = 0
|
221
|
+
ending = cols - padding - 1
|
222
|
+
lines.each_index { |index|
|
223
|
+
if (range === index and words[sum]) and index < ending then
|
224
|
+
#puts "|" + words[sum] + "|"
|
225
|
+
lines[index] = WindowTerminal::Text.new(orientation,words[sum],0).render_line(lines[index],rows,padding)
|
226
|
+
sum += 1
|
227
|
+
end
|
228
|
+
}
|
229
|
+
return lines
|
230
|
+
#++
|
231
|
+
end
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
# Wraps a string based on width.
|
236
|
+
# <i>Credit: RubyCookbook</i>
|
237
|
+
def wrap(s,width)
|
238
|
+
#--
|
239
|
+
if @mode == :destroy then
|
240
|
+
s = s.dup
|
241
|
+
s = s.split(/\s+/) if s.is_a? String
|
242
|
+
lines = []
|
243
|
+
line = ""
|
244
|
+
#puts s.length
|
245
|
+
s.each do |word|
|
246
|
+
if line.size + word.size >= width then
|
247
|
+
lines << line
|
248
|
+
line = word
|
249
|
+
elsif line.empty? then
|
250
|
+
line = word
|
251
|
+
else
|
252
|
+
line << " " << word
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
lines << line if line
|
257
|
+
#puts lines.length
|
258
|
+
return lines
|
259
|
+
else
|
260
|
+
string = s.gsub(/(.{1,#{width}})( |\Z)/, "\\1\n").split(/\n/)
|
261
|
+
#puts string
|
262
|
+
string
|
263
|
+
end
|
264
|
+
end
|
265
|
+
#++
|
266
|
+
end
|
267
|
+
|
268
|
+
# A subclass of WrappedText which adds compatibility for
|
269
|
+
# different colors.
|
270
|
+
class ColoredWrappedText < WrappedText
|
271
|
+
|
272
|
+
# Initializes a ColoredWrappedText object.
|
273
|
+
def initialize(orientation,text,y,color=:green,mode=:destroy)
|
274
|
+
@color = color
|
275
|
+
if WindowTerminal.os == :linux then
|
276
|
+
if @color == :green then
|
277
|
+
text = "\e[#{32}m#{text}\e[0m"
|
278
|
+
elsif @color == :red then
|
279
|
+
text = "\e[#{31}m#{text}\e[0m"
|
280
|
+
elsif @color == :yellow then
|
281
|
+
text = "\e[#{33}m#{text}\e[0m"
|
282
|
+
elsif @color == :pink then
|
283
|
+
text = "\e[#{35}m#{text}\e[0m"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
super(orientation,text,y,mode)
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
# A class which renders a simple
|
292
|
+
# horizontal line on a Window.
|
293
|
+
class Line
|
294
|
+
attr_reader :y
|
295
|
+
|
296
|
+
# Initializes a Line object.
|
297
|
+
def initialize(y)
|
298
|
+
@y = y
|
299
|
+
end
|
300
|
+
|
301
|
+
# Renders on the passed line
|
302
|
+
# and returns the modified line.
|
303
|
+
def render_line(line,width,*args)
|
304
|
+
line.gsub(" ","-")
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# The main class which handles each individual
|
309
|
+
# terminal window.
|
310
|
+
class Window
|
311
|
+
attr_reader :orientation,:name,:objects
|
312
|
+
|
313
|
+
# Initializes Window object.
|
314
|
+
def initialize(orientation,name)
|
315
|
+
raise "Orientation must be passed!" if not orientation.is_a? Orientation
|
316
|
+
@orientation = orientation
|
317
|
+
@name = name
|
318
|
+
@objects = []
|
319
|
+
add_object(WindowTerminal::ColoredText.new(WindowTerminal::Orientation.new,@name,0,:red))
|
320
|
+
add_object(WindowTerminal::Line.new(1))
|
321
|
+
end
|
322
|
+
|
323
|
+
# Makes the to_s method return
|
324
|
+
# something a bit more meaningful.
|
325
|
+
def to_s
|
326
|
+
"<Window: @orientation = #{@orientation.to_s}, @name = #{@name} >"
|
327
|
+
end
|
328
|
+
|
329
|
+
# Adds a single object to the Window's
|
330
|
+
# object list.
|
331
|
+
def add_object(object)
|
332
|
+
raise ArgumentError "Argument must be a renderable object." if not (object.respond_to?(:render_line) or object.respond_to?(:render))
|
333
|
+
@objects << object
|
334
|
+
end
|
335
|
+
|
336
|
+
# Adds all passed arguments to the
|
337
|
+
# Window's object list.
|
338
|
+
def add_objects(*items)
|
339
|
+
raise ArgumentError "Argument missing" if items.length == 0
|
340
|
+
items.each { |item|
|
341
|
+
add_object(item)
|
342
|
+
}
|
343
|
+
end
|
344
|
+
|
345
|
+
# Yields the passed block
|
346
|
+
# with each object.
|
347
|
+
def for_objects()
|
348
|
+
@objects.each {|object|
|
349
|
+
yield object
|
350
|
+
}
|
351
|
+
end
|
352
|
+
|
353
|
+
# Includes the Window in the
|
354
|
+
# WindowTerminal module screen_render.
|
355
|
+
def include_in_render(bool=true)
|
356
|
+
if bool and not WindowTerminal.get_windows().include? self then
|
357
|
+
WindowTerminal.add_window self
|
358
|
+
elsif not bool and WindowTerminal.get_windows().include? self then
|
359
|
+
WindowTerminal.remove_window self
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# Makes handling and switching between
|
365
|
+
# Window objects simpler by creating
|
366
|
+
# an API that allows for objects
|
367
|
+
# to be assigned to pages,
|
368
|
+
# which can, in turn,
|
369
|
+
# be displayed, hidden, or deleted
|
370
|
+
# at leisure.
|
371
|
+
class WindowManager
|
372
|
+
attr_reader :pages
|
373
|
+
|
374
|
+
# Initializes a WindowManager object.
|
375
|
+
def initialize()
|
376
|
+
@pages = []
|
377
|
+
@displayed_page = nil
|
378
|
+
end
|
379
|
+
|
380
|
+
# Creates a new page with the passed
|
381
|
+
# arguments and returns the page
|
382
|
+
# number.
|
383
|
+
def new_page(*items)
|
384
|
+
@pages << []
|
385
|
+
items.each { |item|
|
386
|
+
if item.is_a? Window then
|
387
|
+
@pages[-1] << item
|
388
|
+
end
|
389
|
+
}
|
390
|
+
return @pages.length
|
391
|
+
end
|
392
|
+
|
393
|
+
# Displays the page at the number
|
394
|
+
# specified; defaults to one.
|
395
|
+
def display_page(num=1)
|
396
|
+
raise(ArgumentError, "Argument 1 must be a valid integer!") if not (num.is_a? Fixnum)
|
397
|
+
hide_page(@displayed_page)
|
398
|
+
@pages[num-1].each { |item|
|
399
|
+
WindowTerminal.add_window(item)
|
400
|
+
}
|
401
|
+
@displayed_page = num
|
402
|
+
end
|
403
|
+
|
404
|
+
# Alias for display_page.
|
405
|
+
def show(num=1)
|
406
|
+
display_page(num)
|
407
|
+
end
|
408
|
+
|
409
|
+
# Hides a page by a number,
|
410
|
+
# defaults to doing nothing.
|
411
|
+
def hide_page(num=nil)
|
412
|
+
if num then
|
413
|
+
raise(ArgumentError, "Argument 1 must be a valid integer!") if not (num.is_a? Fixnum)
|
414
|
+
if num == @displayed_page then
|
415
|
+
@pages[num-1].each { |item|
|
416
|
+
WindowTerminal.remove_window(item)
|
417
|
+
}
|
418
|
+
@displayed_page = nil
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
# An alias for hide_page which
|
424
|
+
# always passes the current
|
425
|
+
# displayed page number,
|
426
|
+
# if it exists.
|
427
|
+
def hide()
|
428
|
+
if @displayed_page then
|
429
|
+
hide_page(@displayed_page)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
# Delete a page by page number.
|
434
|
+
def remove_page(num)
|
435
|
+
raise(ArgumentError, "Argument 1 must be a valid integer!") if not (num.is_a? Fixnum)
|
436
|
+
hide_page(num)
|
437
|
+
@pages.delete_at(num-1)
|
438
|
+
end
|
439
|
+
|
440
|
+
# Gets all Text and Text subclass
|
441
|
+
# objects on the page given and
|
442
|
+
# returns them all as an array.
|
443
|
+
def get_page_text(num)
|
444
|
+
raise(ArgumentError, "Argument 1 must be a valid integer!") if not (num.is_a? Fixnum)
|
445
|
+
array = []
|
446
|
+
@pages[num-1].each {|window|
|
447
|
+
array << window.objects.dup
|
448
|
+
array[-1].each {|item|
|
449
|
+
if not (item.respond_to? :set_text) then
|
450
|
+
array[-1].delete(item)
|
451
|
+
end
|
452
|
+
}
|
453
|
+
}
|
454
|
+
return array
|
455
|
+
end
|
456
|
+
|
457
|
+
# An alias for the WindowTerminal method
|
458
|
+
# screen_render.
|
459
|
+
def render()
|
460
|
+
WindowTerminal.screen_render
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# Gets current terminal size based
|
465
|
+
# on the operating system and
|
466
|
+
# returns the result.
|
467
|
+
def self.terminal_size
|
468
|
+
if self.os == :windows then
|
469
|
+
m_GetStdHAndle = Win32Api.new("kernel32","GetStdHandle",["L"],"L")
|
470
|
+
m_GetConsoleScreenBufferInfo = Win32Api.new("kernel32","GetConsoleScreenBufferInfo",["L","P"],"L")
|
471
|
+
|
472
|
+
format = "SSSSSssssSS"
|
473
|
+
buf = ([0] * format.size).pack(format)
|
474
|
+
stdout_handle = m_GetStdHandle.call(STDOUT_HANDLE)
|
475
|
+
|
476
|
+
m_GetConsoleScreenBufferInfo.call(stdout_handle, buf)
|
477
|
+
(bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = buf.unpack(format)
|
478
|
+
return bottom - top + 1, right - left + 1
|
479
|
+
else
|
480
|
+
rows, cols = 25, 80
|
481
|
+
buf = [ 0, 0, 0, 0 ].pack("SSSS")
|
482
|
+
if STDOUT.ioctl(TIOCGNWINSZ, buf) >= 0 then
|
483
|
+
rows, cols, row_pixels, col_pixels = buf.unpack("SSSS")[0..1]
|
484
|
+
end
|
485
|
+
return rows,cols
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# Adds a window to the render.
|
490
|
+
def self.add_window(win)
|
491
|
+
@@windows << win
|
492
|
+
end
|
493
|
+
|
494
|
+
# Remoes a window from the render.
|
495
|
+
def self.remove_window(win)
|
496
|
+
@@windows.delete(win)
|
497
|
+
end
|
498
|
+
|
499
|
+
# Returns the windows currently
|
500
|
+
# being rendered.
|
501
|
+
def self.get_windows()
|
502
|
+
@@windows
|
503
|
+
end
|
504
|
+
|
505
|
+
# Returns current operating system
|
506
|
+
# based on the return value of
|
507
|
+
# ENV["OS"].
|
508
|
+
def self.os
|
509
|
+
op = :blah
|
510
|
+
if ENV["OS"] == nil then
|
511
|
+
op = :linux
|
512
|
+
else
|
513
|
+
op = :windows
|
514
|
+
end
|
515
|
+
return op
|
516
|
+
end
|
517
|
+
|
518
|
+
# Renders the screen using
|
519
|
+
# current window objects.
|
520
|
+
def self.screen_render
|
521
|
+
#--
|
522
|
+
rows,cols = *WindowTerminal.terminal_size
|
523
|
+
line1 = ""
|
524
|
+
line2 = ""
|
525
|
+
cols.times {|num|
|
526
|
+
line1 << "#"
|
527
|
+
line2 << " "
|
528
|
+
}
|
529
|
+
line2[0] = "#"
|
530
|
+
line2[-1] = "#"
|
531
|
+
line1 << "\n"
|
532
|
+
line2 << "\n"
|
533
|
+
lines = ""
|
534
|
+
rows.times {|num|
|
535
|
+
if num != 0 and num != rows - 1 then
|
536
|
+
lines << line2
|
537
|
+
else
|
538
|
+
lines << line1
|
539
|
+
end
|
540
|
+
}
|
541
|
+
if @@windows.length == 1 then
|
542
|
+
window = @@windows[0]
|
543
|
+
# Add lines for window.
|
544
|
+
lines = lines.split("\n")
|
545
|
+
lines.each_index { |index|
|
546
|
+
#puts index
|
547
|
+
if index > 1 and index < rows - 2 then
|
548
|
+
lines[index][1] = "|"
|
549
|
+
lines[index][-2] = "|"
|
550
|
+
end
|
551
|
+
}
|
552
|
+
lines[1].gsub!(" ","-")
|
553
|
+
#lines[3].gsub!(" ","-")
|
554
|
+
lines[-2].gsub!(" ","-")
|
555
|
+
# Render window objects.
|
556
|
+
window.for_objects{ |object|
|
557
|
+
if object.respond_to? :render_line then
|
558
|
+
lines[object.y+2] = object.render_line(lines[object.y+2],cols,2)
|
559
|
+
elsif object.respond_to? :render then
|
560
|
+
lines = object.render(lines,rows,cols)
|
561
|
+
end
|
562
|
+
}
|
563
|
+
lines = lines.join("\n")
|
564
|
+
elsif @@windows.length == 2 then
|
565
|
+
window1,window2 = @@windows[0],@@windows[1]
|
566
|
+
if window1.orientation.x != window2.orientation.x then # Box-Box
|
567
|
+
if window1.orientation.x > window2.orientation.x then
|
568
|
+
left_window = window2
|
569
|
+
right_window = window1
|
570
|
+
else
|
571
|
+
left_window = window1
|
572
|
+
right_window = window2
|
573
|
+
end
|
574
|
+
lines = lines.split("\n")
|
575
|
+
lines_left = []
|
576
|
+
lines_right = []
|
577
|
+
width = (cols - 1)
|
578
|
+
left_dimension = (width / 2).ceil
|
579
|
+
right_dimension = width - left_dimension
|
580
|
+
lines.each_index {|index|
|
581
|
+
#puts "iteration"
|
582
|
+
lines_left[index] = lines[index].slice(0..left_dimension)
|
583
|
+
lines_right[index] = lines[index].slice(right_dimension..width)
|
584
|
+
}
|
585
|
+
[[left_window,lines_left],[right_window,lines_right]].each { |array|
|
586
|
+
window = array[0]
|
587
|
+
current_lines = array[1]
|
588
|
+
current_lines.each_index { |index|
|
589
|
+
if index > 1 and index < rows - 2 then
|
590
|
+
current_lines[index][0] = "#"
|
591
|
+
current_lines[index][-1] = "#"
|
592
|
+
current_lines[index][1] = "|"
|
593
|
+
current_lines[index][-2] = "|"
|
594
|
+
end
|
595
|
+
}
|
596
|
+
local_cols = current_lines[0].length
|
597
|
+
current_lines[1].gsub!(" ","-")
|
598
|
+
current_lines[-2].gsub!(" ","-")
|
599
|
+
window.for_objects{ |object|
|
600
|
+
if object.respond_to? :render_line then
|
601
|
+
current_lines[object.y+2] = object.render_line(current_lines[object.y+2],local_cols,2)
|
602
|
+
end
|
603
|
+
}
|
604
|
+
}
|
605
|
+
lines = []
|
606
|
+
lines_left.each_index { |index|
|
607
|
+
lines[index] = lines_left[index] + lines_right[index]
|
608
|
+
}
|
609
|
+
lines_left = []
|
610
|
+
lines_right = []
|
611
|
+
lines = lines.join("\n")
|
612
|
+
else # Box / Box
|
613
|
+
if window1.orientation.y > window2.orientation.y then
|
614
|
+
up_window = window2
|
615
|
+
down_window = window1
|
616
|
+
else
|
617
|
+
up_window = window1
|
618
|
+
down_window = window2
|
619
|
+
end
|
620
|
+
lines = lines.split("\n")
|
621
|
+
up_lines = []
|
622
|
+
down_lines = []
|
623
|
+
height = (rows - 1)
|
624
|
+
up_dimension = (height / 2).ceil
|
625
|
+
down_dimension = height - up_dimension
|
626
|
+
lines.each_index {|index|
|
627
|
+
#puts "iteration"
|
628
|
+
if index <= up_dimension then
|
629
|
+
up_lines << lines[index]
|
630
|
+
else
|
631
|
+
down_lines << lines[index]
|
632
|
+
end
|
633
|
+
}
|
634
|
+
[[up_window,up_lines],[down_window,down_lines]].each { |array|
|
635
|
+
window = array[0]
|
636
|
+
current_lines = array[1]
|
637
|
+
current_lines[1].gsub!(" ","-")
|
638
|
+
old = current_lines[-1].dup
|
639
|
+
current_lines[-1].gsub!(" ","-")
|
640
|
+
if current_lines[-1] == old then
|
641
|
+
current_lines[-2].gsub!(" ","-")
|
642
|
+
end
|
643
|
+
current_lines.each_index { |index|
|
644
|
+
if index == 0 or index == rows - 1 then
|
645
|
+
current_lines[index] = line1.dup.gsub("\n","")
|
646
|
+
# elsif index == 1 or index == rows - 2 then
|
647
|
+
# current_lines[index].gsub!(" ","-")
|
648
|
+
else
|
649
|
+
#current_lines[index][0] = "#"
|
650
|
+
#current_lines[index][-1] = "#"
|
651
|
+
if current_lines[index][1] != "#" then
|
652
|
+
current_lines[index][1] = "|"
|
653
|
+
current_lines[index][-2] = "|"
|
654
|
+
end
|
655
|
+
end
|
656
|
+
}
|
657
|
+
window.for_objects{ |object|
|
658
|
+
if object.respond_to? :render_line then
|
659
|
+
current_lines[object.y+2] = object.render_line(current_lines[object.y+2],cols,2)
|
660
|
+
end
|
661
|
+
}
|
662
|
+
}
|
663
|
+
lines = []
|
664
|
+
up_lines.each { |line|
|
665
|
+
lines << line
|
666
|
+
}
|
667
|
+
up_lines = []
|
668
|
+
down_lines.each {|line|
|
669
|
+
lines << line
|
670
|
+
}
|
671
|
+
down_lines = []
|
672
|
+
lines = lines.join("\n")
|
673
|
+
end
|
674
|
+
end
|
675
|
+
print lines
|
676
|
+
#++
|
677
|
+
end
|
678
|
+
|
679
|
+
# Gets a string from user using
|
680
|
+
# the highline library ask()
|
681
|
+
# set to no echo.
|
682
|
+
def self.ask_quietly()
|
683
|
+
string = ask("") { |q| q.echo = '' }
|
684
|
+
yield(string) if block_given?
|
685
|
+
self.screen_render
|
686
|
+
return string
|
687
|
+
end
|
688
|
+
|
689
|
+
# Gets a single character from
|
690
|
+
# the terminal emulator
|
691
|
+
# in linux, otherwise it
|
692
|
+
# returns an empty string.
|
693
|
+
#
|
694
|
+
# Also yields the character to
|
695
|
+
# a passed block before rendering.
|
696
|
+
def self.getchr()
|
697
|
+
if self.os == :linux then
|
698
|
+
string = ""
|
699
|
+
begin
|
700
|
+
system("stty raw -echo")
|
701
|
+
string = STDIN.getc
|
702
|
+
ensure
|
703
|
+
system("stty -raw echo")
|
704
|
+
end
|
705
|
+
yield string if block_given?
|
706
|
+
self.screen_render
|
707
|
+
return string
|
708
|
+
else
|
709
|
+
""
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
# Similar to ask_quietly but
|
714
|
+
# renders the screen after
|
715
|
+
# each character is entered.
|
716
|
+
#
|
717
|
+
# A block may also be passed
|
718
|
+
# which will be executed
|
719
|
+
# just before a render with the
|
720
|
+
# current character and the full
|
721
|
+
# string thus far.
|
722
|
+
def self.getchrs()
|
723
|
+
if self.os == :linux then
|
724
|
+
full = ""
|
725
|
+
string = " "
|
726
|
+
string = self.getchr()
|
727
|
+
while (string.ord != 13) do
|
728
|
+
if string.ord == 127 and full.length > 0 then
|
729
|
+
full.slice!(full.length - 1)
|
730
|
+
else
|
731
|
+
full << string.ord
|
732
|
+
end
|
733
|
+
yield(string,full) if block_given?
|
734
|
+
self.screen_render
|
735
|
+
string = self.getchr()
|
736
|
+
end
|
737
|
+
return full
|
738
|
+
else
|
739
|
+
""
|
740
|
+
end
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
# Copyright 2014 Luke Spangler
|
745
|
+
#
|
746
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
747
|
+
# you may not use this file except in compliance with the License.
|
748
|
+
# You may obtain a copy of the License at
|
749
|
+
#
|
750
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
751
|
+
#
|
752
|
+
# Unless required by applicable law or agreed to in writing, software
|
753
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
754
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
755
|
+
# See the License for the specific language governing permissions and
|
756
|
+
# limitations under the License.
|