OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * FileManager constructor. | 6 * FileManager constructor. |
7 * | 7 * |
8 * FileManager objects encapsulate the functionality of the file selector | 8 * FileManager objects encapsulate the functionality of the file selector |
9 * dialogs, as well as the full screen file manager application (though the | 9 * dialogs, as well as the full screen file manager application (though the |
10 * latter is not yet implemented). | 10 * latter is not yet implemented). |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
46 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, | 46 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, |
47 FileManager.DialogType.FULL_PAGE]); | 47 FileManager.DialogType.FULL_PAGE]); |
48 | 48 |
49 // TODO(dgozman): This will be changed to LocaleInfo. | 49 // TODO(dgozman): This will be changed to LocaleInfo. |
50 this.locale_ = new v8Locale(navigator.language); | 50 this.locale_ = new v8Locale(navigator.language); |
51 | 51 |
52 this.initFileSystem_(); | 52 this.initFileSystem_(); |
53 this.initDom_(); | 53 this.initDom_(); |
54 this.initDialogType_(); | 54 this.initDialogType_(); |
55 this.dialogDom_.style.opacity = '1'; | 55 this.dialogDom_.style.opacity = '1'; |
56 | |
57 this.lastNonGDataSearchPath_ = null; | |
56 } | 58 } |
57 | 59 |
58 FileManager.prototype = { | 60 FileManager.prototype = { |
59 __proto__: cr.EventTarget.prototype | 61 __proto__: cr.EventTarget.prototype |
60 }; | 62 }; |
61 | 63 |
62 // Anonymous "namespace". | 64 // Anonymous "namespace". |
63 (function() { | 65 (function() { |
64 | 66 |
65 // Private variables and helper functions. | 67 // Private variables and helper functions. |
(...skipping 905 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
971 this.commands_[key].disabled = !this.canExecute_(key); | 973 this.commands_[key].disabled = !this.canExecute_(key); |
972 }; | 974 }; |
973 | 975 |
974 /** | 976 /** |
975 * @param {string} commandId Command identifier. | 977 * @param {string} commandId Command identifier. |
976 * @return {boolean} True if the command can be executed for current | 978 * @return {boolean} True if the command can be executed for current |
977 * selection. | 979 * selection. |
978 */ | 980 */ |
979 FileManager.prototype.canExecute_ = function(commandId) { | 981 FileManager.prototype.canExecute_ = function(commandId) { |
980 var readonly = this.isOnReadonlyDirectory(); | 982 var readonly = this.isOnReadonlyDirectory(); |
983 var shouldCreate = !util.isSpecialReadonlyDirectory( | |
984 this.directoryModel_.getCurrentDirEntry().fullPath); | |
981 switch (commandId) { | 985 switch (commandId) { |
982 case 'copy': | 986 case 'copy': |
983 case 'cut': | 987 case 'cut': |
984 return this.document_.queryCommandEnabled(commandId); | 988 return this.document_.queryCommandEnabled(commandId); |
985 | 989 |
986 case 'paste': | 990 case 'paste': |
987 return !!this.fileTransferController_ && | 991 return !!this.fileTransferController_ && |
988 this.fileTransferController_.queryPasteCommandEnabled(); | 992 this.fileTransferController_.queryPasteCommandEnabled() && |
SeRya
2012/05/04 10:26:17
What about dropping files into the search results?
tbarzic
2012/05/05 00:56:06
Done.
| |
993 shouldCreate; | |
989 | 994 |
990 case 'rename': | 995 case 'rename': |
991 return (// Initialized to the point where we have a current directory | 996 return (// Initialized to the point where we have a current directory |
992 !readonly && | 997 !readonly && |
993 // Rename not in progress. | 998 // Rename not in progress. |
994 !this.isRenamingInProgress() && | 999 !this.isRenamingInProgress() && |
995 // Only one file selected. | 1000 // Only one file selected. |
996 this.selection && | 1001 this.selection && |
997 this.selection.totalCount == 1); | 1002 this.selection.totalCount == 1); |
998 | 1003 |
999 case 'delete': | 1004 case 'delete': |
1000 return (// Initialized to the point where we have a current directory | 1005 return (// Initialized to the point where we have a current directory |
1001 !readonly && | 1006 !readonly && |
1002 // Rename not in progress. | 1007 // Rename not in progress. |
1003 !this.isRenamingInProgress() && | 1008 !this.isRenamingInProgress() && |
1004 this.selection && | 1009 this.selection && |
1005 this.selection.totalCount > 0); | 1010 this.selection.totalCount > 0); |
1006 | 1011 |
1007 case 'newfolder': | 1012 case 'newfolder': |
1008 return !readonly && | 1013 return !readonly && |
1014 shouldCreate && | |
1009 (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE || | 1015 (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE || |
1010 this.dialogType_ == FileManager.DialogType.FULL_PAGE); | 1016 this.dialogType_ == FileManager.DialogType.FULL_PAGE); |
1011 | 1017 |
1012 case 'unmount': | 1018 case 'unmount': |
1013 return true; | 1019 return true; |
1014 | 1020 |
1015 case 'format': | 1021 case 'format': |
1016 var entry = this.directoryModel_.getCurrentRootDirEntry(); | 1022 var entry = this.directoryModel_.getCurrentRootDirEntry(); |
1017 | 1023 |
1018 return entry && DirectoryModel.getRootType(entry.fullPath) == | 1024 return entry && DirectoryModel.getRootType(entry.fullPath) == |
(...skipping 804 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1823 * Render filename label for grid and list view. | 1829 * Render filename label for grid and list view. |
1824 * @param {Entry} entry The Entry object to render. | 1830 * @param {Entry} entry The Entry object to render. |
1825 * @return {HTMLDivElement} The label. | 1831 * @return {HTMLDivElement} The label. |
1826 */ | 1832 */ |
1827 FileManager.prototype.renderFileNameLabel_ = function(entry) { | 1833 FileManager.prototype.renderFileNameLabel_ = function(entry) { |
1828 // Filename need to be in a '.filename-label' container for correct | 1834 // Filename need to be in a '.filename-label' container for correct |
1829 // work of inplace renaming. | 1835 // work of inplace renaming. |
1830 var fileName = this.document_.createElement('div'); | 1836 var fileName = this.document_.createElement('div'); |
1831 fileName.className = 'filename-label'; | 1837 fileName.className = 'filename-label'; |
1832 | 1838 |
1839 // If the entry is gdata search result, we should calculate name to use | |
1840 // instead of using |entry.name|. | |
1841 var gdataSearchResult = | |
1842 util.getFileAndDisplayNameForGDataSearchResult(entry.fullPath); | |
1843 var displayName = gdataSearchResult ? gdataSearchResult.displayName : | |
1844 entry.name; | |
1845 | |
1833 fileName.textContent = | 1846 fileName.textContent = |
1834 this.directoryModel_.getCurrentDirEntry().name == '' ? | 1847 this.directoryModel_.getCurrentDirEntry().name == '' ? |
1835 this.getRootLabel_(entry.name) : entry.name; | 1848 this.getRootLabel_(displayName) : displayName; |
1836 return fileName; | 1849 return fileName; |
1837 }; | 1850 }; |
1838 | 1851 |
1839 /** | 1852 /** |
1840 * Render the Size column of the detail table. | 1853 * Render the Size column of the detail table. |
1841 * | 1854 * |
1842 * @param {Entry} entry The Entry object to render. | 1855 * @param {Entry} entry The Entry object to render. |
1843 * @param {string} columnId The id of the column to be rendered. | 1856 * @param {string} columnId The id of the column to be rendered. |
1844 * @param {cr.ui.Table} table The table doing the rendering. | 1857 * @param {cr.ui.Table} table The table doing the rendering. |
1845 */ | 1858 */ |
(...skipping 1017 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2863 div.textContent = i == 0 ? this.getRootLabel_(path) : pathName; | 2876 div.textContent = i == 0 ? this.getRootLabel_(path) : pathName; |
2864 | 2877 |
2865 path = path + '/'; | 2878 path = path + '/'; |
2866 div.path = path; | 2879 div.path = path; |
2867 | 2880 |
2868 bc.appendChild(div); | 2881 bc.appendChild(div); |
2869 | 2882 |
2870 if (i == pathNames.length - 1) { | 2883 if (i == pathNames.length - 1) { |
2871 div.classList.add('breadcrumb-last'); | 2884 div.classList.add('breadcrumb-last'); |
2872 } else { | 2885 } else { |
2873 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); | 2886 // This is virtual, inaccessible directory. |
2887 if (path == util.GDATA_SEARCH_ROOT_PATH) { | |
2888 div.classList.add('breadcrumb-last'); | |
SeRya
2012/05/04 10:26:17
breadcrumb-last in the middle of the path looks st
tbarzic
2012/05/05 00:56:06
Done
| |
2889 } else { | |
2890 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); | |
2891 } | |
2874 | 2892 |
2875 var spacer = doc.createElement('div'); | 2893 var spacer = doc.createElement('div'); |
2876 spacer.className = 'separator'; | 2894 spacer.className = 'separator'; |
2877 bc.appendChild(spacer); | 2895 bc.appendChild(spacer); |
2878 } | 2896 } |
2879 } | 2897 } |
2880 this.truncateBreadcrumbs_(); | 2898 this.truncateBreadcrumbs_(); |
2881 }; | 2899 }; |
2882 | 2900 |
2883 FileManager.prototype.isRenamingInProgress = function() { | 2901 FileManager.prototype.isRenamingInProgress = function() { |
(...skipping 649 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3533 }; | 3551 }; |
3534 | 3552 |
3535 FileManager.prototype.onRenameInputBlur_ = function(event) { | 3553 FileManager.prototype.onRenameInputBlur_ = function(event) { |
3536 if (this.isRenamingInProgress() && !this.renameInput_.validation_) | 3554 if (this.isRenamingInProgress() && !this.renameInput_.validation_) |
3537 this.commitRename_(); | 3555 this.commitRename_(); |
3538 }; | 3556 }; |
3539 | 3557 |
3540 FileManager.prototype.commitRename_ = function() { | 3558 FileManager.prototype.commitRename_ = function() { |
3541 var input = this.renameInput_; | 3559 var input = this.renameInput_; |
3542 var entry = input.currentEntry; | 3560 var entry = input.currentEntry; |
3543 var newName = input.value; | 3561 var newNameInput = input.value; |
3562 | |
3563 // If we are renaming gdata search result, we'll have to format newName we | |
3564 // use in file system operations like: <resource_id>.<file_name>. | |
3565 var searchResultName = | |
3566 util.getFileAndDisplayNameForGDataSearchResult(entry.fullPath); | |
3567 | |
3568 var newName = | |
3569 searchResultName ? searchResultName.resourceId + '.' + newNameInput : | |
3570 newNameInput; | |
3571 var oldName = searchResultName ? searchResultName.displayName : entry.name; | |
3544 | 3572 |
3545 if (newName == entry.name) { | 3573 if (newName == entry.name) { |
3546 this.cancelRename_(); | 3574 this.cancelRename_(); |
3547 return; | 3575 return; |
3548 } | 3576 } |
3549 | 3577 |
3550 var nameNode = this.findListItemForNode_(this.renameInput_). | 3578 var nameNode = this.findListItemForNode_(this.renameInput_). |
3551 querySelector('.filename-label'); | 3579 querySelector('.filename-label'); |
3552 | 3580 |
3553 input.validation_ = true; | 3581 input.validation_ = true; |
3554 function validationDone() { | 3582 function validationDone() { |
3555 input.validation_ = false; | 3583 input.validation_ = false; |
3556 // Alert dialog restores focus unless the item removed from DOM. | 3584 // Alert dialog restores focus unless the item removed from DOM. |
3557 if (this.document_.activeElement != input) | 3585 if (this.document_.activeElement != input) |
3558 this.cancelRename_(); | 3586 this.cancelRename_(); |
3559 } | 3587 } |
3560 | 3588 |
3561 if (!this.validateFileName_(newName, validationDone.bind(this))) | 3589 if (!this.validateFileName_(newNameInput, validationDone.bind(this))) |
3562 return; | 3590 return; |
3563 | 3591 |
3564 function onError(err) { | 3592 function onError(err) { |
3565 nameNode.textContent = entry.name; | 3593 nameNode.textContent = entry.name; |
3566 this.alert.show(strf('ERROR_RENAMING', entry.name, | 3594 this.alert.show(strf('ERROR_RENAMING', entry.name, |
3567 util.getFileErrorMnemonic(err.code))); | 3595 util.getFileErrorMnemonic(err.code))); |
3568 } | 3596 } |
3569 | 3597 |
3570 this.cancelRename_(); | 3598 this.cancelRename_(); |
3571 // Optimistically apply new name immediately to avoid flickering in | 3599 // Optimistically apply new name immediately to avoid flickering in |
3572 // case of success. | 3600 // case of success. |
3573 nameNode.textContent = newName; | 3601 nameNode.textContent = newNameInput; |
3574 | 3602 |
3575 this.directoryModel_.doesExist(newName, function(exists, isFile) { | 3603 this.directoryModel_.doesExist(newName, function(exists, isFile) { |
SeRya
2012/05/04 10:26:17
I'd suggest to isolate the UI from knowlage about
tbarzic
2012/05/05 00:56:06
Really good point, that's much cleaner :)
| |
3576 if (!exists) { | 3604 if (!exists) { |
3577 this.directoryModel_.renameEntry(entry, newName, onError.bind(this)); | 3605 this.directoryModel_.renameEntry(entry, newName, onError.bind(this)); |
3578 } else { | 3606 } else { |
3579 nameNode.textContent = entry.name; | 3607 nameNode.textContent = oldName; |
3580 var message = isFile ? 'FILE_ALREADY_EXISTS' : | 3608 var message = isFile ? 'FILE_ALREADY_EXISTS' : |
3581 'DIRECTORY_ALREADY_EXISTS'; | 3609 'DIRECTORY_ALREADY_EXISTS'; |
3582 this.alert.show(strf(message, newName)); | 3610 this.alert.show(strf(message, newNameInput)); |
3583 } | 3611 } |
3584 }.bind(this)); | 3612 }.bind(this)); |
3585 }; | 3613 }; |
3586 | 3614 |
3587 FileManager.prototype.cancelRename_ = function() { | 3615 FileManager.prototype.cancelRename_ = function() { |
3588 this.renameInput_.currentEntry = null; | 3616 this.renameInput_.currentEntry = null; |
3589 | 3617 |
3590 var parent = this.renameInput_.parentNode; | 3618 var parent = this.renameInput_.parentNode; |
3591 if (parent) { | 3619 if (parent) { |
3592 parent.removeAttribute('renaming'); | 3620 parent.removeAttribute('renaming'); |
(...skipping 663 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4256 if (oldValue) { | 4284 if (oldValue) { |
4257 event.target.removeAttribute('checked'); | 4285 event.target.removeAttribute('checked'); |
4258 } else { | 4286 } else { |
4259 event.target.setAttribute('checked', 'checked'); | 4287 event.target.setAttribute('checked', 'checked'); |
4260 } | 4288 } |
4261 }; | 4289 }; |
4262 | 4290 |
4263 FileManager.prototype.onSearchBoxUpdate_ = function(event) { | 4291 FileManager.prototype.onSearchBoxUpdate_ = function(event) { |
4264 var searchString = this.dialogDom_.querySelector('#search-box').value; | 4292 var searchString = this.dialogDom_.querySelector('#search-box').value; |
4265 if (searchString) { | 4293 if (searchString) { |
4266 this.directoryModel_.addFilter( | 4294 if (!this.isOnGData()) { |
SeRya
2012/05/04 10:26:17
I think this is too low level logic. It should be
tbarzic
2012/05/05 00:56:06
Done.
| |
4267 'searchbox', | 4295 this.directoryModel_.addFilter( |
4268 function(e) { | 4296 'searchbox', |
4269 return e.name.substr(0, searchString.length) == searchString; | 4297 function(e) { |
4270 }); | 4298 return e.name.substr(0, searchString.length) == searchString; |
4299 }); | |
4300 } else { | |
4301 // If current path is not under gdata search dir, remeber it, so we can | |
4302 // navigate to it when we exit search mode. | |
4303 var currentDirPath = this.directoryModel_.getCurrentDirEntry().fullPath; | |
4304 if (currentDirPath.search(util.GDATA_SEARCH_ROOT_PATH) != 0) | |
4305 this.lastNonGDataSearchPath_ = currentDirPath; | |
4306 | |
4307 this.directoryModel_.changeDirectory( | |
SeRya
2012/05/04 10:26:17
changeDirectory causes putting the new directory i
tbarzic
2012/05/05 00:56:06
Done.
| |
4308 util.createGDataSearchPath(searchString)); | |
4309 } | |
4271 } else { | 4310 } else { |
4272 this.directoryModel_.removeFilter('searchbox'); | 4311 if (!this.isOnGData()) { |
4312 this.directoryModel_.removeFilter('searchbox'); | |
4313 } else { | |
4314 // Go to last known non gdata search directory. | |
4315 var newDirectory = this.lastNonGDataSearchPath_ || | |
4316 '/' + DirectoryModel.GDATA_DIRECTORY; | |
SeRya
2012/05/04 10:26:17
You set GData root even if search started in a sub
tbarzic
2012/05/05 00:56:06
are you sure? What am I missing?
| |
4317 this.lastNonGDataSearchPath_ = null; | |
4318 this.directoryModel_.changeDirectory(newDirectory); | |
4319 } | |
4273 } | 4320 } |
4274 }; | 4321 }; |
4275 | 4322 |
4276 FileManager.prototype.decorateSplitter = function(splitterElement) { | 4323 FileManager.prototype.decorateSplitter = function(splitterElement) { |
4277 var self = this; | 4324 var self = this; |
4278 | 4325 |
4279 var Splitter = cr.ui.Splitter; | 4326 var Splitter = cr.ui.Splitter; |
4280 | 4327 |
4281 var customSplitter = cr.ui.define('div'); | 4328 var customSplitter = cr.ui.define('div'); |
4282 | 4329 |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4397 | 4444 |
4398 this.directoryModel_.addEventListener('scan-completed', maybeShowBanner); | 4445 this.directoryModel_.addEventListener('scan-completed', maybeShowBanner); |
4399 this.directoryModel_.addEventListener('rescan-completed', maybeShowBanner); | 4446 this.directoryModel_.addEventListener('rescan-completed', maybeShowBanner); |
4400 | 4447 |
4401 var style = this.document_.createElement('link'); | 4448 var style = this.document_.createElement('link'); |
4402 style.rel = 'stylesheet'; | 4449 style.rel = 'stylesheet'; |
4403 style.href = 'css/gdrive_welcome.css'; | 4450 style.href = 'css/gdrive_welcome.css'; |
4404 this.document_.head.appendChild(style); | 4451 this.document_.head.appendChild(style); |
4405 }; | 4452 }; |
4406 })(); | 4453 })(); |
OLD | NEW |