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

DEFAULT_HINTS =
Ruber::World::Environment::DEFAULT_HINTS
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.



88
89
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
138
139
140
141
142
143
144
# File 'lib/ruber/main_window/main_window.rb', line 88

def initialize _manager, pdf
  super nil, 0
  initialize_plugin pdf
  initialize_states_handler
  @active_environment = nil
  @workspace = Workspace.new self
  self.central_widget = @workspace
  @workspace.add_widget Ruber[:world].default_environment.tab_widget
  @ui_states = {}
  @actions_state_handlers = Hash.new{|h, k| h[k] = []}
  @about_plugin_actions = []
  @switch_to_actions = []
  @activate_project_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?
  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 Ruber[:world], SIGNAL('active_environment_changed_2(QObject*, QObject*)'), self, SLOT('slot_active_environment_changed(QObject*, QObject*)')
  connect Ruber[:world], SIGNAL('document_created(QObject*)'), self, SLOT('document_created(QObject*)')
  connect Ruber[:world], SIGNAL('closing_document(QObject*)'), self, SLOT(:update_switch_to_list)

  setup_actions action_collection
  active_project_action = action_collection.action('project-active_project')
  default_view_action = active_project_action.add_action '&None (single files mode)'
  
  connect Ruber[:world], SIGNAL('project_created(QObject*)'), self, SLOT('slot_project_created(QObject*)')
  connect Ruber[:world], SIGNAL('closing_project(QObject*)'), self, SLOT('slot_project_closing(QObject*)')
  
  Ruber[:world].connect SIGNAL('active_project_changed(QObject*)') do |prj|
    @active_environment = Ruber[:world].environment prj
  end
  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'
  open_recent_action =action_collection.action("file_open_recent")
  open_recent_action.load_entries recent_files
  switch_to_recent_action = action_collection.action("window-switch_to_recent_file")
  switch_to_recent_action.load_entries recent_files
  
  # Synchronize the two menus, so that when the user clears one of them the also
  # is also cleared
  connect open_recent_action, SIGNAL(:recentListCleared), switch_to_recent_action, SLOT(:clear)
  connect switch_to_recent_action, SIGNAL(:recentListCleared), open_recent_action, SLOT(:clear)
  
  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
  Ruber[:world].active_environment = Ruber[:world].default_environment
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



82
83
84
# File 'lib/ruber/main_window/main_window.rb', line 82

def last_session_data
  @last_session_data
end

#workspaceWorkspace (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.

Returns:

  • (Workspace)

    the workspace associated with the main window



72
73
74
# File 'lib/ruber/main_window/main_window.rb', line 72

def workspace
  @workspace
end

Instance Method Details

#activate_editor(editor) ⇒ Object

Activates an editor

If the editor is not in the current tab, the tab it belongs to becomes active.

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.

After activating the editor, the #current_document_changed and #active_editor_changed signals are emitted.

Parameters:

  • editor (EditorView, nil)

    the editor to activate. If nil, then the currently active editor will be deactivated



371
372
373
# File 'lib/ruber/main_window/main_window.rb', line 371

def activate_editor editor
  @active_environment.activate_editor editor
end

#active_documentObject Also known as: current_document

The document associated with the active editor



427
428
429
# File 'lib/ruber/main_window/main_window.rb', line 427

def active_document
  (ed = active_editor) ? ed.document : nil
end

#active_editorEditorView? Also known as: current_editor

The active editor

Returns:

  • (EditorView, nil)

    the active editor or nil if no active editor exists

See Also:



352
353
354
# File 'lib/ruber/main_window/main_window.rb', line 352

def active_editor
  @active_environment.active_editor
end

#close_editor(editor, ask = true) ⇒ Object

Note:

Always use this method to close an editor, rather than calling its close method directly, unless you want to leave the corresponding document without a view

Closes an editor view



501
502
503
504
505
506
507
508
509
# File 'lib/ruber/main_window/main_window.rb', line 501

def close_editor editor, ask = true
  @active_environment.close_editor editor, ask
#       doc = editor.document
#       if doc.views.size > 1 
#         editor.close
#         true
#       else doc.close ask
#       end
end

#display_document(doc, hints = {}) ⇒ EditorView? Also known as: display_doc

Displays an editor for the given document

This method is similar to #editor_for! but, after retrieving the editor (creating it and/or the document as needed), it activates and gives focus to it and moves the cursor to the given position.

