Class: Textbringer::Window

Inherits:
Object
  • Object
show all
Defined in:
lib/textbringer/window.rb

Direct Known Subclasses

EchoArea

Constant Summary collapse

KEY_NAMES =
{}
ALT_IS_FUNCTION_KEY =
/mswin32|mingw32/ =~ RUBY_PLATFORM && /PDCurses/ =~ Curses::VERSION
KEY_OFFSET =
0xec00
ALT_0 =
KEY_OFFSET + 0x97
ALT_9 =
KEY_OFFSET + 0xa0
ALT_A =
KEY_OFFSET + 0xa1
ALT_Z =
KEY_OFFSET + 0xba
ALT_NUMBER_BASE =
ALT_0 - ?0.ord
ALT_ALPHA_BASE =
ALT_A - ?a.ord
@@windows =
[]
@@current =
nil
@@echo_area =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(lines, columns, y, x) ⇒ Window

Returns a new instance of Window.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/textbringer/window.rb', line 195

def initialize(lines, columns, y, x)
  @lines = lines
  @columns = columns
  @y = y
  @x = x
  initialize_window(lines, columns, y, x)
  @window.keypad = true
  @window.scrollok(false)
  @window.idlok(true)
  @buffer = nil
  @top_of_window = nil
  @bottom_of_window = nil
  @point_mark = nil
  @deleted = false
  @key_buffer = []
end

Instance Attribute Details

#bufferObject

Returns the value of attribute buffer.



193
194
195
# File 'lib/textbringer/window.rb', line 193

def buffer
  @buffer
end

#columnsObject (readonly)

Returns the value of attribute columns.



193
194
195
# File 'lib/textbringer/window.rb', line 193

def columns
  @columns
end

#linesObject (readonly)

Returns the value of attribute lines.



193
194
195
# File 'lib/textbringer/window.rb', line 193

def lines
  @lines
end

#xObject (readonly)

Returns the value of attribute x.



193
194
195
# File 'lib/textbringer/window.rb', line 193

def x
  @x
end

#yObject (readonly)

Returns the value of attribute y.



193
194
195
# File 'lib/textbringer/window.rb', line 193

def y
  @y
end

Class Method Details

.beepObject



189
190
191
# File 'lib/textbringer/window.rb', line 189

def self.beep
  Curses.beep
end

.columnsObject



163
164
165
# File 'lib/textbringer/window.rb', line 163

def self.columns
  Curses.cols
end

.currentObject



53
54
55
# File 'lib/textbringer/window.rb', line 53

def self.current
  @@current
end

.current=(window) ⇒ Object



57
58
59
60
61
62
63
64
65
# File 'lib/textbringer/window.rb', line 57

def self.current=(window)
  if window.deleted?
    window = @@windows.first
  end
  @@current.save_point if @@current && !@@current.deleted?
  @@current = window
  @@current.restore_point
  Buffer.current = window.buffer
end

.delete_other_windowsObject



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/textbringer/window.rb', line 87

def self.delete_other_windows
  if @@current.echo_area?
    raise EditorError, "Can't expand the echo area to full screen"
  end
  @@windows.delete_if do |window|
    if window.current? || window.echo_area?
      false
    else
      window.delete
      true
    end
  end
  @@current.move(0, 0)
  @@current.resize(Window.lines - 1, @@current.columns)
end

.delete_windowObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/textbringer/window.rb', line 67

def self.delete_window
  if @@current.echo_area?
    raise EditorError, "Can't delete the echo area"
  end
  if @@windows.size == 2
    raise EditorError, "Can't delete the sole window"
  end
  i = @@windows.index(@@current)
  if i == 0
    window = @@windows[1]
    window.move(0, 0)
  else
    window = @@windows[i - 1]
  end
  window.resize(@@current.lines + window.lines, window.columns)
  @@current.delete
  @@windows.delete_at(i)
  self.current = window
end

.echo_areaObject



112
113
114
# File 'lib/textbringer/window.rb', line 112

def self.echo_area
  @@echo_area
end

.linesObject



159
160
161
# File 'lib/textbringer/window.rb', line 159

def self.lines
  Curses.lines
end

.other_windowObject



103
104
105
106
107
108
109
110
# File 'lib/textbringer/window.rb', line 103

def self.other_window
  i = @@windows.index(@@current)
  begin
    i += 1
    window = @@windows[i % @@windows.size]
  end while !window.active?
  self.current = window
end

.redisplayObject



139
140
141
142
143
144
145
# File 'lib/textbringer/window.rb', line 139

def self.redisplay
  @@windows.each do |window|
    window.redisplay unless window.current?
  end
  current.redisplay
  update
end

.redrawObject



