Class: Ruber::MainWindow

Inherits:
KParts::MainWindow
  • Object
show all
Extended by:
Forwardable
Includes:
GuiStatesHandler, PluginLike
Defined in:
lib/ruber/main_window/main_window.rb,
lib/ruber/main_window/main_window_actions.rb,
lib/ruber/main_window/main_window_internal.rb

Overview

The application’s main window. It is made of a menu bar, a tool bar, a workspace and a status bar. The workspace (see Workspace) is the main window’s central widget and where most of the user interaction happens. It contains the editors and the tool widgets.

Constant Summary collapse

OPEN_DLG_FILTERS =

A list of strings to be used to create the filter for open file dialogs

[
'*.rb|Ruby source file (*.rb)', 
'*.yaml *.yml|YAML files (*.yaml, *.yml)',
'*|All files'
]

Instance Attribute Summary collapse

Attributes included from PluginLike

#plugin_description

Instance Method Summary collapse

Methods included from GuiStatesHandler

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

Methods included from PluginLike

#add_extensions_to_project, #add_options_to_project, #add_widgets_to_project, #plugin_name, #register_with_project, #remove_extensions_from_project, #remove_from_project, #remove_options_from_project, #remove_widgets_from_project, #restore_session, #session_data, #shutdown, #unload, #update_project

Constructor Details

#initialize(_manager, pdf) ⇒ MainWindow

Creates a new MainWindow. _manager is the component manager, while pdf is the plugin description for this object.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
# File 'lib/ruber/main_window/main_window.rb', line 90

def initialize _manager, pdf
  super nil, 0
  initialize_plugin pdf
  initialize_states_handler
  self.central_widget = Workspace.new self
  # We need the instance variable to use it with Forwardable
  @workspace = central_widget 
  @views = central_widget.instance_variable_get :@views
  @current_view = nil
  @active_editor = nil
  @auto_activate_editors = true
  @editors_mapper = Qt::SignalMapper.new self
  @ui_states = {}
  @actions_state_handlers = Hash.new{|h, k| h[k] = []}
  @about_plugin_actions = []
  @last_session_data = nil
  self.status_bar = StatusBar.new self
  self.connect(SIGNAL('current_document_changed(QObject*)')) do |doc|
    change_state 'current_document', !doc.nil_object?
  end
  connect Ruber[:components], SIGNAL('component_loaded(QObject*)'), self, SLOT('add_about_plugin_action(QObject*)')
  connect Ruber[:components], SIGNAL('unloading_component(QObject*)'), self, SLOT('remove_about_plugin_action(QObject*)')
  connect Ruber[:components], SIGNAL('unloading_component(QObject*)'), self, SLOT('remove_plugin_ui_actions(QObject*)')
  connect @editors_mapper, SIGNAL('mapped(QWidget*)'), self, SLOT('remove_view(QWidget*)')
  setup_actions action_collection
  @views.connect(SIGNAL('currentChanged(int)')) do |idx|
    if @auto_activate_editors then activate_editor idx 
    else activate_editor nil
    end
  end
  Ruber[:projects].connect( SIGNAL('current_project_changed(QObject*)') ) do |prj|
    change_state "active_project_exists", !prj.nil?
    make_title
  end
  connect Ruber[:projects], SIGNAL('closing_project(QObject*)'), self, SLOT('close_project_files(QObject*)')
  
  setup_GUI
  create_shell_GUI true
  
  config_obj = Ruber[:config].kconfig
  apply_main_window_settings KDE::ConfigGroup.new( config_obj, 'MainWindow')
  recent_files = KDE::ConfigGroup.new config_obj, 'Recent files'
  action_collection.action("file_open_recent").load_entries recent_files
  recent_projects = KDE::ConfigGroup.new config_obj, 'Recent projects'
  action_collection.action("project-open_recent").load_entries recent_projects
  status_bar.show
  setup_initial_states
end

Instance Attribute Details

#last_session_dataObject (readonly)

A hash with the data stored in the session manager by plugins. The hash is only availlable after the restore method has been called (which means that it will always be unavaillable if ruber wasn’t started by the session manager). If the hash isn’t availlable, nil is returned

