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.
@@ -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.
@@ -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.