Class: GtkApp::TextBuffer

Inherits:
Gtk::TextBuffer show all
Defined in:
lib/gtk_app/text_buffer.rb

Constant Summary collapse

DEFAULT_LANG =
"en_US"
DEFAULT_TAGS =
%w[bold italic strikethrough underline error spell_error]

Instance Attribute Summary collapse

Attributes inherited from Gtk::TextBuffer

#insert_end, #insert_start

Instance Method Summary collapse

Methods inherited from Gtk::TextBuffer

#unformat, #unformat_all, #unformat_selection

Constructor Details

#initialize(tag_table = nil, options = {}) ⇒ TextBuffer

Returns a new instance of TextBuffer.



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/gtk_app/text_buffer.rb', line 29

def initialize(tag_table=nil, options={})
  super(tag_table)
  @undo_stack, @redo_stack = [], []
  
  options[:lang] ||= DEFAULT_LANG
  @spell_check = FFI::Aspell::Speller.new(options[:lang])
  
  setup_default_tags
  setup_text_marks
  setup_signals
end

Instance Attribute Details

#redo_stackArray (readonly)

!@attribute [r] redo_stack Collection of actions undone

Returns:

  • (Array)


19
20
21
# File 'lib/gtk_app/text_buffer.rb', line 19

def redo_stack
  @redo_stack
end

#spell_checkFFI::Aspell::Speller (readonly)

!@attribute [r] spell_check Aspell object

Returns:

  • (FFI::Aspell::Speller)


9
10
11
# File 'lib/gtk_app/text_buffer.rb', line 9

def spell_check
  @spell_check
end

#text_marksHash (readonly)

!@attribute [r] text_marks Collection of named text marks used to track user input

Returns:

  • (Hash)


24
25
26
# File 'lib/gtk_app/text_buffer.rb', line 24

def text_marks
  @text_marks
end

#undo_stackArray (readonly)

!@attribute [r] undo_stack Collection of actions performed

Returns:

  • (Array)


14
15
16
# File 'lib/gtk_app/text_buffer.rb', line 14

def undo_stack
  @undo_stack
end

Instance Method Details

#check_range(s_iter, e_iter) ⇒ Object

Parameters:

  • s_iter (Gtk::TextIter)
  • e_iter (Gtk::TextIter)


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/gtk_app/text_buffer.rb', line 110

def check_range(s_iter, e_iter)
  e_iter.forward_word_end if e_iter.inside_word?
  unless s_iter.starts_word?
    if s_iter.inside_word? || s_iter.ends_word?
      s_iter.backward_word_start
    elsif s_iter.forward_word_end
      s_iter.backward_word_start
    end
  end
  # Get the iter at the current cursor position and the pre cursor.
  c_iter  = get_iter_at_offset(cursor_position)
  pc_iter = c_iter.clone
  pc_iter.backward_char
  
  spell_error = tag_table.lookup('spell_error')
  has_error = (c_iter.has_tag?(spell_error) || pc_iter.has_tag?(spell_error))
  clear(:spell_error, s_iter, e_iter)
  
  # NOTE: From GtkSpell, catch rare cases when the replacement occurs at the
  # beginning of the buffer.  An iter at offset 0 seems to always be inside a
  # word even if it's not.
  if get_iter_at_offset(0) == s_iter
    s_iter.forward_word_end
    s_iter.backward_word_start
  end
  
  ws_iter = s_iter.clone # => Word start iter
  while (ws_iter <=> e_iter) < 0 do
    we_iter = ws_iter.clone  # => Word end iter
    we_iter.forward_word_end
    
    if ((ws_iter <=> c_iter) < 0) && ((c_iter <=> we_iter) <= 0)
      # The current word is being actively edited.  Only check it if it's
      # already been identified as incorrect.  Else, check later.
      check_word(ws_iter, we_iter) if has_error  
    else
      check_word(ws_iter, we_iter)
    end
    # Move the word end iter the the beginning of the next word.
    we_iter.forward_word_end
    we_iter.backward_word_start
    break if we_iter == ws_iter

    ws_iter = we_iter.clone
  end
end

#check_spellingObject

Helper method to check the spelling of every word in the buffer.



104
105
106
# File 'lib/gtk_app/text_buffer.rb', line 104

def check_spelling
  check_range(*bounds)
end

#check_word(s_iter, e_iter) ⇒ Object

Parameters:

  • s_iter (Gtk::TextIter)
  • e_iter (Gtk::TextIter)


159
160
161
162
163
164
# File 'lib/gtk_app/text_buffer.rb', line 159

def check_word(s_iter, e_iter)
  word = get_text(s_iter, e_iter)
  unless @spell_check.correct?(word)
    format(:spell_error, s_iter, e_iter)
  end
end

#clear(tag_name, s_iter, e_iter) ⇒ Object

Remove all tags of a given name from from one Gtk::TextIter to another.

Parameters:

  • []

    tag_name

  • s_iter (Gtk::TextIter)
  • e_iter (Gtk::TextIter)


234
235
236
# File 'lib/gtk_app/text_buffer.rb', line 234

def clear(tag_name, s_iter, e_iter)
  remove_tag(tag_name.to_s, s_iter, e_iter)
end

#clear_all(s_iter, e_iter) ⇒ Object

Remove all Gtk::TextTag’s from one Gtk::TextIter to another.