NOTE: only for use by Application when restoring the session



84
85
86
# File 'lib/ruber/main_window/main_window.rb', line 84

def last_session_data
  @last_session_data
end

#workspaceObject (readonly)

The widget which contains the tool widgets.

The primary use of this method is to connect to the signals emitted from the workspace when a tool widget is raised, shown or hidden. All of its other methods are also provided by the MainWindow, so you shouldn’t need to access the workspace to use them.



74
75
76
# File 'lib/ruber/main_window/main_window.rb', line 74

def workspace
  @workspace
end

Instance Method Details

#activate_editor(arg) ⇒ Object

Activates an editor. arg can be either the index of a widget in the tab widget (for internal uses only) or the editor to activate, or nil. If the editor to activate is not in the current tab, the tab which contains it will become the current one. Activating an editor deactivates the previously active one.

Activating an editor means merging its GUI with the main window’s GUI, changing the title of the main window accordingly and telling the status bar to refer to that view. Also, the current_document_changed and active_editor_changed signals are emitted.



246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/ruber/main_window/main_window.rb', line 246

def activate_editor arg
  view = arg.is_a?(Integer) ? @views.widget( arg ) : arg
  @views.current_widget = view if view and @views.current_widget != view
  deactivate_editor @active_editor if @active_editor != view
  @active_editor = view
  if @active_editor
    gui_factory.add_client @active_editor.doc.view.send(:internal)
    @active_editor.document.activate
  end
  make_title
  status_bar.view = @active_editor
  emit current_document_changed @active_editor ?  @active_editor.document : Qt::NilObject
  emit active_editor_changed @active_editor || Qt::NilObject
end

#active_editorObject

Returns the active editor, or nil if there’s no active editor.

Note: strictly speaking, this is not the same as tab_widget.current_widget. For an editor to be active, it’s not enough to be in the current tab, but it also need to having been its gui merged with the main window gui.

TODO: in all places this is called, change the name from current_view to active_editor.



223
224
225
# File 'lib/ruber/main_window/main_window.rb', line 223

def active_editor
  @active_editor
end

#close_editor(ed, ask = true) ⇒ Object

Closes the editor ed, deleting its tab and closing the corresponding document and activating the next tab if auto activating is on. If ask is true, the document will ask whether to save modifications, otherwise it won’t



324
325
326
# File 'lib/ruber/main_window/main_window.rb', line 324

def close_editor ed, ask = true
  ed.document.close_view ed, ask
end

#current_documentObject

Returns the document associated with the active editor or nil if there’s no

current editor.

It's almost the same as <tt>active_editor.doc</tt>, but already takes care of
the case when <tt>active_editor</tt> is *nil*.


268
269
270
271
# File 'lib/ruber/main_window/main_window.rb', line 268

def current_document
  view = @views.current_widget
  view ? view.doc : nil
end

#current_editorObject

Returns the editor in the current tab, or nil if there’s no tab. Note that this may not be the active editor.



231
232
233
# File 'lib/ruber/main_window/main_window.rb', line 231

def current_editor
  @views.current_widget
end

#display_doc(doc, line = nil, col = 0) ⇒ Object Also known as: display_document

Similar to editor_for! but, after having retrieved/created the editor view, it activates and gives focus to it and moves the cursor to the line line and column col (both of them are 0-based indexes).



278
279
280
281
282
283
284
# File 'lib/ruber/main_window/main_window.rb', line 278

def display_doc doc, line = nil, col = 0
  ed = editor_for! doc
  return unless ed
  activate_editor ed
  ed.go_to line, col if line and col
  ed.set_focus
end

#editor_for(doc) ⇒ Object

Similar to editor_for! but it doesn’t create the document or the editor if they don’t exist. If a Document for the string doc doesn’t exist, or if it doesn’t have an editor, returns nil.



207
208
209
210
211
# File 'lib/ruber/main_window/main_window.rb', line 207

def editor_for doc
  doc = Ruber[:documents][doc] if doc.is_a? String
  return unless doc
  doc.view
end

#editor_for!(doc) ⇒ EditorView?

Returns the editor view for the given document, creating it if needed

Parameters:

