accutronic 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|