147
148
149
150
151
152
153
# File 'lib/textbringer/window.rb', line 147

def self.redraw
  @@windows.each do |window|
    window.redraw unless window.current?
  end
  current.redraw
  update
end

.resizeObject



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/textbringer/window.rb', line 167

def self.resize
  @@windows.delete_if do |window|
    if !window.echo_area? && window.y > Window.lines - 4
      window.delete
      true
    else
      false
    end
  end
  @@windows.each_with_index do |window, i|
    unless window.echo_area?
      if i < @@windows.size - 2
        window.resize(window.lines, Window.columns)
      else
        window.resize(Window.lines - 1 - window.y, Window.columns)
      end
    end
  end
  @@echo_area.move(Window.lines - 1, 0)
  @@echo_area.resize(1, Window.columns)
end

.startObject



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/textbringer/window.rb', line 116

def self.start
  Curses.init_screen
  Curses.noecho
  Curses.raw
  begin
    window =
      Textbringer::Window.new(Window.lines - 1, Window.columns, 0, 0)
    window.buffer = Buffer.new_buffer("*scratch*")
    @@windows.push(window)
    Window.current = window
    @@echo_area = Textbringer::EchoArea.new(1, Window.columns,
                                            Window.lines - 1, 0)
    Buffer.minibuffer.keymap = MINIBUFFER_LOCAL_MAP
    @@echo_area.buffer = Buffer.minibuffer
    @@windows.push(@@echo_area)
    yield
  ensure
    Curses.echo
    Curses.noraw
    Curses.close_screen
  end
end

.updateObject



155
156
157
# File 'lib/textbringer/window.rb', line 155

def self.update
  Curses.doupdate
end

.windowsObject



49
50
51
# File 'lib/textbringer/window.rb', line 49

def self.windows
  @@windows
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


216
217
218
# File 'lib/textbringer/window.rb', line 216

def active?
  true
end

#current?Boolean

Returns:

  • (Boolean)


261
262
263
# File 'lib/textbringer/window.rb', line 261

def current?
  self == @@current
end

#deleteObject



224
225
226
227
228
229
230
231
232
233
# File 'lib/textbringer/window.rb', line 224

def delete
  unless @deleted
    if current?
      Window.current = @@windows.first
    end
    delete_marks
    @window.close
    @deleted = true
  end
end

#deleted?Boolean

Returns:

  • (Boolean)


220
221
222
# File 'lib/textbringer/window.rb', line 220

def deleted?
  @deleted
end

#echo_area?Boolean

Returns:

  • (Boolean)


212
213
214
# File 'lib/textbringer/window.rb', line 212

def echo_area?
  false
end

#has_input?Boolean

Returns:

  • (Boolean)


305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/textbringer/window.rb', line 305

def has_input?
  @window.nodelay = true
  begin
    c = @window.get_char
    if c
      Curses.unget_char(c)
    end
    !c.nil?
  ensure
    @window.nodelay = false
  end
end

#move(y, x) ⇒ Object



396
397
398
399
400
401
# File 'lib/textbringer/window.rb', line 396

def move(y, x)
  @y = y
  @x = x
  @window.move(y, x)
  @mode_line.move(y + @window.maxy, x)
end

#read_charObject



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/textbringer/window.rb', line 265

def read_char
  key = get_char
  if key.is_a?(Integer)
    if ALT_IS_FUNCTION_KEY
      if ALT_0 <= key && key <= ALT_9
        @key_buffer.push((key - ALT_NUMBER_BASE).chr)
        return "\e"
      elsif ALT_A <= key && key <= ALT_Z
        @key_buffer.push((key - ALT_ALPHA_BASE).chr)
        return "\e"
      end
    end
    KEY_NAMES[key] || key
  else
    key&.encode(Encoding::UTF_8)&.tr("\r", "\n")
  end
end

#read_char_nonblockObject



283
284
285
286
287
288
289
290
# File 'lib/textbringer/window.rb', line 283

def read_char_nonblock
  @window.nodelay = true
  begin
    read_char
  ensure
    @window.nodelay = false
  end
end

#recenterObject



411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/textbringer/window.rb', line 411

def recenter
  @buffer.save_point do |saved|
    max = (lines - 1) / 2
    count = beginning_of_line_and_count(max)
    while count < max
      break if @buffer.point == 0
      @buffer.backward_char
      count += beginning_of_line_and_count(max - count - 1) + 1
    end
    @buffer.mark_to_point(@top_of_window)
  end
end

#recenter_if_neededObject



424
425
426
427
428
429
# File 'lib/textbringer/window.rb', line 424

def recenter_if_needed
  if @buffer.point_before_mark?(@top_of_window) ||
     @buffer.point_after_mark?(@bottom_of_window)
    recenter
  end