Besides the keys listed in #editor_for!, hints can also contain the two entries @:line@ and @:column@.

Parameters:

  • doc (Document, String, KDE::Url)

    the document. If it is a string representing a relative path, it’ll be considered relative to the current directory. If the string is an Url, it’ll be interpreted as such

  • hints (Hash) (defaults to: {})

    options to tailor the algorithm used to retrieve or create the editor

Options Hash (hints):

  • line (Integer, nil) — default: nil

    the line to move the cursor to. If nil, the cursor won’t be moved

  • column (Integer) — default: 0

    the column to move the cursor to. Ignored if the @:line@ entry is nil

Returns:

  • (EditorView, nil)

    the editor which has been activated or nil if the editor couldn’t be found (or created)

See Also:



451
452
453
454
455
456
457
458
459
460
# File 'lib/ruber/main_window/main_window.rb', line 451

def display_document doc, hints = {}
  @active_environment.display_document doc, hints
#       ed = editor_for! doc, hints
#       return unless ed
#       activate_editor ed
#       line = hints[:line]
#       ed.go_to line, hints[:column] || 0 if line
#       ed.set_focus
#       ed
end

#editor_for!(doc, hints = {}) ⇒ EditorView?

Returns an editor associated with the given document, creating it if needed

If doc is not a document and no document for the file representing by the string or URL doc is open, it will be created.

Since there can be more than one editor associated with the document, the optional hints argument can be used to specify which one should be returned. If no editor exists for the document, or none of the exisiting ones statisfy the @existing@ hint, a new one will be created, unless the @create_if_needed@ hint is false.

Parameters:

  • doc (Document, String, KDE::Url)

    the document. If it is a string representing a relative path, it’ll be considered relative to the current directory. If the string is an Url, it’ll be interpreted as such

  • hints (Hash) (defaults to: {})

    options to tailor the algorithm used to retrieve or create the editor

Options Hash (hints):

  • :existing (Symbol) — default: :always

    when it is acceptable to return an already existing editor. It can be:

    • @:always@: if there’s an existing editor, always use it

    • @:never@: never use an exisiting editor

    • @:current_tab@: use an exisiting editor only if it is in the current pane

  • :create_if_needed (Boolean) — default: true

    whether or not a new editor for the given document should be created if none exists. This is different from passing @:always@ as the value of the @:existing@ option because the latter would cause a new editor to be created in case no one already exists for the document

  • :strategy (Symbol, Array<Symbol>) — default: [:current, :current_tab, :first]

    how to choose the exisiting editor to use in case there’s more than one. If it is an array, the strategies will be applied in turn until one has success. If none of the given strategies succeed, @:first@ will be used. The possible values are

    • @:current@: if the current editor is associated with doc, use it

    • @:current_tab@: if there are editors associated with doc in the current

    tab, the first of them will be used

    • @:last_current_tab@: if there are editors associated with doc in the current

    tab, the first of them will be used

    • @:next@: starting from the current tab, the first editor found associated

    with doc will be used

    • @:previous@: the first editor associated with doc found by looking in all

    tabs from the current one in reverse order will be used (the current tab will be the last one where the editor will be looked for)

    • @:first@: the first editor associated with the document will be used

    • @:last@: the last editor associated the document will be used

    This option is only useful when using an existing editor

  • :new (Symbol, Integer, EditorView) — default: :new_tab

    where to place the new editor, if it needs to be created. It can have one of the following values:

    • @:new_tab@: put the new editor as the single editor in a new tab

    • @:current@: place the new editor in the pane obtained splitting the

    current editor

    • @:current_tab@: place the new editor in the pane obtained splitting the first

    editor in the current tab

    • @:replace@: replace the current editor, if any, with the new one. If no active editor exists, it’s the same as @:new_tab@. This value (if there’s an active editor), implies that the @:existing@ option is set to @:never@

    • an integer: place the new editor in the tab associated with that index (0-based),

    in the pane obtained by splitting the first view

    • an EditorView: place the new editor in the pane obtained splitting the

    pane associated with the given editor

    This option is only used when when creating a new editor

  • :split (Symbol) — default: :horizontal

    the orientation in which an existing editor should be split to accomodate the new editor. It can be @:horizontal@ or @:vertical@. This option is only used when when creating a new editor

  • :show (Boolean) — default: true

    whether the new editor should be shown or not. If it’s false, the editor won’t be placed in any pane. This option is only used when when creating a new editor

