{find, flatten} = require 'underscore-plus' {Emitter, CompositeDisposable} = require 'event-kit' Gutter = require './gutter' Model = require './model' Pane = require './pane' ItemRegistry = require './item-registry' module.exports = class PaneContainer extends Model serializationVersion: 1 root: null stoppedChangingActivePaneItemDelay: 100 stoppedChangingActivePaneItemTimeout: null constructor: (params) -> super {@config, applicationDelegate, notificationManager, deserializerManager} = params @emitter = new Emitter @subscriptions = new CompositeDisposable @itemRegistry = new ItemRegistry @setRoot(new Pane({container: this, @config, applicationDelegate, notificationManager, deserializerManager})) @setActivePane(@getRoot()) @monitorActivePaneItem() @monitorPaneItems() serialize: (params) -> deserializer: 'PaneContainer' version: @serializationVersion root: @root?.serialize() activePaneId: @activePane.id deserialize: (state, deserializerManager) -> return unless state.version is @serializationVersion @setRoot(deserializerManager.deserialize(state.root)) activePane = find @getRoot().getPanes(), (pane) -> pane.id is state.activePaneId @setActivePane(activePane ? @getPanes()[0]) @destroyEmptyPanes() if @config.get('core.destroyEmptyPanes') onDidChangeRoot: (fn) -> @emitter.on 'did-change-root', fn observeRoot: (fn) -> fn(@getRoot()) @onDidChangeRoot(fn) onDidAddPane: (fn) -> @emitter.on 'did-add-pane', fn observePanes: (fn) -> fn(pane) for pane in @getPanes() @onDidAddPane ({pane}) -> fn(pane) onDidDestroyPane: (fn) -> @emitter.on 'did-destroy-pane', fn onWillDestroyPane: (fn) -> @emitter.on 'will-destroy-pane', fn onDidChangeActivePane: (fn) -> @emitter.on 'did-change-active-pane', fn observeActivePane: (fn) -> fn(@getActivePane()) @onDidChangeActivePane(fn) onDidAddPaneItem: (fn) -> @emitter.on 'did-add-pane-item', fn observePaneItems: (fn) -> fn(item) for item in @getPaneItems() @onDidAddPaneItem ({item}) -> fn(item) onDidChangeActivePaneItem: (fn) -> @emitter.on 'did-change-active-pane-item', fn onDidStopChangingActivePaneItem: (fn) -> @emitter.on 'did-stop-changing-active-pane-item', fn observeActivePaneItem: (fn) -> fn(@getActivePaneItem()) @onDidChangeActivePaneItem(fn) onWillDestroyPaneItem: (fn) -> @emitter.on 'will-destroy-pane-item', fn onDidDestroyPaneItem: (fn) -> @emitter.on 'did-destroy-pane-item', fn getRoot: -> @root setRoot: (@root) -> @root.setParent(this) @root.setContainer(this) @emitter.emit 'did-change-root', @root if not @getActivePane()? and @root instanceof Pane @setActivePane(@root) replaceChild: (oldChild, newChild) -> throw new Error("Replacing non-existent child") if oldChild isnt @root @setRoot(newChild) getPanes: -> @getRoot().getPanes() getPaneItems: -> @getRoot().getItems() getActivePane: -> @activePane setActivePane: (activePane) -> if activePane isnt @activePane unless activePane in @getPanes() throw new Error("Setting active pane that is not present in pane container") @activePane = activePane @emitter.emit 'did-change-active-pane', @activePane @activePane getActivePaneItem: -> @getActivePane().getActiveItem() paneForURI: (uri) -> find @getPanes(), (pane) -> pane.itemForURI(uri)? paneForItem: (item) -> find @getPanes(), (pane) -> item in pane.getItems() saveAll: -> pane.saveItems() for pane in @getPanes() return confirmClose: (options) -> allSaved = true for pane in @getPanes() for item in pane.getItems() unless pane.promptToSaveItem(item, options) allSaved = false break allSaved activateNextPane: -> panes = @getPanes() if panes.length > 1 currentIndex = panes.indexOf(@activePane) nextIndex = (currentIndex + 1) % panes.length panes[nextIndex].activate() true else false activatePreviousPane: -> panes = @getPanes() if panes.length > 1 currentIndex = panes.indexOf(@activePane) previousIndex = currentIndex - 1 previousIndex = panes.length - 1 if previousIndex < 0 panes[previousIndex].activate() true else false destroyEmptyPanes: -> pane.destroy() for pane in @getPanes() when pane.items.length is 0 return willDestroyPaneItem: (event) -> @emitter.emit 'will-destroy-pane-item', event didDestroyPaneItem: (event) -> @emitter.emit 'did-destroy-pane-item', event didAddPane: (event) -> @emitter.emit 'did-add-pane', event willDestroyPane: (event) -> @emitter.emit 'will-destroy-pane', event didDestroyPane: (event) -> @emitter.emit 'did-destroy-pane', event # Called by Model superclass when destroyed destroyed: -> @cancelStoppedChangingActivePaneItemTimeout() pane.destroy() for pane in @getPanes() @subscriptions.dispose() @emitter.dispose() cancelStoppedChangingActivePaneItemTimeout: -> if @stoppedChangingActivePaneItemTimeout? clearTimeout(@stoppedChangingActivePaneItemTimeout) monitorActivePaneItem: -> childSubscription = null @subscriptions.add @observeActivePane (activePane) => if childSubscription? @subscriptions.remove(childSubscription) childSubscription.dispose() childSubscription = activePane.observeActiveItem (activeItem) => @emitter.emit 'did-change-active-pane-item', activeItem @cancelStoppedChangingActivePaneItemTimeout() stoppedChangingActivePaneItemCallback = => @stoppedChangingActivePaneItemTimeout = null @emitter.emit 'did-stop-changing-active-pane-item', activeItem @stoppedChangingActivePaneItemTimeout = setTimeout( stoppedChangingActivePaneItemCallback, @stoppedChangingActivePaneItemDelay) @subscriptions.add(childSubscription) monitorPaneItems: -> @subscriptions.add @observePanes (pane) => for item, index in pane.getItems() @addedPaneItem(item, pane, index) pane.onDidAddItem ({item, index, moved}) => @addedPaneItem(item, pane, index) unless moved pane.onDidRemoveItem ({item, moved}) => @removedPaneItem(item) unless moved addedPaneItem: (item, pane, index) -> @itemRegistry.addItem(item) @emitter.emit 'did-add-pane-item', {item, pane, index} removedPaneItem: (item) -> @itemRegistry.removeItem(item)