Returns:

  • (EditorView, nil)

    an editor associated with the document or nil if doc is interpreted as document name but no documents with that name exists

Raises:

  • (ArgumentError)

    if doc is an absolute path or a @KDE::Url@ but the file doesn’t exist



192
193
194
195
196
197
198
199
200
# File 'lib/ruber/main_window/main_window.rb', line 192

def editor_for! doc
  unless doc.is_a? Document
    if doc.is_a? KDE::Url or doc.start_with? '/' then doc = Ruber[:documents].document doc
    else doc = Ruber[:documents].document_with_name doc
    end
  end
  return unless doc
  create_editor_if_needed doc
end

#execute_action(name, *args) ⇒ Object

Executes the action with name action contained in the main window’s action collection.

See GuiPlugin#execute_action for more details



504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
# File 'lib/ruber/main_window/main_window.rb', line 504

def execute_action name, *args
  data = plugin_description.actions[name.to_s]
  if data
    slot = data.slot.sub(/\(.*/, '')
    instance_eval(data.receiver).send slot, *args
    true
  elsif (action = action_collection.action(name))
    if action.class == KDE::ToggleAction then KDE::ToggleAction
      action.instance_eval{emit toggled(*args)}
    elsif action.class == KDE::Action 
      action.instance_eval{emit triggered()}
    else action.instance_eval{emit triggered(*args)}
    end
    true
  else false
  end
end

#focus_on_editor(ed = nil) ⇒ Object

Gives the focus to the current editor view. ed can be:

  • an EditorView

  • any argument acceptable by editor_for!

  • nil

In the first two cases, the editor corresponding to ed is activated and given focus; in the last case the active editor is given focus.



477
478
479
480
481
482
483
# File 'lib/ruber/main_window/main_window.rb', line 477

def focus_on_editor ed = nil
  if ed
    ed = editor_for! ed unless ed.is_a? EditorView
    activate_editor ed if ed
  end
  @active_editor.set_focus if @active_editor
end

#load_settingsObject

Loads the settings (in particular, the default script directory and the default project directory)



463
464
465
466
467
# File 'lib/ruber/main_window/main_window.rb', line 463

def load_settings
  c = Ruber[:config][:general]
  @default_script_dir = KDE::Url.from_path c.default_script_directory
  @default_project_dir = KDE::Url.from_path c.default_project_directory
end

#projects_directoryObject

The directory where to create new projects by default



495
496
497
# File 'lib/ruber/main_window/main_window.rb', line 495

def projects_directory
  @default_project_dir
end

#query_closeObject

Asks the user whether to save the modified documents and saves the chosen ones. Returns true all the selected documents where saved and false if some of the document couldn’t be saved. MOVE: this should be moved to DocumentList, as there might be documents without a window



389
390
391
392
393
394
# File 'lib/ruber/main_window/main_window.rb', line 389

def query_close
  if Ruber[:app].session_saving
    @session_data = Ruber[:components].session_data
  end
  true
end

#safe_open_project(file, allow_reuse = false) ⇒ Object

Provides a standard interface to creating a project from a project file named file, automatically handling the possible exceptions. In particular, a message box will be displayed if the project file doesn’t exist or if it exists but it’s not a valid project file. A message box is also displayed if allow_reuse is false and the project list already contains a project corresponding to file. In all cases in which a message box is displayed nil is returned. Otherwise, the Project object corresponding to file is returned.

If a project was created from the project file, it will be made active and the old active project (if any) will be closed.

Note: file must be an absolute path.



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 'lib/ruber/main_window/main_window.rb', line 410

def safe_open_project file, allow_reuse = false
  prj = Ruber[:projects][file]
  if !allow_reuse and prj
    text = "A project corresponding to the file #{file} is already open. Please, close it before attempting to open it again"
    KDE::MessageBox.sorry self, KDE.i18n(text)
    return nil
  elsif prj then return prj
  end
  begin prj = Ruber[:projects].project file
  rescue Project::InvalidProjectFile => ex
    text = <<-EOS
#{file} isn't a valid project file. The error reported was:
#{ex.message}
    EOS
    KDE::MessageBox.sorry self, KDE.i18n(text)
    return nil
  rescue LoadError
    KDE::MessageBox.sorry self, KDE.i18n(ex.message)
    return nil
  end
# The following two lines should be removed when we'll allow more than one project
# open at the same time
  Ruber[:projects].current_project.close if Ruber[:projects].current_project
  Ruber[:projects].current_project = prj
  prj
end

#save_documents(docs = nil) ⇒ Object

This method is meant to be called in situation where the user may want to save

a number of documents, for example when the application is quitting, as it avoids
displaying a dialog box for each modified document.

_docs_ can be either an array containing the documents which can be saved (documents
in it which aren't modified will be ignored) or *nil*. In this case, all the
open documents (with or without an open editor) will be taken into account.

It displays a dialog where the user can choose, among the documents passed as
first argument, which ones he wants to save. The user has three choiches:
* save some (or all) the files, then proceed with the operation which has caused
  the dialog to be shown (for example, quitting the application)
* don't save any file and go on with the operation
* abort the operation.

In the first case, this method attempts to perform save the selected files. If
any of them can't be saved, the dialog to choose the files to save will be
displayed again, with only those files which couldn't be saved (after informing
the user of the failure). The user can again chose which of those files this method
should attempt to save again, or whether to abort the operation or skip saving.
This will be repeated until all files have been saved or the user gives up

In the second and third cases, the method simply returns respectively +true+ or
*false*.

The value returned by this method is *false* if the user choose to abort the
operation and *true* otherwise. Calling methods should act appropriately (for
example, if this is called from a method which closes all documents and returns
*false*, the documents should't be closed. If it returns true, instead, they
should be closed, without asking again to save them).

<b>Note:</b> if _docs_ only contains non-modified documents, this method will
do nothing and immediately return *true*.


363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/ruber/main_window/main_window.rb', line 363

def save_documents docs = nil
  docs ||= Ruber[:docs]
  to_save = docs.select{|d| d.modified?}
  until to_save.empty?
    dlg = SaveModifiedFilesDlg.new to_save, self
    case dlg.exec
    when KDE::Dialog::Yes
      to_save = Ruber[:docs].save_documents dlg.to_save
      unless to_save.empty?
        msg = "The following documents couldn't be saved: #{to_save.join "\n"}\nPlease, choose how to proceed"
        KDE::MessageBox.sorry nil, KDE.i18n(msg)
      end
    when KDE::Dialog::No then to_save.clear
    when Qt::Dialog::Rejected then return false
    end
  end
  true
end

#save_settingsObject

Saves recent files and projects, the position of the splitters and the size of the window.

TODO: since restoring window position seems to work correctly in Kate (even if there’s still an open bug, it seems to work, at least for me) see whether it’s still necessary to store the window size.



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

def save_settings
  @workspace.store_sizes
# TODO use Ruber[:config] as soon as it works
#       config = Ruber[:config].kconfig
  config = KDE::Global.config
  recent_files = KDE::ConfigGroup.new config, 'Recent files'
  action_collection.action("file_open_recent").save_entries recent_files
  recent_projects = KDE::ConfigGroup.new config, 'Recent projects'
  action_collection.action("project-open_recent").save_entries recent_projects
  config.sync
  c = Ruber[:config][:main_window]
  c.window_size = rect.size
end

#without_activatingObject

Executes the contents of the block with automatical editor activation disabled. This should be used, for example, when more than one editor should be opened at once. Without this, every new editor would become (for a little time) the active one, with its gui merged with the main window and so on. This slows things down and should be avoided. To do so, you use this method:

Ruber[:main_window].without_activating do
  ed = nil
  files.each do |f|
    ed = Ruber[:main_window].editor_for! f
  end
  Ruber[:main_window].activate_editor ed
end

After the block has been called, if there is no editor, or if the current widget in the tab widget is different from the active editor, the current widget will be activated. Note: automatical editor activation will be restored at the end of this method (even if exceptions occur).



306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/ruber/main_window/main_window.rb', line 306

def without_activating
  begin
    @auto_activate_editors = false
    yield
  ensure 
    @auto_activate_editors = true
    if @views.current_index < 0 then activate_editor nil
    elsif @views.current_widget != @active_editor
      activate_editor @views.current_widget
    end
  end
end