Returns:

  • (EditorView, nil)

    an editor associated with the document and nil if no editor associated with the given document exists and the @:create_if_needed@ hint is set to false.

Raises:

  • (ArgumentError)

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



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/ruber/main_window/main_window.rb', line 280

def editor_for! doc, hints = {}
  @active_environment.editor_for! doc, hints
#       hints = DEFAULT_HINTS.merge hints
#       if hints[:new] == :replace
#         if active_editor
#           hints[:existing] = :never
#           hints[:show] = false
#         else hints[:new] = :new_tab
#         end
#       end
#       docs = Ruber[:documents].documents
#       unless doc.is_a? Document
#         unless hints.has_key? :close_starting_document
#           hints[:close_starting_document] = docs.size == 1 && 
#               docs[0].extension(:ruber_default_document).default_document && 
#               docs[0].pristine?
#         end
#         url = doc
#         if url.is_a? String
#           url = KDE::Url.new url
#           if url.relative?
#             path = File.expand_path url.path
#             url.path = path
#           end
#         end
#         doc = Ruber[:documents].document url
#       end
#       return unless doc
#       ed = @view_manager.without_activating{@view_manager.editor_for doc, hints}
#       if hints[:new] == :replace
#         replace_editor active_editor, ed
#       else ed
#       end
end

#execute_action(name, *args) ⇒ Object

Executes a given action

Parameters:

  • name (String)

    the name by which the action has been registered in the main window’s @action_collection@

  • args (Array)

    a list of parameters to pass to the signal or slot associated to the action

See Also:



717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
# File 'lib/ruber/main_window/main_window.rb', line 717

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_editorObject

def focus_on_editor ed = nil, hints = DEFAULT_HINTS

  if ed
    ed = editor_for! ed, hints unless ed.is_a? EditorView
    activate_editor ed
    ed.set_focus
  else active_editor.set_focus if active_editor
  end
  active_editor
end


694
695
696
697
# File 'lib/ruber/main_window/main_window.rb', line 694

def focus_on_editor
  active_editor.set_focus if active_editor
  active_editor
end

#load_settingsnil

Override of PluginLike#load_settings

Returns:

  • (nil)


652
653
654
655
656
657
658
# File 'lib/ruber/main_window/main_window.rb', line 652

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
  @tabs.tabs_closable = Ruber[:config][:workspace, :close_buttons] if @tabs
  nil
end

#projects_directoryString

Returns the default directory where to look for, and create, projects.

Returns:

  • (String)

    the default directory where to look for, and create, projects



703
704
705
# File 'lib/ruber/main_window/main_window.rb', line 703

def projects_directory
  @default_project_dir
end

#query_closeTrueClass

Override of PluginLike#query_close

It stores the session data retrieved by the component manager in case of session saving and does nothing otherwise.

Returns:

  • (TrueClass)


568
569
570
571
572
573
# File 'lib/ruber/main_window/main_window.rb', line 568

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

#replace_editor(old, new_ed) ⇒ EditorView? #replace_editor(old, doc) ⇒ EditorView?

Replaces an editor with another

If the editor to be replaced is the only one associated witha given document and the document is modified, the user is asked whether he wants to save it or not. If he chooses to abort, the editor is not replaced, otherwise the document is closed.

The new editor is put in the same pane which contained the old one (without splitting it).

Overloads:

  • #replace_editor(old, new_ed) ⇒ EditorView?

    Parameters:

    Returns:

  • #replace_editor(old, doc) ⇒ EditorView?
    Note:

    whether or not the user needs to be asked about saving the document is determined regardless of the value of doc. This means that the user may be asked to save the document even if doc is the same document associated with the view (or the URL corresponding to it). To avoid this, create the replacement editor before calling this method, using @editor_for doc, :existing => :never, :show => false@, then use the overloaded version of this method which takes an editor

    Parameters:

    • old (EditorView)

      the editor to replace

    • doc (Document, String, KDE::Url)

      the document to create the editor for or the name of the corresponding file (absolute or relative to the current directory) or the corresponding URL

    Returns:

Returns:

  • (EditorView, nil)

    the new editor or nil if the user choose to abort



403
404
405
# File 'lib/ruber/main_window/main_window.rb', line 403

def replace_editor old, editor_or_doc
  @active_environment.replace_editor old, editor_or_doc
end

#safe_open_project(file, allow_reuse = false) ⇒ Project?

