Repository: spyder-ide/spyder
Pull request: PR: Some File Explorer Enhancements
Fixes #2406 

This pull request fixes issues in #2406, plus a few minor improvements

Changes:
* Add "Open With OS" to all files, but not folders. This action opens the selected files with the default program.
* Remove "Open" from folders, because it was a duplicate functionality of "Open in external file browser"
* Add multi-select (ctrl / shift + click)
* Remove global options from context menu. These are not file related actions and are all present in the gear menu on the top right of the file explorer.
* Rename "Open in external file browser" to the more commonly used "Show in Folder" (MacOS remains "Show in Finder").
* Make "Show in Folder" action highlight the file in Windows Explorer. Previously it would open the folder but not highlight the file. MacOS already has this implemented, and in linux it is not implemented because xdg-open doesn't support highlighting files.
* Move "Open With OS" and "Show in Folder" out of the file editing section of the context menu into their own section.

The context menu will show different actions depending on which files are selected. Other than the changes mentioned above, the rules for what actions are shown remain the same. When multiple files are selected, the set of actions shown is the intersection of the actions for the individual files. Commit/Browse Repository are not shown for multiple selections because they don't operate on multiple files.

*.py file:
![py_only](https://user-images.githubusercontent.com/15150702/29200170-e5da9a40-7e07-11e7-8674-711f5ead9514.png)

folder:
![folder_only](https://user-images.githubusercontent.com/15150702/29200180-fadcd98a-7e07-11e7-9291-23f525e7888a.png)

Multi select: folder, *.py, binary file
![folder_py_bin](https://user-images.githubusercontent.com/15150702/29200347-6c1979ae-7e09-11e7-818f-624167250a1c.png)









Problem statement:
File explorer enhancements
# Description

I have some suggestions on how to enhance the spyder file explorer panel:

At the moment if a file-type/content is supported by the spyder editor, it has an context action "Edit", otherwise it has the somewhat ubiquitous entry "Open" which delegates to the OS. This also results in folders having an "Open" command that opens the OS file explorer at that position.

I came up with these ideas, after trying to delete multiple files at once from within spyder. Which apparently is not possible because a) there is no multiselection+delete b) I could not easily switch to the OS file explorer and delete them there.
# Todo

I would propose
- [ ] an dedicated context menu entry for "open in OS file explorer" common to both files and folders.
- [ ] a "open via OS" action, i.e. allow the OS to determine what program to use to open the file.
- [ ] remove "Open" on folders ~~(or change its function to actually opening it in the spyder file 
  explorer, i.e. same as double-click action)~~
- [ ] Add multiselection (i.e. Ctrl/Shift+Click) and enable the (appropriate?) context menu actions to work on that selection. 
- [ ] remove "global" options from context menu. Move them to panel options menu instead.

Hints from issue discussion:
@penenkel I think this is a good suggestion.

Do you have any other thoughts?

hm, perhaps remove all "global" options from the context-menu and put them in the panel menu instead (most of them are already there). In my opinion context menus should only contain actions that pertain directly to the selected file/folder. Global options like the file filter, the font settings, the show all files and the show icon and text options don't belong there.

Ideally one would mirror the OS menu, but that may be to much (both in scope as well as in difficulty).
I think an "open in OS explorer" (for both files an folders) and an additional "open with OS" (for files) would be sufficient.

Agree, from a UX perspective the global options should not be there!

The 4 options on the bottom of this menu should not be there at all

![image](https://cloud.githubusercontent.com/assets/3627835/11615320/50609b5a-9c2b-11e5-9000-64255b4e7702.png)

@Nodd, @ccordoba12 ?

Agreed

With the exception of "Font" all these options (and they are really options, not menu actions) are present in the panel menu
![unbenannt](https://cloud.githubusercontent.com/assets/7736404/11616180/28efecb0-9c76-11e5-8076-70926ab467b1.png)
I suggest simply adding the font option to there and removing all four of them from the context menu.

@penenkel yes, that is what we are doing :-)

Is anyone working on this? If not, I'll take it on.
Nope, there's no one working on this. Please go ahead.

Commit message: Make "Show in Folder" highlight file in Windows Explorer

Patch to review:
diff --git a/spyder/widgets/explorer.py b/spyder/widgets/explorer.py
index 752f6cccca1..065b888d15a 100644
--- a/spyder/widgets/explorer.py
+++ b/spyder/widgets/explorer.py
@@ -45,15 +45,15 @@
 except:
     nbexporter = None    # analysis:ignore
 
+
 def open_file_in_external_explorer(filename):
     if sys.platform == "darwin":
         subprocess.call(["open", "-R", filename])
+    if os.name == 'nt':
+        subprocess.call(["explorer", "/select,", filename])
     else:
         filename=os.path.dirname(filename)
-        if os.name == 'nt':
-            os.startfile(filename)
-        else:
-            subprocess.call(["xdg-open", filename])
+        subprocess.call(["xdg-open", filename])
 
 def show_in_external_file_explorer(fnames=None):
     """Show files in external file explorer
@@ -180,6 +180,7 @@ def __init__(self, parent=None):
         self.fsmodel = None
         self.setup_fs_model()
         self._scrollbar_positions = None
+        self.setSelectionMode(self.ExtendedSelection)
                 
     #---- Model
     def setup_fs_model(self):
@@ -231,7 +232,8 @@ def get_index(self, filename):
     def get_selected_filenames(self):
         """Return selected filenames"""
         if self.selectionMode() == self.ExtendedSelection:
-            return [self.get_filename(idx) for idx in self.selectedIndexes()]
+            return [self.get_filename(idx) for idx in 
+                    self.selectionModel().selectedRows()]
         else:
             return [self.get_filename(self.currentIndex())]
             
@@ -343,7 +345,8 @@ def create_file_manage_actions(self, fnames):
         rename_action = create_action(self, _("Rename..."),
                                       icon=ima.icon('rename'),
                                       triggered=self.rename)
-        open_action = create_action(self, _("Open"), triggered=self.open)
+        open_external_action = create_action(self, _("Open With OS"), 
+                                             triggered=self.open_external)
         ipynb_convert_action = create_action(self, _("Convert to Python script"),
                                              icon=ima.icon('python'),
                                              triggered=self.convert_notebooks)
@@ -353,20 +356,22 @@ def create_file_manage_actions(self, fnames):
             actions.append(run_action)
         if only_valid and only_files:
             actions.append(edit_action)
-        else:
-            actions.append(open_action)
+        
         if sys.platform == 'darwin':
             text=_("Show in Finder")
         else:
-            text=_("Show in external file explorer")
+            text=_("Show in Folder")
         external_fileexp_action = create_action(self, text, 
                                 triggered=self.show_in_external_file_explorer)        
-        actions.append(external_fileexp_action)
         actions += [delete_action, rename_action]
         basedir = fixpath(osp.dirname(fnames[0]))
         if all([fixpath(osp.dirname(_fn)) == basedir for _fn in fnames]):
             actions.append(move_action)
         actions += [None]
+        if only_files:
+            actions.append(open_external_action)
+        actions.append(external_fileexp_action)
+        actions.append([None])
         if only_notebooks and nbexporter is not None:
             actions.append(ipynb_convert_action)
 
@@ -437,9 +442,6 @@ def create_context_menu_actions(self):
             actions.append(None)
         if fnames and all([osp.isdir(_fn) for _fn in fnames]):
             actions += self.create_folder_manage_actions(fnames)
-        if actions:
-            actions.append(None)
-        actions += self.common_actions
         return actions
 
     def update_menu(self):
@@ -533,6 +535,14 @@ def open(self, fnames=None):
                 self.parent_widget.sig_open_file.emit(fname)
             else:
                 self.open_outside_spyder([fname])
+                
+    @Slot()
+    def open_external(self, fnames=None):
+        """Open files with default application"""
+        if fnames is None:
+            fnames = self.get_selected_filenames()
+        for fname in fnames:
+            self.open_outside_spyder([fname])
         
     def open_outside_spyder(self, fnames):
         """Open file outside Spyder with the appropriate application


Code review comments:
diff_hunk: @@ -45,15 +45,15 @@
 except:
     nbexporter = None    # analysis:ignore
 
+
 def open_file_in_external_explorer(filename):
     if sys.platform == "darwin":
         subprocess.call(["open", "-R", filename])
+    if os.name == 'nt':
path: spyder/widgets/explorer.py
text: @user1:
I think this should be change it for `elif os.name == 'nt':`

```
if sys.platform == "darwin":
    ...
elif os.name == 'nt':
   ...
else:
    ...
```

if not in macOS it will execute both mac and linux command

@author:
Ah yes, I'm glad you caught that. I'll fix it now.