OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * | 10 * |
11 * 2. Redistributions in binary form must reproduce the above | 11 * 2. Redistributions in binary form must reproduce the above |
12 * copyright notice, this list of conditions and the following disclaimer | 12 * copyright notice, this list of conditions and the following disclaimer |
13 * in the documentation and/or other materials provided with the | 13 * in the documentation and/or other materials provided with the |
14 * distribution. | 14 * distribution. |
15 * | 15 * |
16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS | 16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS |
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. | 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. |
20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 */ | 27 */ |
28 | |
29 /** | 28 /** |
30 * @interface | 29 * @interface |
31 */ | 30 */ |
32 WebInspector.TabbedEditorContainerDelegate = function() { }; | 31 WebInspector.TabbedEditorContainerDelegate = function() {}; |
33 | 32 |
34 WebInspector.TabbedEditorContainerDelegate.prototype = { | 33 WebInspector.TabbedEditorContainerDelegate.prototype = { |
35 /** | 34 /** |
36 * @param {!WebInspector.UISourceCode} uiSourceCode | 35 * @param {!WebInspector.UISourceCode} uiSourceCode |
37 * @return {!WebInspector.Widget} | 36 * @return {!WebInspector.Widget} |
38 */ | 37 */ |
39 viewForFile: function(uiSourceCode) { }, | 38 viewForFile: function(uiSourceCode) {}, |
40 }; | 39 }; |
41 | 40 |
42 /** | 41 /** |
43 * @constructor | 42 * @unrestricted |
44 * @extends {WebInspector.Object} | |
45 * @param {!WebInspector.TabbedEditorContainerDelegate} delegate | |
46 * @param {!WebInspector.Setting} setting | |
47 * @param {string} placeholderText | |
48 */ | 43 */ |
49 WebInspector.TabbedEditorContainer = function(delegate, setting, placeholderText
) | 44 WebInspector.TabbedEditorContainer = class extends WebInspector.Object { |
50 { | 45 /** |
51 WebInspector.Object.call(this); | 46 * @param {!WebInspector.TabbedEditorContainerDelegate} delegate |
| 47 * @param {!WebInspector.Setting} setting |
| 48 * @param {string} placeholderText |
| 49 */ |
| 50 constructor(delegate, setting, placeholderText) { |
| 51 super(); |
52 this._delegate = delegate; | 52 this._delegate = delegate; |
53 | 53 |
54 this._tabbedPane = new WebInspector.TabbedPane(); | 54 this._tabbedPane = new WebInspector.TabbedPane(); |
55 this._tabbedPane.setPlaceholderText(placeholderText); | 55 this._tabbedPane.setPlaceholderText(placeholderText); |
56 this._tabbedPane.setTabDelegate(new WebInspector.EditorContainerTabDelegate(
this)); | 56 this._tabbedPane.setTabDelegate(new WebInspector.EditorContainerTabDelegate(
this)); |
57 | 57 |
58 this._tabbedPane.setCloseableTabs(true); | 58 this._tabbedPane.setCloseableTabs(true); |
59 this._tabbedPane.setAllowTabReorder(true, true); | 59 this._tabbedPane.setAllowTabReorder(true, true); |
60 | 60 |
61 this._tabbedPane.addEventListener(WebInspector.TabbedPane.Events.TabClosed,
this._tabClosed, this); | 61 this._tabbedPane.addEventListener(WebInspector.TabbedPane.Events.TabClosed,
this._tabClosed, this); |
62 this._tabbedPane.addEventListener(WebInspector.TabbedPane.Events.TabSelected
, this._tabSelected, this); | 62 this._tabbedPane.addEventListener(WebInspector.TabbedPane.Events.TabSelected
, this._tabSelected, this); |
63 | 63 |
64 WebInspector.persistence.addEventListener(WebInspector.Persistence.Events.Bi
ndingCreated, this._onBindingCreated, this); | 64 WebInspector.persistence.addEventListener( |
65 WebInspector.persistence.addEventListener(WebInspector.Persistence.Events.Bi
ndingRemoved, this._onBindingRemoved, this); | 65 WebInspector.Persistence.Events.BindingCreated, this._onBindingCreated,
this); |
| 66 WebInspector.persistence.addEventListener( |
| 67 WebInspector.Persistence.Events.BindingRemoved, this._onBindingRemoved,
this); |
66 | 68 |
67 this._tabIds = new Map(); | 69 this._tabIds = new Map(); |
68 this._files = {}; | 70 this._files = {}; |
69 | 71 |
70 this._previouslyViewedFilesSetting = setting; | 72 this._previouslyViewedFilesSetting = setting; |
71 this._history = WebInspector.TabbedEditorContainer.History.fromObject(this._
previouslyViewedFilesSetting.get()); | 73 this._history = WebInspector.TabbedEditorContainer.History.fromObject(this._
previouslyViewedFilesSetting.get()); |
| 74 } |
| 75 |
| 76 /** |
| 77 * @param {!WebInspector.Event} event |
| 78 */ |
| 79 _onBindingCreated(event) { |
| 80 var binding = /** @type {!WebInspector.PersistenceBinding} */ (event.data); |
| 81 var networkTabId = this._tabIds.get(binding.network); |
| 82 var fileSystemTabId = this._tabIds.get(binding.fileSystem); |
| 83 |
| 84 if (networkTabId) |
| 85 this._tabbedPane.changeTabTitle( |
| 86 networkTabId, this._titleForFile(binding.fileSystem), this._tooltipFor
File(binding.fileSystem)); |
| 87 if (!fileSystemTabId) |
| 88 return; |
| 89 |
| 90 var wasSelectedInFileSystem = this._currentFile === binding.fileSystem; |
| 91 var currentSelectionRange = this._history.selectionRange(binding.fileSystem.
url()); |
| 92 var currentScrollLineNumber = this._history.scrollLineNumber(binding.fileSys
tem.url()); |
| 93 |
| 94 var tabIndex = this._tabbedPane.tabIndex(fileSystemTabId); |
| 95 var tabsToClose = [fileSystemTabId]; |
| 96 if (networkTabId) |
| 97 tabsToClose.push(networkTabId); |
| 98 this._closeTabs(tabsToClose, true); |
| 99 networkTabId = this._appendFileTab(binding.network, false, tabIndex); |
| 100 this._updateHistory(); |
| 101 |
| 102 if (wasSelectedInFileSystem) |
| 103 this._tabbedPane.selectTab(networkTabId, false); |
| 104 |
| 105 var networkTabView = /** @type {!WebInspector.Widget} */ (this._tabbedPane.t
abView(networkTabId)); |
| 106 this._restoreEditorProperties(networkTabView, currentSelectionRange, current
ScrollLineNumber); |
| 107 } |
| 108 |
| 109 /** |
| 110 * @param {!WebInspector.Event} event |
| 111 */ |
| 112 _onBindingRemoved(event) { |
| 113 var binding = /** @type {!WebInspector.PersistenceBinding} */ (event.data); |
| 114 var networkTabId = this._tabIds.get(binding.network); |
| 115 if (!networkTabId) |
| 116 return; |
| 117 var tabIndex = this._tabbedPane.tabIndex(networkTabId); |
| 118 var wasSelected = this._currentFile === binding.network; |
| 119 var fileSystemTabId = this._appendFileTab(binding.fileSystem, false, tabInde
x); |
| 120 this._updateHistory(); |
| 121 |
| 122 if (wasSelected) |
| 123 this._tabbedPane.selectTab(fileSystemTabId, false); |
| 124 |
| 125 var fileSystemTabView = /** @type {!WebInspector.Widget} */ (this._tabbedPan
e.tabView(fileSystemTabId)); |
| 126 var savedSelectionRange = this._history.selectionRange(binding.network.url()
); |
| 127 var savedScrollLineNumber = this._history.scrollLineNumber(binding.network.u
rl()); |
| 128 this._restoreEditorProperties(fileSystemTabView, savedSelectionRange, savedS
crollLineNumber); |
| 129 } |
| 130 |
| 131 /** |
| 132 * @return {!WebInspector.Widget} |
| 133 */ |
| 134 get view() { |
| 135 return this._tabbedPane; |
| 136 } |
| 137 |
| 138 /** |
| 139 * @return {?WebInspector.Widget} |
| 140 */ |
| 141 get visibleView() { |
| 142 return this._tabbedPane.visibleView; |
| 143 } |
| 144 |
| 145 /** |
| 146 * @return {!Array.<!WebInspector.Widget>} |
| 147 */ |
| 148 fileViews() { |
| 149 return /** @type {!Array.<!WebInspector.Widget>} */ (this._tabbedPane.tabVie
ws()); |
| 150 } |
| 151 |
| 152 /** |
| 153 * @return {!WebInspector.Toolbar} |
| 154 */ |
| 155 leftToolbar() { |
| 156 return this._tabbedPane.leftToolbar(); |
| 157 } |
| 158 |
| 159 /** |
| 160 * @return {!WebInspector.Toolbar} |
| 161 */ |
| 162 rightToolbar() { |
| 163 return this._tabbedPane.rightToolbar(); |
| 164 } |
| 165 |
| 166 /** |
| 167 * @param {!Element} parentElement |
| 168 */ |
| 169 show(parentElement) { |
| 170 this._tabbedPane.show(parentElement); |
| 171 } |
| 172 |
| 173 /** |
| 174 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 175 */ |
| 176 showFile(uiSourceCode) { |
| 177 this._innerShowFile(uiSourceCode, true); |
| 178 } |
| 179 |
| 180 /** |
| 181 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 182 */ |
| 183 closeFile(uiSourceCode) { |
| 184 var tabId = this._tabIds.get(uiSourceCode); |
| 185 if (!tabId) |
| 186 return; |
| 187 this._closeTabs([tabId]); |
| 188 } |
| 189 |
| 190 closeAllFiles() { |
| 191 this._closeTabs(this._tabbedPane.allTabs()); |
| 192 } |
| 193 |
| 194 /** |
| 195 * @return {!Array.<!WebInspector.UISourceCode>} |
| 196 */ |
| 197 historyUISourceCodes() { |
| 198 // FIXME: there should be a way to fetch UISourceCode for its uri. |
| 199 var uriToUISourceCode = {}; |
| 200 for (var id in this._files) { |
| 201 var uiSourceCode = this._files[id]; |
| 202 uriToUISourceCode[uiSourceCode.url()] = uiSourceCode; |
| 203 } |
| 204 |
| 205 var result = []; |
| 206 var uris = this._history._urls(); |
| 207 for (var i = 0; i < uris.length; ++i) { |
| 208 var uiSourceCode = uriToUISourceCode[uris[i]]; |
| 209 if (uiSourceCode) |
| 210 result.push(uiSourceCode); |
| 211 } |
| 212 return result; |
| 213 } |
| 214 |
| 215 _addViewListeners() { |
| 216 if (!this._currentView || !this._currentView.textEditor) |
| 217 return; |
| 218 this._currentView.textEditor.addEventListener( |
| 219 WebInspector.SourcesTextEditor.Events.ScrollChanged, this._scrollChanged
, this); |
| 220 this._currentView.textEditor.addEventListener( |
| 221 WebInspector.SourcesTextEditor.Events.SelectionChanged, this._selectionC
hanged, this); |
| 222 } |
| 223 |
| 224 _removeViewListeners() { |
| 225 if (!this._currentView || !this._currentView.textEditor) |
| 226 return; |
| 227 this._currentView.textEditor.removeEventListener( |
| 228 WebInspector.SourcesTextEditor.Events.ScrollChanged, this._scrollChanged
, this); |
| 229 this._currentView.textEditor.removeEventListener( |
| 230 WebInspector.SourcesTextEditor.Events.SelectionChanged, this._selectionC
hanged, this); |
| 231 } |
| 232 |
| 233 /** |
| 234 * @param {!WebInspector.Event} event |
| 235 */ |
| 236 _scrollChanged(event) { |
| 237 if (this._scrollTimer) |
| 238 clearTimeout(this._scrollTimer); |
| 239 var lineNumber = /** @type {number} */ (event.data); |
| 240 this._scrollTimer = setTimeout(saveHistory.bind(this), 100); |
| 241 this._history.updateScrollLineNumber(this._currentFile.url(), lineNumber); |
| 242 |
| 243 /** |
| 244 * @this {WebInspector.TabbedEditorContainer} |
| 245 */ |
| 246 function saveHistory() { |
| 247 this._history.save(this._previouslyViewedFilesSetting); |
| 248 } |
| 249 } |
| 250 |
| 251 /** |
| 252 * @param {!WebInspector.Event} event |
| 253 */ |
| 254 _selectionChanged(event) { |
| 255 var range = /** @type {!WebInspector.TextRange} */ (event.data); |
| 256 this._history.updateSelectionRange(this._currentFile.url(), range); |
| 257 this._history.save(this._previouslyViewedFilesSetting); |
| 258 } |
| 259 |
| 260 /** |
| 261 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 262 * @param {boolean=} userGesture |
| 263 */ |
| 264 _innerShowFile(uiSourceCode, userGesture) { |
| 265 var binding = WebInspector.persistence.binding(uiSourceCode); |
| 266 uiSourceCode = binding ? binding.network : uiSourceCode; |
| 267 if (this._currentFile === uiSourceCode) |
| 268 return; |
| 269 |
| 270 this._removeViewListeners(); |
| 271 this._currentFile = uiSourceCode; |
| 272 |
| 273 var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCo
de, userGesture); |
| 274 |
| 275 this._tabbedPane.selectTab(tabId, userGesture); |
| 276 if (userGesture) |
| 277 this._editorSelectedByUserAction(); |
| 278 |
| 279 var previousView = this._currentView; |
| 280 this._currentView = this.visibleView; |
| 281 this._addViewListeners(); |
| 282 |
| 283 var eventData = { |
| 284 currentFile: this._currentFile, |
| 285 currentView: this._currentView, |
| 286 previousView: previousView, |
| 287 userGesture: userGesture |
| 288 }; |
| 289 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.Edit
orSelected, eventData); |
| 290 } |
| 291 |
| 292 /** |
| 293 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 294 * @return {string} |
| 295 */ |
| 296 _titleForFile(uiSourceCode) { |
| 297 var binding = WebInspector.persistence.binding(uiSourceCode); |
| 298 var titleUISourceCode = binding ? binding.fileSystem : uiSourceCode; |
| 299 var maxDisplayNameLength = 30; |
| 300 var title = titleUISourceCode.displayName(true).trimMiddle(maxDisplayNameLen
gth); |
| 301 if (uiSourceCode.isDirty() || WebInspector.persistence.hasUnsavedCommittedCh
anges(uiSourceCode)) |
| 302 title += '*'; |
| 303 return title; |
| 304 } |
| 305 |
| 306 /** |
| 307 * @param {string} id |
| 308 * @param {string} nextTabId |
| 309 */ |
| 310 _maybeCloseTab(id, nextTabId) { |
| 311 var uiSourceCode = this._files[id]; |
| 312 var shouldPrompt = uiSourceCode.isDirty() && uiSourceCode.project().canSetFi
leContent(); |
| 313 // FIXME: this should be replaced with common Save/Discard/Cancel dialog. |
| 314 if (!shouldPrompt || |
| 315 confirm(WebInspector.UIString('Are you sure you want to close unsaved fi
le: %s?', uiSourceCode.name()))) { |
| 316 uiSourceCode.resetWorkingCopy(); |
| 317 var previousView = this._currentView; |
| 318 if (nextTabId) |
| 319 this._tabbedPane.selectTab(nextTabId, true); |
| 320 this._tabbedPane.closeTab(id, true); |
| 321 return true; |
| 322 } |
| 323 return false; |
| 324 } |
| 325 |
| 326 /** |
| 327 * @param {!Array.<string>} ids |
| 328 * @param {boolean=} forceCloseDirtyTabs |
| 329 */ |
| 330 _closeTabs(ids, forceCloseDirtyTabs) { |
| 331 var dirtyTabs = []; |
| 332 var cleanTabs = []; |
| 333 for (var i = 0; i < ids.length; ++i) { |
| 334 var id = ids[i]; |
| 335 var uiSourceCode = this._files[id]; |
| 336 if (!forceCloseDirtyTabs && uiSourceCode.isDirty()) |
| 337 dirtyTabs.push(id); |
| 338 else |
| 339 cleanTabs.push(id); |
| 340 } |
| 341 if (dirtyTabs.length) |
| 342 this._tabbedPane.selectTab(dirtyTabs[0], true); |
| 343 this._tabbedPane.closeTabs(cleanTabs, true); |
| 344 for (var i = 0; i < dirtyTabs.length; ++i) { |
| 345 var nextTabId = i + 1 < dirtyTabs.length ? dirtyTabs[i + 1] : null; |
| 346 if (!this._maybeCloseTab(dirtyTabs[i], nextTabId)) |
| 347 break; |
| 348 } |
| 349 } |
| 350 |
| 351 /** |
| 352 * @param {string} tabId |
| 353 * @param {!WebInspector.ContextMenu} contextMenu |
| 354 */ |
| 355 _onContextMenu(tabId, contextMenu) { |
| 356 var uiSourceCode = this._files[tabId]; |
| 357 if (uiSourceCode) |
| 358 contextMenu.appendApplicableItems(uiSourceCode); |
| 359 } |
| 360 |
| 361 /** |
| 362 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 363 */ |
| 364 addUISourceCode(uiSourceCode) { |
| 365 var uri = uiSourceCode.url(); |
| 366 var index = this._history.index(uri); |
| 367 if (index === -1) |
| 368 return; |
| 369 |
| 370 if (!this._tabIds.has(uiSourceCode)) |
| 371 this._appendFileTab(uiSourceCode, false); |
| 372 |
| 373 // Select tab if this file was the last to be shown. |
| 374 if (!index) { |
| 375 this._innerShowFile(uiSourceCode, false); |
| 376 return; |
| 377 } |
| 378 |
| 379 if (!this._currentFile) |
| 380 return; |
| 381 var currentProjectType = this._currentFile.project().type(); |
| 382 var addedProjectType = uiSourceCode.project().type(); |
| 383 var snippetsProjectType = WebInspector.projectTypes.Snippets; |
| 384 if (this._history.index(this._currentFile.url()) && currentProjectType === s
nippetsProjectType && |
| 385 addedProjectType !== snippetsProjectType) |
| 386 this._innerShowFile(uiSourceCode, false); |
| 387 } |
| 388 |
| 389 /** |
| 390 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 391 */ |
| 392 removeUISourceCode(uiSourceCode) { |
| 393 this.removeUISourceCodes([uiSourceCode]); |
| 394 } |
| 395 |
| 396 /** |
| 397 * @param {!Array.<!WebInspector.UISourceCode>} uiSourceCodes |
| 398 */ |
| 399 removeUISourceCodes(uiSourceCodes) { |
| 400 var tabIds = []; |
| 401 for (var i = 0; i < uiSourceCodes.length; ++i) { |
| 402 var uiSourceCode = uiSourceCodes[i]; |
| 403 var tabId = this._tabIds.get(uiSourceCode); |
| 404 if (tabId) |
| 405 tabIds.push(tabId); |
| 406 } |
| 407 this._tabbedPane.closeTabs(tabIds); |
| 408 } |
| 409 |
| 410 /** |
| 411 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 412 */ |
| 413 _editorClosedByUserAction(uiSourceCode) { |
| 414 this._history.remove(uiSourceCode.url()); |
| 415 this._updateHistory(); |
| 416 } |
| 417 |
| 418 _editorSelectedByUserAction() { |
| 419 this._updateHistory(); |
| 420 } |
| 421 |
| 422 _updateHistory() { |
| 423 var tabIds = |
| 424 this._tabbedPane.lastOpenedTabIds(WebInspector.TabbedEditorContainer.max
imalPreviouslyViewedFilesCount); |
| 425 |
| 426 /** |
| 427 * @param {string} tabId |
| 428 * @this {WebInspector.TabbedEditorContainer} |
| 429 */ |
| 430 function tabIdToURI(tabId) { |
| 431 return this._files[tabId].url(); |
| 432 } |
| 433 |
| 434 this._history.update(tabIds.map(tabIdToURI.bind(this))); |
| 435 this._history.save(this._previouslyViewedFilesSetting); |
| 436 } |
| 437 |
| 438 /** |
| 439 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 440 * @return {string} |
| 441 */ |
| 442 _tooltipForFile(uiSourceCode) { |
| 443 uiSourceCode = WebInspector.persistence.fileSystem(uiSourceCode) || uiSource
Code; |
| 444 return uiSourceCode.url(); |
| 445 } |
| 446 |
| 447 /** |
| 448 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 449 * @param {boolean=} userGesture |
| 450 * @param {number=} index |
| 451 * @return {string} |
| 452 */ |
| 453 _appendFileTab(uiSourceCode, userGesture, index) { |
| 454 var view = this._delegate.viewForFile(uiSourceCode); |
| 455 var title = this._titleForFile(uiSourceCode); |
| 456 var tooltip = this._tooltipForFile(uiSourceCode); |
| 457 |
| 458 var tabId = this._generateTabId(); |
| 459 this._tabIds.set(uiSourceCode, tabId); |
| 460 this._files[tabId] = uiSourceCode; |
| 461 |
| 462 var savedSelectionRange = this._history.selectionRange(uiSourceCode.url()); |
| 463 var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.url(
)); |
| 464 this._restoreEditorProperties(view, savedSelectionRange, savedScrollLineNumb
er); |
| 465 |
| 466 this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture, undefin
ed, index); |
| 467 |
| 468 this._updateFileTitle(uiSourceCode); |
| 469 this._addUISourceCodeListeners(uiSourceCode); |
| 470 return tabId; |
| 471 } |
| 472 |
| 473 /** |
| 474 * @param {!WebInspector.Widget} editorView |
| 475 * @param {!WebInspector.TextRange=} selection |
| 476 * @param {number=} firstLineNumber |
| 477 */ |
| 478 _restoreEditorProperties(editorView, selection, firstLineNumber) { |
| 479 var sourceFrame = |
| 480 editorView instanceof WebInspector.SourceFrame ? /** @type {!WebInspecto
r.SourceFrame} */ (editorView) : null; |
| 481 if (!sourceFrame) |
| 482 return; |
| 483 if (selection) |
| 484 sourceFrame.setSelection(selection); |
| 485 if (typeof firstLineNumber === 'number') |
| 486 sourceFrame.scrollToLine(firstLineNumber); |
| 487 } |
| 488 |
| 489 /** |
| 490 * @param {!WebInspector.Event} event |
| 491 */ |
| 492 _tabClosed(event) { |
| 493 var tabId = /** @type {string} */ (event.data.tabId); |
| 494 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); |
| 495 |
| 496 var uiSourceCode = this._files[tabId]; |
| 497 if (this._currentFile === uiSourceCode) { |
| 498 this._removeViewListeners(); |
| 499 delete this._currentView; |
| 500 delete this._currentFile; |
| 501 } |
| 502 this._tabIds.remove(uiSourceCode); |
| 503 delete this._files[tabId]; |
| 504 |
| 505 this._removeUISourceCodeListeners(uiSourceCode); |
| 506 |
| 507 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.Edit
orClosed, uiSourceCode); |
| 508 |
| 509 if (userGesture) |
| 510 this._editorClosedByUserAction(uiSourceCode); |
| 511 } |
| 512 |
| 513 /** |
| 514 * @param {!WebInspector.Event} event |
| 515 */ |
| 516 _tabSelected(event) { |
| 517 var tabId = /** @type {string} */ (event.data.tabId); |
| 518 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); |
| 519 |
| 520 var uiSourceCode = this._files[tabId]; |
| 521 this._innerShowFile(uiSourceCode, userGesture); |
| 522 } |
| 523 |
| 524 /** |
| 525 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 526 */ |
| 527 _addUISourceCodeListeners(uiSourceCode) { |
| 528 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged,
this._uiSourceCodeTitleChanged, this); |
| 529 uiSourceCode.addEventListener( |
| 530 WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeW
orkingCopyChanged, this); |
| 531 uiSourceCode.addEventListener( |
| 532 WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCod
eWorkingCopyCommitted, this); |
| 533 } |
| 534 |
| 535 /** |
| 536 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 537 */ |
| 538 _removeUISourceCodeListeners(uiSourceCode) { |
| 539 uiSourceCode.removeEventListener( |
| 540 WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleCh
anged, this); |
| 541 uiSourceCode.removeEventListener( |
| 542 WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeW
orkingCopyChanged, this); |
| 543 uiSourceCode.removeEventListener( |
| 544 WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCod
eWorkingCopyCommitted, this); |
| 545 } |
| 546 |
| 547 /** |
| 548 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 549 */ |
| 550 _updateFileTitle(uiSourceCode) { |
| 551 var tabId = this._tabIds.get(uiSourceCode); |
| 552 if (tabId) { |
| 553 var title = this._titleForFile(uiSourceCode); |
| 554 this._tabbedPane.changeTabTitle(tabId, title); |
| 555 if (WebInspector.persistence.hasUnsavedCommittedChanges(uiSourceCode)) |
| 556 this._tabbedPane.setTabIcon( |
| 557 tabId, 'warning-icon', WebInspector.UIString('Changes to this file w
ere not saved to file system.')); |
| 558 else |
| 559 this._tabbedPane.setTabIcon(tabId, ''); |
| 560 } |
| 561 } |
| 562 |
| 563 _uiSourceCodeTitleChanged(event) { |
| 564 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); |
| 565 this._updateFileTitle(uiSourceCode); |
| 566 this._updateHistory(); |
| 567 } |
| 568 |
| 569 _uiSourceCodeWorkingCopyChanged(event) { |
| 570 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); |
| 571 this._updateFileTitle(uiSourceCode); |
| 572 } |
| 573 |
| 574 _uiSourceCodeWorkingCopyCommitted(event) { |
| 575 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); |
| 576 this._updateFileTitle(uiSourceCode); |
| 577 } |
| 578 |
| 579 /** |
| 580 * @return {string} |
| 581 */ |
| 582 _generateTabId() { |
| 583 return 'tab_' + (WebInspector.TabbedEditorContainer._tabId++); |
| 584 } |
| 585 |
| 586 /** |
| 587 * @return {?WebInspector.UISourceCode} uiSourceCode |
| 588 */ |
| 589 currentFile() { |
| 590 return this._currentFile || null; |
| 591 } |
72 }; | 592 }; |
73 | 593 |
74 /** @enum {symbol} */ | 594 /** @enum {symbol} */ |
75 WebInspector.TabbedEditorContainer.Events = { | 595 WebInspector.TabbedEditorContainer.Events = { |
76 EditorSelected: Symbol("EditorSelected"), | 596 EditorSelected: Symbol('EditorSelected'), |
77 EditorClosed: Symbol("EditorClosed") | 597 EditorClosed: Symbol('EditorClosed') |
78 }; | 598 }; |
79 | 599 |
80 WebInspector.TabbedEditorContainer._tabId = 0; | 600 WebInspector.TabbedEditorContainer._tabId = 0; |
81 | 601 |
82 WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount = 30; | 602 WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount = 30; |
83 | 603 |
84 WebInspector.TabbedEditorContainer.prototype = { | |
85 /** | |
86 * @param {!WebInspector.Event} event | |
87 */ | |
88 _onBindingCreated: function(event) | |
89 { | |
90 var binding = /** @type {!WebInspector.PersistenceBinding} */(event.data
); | |
91 var networkTabId = this._tabIds.get(binding.network); | |
92 var fileSystemTabId = this._tabIds.get(binding.fileSystem); | |
93 | |
94 if (networkTabId) | |
95 this._tabbedPane.changeTabTitle(networkTabId, this._titleForFile(bin
ding.fileSystem), this._tooltipForFile(binding.fileSystem)); | |
96 if (!fileSystemTabId) | |
97 return; | |
98 | |
99 var wasSelectedInFileSystem = this._currentFile === binding.fileSystem; | |
100 var currentSelectionRange = this._history.selectionRange(binding.fileSys
tem.url()); | |
101 var currentScrollLineNumber = this._history.scrollLineNumber(binding.fil
eSystem.url()); | |
102 | |
103 var tabIndex = this._tabbedPane.tabIndex(fileSystemTabId); | |
104 var tabsToClose = [fileSystemTabId]; | |
105 if (networkTabId) | |
106 tabsToClose.push(networkTabId); | |
107 this._closeTabs(tabsToClose, true); | |
108 networkTabId = this._appendFileTab(binding.network, false, tabIndex); | |
109 this._updateHistory(); | |
110 | |
111 if (wasSelectedInFileSystem) | |
112 this._tabbedPane.selectTab(networkTabId, false); | |
113 | |
114 var networkTabView = /** @type {!WebInspector.Widget} */(this._tabbedPan
e.tabView(networkTabId)); | |
115 this._restoreEditorProperties(networkTabView, currentSelectionRange, cur
rentScrollLineNumber); | |
116 }, | |
117 | |
118 /** | |
119 * @param {!WebInspector.Event} event | |
120 */ | |
121 _onBindingRemoved: function(event) | |
122 { | |
123 var binding = /** @type {!WebInspector.PersistenceBinding} */(event.data
); | |
124 var networkTabId = this._tabIds.get(binding.network); | |
125 if (!networkTabId) | |
126 return; | |
127 var tabIndex = this._tabbedPane.tabIndex(networkTabId); | |
128 var wasSelected = this._currentFile === binding.network; | |
129 var fileSystemTabId = this._appendFileTab(binding.fileSystem, false, tab
Index); | |
130 this._updateHistory(); | |
131 | |
132 if (wasSelected) | |
133 this._tabbedPane.selectTab(fileSystemTabId, false); | |
134 | |
135 var fileSystemTabView = /** @type {!WebInspector.Widget} */(this._tabbed
Pane.tabView(fileSystemTabId)); | |
136 var savedSelectionRange = this._history.selectionRange(binding.network.u
rl()); | |
137 var savedScrollLineNumber = this._history.scrollLineNumber(binding.netwo
rk.url()); | |
138 this._restoreEditorProperties(fileSystemTabView, savedSelectionRange, sa
vedScrollLineNumber); | |
139 }, | |
140 | |
141 /** | |
142 * @return {!WebInspector.Widget} | |
143 */ | |
144 get view() | |
145 { | |
146 return this._tabbedPane; | |
147 }, | |
148 | |
149 /** | |
150 * @type {!WebInspector.Widget} | |
151 */ | |
152 get visibleView() | |
153 { | |
154 return this._tabbedPane.visibleView; | |
155 }, | |
156 | |
157 /** | |
158 * @return {!Array.<!WebInspector.Widget>} | |
159 */ | |
160 fileViews: function() | |
161 { | |
162 return /** @type {!Array.<!WebInspector.Widget>} */ (this._tabbedPane.ta
bViews()); | |
163 }, | |
164 | |
165 /** | |
166 * @return {!WebInspector.Toolbar} | |
167 */ | |
168 leftToolbar: function() | |
169 { | |
170 return this._tabbedPane.leftToolbar(); | |
171 }, | |
172 | |
173 /** | |
174 * @return {!WebInspector.Toolbar} | |
175 */ | |
176 rightToolbar: function() | |
177 { | |
178 return this._tabbedPane.rightToolbar(); | |
179 }, | |
180 | |
181 /** | |
182 * @param {!Element} parentElement | |
183 */ | |
184 show: function(parentElement) | |
185 { | |
186 this._tabbedPane.show(parentElement); | |
187 }, | |
188 | |
189 /** | |
190 * @param {!WebInspector.UISourceCode} uiSourceCode | |
191 */ | |
192 showFile: function(uiSourceCode) | |
193 { | |
194 this._innerShowFile(uiSourceCode, true); | |
195 }, | |
196 | |
197 /** | |
198 * @param {!WebInspector.UISourceCode} uiSourceCode | |
199 */ | |
200 closeFile: function(uiSourceCode) | |
201 { | |
202 var tabId = this._tabIds.get(uiSourceCode); | |
203 if (!tabId) | |
204 return; | |
205 this._closeTabs([tabId]); | |
206 }, | |
207 | |
208 closeAllFiles: function() | |
209 { | |
210 this._closeTabs(this._tabbedPane.allTabs()); | |
211 }, | |
212 | |
213 /** | |
214 * @return {!Array.<!WebInspector.UISourceCode>} | |
215 */ | |
216 historyUISourceCodes: function() | |
217 { | |
218 // FIXME: there should be a way to fetch UISourceCode for its uri. | |
219 var uriToUISourceCode = {}; | |
220 for (var id in this._files) { | |
221 var uiSourceCode = this._files[id]; | |
222 uriToUISourceCode[uiSourceCode.url()] = uiSourceCode; | |
223 } | |
224 | |
225 var result = []; | |
226 var uris = this._history._urls(); | |
227 for (var i = 0; i < uris.length; ++i) { | |
228 var uiSourceCode = uriToUISourceCode[uris[i]]; | |
229 if (uiSourceCode) | |
230 result.push(uiSourceCode); | |
231 } | |
232 return result; | |
233 }, | |
234 | |
235 _addViewListeners: function() | |
236 { | |
237 if (!this._currentView || !this._currentView.textEditor) | |
238 return; | |
239 this._currentView.textEditor.addEventListener(WebInspector.SourcesTextEd
itor.Events.ScrollChanged, this._scrollChanged, this); | |
240 this._currentView.textEditor.addEventListener(WebInspector.SourcesTextEd
itor.Events.SelectionChanged, this._selectionChanged, this); | |
241 }, | |
242 | |
243 _removeViewListeners: function() | |
244 { | |
245 if (!this._currentView || !this._currentView.textEditor) | |
246 return; | |
247 this._currentView.textEditor.removeEventListener(WebInspector.SourcesTex
tEditor.Events.ScrollChanged, this._scrollChanged, this); | |
248 this._currentView.textEditor.removeEventListener(WebInspector.SourcesTex
tEditor.Events.SelectionChanged, this._selectionChanged, this); | |
249 }, | |
250 | |
251 /** | |
252 * @param {!WebInspector.Event} event | |
253 */ | |
254 _scrollChanged: function(event) | |
255 { | |
256 if (this._scrollTimer) | |
257 clearTimeout(this._scrollTimer); | |
258 var lineNumber = /** @type {number} */ (event.data); | |
259 this._scrollTimer = setTimeout(saveHistory.bind(this), 100); | |
260 this._history.updateScrollLineNumber(this._currentFile.url(), lineNumber
); | |
261 | |
262 /** | |
263 * @this {WebInspector.TabbedEditorContainer} | |
264 */ | |
265 function saveHistory() | |
266 { | |
267 this._history.save(this._previouslyViewedFilesSetting); | |
268 } | |
269 }, | |
270 | |
271 /** | |
272 * @param {!WebInspector.Event} event | |
273 */ | |
274 _selectionChanged: function(event) | |
275 { | |
276 var range = /** @type {!WebInspector.TextRange} */ (event.data); | |
277 this._history.updateSelectionRange(this._currentFile.url(), range); | |
278 this._history.save(this._previouslyViewedFilesSetting); | |
279 }, | |
280 | |
281 /** | |
282 * @param {!WebInspector.UISourceCode} uiSourceCode | |
283 * @param {boolean=} userGesture | |
284 */ | |
285 _innerShowFile: function(uiSourceCode, userGesture) | |
286 { | |
287 var binding = WebInspector.persistence.binding(uiSourceCode); | |
288 uiSourceCode = binding ? binding.network : uiSourceCode; | |
289 if (this._currentFile === uiSourceCode) | |
290 return; | |
291 | |
292 this._removeViewListeners(); | |
293 this._currentFile = uiSourceCode; | |
294 | |
295 var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSour
ceCode, userGesture); | |
296 | |
297 this._tabbedPane.selectTab(tabId, userGesture); | |
298 if (userGesture) | |
299 this._editorSelectedByUserAction(); | |
300 | |
301 var previousView = this._currentView; | |
302 this._currentView = this.visibleView; | |
303 this._addViewListeners(); | |
304 | |
305 var eventData = { | |
306 currentFile: this._currentFile, | |
307 currentView: this._currentView, | |
308 previousView: previousView, | |
309 userGesture: userGesture | |
310 }; | |
311 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.
EditorSelected, eventData); | |
312 }, | |
313 | |
314 /** | |
315 * @param {!WebInspector.UISourceCode} uiSourceCode | |
316 * @return {string} | |
317 */ | |
318 _titleForFile: function(uiSourceCode) | |
319 { | |
320 var binding = WebInspector.persistence.binding(uiSourceCode); | |
321 var titleUISourceCode = binding ? binding.fileSystem : uiSourceCode; | |
322 var maxDisplayNameLength = 30; | |
323 var title = titleUISourceCode.displayName(true).trimMiddle(maxDisplayNam
eLength); | |
324 if (uiSourceCode.isDirty() || WebInspector.persistence.hasUnsavedCommitt
edChanges(uiSourceCode)) | |
325 title += "*"; | |
326 return title; | |
327 }, | |
328 | |
329 /** | |
330 * @param {string} id | |
331 * @param {string} nextTabId | |
332 */ | |
333 _maybeCloseTab: function(id, nextTabId) | |
334 { | |
335 var uiSourceCode = this._files[id]; | |
336 var shouldPrompt = uiSourceCode.isDirty() && uiSourceCode.project().canS
etFileContent(); | |
337 // FIXME: this should be replaced with common Save/Discard/Cancel dialog
. | |
338 if (!shouldPrompt || confirm(WebInspector.UIString("Are you sure you wan
t to close unsaved file: %s?", uiSourceCode.name()))) { | |
339 uiSourceCode.resetWorkingCopy(); | |
340 var previousView = this._currentView; | |
341 if (nextTabId) | |
342 this._tabbedPane.selectTab(nextTabId, true); | |
343 this._tabbedPane.closeTab(id, true); | |
344 return true; | |
345 } | |
346 return false; | |
347 }, | |
348 | |
349 /** | |
350 * @param {!Array.<string>} ids | |
351 * @param {boolean=} forceCloseDirtyTabs | |
352 */ | |
353 _closeTabs: function(ids, forceCloseDirtyTabs) | |
354 { | |
355 var dirtyTabs = []; | |
356 var cleanTabs = []; | |
357 for (var i = 0; i < ids.length; ++i) { | |
358 var id = ids[i]; | |
359 var uiSourceCode = this._files[id]; | |
360 if (!forceCloseDirtyTabs && uiSourceCode.isDirty()) | |
361 dirtyTabs.push(id); | |
362 else | |
363 cleanTabs.push(id); | |
364 } | |
365 if (dirtyTabs.length) | |
366 this._tabbedPane.selectTab(dirtyTabs[0], true); | |
367 this._tabbedPane.closeTabs(cleanTabs, true); | |
368 for (var i = 0; i < dirtyTabs.length; ++i) { | |
369 var nextTabId = i + 1 < dirtyTabs.length ? dirtyTabs[i + 1] : null; | |
370 if (!this._maybeCloseTab(dirtyTabs[i], nextTabId)) | |
371 break; | |
372 } | |
373 }, | |
374 | |
375 /** | |
376 * @param {string} tabId | |
377 * @param {!WebInspector.ContextMenu} contextMenu | |
378 */ | |
379 _onContextMenu: function(tabId, contextMenu) | |
380 { | |
381 var uiSourceCode = this._files[tabId]; | |
382 if (uiSourceCode) | |
383 contextMenu.appendApplicableItems(uiSourceCode); | |
384 }, | |
385 | |
386 /** | |
387 * @param {!WebInspector.UISourceCode} uiSourceCode | |
388 */ | |
389 addUISourceCode: function(uiSourceCode) | |
390 { | |
391 var uri = uiSourceCode.url(); | |
392 var index = this._history.index(uri); | |
393 if (index === -1) | |
394 return; | |
395 | |
396 if (!this._tabIds.has(uiSourceCode)) | |
397 this._appendFileTab(uiSourceCode, false); | |
398 | |
399 // Select tab if this file was the last to be shown. | |
400 if (!index) { | |
401 this._innerShowFile(uiSourceCode, false); | |
402 return; | |
403 } | |
404 | |
405 if (!this._currentFile) | |
406 return; | |
407 var currentProjectType = this._currentFile.project().type(); | |
408 var addedProjectType = uiSourceCode.project().type(); | |
409 var snippetsProjectType = WebInspector.projectTypes.Snippets; | |
410 if (this._history.index(this._currentFile.url()) && currentProjectType =
== snippetsProjectType && addedProjectType !== snippetsProjectType) | |
411 this._innerShowFile(uiSourceCode, false); | |
412 }, | |
413 | |
414 /** | |
415 * @param {!WebInspector.UISourceCode} uiSourceCode | |
416 */ | |
417 removeUISourceCode: function(uiSourceCode) | |
418 { | |
419 this.removeUISourceCodes([uiSourceCode]); | |
420 }, | |
421 | |
422 /** | |
423 * @param {!Array.<!WebInspector.UISourceCode>} uiSourceCodes | |
424 */ | |
425 removeUISourceCodes: function(uiSourceCodes) | |
426 { | |
427 var tabIds = []; | |
428 for (var i = 0; i < uiSourceCodes.length; ++i) { | |
429 var uiSourceCode = uiSourceCodes[i]; | |
430 var tabId = this._tabIds.get(uiSourceCode); | |
431 if (tabId) | |
432 tabIds.push(tabId); | |
433 } | |
434 this._tabbedPane.closeTabs(tabIds); | |
435 }, | |
436 | |
437 /** | |
438 * @param {!WebInspector.UISourceCode} uiSourceCode | |
439 */ | |
440 _editorClosedByUserAction: function(uiSourceCode) | |
441 { | |
442 this._history.remove(uiSourceCode.url()); | |
443 this._updateHistory(); | |
444 }, | |
445 | |
446 _editorSelectedByUserAction: function() | |
447 { | |
448 this._updateHistory(); | |
449 }, | |
450 | |
451 _updateHistory: function() | |
452 { | |
453 var tabIds = this._tabbedPane.lastOpenedTabIds(WebInspector.TabbedEditor
Container.maximalPreviouslyViewedFilesCount); | |
454 | |
455 /** | |
456 * @param {string} tabId | |
457 * @this {WebInspector.TabbedEditorContainer} | |
458 */ | |
459 function tabIdToURI(tabId) | |
460 { | |
461 return this._files[tabId].url(); | |
462 } | |
463 | |
464 this._history.update(tabIds.map(tabIdToURI.bind(this))); | |
465 this._history.save(this._previouslyViewedFilesSetting); | |
466 }, | |
467 | |
468 /** | |
469 * @param {!WebInspector.UISourceCode} uiSourceCode | |
470 * @return {string} | |
471 */ | |
472 _tooltipForFile: function(uiSourceCode) | |
473 { | |
474 uiSourceCode = WebInspector.persistence.fileSystem(uiSourceCode) || uiSo
urceCode; | |
475 return uiSourceCode.url(); | |
476 }, | |
477 | |
478 /** | |
479 * @param {!WebInspector.UISourceCode} uiSourceCode | |
480 * @param {boolean=} userGesture | |
481 * @param {number=} index | |
482 * @return {string} | |
483 */ | |
484 _appendFileTab: function(uiSourceCode, userGesture, index) | |
485 { | |
486 var view = this._delegate.viewForFile(uiSourceCode); | |
487 var title = this._titleForFile(uiSourceCode); | |
488 var tooltip = this._tooltipForFile(uiSourceCode); | |
489 | |
490 var tabId = this._generateTabId(); | |
491 this._tabIds.set(uiSourceCode, tabId); | |
492 this._files[tabId] = uiSourceCode; | |
493 | |
494 var savedSelectionRange = this._history.selectionRange(uiSourceCode.url(
)); | |
495 var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.
url()); | |
496 this._restoreEditorProperties(view, savedSelectionRange, savedScrollLine
Number); | |
497 | |
498 this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture, und
efined, index); | |
499 | |
500 this._updateFileTitle(uiSourceCode); | |
501 this._addUISourceCodeListeners(uiSourceCode); | |
502 return tabId; | |
503 }, | |
504 | |
505 /** | |
506 * @param {!WebInspector.Widget} editorView | |
507 * @param {!WebInspector.TextRange=} selection | |
508 * @param {number=} firstLineNumber | |
509 */ | |
510 _restoreEditorProperties: function(editorView, selection, firstLineNumber) | |
511 { | |
512 var sourceFrame = editorView instanceof WebInspector.SourceFrame ? /** @
type {!WebInspector.SourceFrame} */ (editorView) : null; | |
513 if (!sourceFrame) | |
514 return; | |
515 if (selection) | |
516 sourceFrame.setSelection(selection); | |
517 if (typeof firstLineNumber === "number") | |
518 sourceFrame.scrollToLine(firstLineNumber); | |
519 }, | |
520 | |
521 /** | |
522 * @param {!WebInspector.Event} event | |
523 */ | |
524 _tabClosed: function(event) | |
525 { | |
526 var tabId = /** @type {string} */ (event.data.tabId); | |
527 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); | |
528 | |
529 var uiSourceCode = this._files[tabId]; | |
530 if (this._currentFile === uiSourceCode) { | |
531 this._removeViewListeners(); | |
532 delete this._currentView; | |
533 delete this._currentFile; | |
534 } | |
535 this._tabIds.remove(uiSourceCode); | |
536 delete this._files[tabId]; | |
537 | |
538 this._removeUISourceCodeListeners(uiSourceCode); | |
539 | |
540 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.
EditorClosed, uiSourceCode); | |
541 | |
542 if (userGesture) | |
543 this._editorClosedByUserAction(uiSourceCode); | |
544 }, | |
545 | |
546 /** | |
547 * @param {!WebInspector.Event} event | |
548 */ | |
549 _tabSelected: function(event) | |
550 { | |
551 var tabId = /** @type {string} */ (event.data.tabId); | |
552 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); | |
553 | |
554 var uiSourceCode = this._files[tabId]; | |
555 this._innerShowFile(uiSourceCode, userGesture); | |
556 }, | |
557 | |
558 /** | |
559 * @param {!WebInspector.UISourceCode} uiSourceCode | |
560 */ | |
561 _addUISourceCodeListeners: function(uiSourceCode) | |
562 { | |
563 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChan
ged, this._uiSourceCodeTitleChanged, this); | |
564 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCo
pyChanged, this._uiSourceCodeWorkingCopyChanged, this); | |
565 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCo
pyCommitted, this._uiSourceCodeWorkingCopyCommitted, this); | |
566 }, | |
567 | |
568 /** | |
569 * @param {!WebInspector.UISourceCode} uiSourceCode | |
570 */ | |
571 _removeUISourceCodeListeners: function(uiSourceCode) | |
572 { | |
573 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleC
hanged, this._uiSourceCodeTitleChanged, this); | |
574 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.Workin
gCopyChanged, this._uiSourceCodeWorkingCopyChanged, this); | |
575 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.Workin
gCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this); | |
576 }, | |
577 | |
578 /** | |
579 * @param {!WebInspector.UISourceCode} uiSourceCode | |
580 */ | |
581 _updateFileTitle: function(uiSourceCode) | |
582 { | |
583 var tabId = this._tabIds.get(uiSourceCode); | |
584 if (tabId) { | |
585 var title = this._titleForFile(uiSourceCode); | |
586 this._tabbedPane.changeTabTitle(tabId, title); | |
587 if (WebInspector.persistence.hasUnsavedCommittedChanges(uiSourceCode
)) | |
588 this._tabbedPane.setTabIcon(tabId, "warning-icon", WebInspector.
UIString("Changes to this file were not saved to file system.")); | |
589 else | |
590 this._tabbedPane.setTabIcon(tabId, ""); | |
591 } | |
592 }, | |
593 | |
594 _uiSourceCodeTitleChanged: function(event) | |
595 { | |
596 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.targ
et); | |
597 this._updateFileTitle(uiSourceCode); | |
598 this._updateHistory(); | |
599 }, | |
600 | |
601 _uiSourceCodeWorkingCopyChanged: function(event) | |
602 { | |
603 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.targ
et); | |
604 this._updateFileTitle(uiSourceCode); | |
605 }, | |
606 | |
607 _uiSourceCodeWorkingCopyCommitted: function(event) | |
608 { | |
609 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.targ
et); | |
610 this._updateFileTitle(uiSourceCode); | |
611 }, | |
612 | |
613 /** | |
614 * @return {string} | |
615 */ | |
616 _generateTabId: function() | |
617 { | |
618 return "tab_" + (WebInspector.TabbedEditorContainer._tabId++); | |
619 }, | |
620 | |
621 /** | |
622 * @return {?WebInspector.UISourceCode} uiSourceCode | |
623 */ | |
624 currentFile: function() | |
625 { | |
626 return this._currentFile || null; | |
627 }, | |
628 | |
629 __proto__: WebInspector.Object.prototype | |
630 }; | |
631 | |
632 /** | 604 /** |
633 * @constructor | 605 * @unrestricted |
634 * @param {string} url | |
635 * @param {!WebInspector.TextRange=} selectionRange | |
636 * @param {number=} scrollLineNumber | |
637 */ | 606 */ |
638 WebInspector.TabbedEditorContainer.HistoryItem = function(url, selectionRange, s
crollLineNumber) | 607 WebInspector.TabbedEditorContainer.HistoryItem = class { |
639 { | 608 /** |
| 609 * @param {string} url |
| 610 * @param {!WebInspector.TextRange=} selectionRange |
| 611 * @param {number=} scrollLineNumber |
| 612 */ |
| 613 constructor(url, selectionRange, scrollLineNumber) { |
640 /** @const */ this.url = url; | 614 /** @const */ this.url = url; |
641 /** @const */ this._isSerializable = url.length < WebInspector.TabbedEditorC
ontainer.HistoryItem.serializableUrlLengthLimit; | 615 /** @const */ this._isSerializable = |
| 616 url.length < WebInspector.TabbedEditorContainer.HistoryItem.serializable
UrlLengthLimit; |
642 this.selectionRange = selectionRange; | 617 this.selectionRange = selectionRange; |
643 this.scrollLineNumber = scrollLineNumber; | 618 this.scrollLineNumber = scrollLineNumber; |
| 619 } |
| 620 |
| 621 /** |
| 622 * @param {!Object} serializedHistoryItem |
| 623 * @return {!WebInspector.TabbedEditorContainer.HistoryItem} |
| 624 */ |
| 625 static fromObject(serializedHistoryItem) { |
| 626 var selectionRange = serializedHistoryItem.selectionRange ? |
| 627 WebInspector.TextRange.fromObject(serializedHistoryItem.selectionRange)
: |
| 628 undefined; |
| 629 return new WebInspector.TabbedEditorContainer.HistoryItem( |
| 630 serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollL
ineNumber); |
| 631 } |
| 632 |
| 633 /** |
| 634 * @return {?Object} |
| 635 */ |
| 636 serializeToObject() { |
| 637 if (!this._isSerializable) |
| 638 return null; |
| 639 var serializedHistoryItem = {}; |
| 640 serializedHistoryItem.url = this.url; |
| 641 serializedHistoryItem.selectionRange = this.selectionRange; |
| 642 serializedHistoryItem.scrollLineNumber = this.scrollLineNumber; |
| 643 return serializedHistoryItem; |
| 644 } |
644 }; | 645 }; |
645 | 646 |
646 WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096
; | 647 WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096
; |
647 | 648 |
| 649 |
648 /** | 650 /** |
649 * @param {!Object} serializedHistoryItem | 651 * @unrestricted |
650 * @return {!WebInspector.TabbedEditorContainer.HistoryItem} | |
651 */ | 652 */ |
652 WebInspector.TabbedEditorContainer.HistoryItem.fromObject = function(serializedH
istoryItem) | 653 WebInspector.TabbedEditorContainer.History = class { |
653 { | 654 /** |
654 var selectionRange = serializedHistoryItem.selectionRange ? WebInspector.Tex
tRange.fromObject(serializedHistoryItem.selectionRange) : undefined; | 655 * @param {!Array.<!WebInspector.TabbedEditorContainer.HistoryItem>} items |
655 return new WebInspector.TabbedEditorContainer.HistoryItem(serializedHistoryI
tem.url, selectionRange, serializedHistoryItem.scrollLineNumber); | 656 */ |
656 }; | 657 constructor(items) { |
657 | |
658 WebInspector.TabbedEditorContainer.HistoryItem.prototype = { | |
659 /** | |
660 * @return {?Object} | |
661 */ | |
662 serializeToObject: function() | |
663 { | |
664 if (!this._isSerializable) | |
665 return null; | |
666 var serializedHistoryItem = {}; | |
667 serializedHistoryItem.url = this.url; | |
668 serializedHistoryItem.selectionRange = this.selectionRange; | |
669 serializedHistoryItem.scrollLineNumber = this.scrollLineNumber; | |
670 return serializedHistoryItem; | |
671 } | |
672 }; | |
673 | |
674 /** | |
675 * @constructor | |
676 * @param {!Array.<!WebInspector.TabbedEditorContainer.HistoryItem>} items | |
677 */ | |
678 WebInspector.TabbedEditorContainer.History = function(items) | |
679 { | |
680 this._items = items; | 658 this._items = items; |
681 this._rebuildItemIndex(); | 659 this._rebuildItemIndex(); |
682 }; | 660 } |
683 | 661 |
684 /** | 662 /** |
685 * @param {!Array.<!Object>} serializedHistory | 663 * @param {!Array.<!Object>} serializedHistory |
686 * @return {!WebInspector.TabbedEditorContainer.History} | 664 * @return {!WebInspector.TabbedEditorContainer.History} |
687 */ | 665 */ |
688 WebInspector.TabbedEditorContainer.History.fromObject = function(serializedHisto
ry) | 666 static fromObject(serializedHistory) { |
689 { | |
690 var items = []; | 667 var items = []; |
691 for (var i = 0; i < serializedHistory.length; ++i) | 668 for (var i = 0; i < serializedHistory.length; ++i) |
692 items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(ser
ializedHistory[i])); | 669 items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(seria
lizedHistory[i])); |
693 return new WebInspector.TabbedEditorContainer.History(items); | 670 return new WebInspector.TabbedEditorContainer.History(items); |
| 671 } |
| 672 |
| 673 /** |
| 674 * @param {string} url |
| 675 * @return {number} |
| 676 */ |
| 677 index(url) { |
| 678 return this._itemsIndex.has(url) ? /** @type {number} */ (this._itemsIndex.g
et(url)) : -1; |
| 679 } |
| 680 |
| 681 _rebuildItemIndex() { |
| 682 /** @type {!Map<string, number>} */ |
| 683 this._itemsIndex = new Map(); |
| 684 for (var i = 0; i < this._items.length; ++i) { |
| 685 console.assert(!this._itemsIndex.has(this._items[i].url)); |
| 686 this._itemsIndex.set(this._items[i].url, i); |
| 687 } |
| 688 } |
| 689 |
| 690 /** |
| 691 * @param {string} url |
| 692 * @return {!WebInspector.TextRange|undefined} |
| 693 */ |
| 694 selectionRange(url) { |
| 695 var index = this.index(url); |
| 696 return index !== -1 ? this._items[index].selectionRange : undefined; |
| 697 } |
| 698 |
| 699 /** |
| 700 * @param {string} url |
| 701 * @param {!WebInspector.TextRange=} selectionRange |
| 702 */ |
| 703 updateSelectionRange(url, selectionRange) { |
| 704 if (!selectionRange) |
| 705 return; |
| 706 var index = this.index(url); |
| 707 if (index === -1) |
| 708 return; |
| 709 this._items[index].selectionRange = selectionRange; |
| 710 } |
| 711 |
| 712 /** |
| 713 * @param {string} url |
| 714 * @return {number|undefined} |
| 715 */ |
| 716 scrollLineNumber(url) { |
| 717 var index = this.index(url); |
| 718 return index !== -1 ? this._items[index].scrollLineNumber : undefined; |
| 719 } |
| 720 |
| 721 /** |
| 722 * @param {string} url |
| 723 * @param {number} scrollLineNumber |
| 724 */ |
| 725 updateScrollLineNumber(url, scrollLineNumber) { |
| 726 var index = this.index(url); |
| 727 if (index === -1) |
| 728 return; |
| 729 this._items[index].scrollLineNumber = scrollLineNumber; |
| 730 } |
| 731 |
| 732 /** |
| 733 * @param {!Array.<string>} urls |
| 734 */ |
| 735 update(urls) { |
| 736 for (var i = urls.length - 1; i >= 0; --i) { |
| 737 var index = this.index(urls[i]); |
| 738 var item; |
| 739 if (index !== -1) { |
| 740 item = this._items[index]; |
| 741 this._items.splice(index, 1); |
| 742 } else |
| 743 item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i]); |
| 744 this._items.unshift(item); |
| 745 this._rebuildItemIndex(); |
| 746 } |
| 747 } |
| 748 |
| 749 /** |
| 750 * @param {string} url |
| 751 */ |
| 752 remove(url) { |
| 753 var index = this.index(url); |
| 754 if (index !== -1) { |
| 755 this._items.splice(index, 1); |
| 756 this._rebuildItemIndex(); |
| 757 } |
| 758 } |
| 759 |
| 760 /** |
| 761 * @param {!WebInspector.Setting} setting |
| 762 */ |
| 763 save(setting) { |
| 764 setting.set(this._serializeToObject()); |
| 765 } |
| 766 |
| 767 /** |
| 768 * @return {!Array.<!Object>} |
| 769 */ |
| 770 _serializeToObject() { |
| 771 var serializedHistory = []; |
| 772 for (var i = 0; i < this._items.length; ++i) { |
| 773 var serializedItem = this._items[i].serializeToObject(); |
| 774 if (serializedItem) |
| 775 serializedHistory.push(serializedItem); |
| 776 if (serializedHistory.length === WebInspector.TabbedEditorContainer.maxima
lPreviouslyViewedFilesCount) |
| 777 break; |
| 778 } |
| 779 return serializedHistory; |
| 780 } |
| 781 |
| 782 /** |
| 783 * @return {!Array.<string>} |
| 784 */ |
| 785 _urls() { |
| 786 var result = []; |
| 787 for (var i = 0; i < this._items.length; ++i) |
| 788 result.push(this._items[i].url); |
| 789 return result; |
| 790 } |
694 }; | 791 }; |
695 | 792 |
696 WebInspector.TabbedEditorContainer.History.prototype = { | 793 |
697 /** | 794 /** |
698 * @param {string} url | 795 * @implements {WebInspector.TabbedPaneTabDelegate} |
699 * @return {number} | 796 * @unrestricted |
700 */ | 797 */ |
701 index: function(url) | 798 WebInspector.EditorContainerTabDelegate = class { |
702 { | 799 /** |
703 return this._itemsIndex.has(url) ? /** @type {number} */(this._itemsInde
x.get(url)) : -1; | 800 * @param {!WebInspector.TabbedEditorContainer} editorContainer |
704 }, | 801 */ |
705 | 802 constructor(editorContainer) { |
706 _rebuildItemIndex: function() | 803 this._editorContainer = editorContainer; |
707 { | 804 } |
708 /** @type {!Map<string, number>} */ | 805 |
709 this._itemsIndex = new Map(); | 806 /** |
710 for (var i = 0; i < this._items.length; ++i) { | 807 * @override |
711 console.assert(!this._itemsIndex.has(this._items[i].url)); | 808 * @param {!WebInspector.TabbedPane} tabbedPane |
712 this._itemsIndex.set(this._items[i].url, i); | 809 * @param {!Array.<string>} ids |
713 } | 810 */ |
714 }, | 811 closeTabs(tabbedPane, ids) { |
715 | 812 this._editorContainer._closeTabs(ids); |
716 /** | 813 } |
717 * @param {string} url | 814 |
718 * @return {!WebInspector.TextRange|undefined} | 815 /** |
719 */ | 816 * @override |
720 selectionRange: function(url) | 817 * @param {string} tabId |
721 { | 818 * @param {!WebInspector.ContextMenu} contextMenu |
722 var index = this.index(url); | 819 */ |
723 return index !== -1 ? this._items[index].selectionRange : undefined; | 820 onContextMenu(tabId, contextMenu) { |
724 }, | 821 this._editorContainer._onContextMenu(tabId, contextMenu); |
725 | 822 } |
726 /** | |
727 * @param {string} url | |
728 * @param {!WebInspector.TextRange=} selectionRange | |
729 */ | |
730 updateSelectionRange: function(url, selectionRange) | |
731 { | |
732 if (!selectionRange) | |
733 return; | |
734 var index = this.index(url); | |
735 if (index === -1) | |
736 return; | |
737 this._items[index].selectionRange = selectionRange; | |
738 }, | |
739 | |
740 /** | |
741 * @param {string} url | |
742 * @return {number|undefined} | |
743 */ | |
744 scrollLineNumber: function(url) | |
745 { | |
746 var index = this.index(url); | |
747 return index !== -1 ? this._items[index].scrollLineNumber : undefined; | |
748 }, | |
749 | |
750 /** | |
751 * @param {string} url | |
752 * @param {number} scrollLineNumber | |
753 */ | |
754 updateScrollLineNumber: function(url, scrollLineNumber) | |
755 { | |
756 var index = this.index(url); | |
757 if (index === -1) | |
758 return; | |
759 this._items[index].scrollLineNumber = scrollLineNumber; | |
760 }, | |
761 | |
762 /** | |
763 * @param {!Array.<string>} urls | |
764 */ | |
765 update: function(urls) | |
766 { | |
767 for (var i = urls.length - 1; i >= 0; --i) { | |
768 var index = this.index(urls[i]); | |
769 var item; | |
770 if (index !== -1) { | |
771 item = this._items[index]; | |
772 this._items.splice(index, 1); | |
773 } else | |
774 item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i
]); | |
775 this._items.unshift(item); | |
776 this._rebuildItemIndex(); | |
777 } | |
778 }, | |
779 | |
780 /** | |
781 * @param {string} url | |
782 */ | |
783 remove: function(url) | |
784 { | |
785 var index = this.index(url); | |
786 if (index !== -1) { | |
787 this._items.splice(index, 1); | |
788 this._rebuildItemIndex(); | |
789 } | |
790 }, | |
791 | |
792 /** | |
793 * @param {!WebInspector.Setting} setting | |
794 */ | |
795 save: function(setting) | |
796 { | |
797 setting.set(this._serializeToObject()); | |
798 }, | |
799 | |
800 /** | |
801 * @return {!Array.<!Object>} | |
802 */ | |
803 _serializeToObject: function() | |
804 { | |
805 var serializedHistory = []; | |
806 for (var i = 0; i < this._items.length; ++i) { | |
807 var serializedItem = this._items[i].serializeToObject(); | |
808 if (serializedItem) | |
809 serializedHistory.push(serializedItem); | |
810 if (serializedHistory.length === WebInspector.TabbedEditorContainer.
maximalPreviouslyViewedFilesCount) | |
811 break; | |
812 } | |
813 return serializedHistory; | |
814 }, | |
815 | |
816 | |
817 /** | |
818 * @return {!Array.<string>} | |
819 */ | |
820 _urls: function() | |
821 { | |
822 var result = []; | |
823 for (var i = 0; i < this._items.length; ++i) | |
824 result.push(this._items[i].url); | |
825 return result; | |
826 } | |
827 }; | 823 }; |
828 | |
829 /** | |
830 * @constructor | |
831 * @implements {WebInspector.TabbedPaneTabDelegate} | |
832 * @param {!WebInspector.TabbedEditorContainer} editorContainer | |
833 */ | |
834 WebInspector.EditorContainerTabDelegate = function(editorContainer) | |
835 { | |
836 this._editorContainer = editorContainer; | |
837 }; | |
838 | |
839 WebInspector.EditorContainerTabDelegate.prototype = { | |
840 /** | |
841 * @override | |
842 * @param {!WebInspector.TabbedPane} tabbedPane | |
843 * @param {!Array.<string>} ids | |
844 */ | |
845 closeTabs: function(tabbedPane, ids) | |
846 { | |
847 this._editorContainer._closeTabs(ids); | |
848 }, | |
849 | |
850 /** | |
851 * @override | |
852 * @param {string} tabId | |
853 * @param {!WebInspector.ContextMenu} contextMenu | |
854 */ | |
855 onContextMenu: function(tabId, contextMenu) | |
856 { | |
857 this._editorContainer._onContextMenu(tabId, contextMenu); | |
858 } | |
859 }; | |
OLD | NEW |