Opens a project, displaying amessage boxe in case of errors

This method provides a standard interface for creating a project from a project file named, 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.

If the project corresponding to the given file is already open, the behaviour depends on the value of allow_reuse. If false, the user is warned with a message box that the project is already open and nothing is done. If allow_reuse is true, the existing project is returned.

The new project will be made active and the existing one (if any) will be closed

Parameters:

  • file (String)

    the absolute path of the project file to open

  • allow_reuse (Boolean) (defaults to: false)

    what to do in case the project associated to file is already open. If true, the existing project will be returned; if false, the user will be warned and nil will be returned

Returns:

  • (Project, nil)

    the project associated with file or nil if an error occurs (including the project being already open in case allow_reuse is false)



597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
# File 'lib/ruber/main_window/main_window.rb', line 597

def safe_open_project file, allow_reuse = false
  prj = Ruber[:world].projects[file]
  if !allow_reuse and prj
    text = i18n("A project corresponding to the file %s is already open. Please, close it before attempting to open it again" % file)
    KDE::MessageBox.sorry self, KDE.i18n(text)
    return nil
  elsif prj then return prj
  end
  message = nil
  begin prj = Ruber[:world].project file
  rescue Project::InvalidProjectFile => ex
    text = "%s isn't a valid project file. The error reported was:\n%s"
    message = i18n(text) % [file, ex.message]
  rescue LoadError then message = i18n(ex.message)
  end
  if prj
    # The following two line 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[:world].active_project = prj
    prj
  else
    KDE::MessageBox.sorry self, message
    nil
  end
end

#save_documents(docs = nil) ⇒ Boolean

Asks the user to save multiple documents

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.

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.

Parameters:

  • docs (Array<Document>) (defaults to: nil)

    the list of documents to save. If any document isn’t modified, it’ll be ignored. If no document is mdified, nothing will be done

Returns:

  • (Boolean)

    true if the operation which caused the dialog to be shown can be carried on and false if the user chose to abort it



540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
# File 'lib/ruber/main_window/main_window.rb', line 540

def save_documents docs = nil
  docs ||= Ruber[:world].documents
  to_save = docs.select{|d| d.modified?}
  until to_save.empty?
    dlg = SaveModifiedFilesDlg.new to_save, self
    to_save = dlg.to_save
    case dlg.exec
    when KDE::Dialog::Yes
      to_save.delete_if{|doc| doc.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_settingsnil

Override of PluginLike#save_settings

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

Returns:

  • (nil)


632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
# File 'lib/ruber/main_window/main_window.rb', line 632

def save_settings
  @workspace.store_sizes
# TODO see if the following line works. Otherwise, remove it and uncomment the one
# following it
  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
  nil
end

#tab(idx) ⇒ Pane #tab(editor) ⇒ Pane

The toplevel pane corresponding to the given index or editor

Overloads:

  • #tab(idx) ⇒ Pane

    Returns the toplevel pane in the tab number idx.

    Parameters:

    • idx (Integer)

      the index of the tab

    Returns:

    • (Pane)

      the toplevel pane in the tab number idx

  • #tab(editor) ⇒ Pane

    Returns the toplevel pane containing the given editor.

    Parameters:

    • editor (EditorView)

      the editor to retrieve the pane for

    Returns:

    • (Pane)

      the toplevel pane containing the given editor



417
418
419
# File 'lib/ruber/main_window/main_window.rb', line 417

def tab arg
  @active_environment.tab arg
end

#tabsObject

The toplevel panes associated with the active environment



151
152
153
# File 'lib/ruber/main_window/main_window.rb', line 151

def tabs
  @active_environment.tabs
end

#views(doc = nil) ⇒ Object

The views contained in the active environment



162
163
164
# File 'lib/ruber/main_window/main_window.rb', line 162

def views doc = nil
  @active_environment.views
end

#without_activating { ... } ⇒ Object

Note:

automatical editor activation will be restored at the end of this method (even if exceptions occur).

Executes the given block without automatically activating an editor whenever the current tab changes

This method 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:

bc. Ruber.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 calling this method, the focus widget of the current tab gets focus

Yields:

  • the block which should be executed without automatically activating editors

Returns:

  • (Object)

    the value returned by the block



487
488
489
490
# File 'lib/ruber/main_window/main_window.rb', line 487

def without_activating &blk
  blk.call
#       @view_manager.without_activating &blk
end