Class: Ruber::FindInFiles::ReplaceWidget

Inherits:
OutputWidget show all
Defined in:
plugins/find_in_files/find_in_files_widgets.rb

Overview

Tool widget which displays the results of a replace operation

The widget consists of a checkable tree view with two columns. The first column contains the text which will be replaced, while the second contains the text after the replacement. The user can uncheck the replacements he doesn’t want, both linewise or filewise. The replacement is only carried out when the user presses the Replace button in the tool widget. A Clear button empties the tool widget.

Defined Under Namespace

Classes: Model

Constant Summary

Constants inherited from OutputWidget

IsTitleRole, OutputTypeRole

Instance Attribute Summary

Attributes inherited from OutputWidget

action_list, actions, auto_scroll, ignore_word_wrap_option, model, skip_first_file_in_title, view, working_dir

Instance Method Summary (collapse)

Methods inherited from OutputWidget

#copy, #copy_selected, #create_standard_actions, #create_widgets, #do_auto_scroll, #fill_menu, #find_filename_in_index, #find_filename_in_string, #has_title?, #hints, #load_settings, #maybe_open_file, #pinned_down?, #rows_changed, #scroll_to, #selection_changed, #set_color_for, #set_output_type, #setup_model, #show_menu, #text_for_clipboard, #title=, #update_index_color, #with_auto_scrolling

Methods included from GuiStatesHandler

#change_state, included, #initialize_states_handler, #register_action_handler, #remove_action_handler_for, #state

Signal Summary

Constructor Details

- (ReplaceWidget) initialize(parent = nil)

Creates a new instance

Parameters:

  • parent (Qt::Object, nil) (defaults to: nil)

    the parent object



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'plugins/find_in_files/find_in_files_widgets.rb', line 235

def initialize parent = nil
  super parent, :view => :tree, :use_default_font => true
  self.auto_scroll = false
  model.global_flags |= Qt::ItemIsUserCheckable.to_i
  def model.flags idx
    if idx.column == 0 then super idx
    else (Qt::ItemIsEnabled | Qt::ItemIsSelectable).to_i
    end
  end
  @replace_button = Qt::PushButton.new( 'Replace', self){self.enabled = false}
  @clear_button = Qt::PushButton.new('Clear', self){self.enabled = false}
  layout.remove_widget view
  layout.add_widget view, 0,0,1,0
  layout.add_widget @replace_button, 1, 0
  layout.add_widget @clear_button, 1, 1
  model.horizontal_header_labels = ['Line', 'Original text', 'Replacement text']
  @file_items = {}
  @watcher = KDE::DirWatch.new self
  connect @watcher, SIGNAL('dirty(QString)'), self, SLOT('file_modified(QString)')
  connect @replace_button, SIGNAL(:clicked), self, SLOT(:replace)
  connect @clear_button, SIGNAL(:clicked) , self, SLOT(:clear_output)
  self.connect(SIGNAL(:about_to_fill_menu)) do
    actions.delete 'copy'
    actions.delete 'copy_selected'
    action_list.delete 'copy'
    action_list.delete 'copy_selected'
  end
  model.connect(SIGNAL('rowsInserted(QModelIndex, int, int)')) do |par, st, en|
    if !par.valid?
      st.upto(en) do |i|
        view.set_first_column_spanned i, par, true
        view.expand model.index(i, 0, par)
      end
    end
  end
  Ruber[:find_in_files].connect SIGNAL(:replace_search_started) do
    @replace_button.enabled = false
    @clear_button.enabled = false
    view.header.resize_mode = Qt::HeaderView::Fixed
    self.cursor = Qt::Cursor.new Qt::WaitCursor
  end
  Ruber[:find_in_files].connect SIGNAL(:replace_search_finished) do
    @watcher.start_scan
    @replace_button.enabled = true
    @clear_button.enabled = true
    h = view.header
    view.resize_column_to_contents 0
    view.header.resize_mode = Qt::HeaderView::Fixed
    av_size = h.rect.width - h.section_size( 0)
    view.set_column_width 1,  av_size / 2.0
    view.set_column_width 2, av_size / 2.0
    unset_cursor
  end
  view.all_columns_show_focus =  true
end

Instance Method Details

- (nil) add_line(file, line, orig, repl)

Adds a replacement line to the widget

A replacement line is made of three columns: the line number in the file, the original text and the text after the replacement.

Parameters:

  • file (String)

    the file where the line is

  • line (Integer)

    the line number (0 based)

  • orig (String)

    the original text of the line

  • repl (String)

    the text of the line after the replacement

Returns:

  • (nil)


324
325
326
327
328
329
330
# File 'plugins/find_in_files/find_in_files_widgets.rb', line 324

def add_line file, line, orig, repl
  parent = @file_items[file]
  row = model.insert [(line+1).to_s, orig, repl], [:output1, :output, :output], nil, :parent => parent
  row[0].checked = true
  view.expand parent.index
  nil