end

#redisplayObject



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/textbringer/window.rb', line 318

def redisplay
  return if has_input?
  return if @buffer.nil?
  redisplay_mode_line
  @buffer.save_point do |saved|
    if current?
      point = saved
    else
      point = @point_mark
      @buffer.point_to_mark(@point_mark)
    end
    framer
    y = x = 0
    @buffer.point_to_mark(@top_of_window)
    @window.erase
    @window.setpos(0, 0)
    if current? && @buffer.visible_mark &&
       @buffer.point_after_mark?(@buffer.visible_mark)
      @window.attron(Curses::A_REVERSE)
    end
    while !@buffer.end_of_buffer?
      if @buffer.point_at_mark?(point)
        y, x = @window.cury, @window.curx
        if current? && @buffer.visible_mark
          if @buffer.point_after_mark?(@buffer.visible_mark)
            @window.attroff(Curses::A_REVERSE)
          elsif @buffer.point_before_mark?(@buffer.visible_mark)
            @window.attron(Curses::A_REVERSE)
          end
        end
      end
      if current? && @buffer.visible_mark &&
         @buffer.point_at_mark?(@buffer.visible_mark)
        if @buffer.point_after_mark?(point)
          @window.attroff(Curses::A_REVERSE)
        elsif @buffer.point_before_mark?(point)
          @window.attron(Curses::A_REVERSE)
        end
      end
      c = @buffer.char_after
      if c == "\n"
        @window.clrtoeol
        break if @window.cury == lines - 2   # lines include mode line
      elsif c == "\t"
        n = calc_tab_width(@window.curx)
        c = " " * n
      else
        c = escape(c)
      end
      @window.addstr(c)
      break if @window.cury == lines - 2 &&  # lines include mode line
        @window.curx == columns
      @buffer.forward_char
    end
    if current? && @buffer.visible_mark
      @window.attroff(Curses::A_REVERSE)
    end
    @buffer.mark_to_point(@bottom_of_window)
    if @buffer.point_at_mark?(point)
      y, x = @window.cury, @window.curx
    end
    if x == columns - 1
      c = @buffer.char_after(point.location)
      if c && Buffer.display_width(c) > 1
        y += 1
        x = 0
      end
    end
    @window.setpos(y, x)
    @window.noutrefresh
  end
end

#redrawObject



391
392
393
394
# File 'lib/textbringer/window.rb', line 391

def redraw
  @window.redraw
  @mode_line.redraw
end

#resize(lines, columns) ⇒ Object



403
404
405
406
407
408
409
# File 'lib/textbringer/window.rb', line 403

def resize(lines, columns)
  @lines = lines
  @columns = columns
  @window.resize(lines - 1, columns)
  @mode_line.move(@y + lines - 1, @x)
  @mode_line.resize(1, columns)
end

#restore_pointObject



257
258
259
# File 'lib/textbringer/window.rb', line 257

def restore_point
  @buffer.point_to_mark(@point_mark)
end

#save_pointObject



249
250
251
252
253
254
255
# File 'lib/textbringer/window.rb', line 249

def save_point
  @buffer[:top_of_window] ||= @buffer.new_mark
  @buffer[:top_of_window].location = @top_of_window.location
  @buffer[:bottom_of_window] ||= @buffer.new_mark
  @buffer[:bottom_of_window].location = @bottom_of_window.location
  @buffer.mark_to_point(@point_mark)
end

#scroll_downObject



438
439
440
441
442
443
# File 'lib/textbringer/window.rb', line 438

def scroll_down
  @buffer.point_to_mark(@top_of_window)
  @buffer.next_line
  @buffer.beginning_of_line
  @top_of_window.location = 0
end

#scroll_upObject



431
432
433
434
435
436
# File 'lib/textbringer/window.rb', line 431

def scroll_up
  @buffer.point_to_mark(@bottom_of_window)
  @buffer.previous_line
  @buffer.beginning_of_line
  @buffer.mark_to_point(@top_of_window)
end

#splitObject



445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/textbringer/window.rb', line 445

def split
  if lines < 6
    raise EditorError, "Window too small"
  end
  old_lines = lines
  new_lines = (old_lines / 2.0).ceil
  resize(new_lines, columns)
  new_window = Window.new(old_lines - new_lines, columns, y + new_lines, x)
  new_window.buffer = buffer
  i = @@windows.index(self)
  @@windows.insert(i + 1, new_window)
end

#wait_input(msecs) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/textbringer/window.rb', line 292

def wait_input(msecs)
  @window.timeout = msecs
  begin
    c = @window.get_char
    if c
      Curses.unget_char(c)
    end
    c
  ensure
    @window.timeout = -1
  end
end