Parameters:

  • s_iter (Gtk::TextIter)
  • e_iter (Gtk::TextIter)


243
244
245
# File 'lib/gtk_app/text_buffer.rb', line 243

def clear_all(s_iter, e_iter)
  remove_all_tags(s_iter, e_iter)
end

#clear_selection(*tag_names) ⇒ Object

Remove all occurrences of a Gtk::TextTag in the given selection range.



217
218
219
220
221
222
223
224
225
226
# File 'lib/gtk_app/text_buffer.rb', line 217

def clear_selection(*tag_names)
  s_iter, e_iter, text_selected = selection_bounds
  if text_selected
    if tag_names.empty?
      clear_all(s_iter, e_iter)
    else
      tag_names.each { |tag_name| clear(tag_name, s_iter, e_iter) }
    end
  end
end

#find(string) ⇒ Object

Locate text in selection or the entire buffer. If found, a Gtk::TextMark is returned. Else, nil.

Parameters:

  • string (String)

    Text to search.



177
178
179
180
181
182
# File 'lib/gtk_app/text_buffer.rb', line 177

def find(string)
  s_iter, e_iter, text_selected = selection_bounds
  s_iter = start_iter unless text_selected
  s_iter, e_iter = s_iter.forward_search(string, Gtk::TextIter::SEARCH_TEXT_ONLY, e_iter)
  s_iter ? create_mark(nil, s_iter, false) : nil
end

#format(tag_name, s_iter, e_iter) ⇒ Object

This is a small wrapper around the Gtk::TextBuffer#apply_tag method. It allows the Gtk::TextTag name to be passed as a symbol.

Parameters:

  • []

    tag_name

  • s_iter (Gtk::TextIter)
  • e_iter (Gtk::TextIter)


210
211
212
# File 'lib/gtk_app/text_buffer.rb', line 210

def format(tag_name, s_iter, e_iter)
  apply_tag(tag_name.to_s, s_iter, e_iter)
end

#format_selection(tag_name) ⇒ Object

Format text in the current selection range with a Gtk::TextTag identified by the given name.

Parameters:

  • tag_name (String)


199
200
201
202
# File 'lib/gtk_app/text_buffer.rb', line 199

def format_selection(tag_name)
  s_iter, e_iter, text_selected = selection_bounds
  format(tag_name, s_iter, e_iter) if text_selected
end

#redoObject

Pop the last action off the redo stack and apply the changes. If and action was performed, the cursor is placed at the actions starting Gtk::TextIter.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/gtk_app/text_buffer.rb', line 70

def redo
  if @redo_stack.empty?
    Gdk.beep
  else
    action = @redo_stack.pop
    s_iter = get_iter_at_offset(action[1])
    e_iter = get_iter_at_offset(action[2])
    case action[0]
    when :insert then insert(s_iter, action[3])
    when :delete then delete(s_iter, e_iter)
    end
    @undo_stack.push(action)
    place_cursor(s_iter)
  end
end

#replace(string, s_iter, e_iter) ⇒ Object

Parameters:

  • string (String)

    Text to replace the selection with.

  • s_iter (Gtk::TextIter)
  • e_iter (Gtk::TextIter)


187
188
189
190
191
192
193
# File 'lib/gtk_app/text_buffer.rb', line 187

def replace(string, s_iter, e_iter)
  begin_user_action
  delete(s_iter, e_iter)
  insert(s_iter, string)
  end_user_action
  place_cursor(s_iter)
end

#spelling_errors?Boolean

Does the Gtk::TextTagTable contain any ‘spell_error’ tags?

Returns:

  • (Boolean)


169
170
171
# File 'lib/gtk_app/text_buffer.rb', line 169

def spelling_errors?
  !tag_table.lookup('spell_error').nil?
end

#undoObject

Pop the last action off the undo stack and rewind changes. If an action was performed, the cursor is placed at the actions starting Gtk::TextIter.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/gtk_app/text_buffer.rb', line 52

def undo
  if @undo_stack.empty?
    Gdk.beep
  else
    action = @undo_stack.pop
    s_iter = get_iter_at_offset(action[1])
    case action[0]
    when :insert then delete(s_iter, get_iter_at_offset(action[2]))
    when :delete then insert(s_iter, action[3])
    end
    @redo_stack.push(action)
    place_cursor(s_iter)
  end
end

#view=(text_view) ⇒ Object

Parameters:



42
43
44
45
46
47
# File 'lib/gtk_app/text_buffer.rb', line 42

def view=(text_view)
  text_view.instance_eval do
    signal_connect('button-press-event', &:__button_press_event)
    signal_connect('populate-popup', &:__populate_popup)
  end
end

#word_at_cursorString

Retrieve the word at the current cursor position

Returns:

  • (String)

    The word.



88
89
90
# File 'lib/gtk_app/text_buffer.rb', line 88

def word_at_cursor
  get_text(*word_bounds).strip
end

#word_boundsArray

Determine the boudaries of the word at the current cursor position.

Returns:

  • (Array)

    The start and end iter.



94
95
96
97
98
99
100
101
# File 'lib/gtk_app/text_buffer.rb', line 94

def word_bounds
  iter = get_iter_at_offset(cursor_position)
  s_iter, e_iter = iter.clone, iter.clone
  s_iter.backward_word_start unless s_iter.starts_word?
  e_iter.forward_word_end unless e_iter.ends_word?

  [s_iter, e_iter]
end