end

- (nil) clear_output

Empties the widget

Returns:

  • (nil)


337
338
339
340
341
342
343
# File 'plugins/find_in_files/find_in_files_widgets.rb', line 337

def clear_output
  @file_items.clear
  @file_items.each_key{|k| @watcher.remove_file k}
  @replace_button.enabled = false
  @clear_button.enabled = false
  super
end

- (nil) display_output(lines)

Inserts the name of the files in the output widget

Each file is added to the file watcher, so that replacing in it can be disabled if it changes.

Parameters:

  • lines (<String>)

    an array containing the name of the files

Returns:

  • (nil)


300
301
302
303
304
305
306
307
308
309
310
# File 'plugins/find_in_files/find_in_files_widgets.rb', line 300

def display_output lines
  lines.each do |l|
    it = model.insert([l, nil, nil], :message, nil)[0]
    it.checked = true
    @watcher.add_file l
    @watcher.stop_scan
    @file_items[l] = it
    emit file_added(l)
  end
  nil
end

- (nil) file_modified(file) (private)

Slot called when a file among those listed for replacement is modified on disk

When this happens, the file is marked as modified in the view and it is no longer checkable (the same happens for its children)

Returns:

  • (nil)


445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'plugins/find_in_files/find_in_files_widgets.rb', line 445

def file_modified file
  @watcher.remove_file file
  it = @file_items[file]
  if it
    it.text = it.text + "\t [MODIFIED]"
    it.checked = false
    view.collapse it.index
    model.set_data it.index, Qt::Variant.new, Qt::ForegroundRole
    it.flags = Qt::ItemIsSelectable
    it.each_row do |r|
      r[0].checked = false
      r.each{|i| i.flags = Qt::ItemIsSelectable}
    end
  end
  nil
end

Slot Signature:

file_modified(QString)

- (nil) replace (private)

Performs the replacements chosen by the user

Calling this method applies replaces the original text with the replacement texts for all the lines chosen by the user (that is, the checked lines which belong to a checked file). A message box is shown if some replacements cannot be carrried out.

Items corresponding to successful replacements are removed from the view.

Returns:

  • (nil)


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
# File 'plugins/find_in_files/find_in_files_widgets.rb', line 359

def replace
  failed = {}
  success = []
  docs = Ruber[:world].documents.documents_with_file.map{|d| [d.path, d]}.to_h
  model.each_row.each_with_index do |r, i|
    if r[0].checked?
      res = replace_file r[0], docs[r[0].text]
      if res then failed[r[0].text] = res 
      else success << r[0]
      end
    end
  end
  success.reverse_each do |i| 
    @file_items.delete i.text
    model.remove_row i.row
  end
  create_error_message = Proc.new do |f, err|
    if err == :doc_modified then "#{f}: modified in editor"
    else "#{f}: #{err.message}"
    end
  end
  unless failed.empty?
    failed_text = failed.map{|f, err| create_error_message.call f, err}.join "\n"
    KDE::MessageBox.sorry Ruber[:main_window], "The following files couldn't be modified:\n#{failed_text}"
  end
  nil
end

Slot Signature:

replace()

- (Symbol, ...) replace_file(it, doc) (private)

Carries out the replacements for a file

If the file is associated with a document and the document isn’t modified, the text in the editor is changed to reflect the modifications in the file. If the document is modified (which means its contents differ from the contents of the file), instead, nothing will be done.

correctly; an exception derived from SystemCallError if it wasn’t possible to write the file and the symbol :doc_modified if the replacement wasn’t attempted because the document corresponding to the file was modified

Parameters:

  • it (Qt::StandardItem)

    the item corresponding to the file in the model

  • doc (Document, nil)

    the document associated with the file, if any

Returns:

  • (Symbol, SystemCallError, nil)

    nil if the replacement was carried out



402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'plugins/find_in_files/find_in_files_widgets.rb', line 402

def replace_file it, doc
  file = it.text
  lines_to_replace = {}
  it.each_row do |line, _, repl|
    lines_to_replace[line.text.to_i] = repl.text if line.checked?
  end
  lines = File.readlines(file)
  lines_to_replace.each_pair{|idx, text| lines[idx - 1] = text + "\n"}
  new_text = lines.join ''
  if doc
    pos = doc.view.cursor_position if doc.view
    return :doc_modified if doc.modified?
    text = nil
    doc.editing do
      text = doc.text
      doc.clear
      doc.text = new_text
    end
    doc.save
    #If the document doesn't have a view, pos will be nil
    doc.view.go_to pos.line, pos.column if pos
  else
    Tempfile.open(File.basename(file)) do |f|
      f.write new_text
      f.flush
      begin 
        FileUtils.cp f.path, file
      rescue SystemCallError => e
        return e
      end
    end
  end
  nil
end

Signal Details

- file_added(QString str)

Signal emitted when a new file is added to the model.

Parameters:

  • str (String)

    the name of the file