Merge lp:~jeremywootten/pantheon-files/fix-network-browsing into lp:~elementary-apps/pantheon-files/trunk
- fix-network-browsing
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Danielle Foré |
Approved revision: | 1704 |
Merged at revision: | 1733 |
Proposed branch: | lp:~jeremywootten/pantheon-files/fix-network-browsing |
Merge into: | lp:~elementary-apps/pantheon-files/trunk |
Diff against target: |
3997 lines (+1190/-604) 37 files modified
libcore/AbstractSlot.vala (+3/-1) libcore/gof-callwhenready.vala (+3/-6) libcore/gof-directory-async.vala (+171/-43) libcore/gof-file.c (+187/-86) libcore/gof-file.h (+5/-0) libcore/marlin-file-operations.c (+35/-12) libcore/marlin-file-operations.h (+3/-2) libcore/marlin-trash-monitor.c (+0/-2) libcore/marlin-undostack-manager.c (+7/-10) libcore/marlin-undostack-manager.h (+2/-2) libcore/pantheon-files-core-C.vapi (+18/-13) libwidgets/LocationBar.vala (+47/-51) plugins/contractor/plugin.vala (+5/-2) plugins/network-places/plugin.vala (+0/-26) plugins/pantheon-files-trash/plugin.vala (+2/-2) src/Application.vala (+13/-0) src/BookmarkList.vala (+4/-4) src/DndHandler.vala (+1/-3) src/MimeActions.vala (+8/-5) src/MultiLineEditableLabel.vala (+3/-0) src/View/AbstractDirectoryView.vala (+325/-144) src/View/ColumnView.vala (+1/-1) src/View/DiskRenderer.vala (+8/-3) src/View/LocationBar.vala (+27/-21) src/View/OverlayBar.vala (+2/-3) src/View/PropertiesWindow.vala (+15/-9) src/View/Resources.vala (+52/-0) src/View/Sidebar.vala (+51/-44) src/View/Slot.vala (+16/-17) src/View/ViewContainer.vala (+101/-57) src/View/Window.vala (+30/-10) src/View/directory_view_popup.ui (+10/-0) src/ZeitgeistManager.vala (+1/-1) src/gtk+-3.0.vapi (+16/-1) src/marlin-clipboard-manager.c (+16/-21) src/marlin-clipboard-manager.h (+1/-1) src/marlin.vapi (+1/-1) |
To merge this branch: | bzr merge lp:~jeremywootten/pantheon-files/fix-network-browsing |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Danielle Foré | Approve | ||
PerfectCarl | Pending | ||
Review via email: mp+243919@code.launchpad.net |
Commit message
Fix several issues affecting use of Samba shares.
Description of the change
This branch fixes several issues preventing basic use Files for network files and folders using the smb protocol. It is not intended to fix any other protocol,
TO TEST:
Testing of network browsing branch.
Create public and a password-protected shares with read/write access on a Samba server (preferably other than the test machine).
Open Entire Network then HOME: The Samba shares and Windows Network are displayed.
Navigate to the public share. A new entry in the sidebar (under Devices) appears with an eject icon.
Create a folder in the share. Rename the folder. Bookmark the folder by drag/drop onto the sidebar. Navigate away from the network using the sidebar. Click on the bookmarked network folder. Folder opens in view.
Navigate away from the network. Eject the public share using the sidebar. Click on the bookmarked network folder - the share remounts and the folder opens.
Select the bookmarked folder and delete it by pressing "Delete" or using the context menu (the "Trash" option is replaced by "Delete pernanently"). Click on the bookmark - the "folder does not exist" view is shown; the option to create the folder functions as expected.
Create a new file in the public share. Drag and drop it onto the new folder. The file is copied into the folder (This is because the Gdk suggested action in this situation is Gdk.DragAction.
Bookmark a file in a samba share folder, using the context menu. Bookmark appears in sidebar. Navigate away from the folder. Click the bookmark. Folder opens in view with file selected.
Open a new window and navigate to a folder containing movable files. Drag and drop files and folders between this window and the public share.
Navigate to the password protected share - a dialog requesting entry of the password appears. On entry of the correct password the share is mounted and opens. File creation, renaming, bookmarking and drag/drop should work as for the public share.
Password can be remembered by selecting the required option in the dialog.
A bookmark to a folder on password protected shared folder will bring up the password dialog if the password has not been remembered. This does not work properly with bookmarked files on password protected shares and will fixed if possible in a different branch.
Type "network://" or "network:///" into pathbar - the Entire Network view opens.
Type "smb://" or "smb:///" into pathbar. Samba root location loads.
Type "smb://workgroup" into pathbar. Windows workgroup folder opens.
Set the "restore-tabs" preference to true. Navigate to a public smb share location and close the application. Re-open to application - the smb shared folder loads. Close the application. Log out of the smb share using a different app or otherwise. Re-open FIles - the smb share is remounted and the folder displayed.
Set the "restore-tabs" preference to true. Navigate to a password protected smb share location without saving the password, and close the application. Re-open to application - folder is displayed. Close the application. Log out of the smb share using a different app or otherwise. Re-open Files - the password dialog shows.
Navigation history buttons work as expected when history includes network folders.
Pathbar work as expected, except that network default locations and shares are displayed like folders (with corresponding breadcrumbs) but right clicking on the corresponding breadcrumb does not bring up a context menu as they are not true folders.
Right clicking on network default locations and shares in view brings up a truncated context menu showing only applicable actions (except that plugins may show unapplicable actions)
PerfectCarl (name-is-carl) wrote : | # |
PerfectCarl (name-is-carl) wrote : | # |
4) The "Windows network" has a property windows that let the user change the permission (chmod) of the item. Is this really useful/taken into account?
5) The right click menu of "Windows network" shows a "open in terminal" that does nothing (the terminal app is opened though)
6) The error message is not very helpful "invalid argument is very vague"
http://
7) Put sample text in the field to help the user to fill out the form. At the moment empty fields are not very helpful.
http://
I would recommend :
- server: 12.34.56.78
- folder: nothing
- Domain name: WORKGROUP
8) The port field must be hidden if it can't be used
9) I have a samba server on my local network but I haven't configured it in Files. Viewing the "Windows server" causes the application to be irresponsive.
The console displays this:
[_LOG_LEVEL_FATAL 14:33:06.441194] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 14:33:06.441324] Files will not function properly.
[_LOG_LEVEL_FATAL 14:33:12.247325] [GLib-GIO] g_app_info_
[_LOG_LEVEL_FATAL 14:33:12.247420] Files will not function properly.
[_LOG_LEVEL_WARN 14:33:12.255183] plugin.vala:96: GDBus.Error:
[_LOG_LEVEL_FATAL 14:33:14.680825] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 14:33:14.680882] Files will not function properly.
[_LOG_LEVEL_WARN 14:33:48.733310] ZeitgeistManage
[_LOG_LEVEL_WARN 14:34:07.293536] ZeitgeistManage
[_LOG_LEVEL_WARN 14:34:07.293807] ZeitgeistManage
After some time, files displays "network://smb:0/" which is empty
Going from a tab displaying local data to "network://smb:0/" is also slow.
10) Failing to connect to the server because of invalid details give one error message "Failed to mount Windows share". If I click to "continue" without changing anything, I get another message "Please verify your user details"
http://
http://
Note that the button label is changing. Not sure that this is a good idea
PerfectCarl (name-is-carl) wrote : | # |
10) there is no way to edit the smb details once the share is mounted
11) once the share is mounted, it does not appear in the "Windows Network"
12) I have the following warnings in the console
[_LOG_LEVEL_WARN 14:49:50.850173] gof-directory-
[_LOG_LEVEL_WARN 14:49:55.875373] gof-directory-
13) For some reason, some folders can't be listed randomly: all I see is the spinning wheel. That error pops up randomly.
Note: I'm using "Samba Filesharing for Android" as a samba server
PerfectCarl (name-is-carl) wrote : | # |
14) the servers are not remembered in the "Windows network" meaning that I have to enter the details to the server every time
15) the local discovery doesn't work: my samba server that sits on my local network is not discovered
16) when I bookmark the "Windows network" and click on the newly created bookmark it says that folder does not exist (the path bar is "network://Windows Network/")
Jeremy Wootten (jeremywootten) wrote : | # |
Thanks for the comprehensive review, Carl. I think the scope of this branch will have to be restricted to very basic network browsing (essentially the 4 linked bugs and no regressions although the branch does start address some other issues).
I know there are a lot of other issues around the network browsing - some of which I don't know how to fix at the moment. I suspect some separate bugs with a decent bounty on will be required ...
I'll go through your review in detail and fix what I can.
Jeremy Wootten (jeremywootten) wrote : | # |
Initial responses to Carl's review:
1) Should we not restore network folders?
2) The "fatal" errors are not really fatal. I'll see if I can quiet them. Do we need a message in the user interface?
3) This is outside the scope of the branch -and also happens on other File Managers (Thunar, Nautilus). Do we need "Properties" dialog for network locations?
4) Do we need "Properties" dialog for network locations?
5) Will not show "Open in Terminal" for network locations.
6) and 7) and 8) Server connection dialog is outside scope of branch
9) Could you give more explanation? I am not sure what steps you carried out.
10a) This is an issue with the connect to server dialog - outside scope of branch.
10b) Could you give more explanation? I am not sure what steps you carried out.
11) Could you give more explanation? Do you mean it doesn't appear in sidebar (in the right place)?
12) Warnings are due to failed attempt to connect a FileMonitor to a network folder that does not support it. It is expected to fail under these circumstances, so the warning is not really required. I'll change to debug.
13) This needs investigating - if you can identify which circumstances cause it please let me know. I'll try to reproduce. I think I saw it in earlier versions of the branch but not recently.
14) Do you mean servers are not remembered in the "Connect to Server" dialog? This is outside the scope of the branch
15) This suddenly stopped working for me too, seemingly after connecting a Windows machine to the network. But it was the same with Thunar and Nautilus. Fixed it by changing the name resolve order in etc/samba/samb.conf [global] section to "name resolve order = bcast host lmhosts wins". It appears to be a quirk/bug in Samba itself.
16) Confirmed. This is because "Windows Network" is not a real folder but the location bar thinks it is. I'll inhibit bookmarking of pseudo folders for now.
PerfectCarl (name-is-carl) wrote : | # |
I totally agree with you about keeping the scope tight.
I did report everything that I noticed so I can have your opinion about "bug" or "out of scope".
Not that I have your feedback, I filed the following reports.
3) This is outside the scope of the branch -and also happens on other File Managers (Thunar, Nautilus). Do we need "Properties" dialog for network locations?
-> https:/
6) and 7) and 8) Server connection dialog is outside scope of branch
-> https:/
-> https:/
-> https:/
14) Do you mean servers are not remembered in the "Connect to Server" dialog? This is outside the scope of the branch
-> https:/
All those issues can be found there : https:/
(two older issues are not mine)
PerfectCarl (name-is-carl) wrote : | # |
1) Should we not restore network folders?
Well I don't know if the network folders should be restored mounted or un-mounted (I would vote for un-mounted though - as it is the case for other drives).
What ever the solution, restoring network folders must be done more asynchronously not to stop the UI thread.
4) Do we need "Properties" dialog for network locations?
Yes very much so.
One because those information are entered by the user. Not offering a way to edit them is akin to data loss to me (not a catastrophic one, I agree).
Second because once I'm connected to the server, there is no way to check what I'm connected to - or how. Suppose I don't remember which server (or user) I entered, I'm stuck.
Again I'm not sure that this is in the scope of this merge proposal but this is very important to make network folders actually usable.
PerfectCarl (name-is-carl) wrote : | # |
A blueprint has been filed with suggestion to improve how Files handle network folders
https:/
> 4)
See https:/
and https:/
> 9) Could you give more explanation? I am not sure what steps you carried out.
Well I just set up a SAMBA server on my android phone and I thought I noticed that
Files were not as responsive.
I posited that it could have been due to some SAMBA discovery code that kicked in.
Of course, I might be very wrong.
> 10b) Could you give more explanation? I am not sure what steps you carried out.
Liked to 4) See my explanation in the comment right above
> 11) Could you give more explanation? Do you mean it doesn't appear in sidebar (in the right place)?
Well I would expect the "Windows network" to list all the all the servers that
are around the computer.
Isn't it the point of that item?
In my case, I just added a connection to a Windows server and the server isn't even
listed in that special "Windows network" folder.
Could you confirm me that this is out of scope?
PerfectCarl (name-is-carl) wrote : | # |
>> 13) For some reason, some folders can't be listed randomly: all I see is the spinning wheel.
>> That error pops up randomly.
>> Note: I'm using "Samba Filesharing for Android" as a samba server
>
> 13) This needs investigating - if you can identify which circumstances cause it please let me
> know. I'll try to reproduce. I think I saw it in earlier versions of the branch but not recently.
For the time being I can't do anything because some time a folder can be listed. Some other time, the same folder can't be listed.
I can try to set up a a local folder that I would share via SAMBA and connect to it.
Maybe adding some debbuging trace could help me send you a useful report.
>> 15) the local discovery doesn't work: my samba server that sits
>> on my local network is not discovered
>
> 15) This suddenly stopped working for me too, seemingly after connecting a
> Windows machine to the network. But it was the same with Thunar and Nautilus. Fixed
> it by changing the name resolve order in etc/samba/samb.conf [global] section to "name
> resolve order = bcast host lmhosts wins". It appears to be a quirk/bug in Samba itself.
I may be very wrong but isn't something that must be handled one way or another? (either at the
distribution level, or by Files)
Note: the complete path is /etc/samba/smb.conf
My file doesn't have a "name resolver order" entry and has
# wins support = no
So I suppose that is equivalent to "wins support=yes"
Jeremy Wootten (jeremywootten) wrote : | # |
9) There is certainly a certain delay involved in the discovery process, which will depend on your network setup, but I think this should only occur the first time the network is browsed. Display of network files may also be slower depending on the speed of the connection. The aim is that Files should at least be comparable to Nautilus in speed (but this may not yet always be so ...)
11) On my setup the "Windows Network" 'folder' contains one 'folder' "Workgroup" and this contains 'folders' for each of the Samba servers on the network (One Linux and one Windows). Within each of these are 'folders' for each of the shares on those servers. I am not sure why servers should be missing - unless one is configured not to broadcast its presence? The process of discovery of servers is handled outside Files. I can't think of anything in Files that would effect this.
15) I should have said that the line needs to be added to the [global] section. I agree this is not ideal, but fixing it is definitely outside the scope of this branch (and probably my expertise).
PerfectCarl (name-is-carl) wrote : | # |
The smb.conf changes has been filed as lp:1401693
Jeremy Wootten (jeremywootten) wrote : | # |
Re 4) You seem to be referring to the "Connect Server" dialog whereas I was referring to the "Properties Window" (the blueprints you mention are for the connect server dialog). I have suppressed the Properties Window for network locations that are not real files, as at the moment it gives limited and in some cases incorrect information. We need a new way of displaying (and remembering) network connection properties.
Jeremy Wootten (jeremywootten) wrote : | # |
I can confirm that the delay in loading network views is significantly longer than before - and this is creating a bug in the column view where the new view is not filled unless you open it twice. I'll set this branch back to "In progress" while I try to address these issues.
PerfectCarl (name-is-carl) wrote : | # |
20) the mount buttons are cut off in the side bar.
http://
21) the application displays the following warnings:
[_LOG_LEVEL_WARN 13:41:07.172015] gof-directory-
[_LOG_LEVEL_WARN 13:41:07.827718] gof-directory-
[_LOG_LEVEL_WARN 13:41:07.834263] The specified location is not mounted [code 16]
[_LOG_LEVEL_WARN 13:41:08.686950] gof-directory-
[_LOG_LEVEL_WARN 13:41:09.826451] gof-directory-
[_LOG_LEVEL_WARN 13:41:09.832415] The specified location is not mounted [code 16]
[_LOG_LEVEL_WARN 13:41:11.089177] The specified location is not mounted [code 16]
22) when browsing a samba server that doesn't exist, the right click menu displayed is the one for local folders. Along with the new options :)
http://
PerfectCarl (name-is-carl) wrote : | # |
23) My samba domain (WORKGROUP) and samba share are compressable.
http://
PerfectCarl (name-is-carl) wrote : | # |
24) when I try to connect to a SAMBA folder that doesn't require a password as guest, the "connect to server" dialog shows me an error asking me for a password. When I try to click the connect button another time (without changing anything or inputting a password) it works
PerfectCarl (name-is-carl) wrote : | # |
25) Files let me connect as a non existing user to public share (shared as guest)
I don't know if that's normal but this weird.
I try to connect to smb://WORKGROUP
Files asks me for a password. I enter a bogus password for this account that doesn't exist and Files displays the folder.
Is it because the SAMBA share is public? Is it because I'm already connected to it via a another tab (see the multiple connections to the same folder in the sidebar)
PerfectCarl (name-is-carl) wrote : | # |
26) when I click on the "CRAN-virtualbox" (my SAMBA server) in Files it doesn't display the shares. On the other hand, when I click on Windows Network/
Once the shares got displayed once, I can click on SAMBA server
27) I cannot create a folder in a SAMBA share while I have read/write permission.
The folders are created on the server but not displayed by Files and the following error message is displayed:
[_LOG_LEVEL_WARN 14:08:36.609732] gof-directory-
[_LOG_LEVEL_WARN 14:08:48.812230] AbstractDirecto
[_LOG_LEVEL_WARN 14:09:03.537883] AbstractDirecto
http://
When I refresh the folder (Ctrl+R) the folder are displayed (and I can rename them)
PerfectCarl (name-is-carl) wrote : | # |
28) the first time I open a share that I can write in but that has no files or folder, the right click menu does not show any option to create files.
http://
After I work in that folder, the right click menu gets updated.
PerfectCarl (name-is-carl) wrote : | # |
29) When I drop a folder from a password protected SAMBA share to the bookmark side bar, the folder gets added. But when I unmount the share, the folder disappears from the sidebar.
When I re-mount the share the bookmark is not added
It works for public SAMBA share folder
Note: I can't do that with the SAMBA share. Only folder contained in the share. I wish I could.
PerfectCarl (name-is-carl) wrote : | # |
About 29) it works now. I think there is a tricky case when it doesn't (also at some point the app segfaults but I cannot reproduce it)
Here is the console output:
[_LOG_LEVEL_FATAL 14:24:10.441835] [GLib-GIO] g_file_equal: assertion 'G_IS_FILE (file1)' failed
[_LOG_LEVEL_FATAL 14:24:10.441885] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.442026] [GLib-GIO] g_file_get_uri: assertion 'G_IS_FILE (file)' failed
[_LOG_LEVEL_FATAL 14:24:10.442070] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.442113] [GLib] g_string_append: assertion 'val != NULL' failed
[_LOG_LEVEL_FATAL 14:24:10.442141] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.443330] [GLib-GIO] g_file_is_native: assertion 'G_IS_FILE (file)' failed
[_LOG_LEVEL_FATAL 14:24:10.443364] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.443407] [GLib-GIO] g_file_get_uri: assertion 'G_IS_FILE (file)' failed
[_LOG_LEVEL_FATAL 14:24:10.443436] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.443476] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 14:24:10.443504] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.443544] marlin_
[_LOG_LEVEL_FATAL 14:24:10.443573] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.517906] [GLib-GIO] g_file_is_native: assertion 'G_IS_FILE (file)' failed
[_LOG_LEVEL_FATAL 14:24:10.518017] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.520824] [GLib-GIO] g_file_is_native: assertion 'G_IS_FILE (file)' failed
[_LOG_LEVEL_FATAL 14:24:10.520870] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.520908] [GLib-GIO] g_file_get_uri: assertion 'G_IS_FILE (file)' failed
[_LOG_LEVEL_FATAL 14:24:10.520932] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.520963] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 14:24:10.520987] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.521017] marlin_
[_LOG_LEVEL_FATAL 14:24:10.521043] Files will not function properly.
[_LOG_LEVEL_WARN 14:24:11.760355] gof-directory-
Segmentation fault (core dumped)
Jeremy Wootten (jeremywootten) wrote : | # |
Thanks again Carl for your thorough testing!
20) Mount button truncation also occurs in trunk and is a bug in the sidebar, so will need a separate bug report.
21) I will set the mount_mountable warnings to "debug" so that will not normally appear.
22, 23 & 28) I will try to improve the relevance of the context menus. Its a trickier with network locations. The appearance of inappropriate plugin/contractor entries like "Compress" may have to be dealt with in the plugins/contractors themselves.
24, 25) These are issues with connect server dialog and outside the scope of this branch - I suggest they are added to your networking blueprint. It appears that Samba lets you connect to a public share using any credentials. Each connection with a different set of credentials appears as another entry in the sidebar but you can only distinguish them by looking at the tooltip. I haven't investigated whether there are any problems having multiple connections like this.
26, 27, 28, 29) I can confirm these (except for the segfault) but haven't found the reason yet.
Jeremy Wootten (jeremywootten) wrote : | # |
The latest revisions -
1) Silence non-critical mount_moutable warnings
2) Improve context menu relevance
3) Fix truncation of eject button
4) Less likely to fail to display servers and shares
The problems with bookmarking password protected samba shares and renaming files could not be reproduced. Please confirm still occurring in latest version.
PerfectCarl (name-is-carl) wrote : | # |
Hello,
All in all, it feels more stable and faster.
Congratulations!
I haven't tested everything yet but here's my feedback
26, 27, 28 are fixed (nice!)
22 is fixed
23 I added an issue for contractor : https:/
30) I still have the following critical warnings:
[_LOG_LEVEL_FATAL 23:16:12.363828] gof_file_
[_LOG_LEVEL_FATAL 23:16:12.363878] Files will not function properly.
31) is it expected that the permission tab in the properties dialog is not shown for files in SAMBA share whether I have read/write permissions or not?
32) is it on purpose that I can't add a samba server (\\CRAN-VIRTUALBOX) or even a samba share (\\CRAN-
I find adding at a samba share useful.
Jeremy Wootten (jeremywootten) wrote : | # |
Thanks Carl. I need to fix the conflict with current trunk now and make sure there are no regressions.
You are supposed to be able to bookmark a share (using the context menu), but I now notice that this only works if you open the share and right click on the background. If you display the server and right-click on the "folder" representing the share then you only get the minimal menu. I'll try to fix this.
I don't think there is any technical reason why a bookmark to a server should not work but at the moment it is not implemented. I'll try it.
I have deliberately not addressed the properties dialog issues in this branch. The whole issue of getting meta data from network files needs addressing. This branch is targeted mainly at browsing - i.e. connecting to and navigating network locations.
PerfectCarl (name-is-carl) wrote : | # |
> I have deliberately not addressed the properties dialog issues in this branch.
> The whole issue of getting meta data from network files needs addressing.
> This branch is targeted mainly at browsing - i.e. connecting to and navigating
> network locations.
Do you want me to file a general bug report for this?
Or is this already on your todo list?
Jeremy Wootten (jeremywootten) wrote : | # |
There are these related bugs:
https:/
https:/
but a more general bug, or series of bugs, requesting a more general overhaul of the properties dialog shown when looking at network entities (including servers), tailored to the particular protocol in use, could be filed. I could then put a bounty on the ones requiring significant effort to try and attract some assistance.
Jeremy Wootten (jeremywootten) wrote : | # |
The new revisions enable the bookmarking of network locations using the context menu and drag/drop.
Only mounted locations should be bookmarked.
PerfectCarl (name-is-carl) wrote : | # |
# Test with a SMB share from a luna VM (samba = 2:3.6.3-
40) the first time I connect to the SMB domain, all the shares are displayed with a red cross.
What is the meaning of the cross?
http://
41) Not sure if that's a bug or a feature but when two windows are opened to the same shared folder, any operation done to one window is not synchronized to the other one.
Unlike how it is done to local folders.
Please confirm that this is done on purpose.
(Unmounting the share refreshes both windows though - neat!)
42) when the mouse moves over the samba icon in the sidebar, there is a fuzzy effect on the icon
https:/
Is this intended?
43) when displaying the property dialog for a shared folder, the free/total space of the current local partition is displayed.
Either display the remote free/total space of the remote share (if available) or either hide the bar
44) question: shouldn't be the tab named 'private' instead of '/' ?
http://
45) Can't create an archive in a folder where I can create files in.
Not sure if file-roller is to blame here or Files, but that's annoying.
(Same error with Nautilus though so I would say file-roller)
46) Create a shortcut to a file present on a share folder in the sidebar.
Unmount the share.
Click on the shorcut: you will be said that the file doesn't exist.
If you click on the file one more time, the file will become available.
PerfectCarl (name-is-carl) wrote : | # |
47) message displayed in the console with a typo:
PropertiesWindo
Jeremy Wootten (jeremywootten) wrote : | # |
40) I've not seen this, but it looks like a fall-back emblem for the "locked" emblem you get on all unwritable or unreadable files. Are you using the default icon-theme?
Jeremy Wootten (jeremywootten) wrote : | # |
41) This is a consequence of not being able to create a FileMonitor for remote folders. This is what alerts the other windows that something changed for local folders, either as a result of a Files action by another window or externally. Some kind of regular polling of the remote folders would have to be implemented for changes that were initiated outside of Files to be reflected in the Files windows, but it should be possible for internal actions to trigger an update of all Files windows. I'll look into it.
Jeremy Wootten (jeremywootten) wrote : | # |
42) That appears to be due to the icon being changed to the PRELIT state by the TreeView, which is normal behaviour. The fact it appears fuzzy is presumably a theme design issue?
Jeremy Wootten (jeremywootten) wrote : | # |
43) I am getting the correct information for the remote share displayed - which is a real remote machine. Could this be due to the fact you are running the share on a VM locally?
Jeremy Wootten (jeremywootten) wrote : | # |
44) This is because "private" is treated as a share name rather than a folder, so you are displaying the root folder of the share. However, I agree from the users point of view it would be less confusing to name the tab with the share name in this situation.
Jeremy Wootten (jeremywootten) wrote : | # |
45) Compression is offered through a contractor so this needs to be fixed (if possible) there. I think it outside the scope of this branch.
Jeremy Wootten (jeremywootten) wrote : | # |
46) I cannot reproduce this either with public folder or a password protected one. Could be a timing issue? If you just click once, is the share (eventually) remounted (even though the view shows "does not exist"?
Jeremy Wootten (jeremywootten) wrote : | # |
40) I am now getting the red crosses so it may be due to something merged from trunk. It is shown where the user has neither read nor write permissions. Read-only files are shown with a lock. So it looks like the permissions on the smb domain are not correctly determined. The properties window gives them as 000. As a short-term fix the emblems could be suppressed where permissions are 000.
46) I have reproduced this now - it happens for a file but not a folder. I'll try and fix.
Jeremy Wootten (jeremywootten) wrote : | # |
40) Emblems no longer shown on remote files.
46) There is no simple fix for this so I have prevented bookmarking of files on remote locations. When a bookmark is clicked for a file on an unmounted location, there is no simple way of knowing whether it is pointing to a file or folder so it is assumed to be a folder.
PerfectCarl (name-is-carl) wrote : | # |
50)
It seems that launching files with bookmark that are unmounted produces the following output
10) when I launch the app, I have the following output.
[_LOG_LEVEL_FATAL 22:00:42.019477] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 22:00:42.019547] Files will not function properly.
[_LOG_LEVEL_FATAL 22:00:42.020752] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 22:00:42.020802] Files will not function properly.
[_LOG_LEVEL_FATAL 22:00:42.020901] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 22:00:42.020930] Files will not function properly.
Here's the full stacktrace:
#0 g_logv (log_domain=
args=
#1 0x00007ffff524ab32 in g_log (log_domain=
log_
at /build/
#2 0x00007ffff524ab59 in g_return_
pretty_
expression=
#3 0x00007ffff5a086fc in g_file_
at /build/
#4 0x00007ffff7baf2d2 in gof_file_
at /home/cran/
#5 0x00007ffff7baf3ef in gof_file_
at /home/cran/
#6 0x00007ffff7baf359 in gof_file_
at /home/cran/
#7 0x00007ffff7baf509 in gof_file_
at /home/cran/
#8 0x000000000042fa25 in marlin_
at /home/cran/
#9 0x000000000046e1a3 in marlin_
at /home/cran/
#10 marlin_
at /home/cran/
#11 0x00007ffff57761c8 in g_closure_invoke (closure=0xa08060, return_value=0x0, n_param_values=1, param_values=
...
Jeremy Wootten (jeremywootten) wrote : | # |
Then main effects of the latest revisions are to stop thumbnailing remote files and to remove inappropriate context menu entries from workgroups.
Danielle Foré (danrabbit) wrote : | # |
When attempting to merge trunk I get:
Text conflict in libcore/
Text conflict in libwidgets/
Text conflict in src/View/
3 conflicts encountered.
If we can get those conflicts solved, Cody and I would like to merge this within the next day or so
Danielle Foré (danrabbit) wrote : | # |
Right click a breadcrumb in a local path > Open in New Tab > "Unable to mount folder"
Danielle Foré (danrabbit) wrote : | # |
Something with the files open is broken. Clicking a local .jpg image gives me a "folder does not exist" error
Danielle Foré (danrabbit) wrote : | # |
Probably related to the above error, sorting is now broken. Files doesn't seem to know the difference between folders and files.
Jeremy Wootten (jeremywootten) wrote : | # |
rev 1695 fixes 'Right click a breadcrumb in a local path > Open in New Tab > "Unable to mount folder"'
rev 1696 fixes 'Something with the files open is broken. Clicking a local .jpg image gives me a "folder does not exist" error' and 'sorting is now broken. Files doesn't seem to know the difference between folders and files.'
Danielle Foré (danrabbit) wrote : | # |
Great, thanks for addressing those regressions! I'm noticing now, however, that my XDG folders don't seem to have special icons. They only have the default folder icon.
Danielle Foré (danrabbit) wrote : | # |
Cody and I have been testing, so we're gonna merge. Thanks Jeremy!
Preview Diff
1 | === modified file 'libcore/AbstractSlot.vala' |
2 | --- libcore/AbstractSlot.vala 2014-11-01 11:27:38 +0000 |
3 | +++ libcore/AbstractSlot.vala 2015-02-07 19:01:29 +0000 |
4 | @@ -69,7 +69,9 @@ |
5 | public virtual void zoom_in () {} |
6 | public virtual void zoom_normal () {} |
7 | public virtual bool set_all_selected (bool all_selected) {return false;} |
8 | - public virtual Gtk.Widget get_content_box () {return content_box as Gtk.Widget;} |
9 | + public virtual Gtk.Widget get_content_box () { |
10 | + return content_box as Gtk.Widget; |
11 | + } |
12 | public virtual string? get_root_uri () {return directory.file.uri;} |
13 | public virtual string? get_tip_uri () {return null;} |
14 | } |
15 | |
16 | === modified file 'libcore/gof-callwhenready.vala' |
17 | --- libcore/gof-callwhenready.vala 2014-11-21 19:24:45 +0000 |
18 | +++ libcore/gof-callwhenready.vala 2015-02-07 19:01:29 +0000 |
19 | @@ -36,10 +36,8 @@ |
20 | if (gof.info == null) { |
21 | call_when_ready_list.prepend (gof); |
22 | query_info_async.begin (gof, file_ready); |
23 | - //message ("cwr %s", gof.uri); |
24 | - } else { |
25 | + } else |
26 | count++; |
27 | - } |
28 | } |
29 | |
30 | /* we didn't need to queue anything, all the infos were available */ |
31 | @@ -51,7 +49,6 @@ |
32 | |
33 | private void file_ready (GOF.File gof) { |
34 | gof.update (); |
35 | - //message ("file ready %s", gof.uri); |
36 | } |
37 | |
38 | /* TODO move this to GOF.File */ |
39 | @@ -67,7 +64,7 @@ |
40 | if (fqi != null) |
41 | fqi (gof); |
42 | } catch (Error err) { |
43 | - warning ("query info failed, %s %s", err.message, gof.uri); |
44 | + debug ("query info failed, %s %s", err.message, gof.uri); |
45 | if (err is IOError.NOT_FOUND) |
46 | gof.exists = false; |
47 | if (err is IOError.NOT_MOUNTED) |
48 | @@ -76,7 +73,7 @@ |
49 | |
50 | call_when_ready_list.remove (gof); |
51 | if (call_when_ready_list == null) { |
52 | - message ("call when ready OK - empty list"); |
53 | + debug ("call when ready OK - empty list"); |
54 | if (f != null) |
55 | f (files); |
56 | } |
57 | |
58 | === modified file 'libcore/gof-directory-async.vala' |
59 | --- libcore/gof-directory-async.vala 2015-01-26 10:32:04 +0000 |
60 | +++ libcore/gof-directory-async.vala 2015-02-07 19:01:29 +0000 |
61 | @@ -22,6 +22,7 @@ |
62 | |
63 | public class GOF.Directory.Async : Object { |
64 | public GLib.File location; |
65 | + public GLib.File? selected_file = null; |
66 | public GOF.File file; |
67 | public int icon_size = 32; |
68 | |
69 | @@ -64,7 +65,6 @@ |
70 | |
71 | private unowned string gio_attrs { |
72 | get { |
73 | - var scheme = location.get_uri_scheme (); |
74 | if (scheme == "network" || scheme == "computer" || scheme == "smb") |
75 | return "*"; |
76 | else |
77 | @@ -72,14 +72,35 @@ |
78 | } |
79 | } |
80 | |
81 | + private string scheme; |
82 | + public bool is_local; |
83 | + public bool is_trash; |
84 | + public bool has_trash_dirs; |
85 | + public bool can_load; |
86 | + private bool _is_ready; |
87 | + public bool is_ready { |
88 | + get { return _is_ready;} |
89 | + private set {_is_ready = value;} |
90 | + } |
91 | + |
92 | + public bool is_cancelled { |
93 | + get { return cancellable.is_cancelled (); } |
94 | + } |
95 | + |
96 | private Async (GLib.File _file) { |
97 | location = _file; |
98 | file = GOF.File.get (location); |
99 | - file.exists = true; |
100 | cancellable = new Cancellable (); |
101 | - |
102 | - if (file.info == null) |
103 | - file.query_update (); |
104 | + state = State.NOT_LOADED; |
105 | + is_ready = false; |
106 | + can_load = true; |
107 | + |
108 | + scheme = location.get_uri_scheme (); |
109 | + is_trash = (scheme == "trash"); |
110 | + is_local = is_trash || (scheme == "file"); |
111 | + |
112 | + if (!prepare_directory ()) |
113 | + return; |
114 | |
115 | assert (directory_cache != null); |
116 | directory_cache.insert (location, this); |
117 | @@ -92,6 +113,113 @@ |
118 | uri_contain_keypath_icons = "/icons" in file.uri || "/.icons" in file.uri; |
119 | } |
120 | |
121 | + /* This is also called when reloading the directory so that another attempt to connect to |
122 | + * the network is made */ |
123 | + private bool prepare_directory () { |
124 | + if (!get_file_info ()) { |
125 | + is_ready = true; |
126 | + /* local uris are deemed loadable even if they do not exist |
127 | + * If they do not exist an opportunity will be given to create them */ |
128 | + can_load = is_local; |
129 | + return false; |
130 | + } |
131 | + |
132 | + if (!file.is_folder () && !file.is_root_network_folder () && !try_parent ()) { |
133 | + is_ready = true; |
134 | + can_load = false; |
135 | + return false; |
136 | + } |
137 | + return true; |
138 | + } |
139 | + |
140 | + private bool try_parent () { |
141 | + if (file.is_connected) { |
142 | + GLib.File? parent = location.get_parent (); |
143 | + if (parent != null) { |
144 | + file = GOF.File.get (parent); |
145 | + selected_file = location.dup (); |
146 | + location = parent; |
147 | + if (get_file_info ()) |
148 | + return true; |
149 | + } |
150 | + } |
151 | + return false; |
152 | + } |
153 | + |
154 | + private bool get_file_info () { |
155 | + if (!is_local && !check_network ()) { |
156 | + return false; |
157 | + } |
158 | + |
159 | + if (!file.ensure_query_info()) { |
160 | + if (is_local || !file.is_connected) |
161 | + return false; |
162 | + } |
163 | + |
164 | + can_load = true; |
165 | + if (!is_local) { |
166 | + mount_mountable.begin ((obj,res) => { |
167 | + can_load = false; |
168 | + try { |
169 | + mount_mountable.end (res); |
170 | + can_load = true; |
171 | + } catch (Error e) { |
172 | + debug ("mount_mountable failed: %s", e.message); |
173 | + |
174 | + if (e is IOError.ALREADY_MOUNTED) { |
175 | + can_load = true; |
176 | + } else if (e is IOError.PERMISSION_DENIED || |
177 | + e is IOError.FAILED_HANDLED) { |
178 | + |
179 | + permission_denied = true; |
180 | + } |
181 | + } |
182 | + make_ready (); |
183 | + }); |
184 | + } else |
185 | + make_ready (); |
186 | + |
187 | + return true; |
188 | + } |
189 | + |
190 | + public bool check_network () { |
191 | + var net_mon = GLib.NetworkMonitor.get_default (); |
192 | + var net_available = net_mon.get_network_available (); |
193 | + |
194 | + if (!net_available) { |
195 | + SocketConnectable? connectable = null; |
196 | + try { |
197 | + connectable = NetworkAddress.parse_uri (file.uri, 21); |
198 | + } |
199 | + catch (GLib.Error e) {} |
200 | + |
201 | + if (connectable != null) { |
202 | + try { |
203 | + if (net_mon.can_reach (connectable)) |
204 | + return true; |
205 | + } |
206 | + catch (GLib.Error e) {} |
207 | + } |
208 | + |
209 | + return false; |
210 | + } |
211 | + |
212 | + return true; |
213 | + } |
214 | + |
215 | + private void make_ready () { |
216 | + is_ready = true; |
217 | + unowned GLib.List? trash_dirs = null; |
218 | + file.mount = GOF.File.get_mount_at (location); |
219 | + |
220 | + if (file.mount != null) { |
221 | + file.is_mounted = true; |
222 | + trash_dirs = Marlin.FileOperations.get_trash_dirs_for_mount (file.mount); |
223 | + has_trash_dirs = (trash_dirs != null); |
224 | + } else |
225 | + has_trash_dirs = is_local; |
226 | + } |
227 | + |
228 | private static void toggle_ref_notify (void* data, Object object, bool is_last) { |
229 | return_if_fail (object != null && object is Object); |
230 | if (is_last) { |
231 | @@ -102,7 +230,6 @@ |
232 | dir.remove_dir_from_cache (); |
233 | |
234 | dir.remove_toggle_ref ((ToggleNotify) toggle_ref_notify); |
235 | - } else { |
236 | } |
237 | } |
238 | |
239 | @@ -122,9 +249,11 @@ |
240 | idle_consume_changes_id = 0; |
241 | } |
242 | |
243 | + if (file_hash != null) |
244 | + file_hash.remove_all (); |
245 | + |
246 | monitor = null; |
247 | sorted_dirs = null; |
248 | - file_hash.remove_all (); |
249 | files_count = 0; |
250 | state = State.NOT_LOADED; |
251 | } |
252 | @@ -141,14 +270,11 @@ |
253 | public void load (GOFFileLoadedFunc? file_loaded_func = null) { |
254 | cancellable.reset (); |
255 | longest_file_name = ""; |
256 | - |
257 | + permission_denied = false; |
258 | if (state == State.LOADING) |
259 | return; |
260 | |
261 | if (state != State.LOADED) { |
262 | - /* clear directory info if it's not fully loaded */ |
263 | - if (state == State.LOADING) |
264 | - clear_directory_info (); |
265 | |
266 | list_directory.begin (file_loaded_func); |
267 | |
268 | @@ -159,7 +285,8 @@ |
269 | monitor.changed.connect (directory_changed); |
270 | } catch (IOError e) { |
271 | if (!(e is IOError.NOT_MOUNTED)) { |
272 | - warning ("directory monitor failed: %s %s", e.message, file.uri); |
273 | + /* Will fail for remote filesystems - not an error */ |
274 | + debug ("directory monitor failed: %s %s", e.message, file.uri); |
275 | } |
276 | } |
277 | } |
278 | @@ -200,6 +327,8 @@ |
279 | monitor_blocked = false; |
280 | monitor.changed.connect (directory_changed); |
281 | } |
282 | + if (!is_local) |
283 | + need_reload (); |
284 | } |
285 | |
286 | private void update_longest_file_name (GOF.File gof) { |
287 | @@ -208,11 +337,14 @@ |
288 | } |
289 | |
290 | public void load_hiddens () { |
291 | + if (!can_load) |
292 | + return; |
293 | + |
294 | if (state != State.LOADED) { |
295 | load (); |
296 | } else { |
297 | foreach (GOF.File gof in file_hash.get_values ()) { |
298 | - if (gof != null && gof.info != null && gof.is_hidden) { |
299 | + if (gof != null && gof.is_hidden) { |
300 | if (track_longest_name) |
301 | update_longest_file_name (gof); |
302 | |
303 | @@ -220,8 +352,8 @@ |
304 | } |
305 | } |
306 | } |
307 | - if (!cancellable.is_cancelled ()) |
308 | - done_loading (); |
309 | + |
310 | + done_loading (); |
311 | } |
312 | |
313 | public void update_desktop_files () { |
314 | @@ -229,45 +361,40 @@ |
315 | if (gof != null && gof.info != null |
316 | && (!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files) |
317 | && gof.is_desktop) |
318 | + |
319 | gof.update_desktop_file (); |
320 | } |
321 | } |
322 | |
323 | public async void mount_mountable () throws Error { |
324 | - debug ("mount_mountable %s", file.uri); |
325 | - |
326 | /* TODO pass GtkWindow *parent to Gtk.MountOperation */ |
327 | var mount_op = new Gtk.MountOperation (null); |
328 | - |
329 | - if (file.file_type != FileType.MOUNTABLE) |
330 | - yield location.mount_enclosing_volume (0, mount_op, cancellable); |
331 | - else |
332 | - yield location.mount_mountable (0, mount_op, cancellable); |
333 | - |
334 | - file.is_mounted = true; |
335 | - yield query_info_async (file, file_info_available); |
336 | + yield location.mount_enclosing_volume (0, mount_op, cancellable); |
337 | } |
338 | |
339 | private async void list_directory (GOFFileLoadedFunc? file_loaded_func = null) { |
340 | + if (!can_load) { |
341 | + state = State.NOT_LOADED; |
342 | + return; |
343 | + } |
344 | + |
345 | file.exists = true; |
346 | files_count = 0; |
347 | state = State.LOADING; |
348 | |
349 | - debug ("list directory %s", file.uri); |
350 | - |
351 | try { |
352 | var e = yield this.location.enumerate_children_async (gio_attrs, 0, 0, cancellable); |
353 | while (state == State.LOADING) { |
354 | var files = yield e.next_files_async (200, 0, cancellable); |
355 | - |
356 | if (files == null) |
357 | break; |
358 | |
359 | bool show_hidden = Preferences.get_default ().pref_show_hidden_files; |
360 | |
361 | foreach (var file_info in files) { |
362 | - GLib.File loc = location.get_child ((string) file_info.get_name ()); |
363 | - GOF.File gof = GOF.File.cache_lookup (loc); |
364 | + string uri = Path.build_filename (location.get_uri (), file_info.get_name ()); |
365 | + GLib.File loc = GLib.File.new_for_uri (uri); |
366 | + GOF.File? gof = GOF.File.cache_lookup (loc); |
367 | |
368 | if (gof == null) |
369 | gof = new GOF.File (loc, location); |
370 | @@ -295,22 +422,21 @@ |
371 | file.exists = true; |
372 | state = State.LOADED; |
373 | } else { |
374 | - debug ("WARNING load() has been called again before LOADING finished"); |
375 | + warning ("WARNING load() has been called again before LOADING finished"); |
376 | return; |
377 | } |
378 | } catch (Error err) { |
379 | - warning ("%s %s", err.message, file.uri); |
380 | + warning ("Listing directory error: %s %s", err.message, file.uri); |
381 | state = State.NOT_LOADED; |
382 | |
383 | if (err is IOError.NOT_FOUND || err is IOError.NOT_DIRECTORY) |
384 | file.exists = false; |
385 | - |
386 | else if (err is IOError.PERMISSION_DENIED) |
387 | permission_denied = true; |
388 | - |
389 | else if (err is IOError.NOT_MOUNTED) |
390 | file.is_mounted = false; |
391 | } |
392 | + |
393 | if (file_loaded_func == null && !cancellable.is_cancelled ()) |
394 | done_loading (); |
395 | } |
396 | @@ -366,7 +492,7 @@ |
397 | |
398 | gof.update (); |
399 | |
400 | - if (gof.info != null && (!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files)) |
401 | + if ((!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files)) |
402 | file_added (gof); |
403 | |
404 | if (!gof.is_hidden && gof.is_folder ()) { |
405 | @@ -380,10 +506,6 @@ |
406 | } |
407 | } |
408 | |
409 | - private void file_info_available (GOF.File gof) { |
410 | - gof.update (); |
411 | - } |
412 | - |
413 | private void notify_file_changed (GOF.File gof) { |
414 | query_info_async.begin (gof, changed_and_refresh); |
415 | } |
416 | @@ -549,7 +671,11 @@ |
417 | |
418 | public static Async from_gfile (GLib.File file) { |
419 | /* Note: cache_lookup creates directory_cache if necessary */ |
420 | - return cache_lookup (file) ?? new Async (file); |
421 | + Async? dir = cache_lookup (file); |
422 | + if (dir != null && !dir.is_local) |
423 | + dir = null; |
424 | + |
425 | + return dir ?? new Async (file); |
426 | } |
427 | |
428 | public static Async from_file (GOF.File gof) { |
429 | @@ -578,7 +704,7 @@ |
430 | cached_dir = directory_cache.lookup (file); |
431 | |
432 | if (cached_dir != null) { |
433 | - debug ("found cached dir %s\n", cached_dir.file.uri); |
434 | + debug ("found cached dir %s", cached_dir.file.uri); |
435 | if (cached_dir.file.info == null) |
436 | cached_dir.file.query_update (); |
437 | } |
438 | @@ -645,12 +771,11 @@ |
439 | return sorted_dirs; |
440 | |
441 | foreach (var gof in file_hash.get_values()) { |
442 | - if (gof.info != null && !gof.is_hidden && gof.is_folder ()) |
443 | + if (!gof.is_hidden && gof.is_folder ()) |
444 | sorted_dirs.prepend (gof); |
445 | } |
446 | |
447 | sorted_dirs.sort (GOF.File.compare_by_display_name); |
448 | - |
449 | return sorted_dirs; |
450 | } |
451 | |
452 | @@ -714,6 +839,9 @@ |
453 | } |
454 | |
455 | public void queue_load_thumbnails (int size) { |
456 | + if (!is_local) |
457 | + return; |
458 | + |
459 | icon_size = size; |
460 | if (this.state == State.LOADING) |
461 | return; |
462 | |
463 | === modified file 'libcore/gof-file.c' |
464 | --- libcore/gof-file.c 2015-01-16 23:04:51 +0000 |
465 | +++ libcore/gof-file.c 2015-02-07 19:01:29 +0000 |
466 | @@ -68,7 +68,6 @@ |
467 | } |
468 | |
469 | const gchar *gof_file_get_thumbnail_path (GOFFile *file); |
470 | -static GMount *get_mount_at (GFile *target); |
471 | |
472 | static GIcon * |
473 | get_icon_user_special_dirs(char *path) |
474 | @@ -175,19 +174,6 @@ |
475 | file->can_unmount = FALSE; |
476 | } |
477 | |
478 | -static gboolean |
479 | -gof_file_compare_uri_schemes (GOFFile *file, const char **schemes) |
480 | -{ |
481 | - int iterator; |
482 | - |
483 | - for (iterator = 0; iterator < G_N_ELEMENTS (schemes); iterator++) { |
484 | - if (g_file_has_uri_scheme (file->location, schemes[iterator])) |
485 | - return TRUE; |
486 | - } |
487 | - |
488 | - return FALSE; |
489 | -} |
490 | - |
491 | /** |
492 | * gof_file_is_location_uri_default: |
493 | * |
494 | @@ -198,56 +184,130 @@ |
495 | static gboolean |
496 | gof_file_is_location_uri_default (GOFFile *file) |
497 | { |
498 | + g_return_val_if_fail (file->info != NULL, FALSE); |
499 | + gboolean res; |
500 | + |
501 | const char *target_uri = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); |
502 | |
503 | - if (target_uri != NULL) { |
504 | - gchar **split = g_strsplit (target_uri, "/", 4); |
505 | - if (split[3] == NULL || !strcmp (split[3], "")) { |
506 | - g_strfreev (split); |
507 | - return TRUE; |
508 | + if (target_uri == NULL) |
509 | + target_uri = file->uri; |
510 | + |
511 | + gchar **split = g_strsplit (target_uri, "/", 4); |
512 | + res = (split[3] == NULL || !strcmp (split[3], "")); |
513 | + g_strfreev (split); |
514 | + |
515 | + return res; |
516 | +} |
517 | + |
518 | +gboolean |
519 | +gof_file_is_mountable (GOFFile *file) { |
520 | + g_return_val_if_fail (file->info != NULL, FALSE); |
521 | + return g_file_info_get_file_type(file->info) == G_FILE_TYPE_MOUNTABLE; |
522 | +} |
523 | + |
524 | +guint |
525 | +get_number_of_uri_parts (GOFFile *file) { |
526 | + const char *target_uri = NULL; |
527 | + if (file->info != NULL) |
528 | + target_uri = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); |
529 | + |
530 | + if (target_uri == NULL) |
531 | + target_uri = file->uri; |
532 | + |
533 | + gchar **split = g_strsplit (target_uri, "/", 6); |
534 | + guint i, count; |
535 | + count = 0; |
536 | + for (i = 0; i < g_strv_length (split); i++) { |
537 | + if (split [i][0] != NULL) { |
538 | + count++; |
539 | } |
540 | - g_strfreev (split); |
541 | - } |
542 | - |
543 | - return FALSE; |
544 | + } |
545 | + g_strfreev (split); |
546 | + return count; |
547 | +} |
548 | + |
549 | +gboolean |
550 | +gof_file_is_smb_share (GOFFile *file) |
551 | +{ |
552 | + gboolean res; |
553 | + res = FALSE; |
554 | + |
555 | + if (gof_file_is_smb_uri_scheme (file) || gof_file_is_network_uri_scheme (file)) { |
556 | + res = get_number_of_uri_parts (file) == 3; |
557 | + } |
558 | + |
559 | + return res; |
560 | +} |
561 | + |
562 | +gboolean |
563 | +gof_file_is_smb_server (GOFFile *file) |
564 | +{ |
565 | + gboolean res; |
566 | + res = FALSE; |
567 | + |
568 | + if (gof_file_is_smb_uri_scheme (file) || gof_file_is_network_uri_scheme (file)){ |
569 | + res = get_number_of_uri_parts (file) == 2; |
570 | + } |
571 | + |
572 | + return res; |
573 | } |
574 | |
575 | gboolean |
576 | gof_file_is_remote_uri_scheme (GOFFile *file) |
577 | { |
578 | - if (gof_file_is_root_network_folder (file)) |
579 | + if (gof_file_is_root_network_folder (file) || gof_file_is_other_uri_scheme (file)) |
580 | return TRUE; |
581 | - |
582 | - const char* SCHEMES[] = { "afp", "dav", "davs", "ftp", "sftp" }; |
583 | - return gof_file_compare_uri_schemes (file, SCHEMES); |
584 | } |
585 | |
586 | gboolean |
587 | gof_file_is_root_network_folder (GOFFile *file) |
588 | { |
589 | - if (gof_file_is_network_uri_scheme (file)) |
590 | - return TRUE; |
591 | - |
592 | - return (gof_file_is_smb_uri_scheme (file) && gof_file_is_location_uri_default (file)); |
593 | + return (gof_file_is_network_uri_scheme (file) || gof_file_is_smb_server (file)); |
594 | } |
595 | |
596 | gboolean |
597 | gof_file_is_network_uri_scheme (GOFFile *file) |
598 | { |
599 | - const char* SCHEMES[] = { "network" }; |
600 | - return gof_file_compare_uri_schemes (file, SCHEMES); |
601 | + if (!G_IS_FILE (file->location)) |
602 | + return TRUE; |
603 | + |
604 | + return g_file_has_uri_scheme (file->location, "network"); |
605 | } |
606 | |
607 | gboolean |
608 | gof_file_is_smb_uri_scheme (GOFFile *file) |
609 | { |
610 | - const char* SCHEMES[] = { "smb" }; |
611 | - return gof_file_compare_uri_schemes (file, SCHEMES); |
612 | -} |
613 | - |
614 | -void gof_file_get_folder_icon_from_uri_or_path (GOFFile *file) |
615 | -{ |
616 | - if (!file->is_hidden && file->uri != NULL && file->icon == NULL) { |
617 | + if (!G_IS_FILE (file->location)) |
618 | + return TRUE; |
619 | + |
620 | + return g_file_has_uri_scheme (file->location, "smb"); |
621 | +} |
622 | + |
623 | +gboolean |
624 | +gof_file_is_other_uri_scheme (GOFFile *file) |
625 | +{ |
626 | + GFile *loc = file->location; |
627 | + if (!G_IS_FILE (file->location)) |
628 | + return TRUE; |
629 | + |
630 | + gboolean res; |
631 | + |
632 | + res = g_file_has_uri_scheme (loc, "ftp") || |
633 | + g_file_has_uri_scheme (loc, "sftp") || |
634 | + g_file_has_uri_scheme (loc, "afp") || |
635 | + g_file_has_uri_scheme (loc, "dav") || |
636 | + g_file_has_uri_scheme (loc, "davs"); |
637 | + |
638 | + return res; |
639 | +} |
640 | + |
641 | +void |
642 | +gof_file_get_folder_icon_from_uri_or_path (GOFFile *file) |
643 | +{ |
644 | + if (file->icon != NULL) |
645 | + return; |
646 | + |
647 | + if (!file->is_hidden && file->uri != NULL) { |
648 | char *path = g_filename_from_uri (file->uri, NULL, NULL); |
649 | file->icon = get_icon_user_special_dirs(path); |
650 | _g_free0 (path); |
651 | @@ -364,8 +424,10 @@ |
652 | file->target_location = g_file_new_for_uri (target_uri); |
653 | gof_file_target_location_update (file); |
654 | |
655 | - if (file->file_type == G_FILE_TYPE_MOUNTABLE) |
656 | - file->mount = get_mount_at (file->target_location); |
657 | + if (file->file_type == G_FILE_TYPE_MOUNTABLE) { |
658 | + file->mount = gof_file_get_mount_at (file->target_location); |
659 | + file->is_mounted = (file->mount != NULL); |
660 | + } |
661 | } |
662 | } |
663 | |
664 | @@ -457,10 +519,8 @@ |
665 | |
666 | /* sizes */ |
667 | gof_file_update_size (file); |
668 | - |
669 | /* modified date */ |
670 | file->formated_modified = gof_file_get_formated_time (file, G_FILE_ATTRIBUTE_TIME_MODIFIED); |
671 | - |
672 | /* icon */ |
673 | if (file->is_directory) { |
674 | gof_file_get_folder_icon_from_uri_or_path (file); |
675 | @@ -486,25 +546,30 @@ |
676 | file->permissions = g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_MODE); |
677 | const char *owner = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_OWNER_USER); |
678 | const char *group = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_OWNER_GROUP); |
679 | + |
680 | if (owner != NULL) |
681 | file->owner = strdup (owner); |
682 | + |
683 | if (group != NULL) |
684 | file->group = strdup (group); |
685 | + |
686 | if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_UNIX_UID)) { |
687 | file->uid = g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_UID); |
688 | if (file->owner == NULL) |
689 | file->owner = g_strdup_printf ("%d", file->uid); |
690 | } |
691 | + |
692 | if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_UNIX_GID)) { |
693 | file->gid = g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_GID); |
694 | if (file->group == NULL) |
695 | file->group = g_strdup_printf ("%d", file->gid); |
696 | } |
697 | |
698 | - if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT)) { |
699 | + if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT)) |
700 | file->can_unmount = g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT); |
701 | - } |
702 | + |
703 | gof_file_update_trash_info (file); |
704 | + |
705 | gof_file_update_emblem (file); |
706 | } |
707 | |
708 | @@ -551,7 +616,6 @@ |
709 | if (flags & GOF_FILE_ICON_FLAGS_USE_THUMBNAILS |
710 | && file->flags == GOF_FILE_THUMB_STATE_LOADING) { |
711 | gicon = g_themed_icon_new (ICON_NAME_THUMBNAIL_LOADING); |
712 | - //printf ("thumbnail loading\n"); |
713 | } else { |
714 | gicon = _g_object_ref0 (file->icon); |
715 | } |
716 | @@ -645,6 +709,14 @@ |
717 | |
718 | void gof_file_update_emblem (GOFFile *file) |
719 | { |
720 | + /* Do not try to add emblems to network and remote files (except smb) - can cause blocking io*/ |
721 | + if (gof_file_is_other_uri_scheme (file) || gof_file_is_network_uri_scheme (file)) |
722 | + return; |
723 | + |
724 | + /* Do not try to add emblems to smb shares either */ |
725 | + if (gof_file_is_smb_share (file)) |
726 | + return; |
727 | + |
728 | /* erase previous stored emblems */ |
729 | if (file->emblems_list != NULL) { |
730 | g_list_free (file->emblems_list); |
731 | @@ -654,6 +726,7 @@ |
732 | if(plugins != NULL) |
733 | marlin_plugin_manager_update_file_info (plugins, file); |
734 | |
735 | + |
736 | if(gof_file_is_symlink(file) || (file->is_desktop && file->target_gof)) |
737 | { |
738 | gof_file_add_emblem(file, "emblem-symbolic-link"); |
739 | @@ -670,10 +743,12 @@ |
740 | else |
741 | gof_file_add_emblem (file, "emblem-unreadable"); |
742 | } |
743 | + |
744 | /* TODO update signal on real change */ |
745 | //g_warning ("update emblem %s", file.uri); |
746 | if (file->emblems_list != NULL) |
747 | gof_file_icon_changed (file); |
748 | + |
749 | } |
750 | |
751 | void gof_file_add_emblem (GOFFile* file, const gchar* emblem) |
752 | @@ -694,13 +769,13 @@ |
753 | { |
754 | if (error != NULL) |
755 | { |
756 | - g_warning ("%s [code %d]\n", error->message, error->code); |
757 | + g_debug ("%s [code %d]\n", error->message, error->code); |
758 | g_clear_error (&error); |
759 | } |
760 | } |
761 | |
762 | -static GMount * |
763 | -get_mount_at (GFile *target) |
764 | +GMount * |
765 | +gof_file_get_mount_at (GFile *target) |
766 | { |
767 | GVolumeMonitor *monitor; |
768 | GFile *root; |
769 | @@ -738,18 +813,21 @@ |
770 | GFileInfo *info = NULL; |
771 | GError *err = NULL; |
772 | |
773 | - info = g_file_query_info (file->location, GOF_FILE_GIO_DEFAULT_ATTRIBUTES, |
774 | - 0, NULL, &err); |
775 | + g_return_val_if_fail (G_IS_FILE (file->location), NULL); |
776 | + |
777 | + info = g_file_query_info (file->location, "*", 0, NULL, &err); |
778 | |
779 | if (err != NULL) { |
780 | if (err->domain == G_IO_ERROR && err->code == G_IO_ERROR_NOT_MOUNTED) { |
781 | file->is_mounted = FALSE; |
782 | - } |
783 | - if (err->code == G_IO_ERROR_NOT_FOUND |
784 | + } else if (err->code == G_IO_ERROR_NOT_FOUND |
785 | || err->code == G_IO_ERROR_NOT_DIRECTORY) { |
786 | file->exists = FALSE; |
787 | + } else if (err->code == G_IO_ERROR_TIMED_OUT) { |
788 | + file->is_connected = FALSE; |
789 | } |
790 | - print_error (err); |
791 | + |
792 | + print_error (err); /* also frees error */ |
793 | } |
794 | return info; |
795 | } |
796 | @@ -853,6 +931,7 @@ |
797 | /* assume the file is mounted by default */ |
798 | file->is_mounted = TRUE; |
799 | file->exists = TRUE; |
800 | + file->is_connected = TRUE; |
801 | |
802 | file->flags = 0; |
803 | file->pix_size = -1; |
804 | @@ -878,10 +957,8 @@ |
805 | #endif |
806 | |
807 | g_clear_object (&file->info); |
808 | - if (file->location) |
809 | - g_object_unref (file->location); |
810 | - if (file->directory) |
811 | - g_object_unref (file->directory); |
812 | + _g_object_unref0 (file->location); |
813 | + _g_object_unref0 (file->directory); |
814 | _g_free0 (file->uri); |
815 | _g_free0(file->basename); |
816 | _g_free0(file->utf8_collation_key); |
817 | @@ -889,8 +966,7 @@ |
818 | _g_free0(file->format_size); |
819 | _g_free0(file->formated_modified); |
820 | _g_object_unref0 (file->icon); |
821 | - if (file->pix) |
822 | - g_object_unref (file->pix); |
823 | + _g_object_unref0 (file->pix); |
824 | //g_clear_object (&file->pix); |
825 | |
826 | _g_free0 (file->custom_display_name); |
827 | @@ -1121,7 +1197,6 @@ |
828 | } |
829 | |
830 | result = gof_file_compare_for_sort_internal (file1, file2, directories_first, reversed); |
831 | - //g_message ("res %d %s %s\n", result, file1->name, file2->name); |
832 | |
833 | if (result == 0) { |
834 | switch (sort_type) { |
835 | @@ -1216,15 +1291,19 @@ |
836 | { |
837 | g_return_val_if_fail (GOF_IS_FILE (file), FALSE); |
838 | |
839 | - if (file->target_gof) |
840 | + /* Take care not to create infinite loop */ |
841 | + if (file->target_gof && !g_file_equal (file->location, file->target_gof->location)) |
842 | return gof_file_is_writable (file->target_gof); |
843 | + |
844 | if (file->info == NULL) |
845 | return FALSE; |
846 | + |
847 | if (!g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) { |
848 | - /* For some reason, the trash folder doesn't have this attribute defined. |
849 | - * The function must be forced to return TRUE if the folder in question |
850 | - * is the trash folder (since it is writable). */ |
851 | - if (strncmp (file->uri, "trash:///", 10) == 0) |
852 | + /* Trash folder and network folders do not necessarily have this attribute defined. |
853 | + * The function must be forced to return TRUE in these cases. */ |
854 | + if (strncmp (file->uri, "trash:///", 10) == 0 || |
855 | + gof_file_is_smb_uri_scheme (file) || |
856 | + gof_file_is_remote_uri_scheme (file)) |
857 | return TRUE; |
858 | |
859 | return FALSE; |
860 | @@ -1578,16 +1657,20 @@ |
861 | /* default to whatever GTK+ thinks for the suggested action */ |
862 | suggested_action = gdk_drag_context_get_suggested_action (context); |
863 | |
864 | - char *uri = g_file_get_uri (file_list->data); |
865 | - g_debug ("%s %s %s\n", G_STRFUNC, file->uri, uri); |
866 | - _g_free0 (uri); |
867 | - |
868 | /* check if we have a writable directory here or an executable file */ |
869 | if (gof_file_is_folder (file) && gof_file_is_writable (file)) |
870 | { |
871 | /* determine the possible actions */ |
872 | actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK); |
873 | |
874 | + char *scheme; |
875 | + scheme = g_file_get_uri_scheme (gof_file_get_target_location (file)); |
876 | + /* do not allow symbolic links to remote filesystems */ |
877 | + if (!g_str_has_prefix (scheme, "file")) |
878 | + actions &= ~(GDK_ACTION_LINK); |
879 | + |
880 | + g_free (scheme); |
881 | + |
882 | /* cannot create symbolic links in the trash or copy to the trash */ |
883 | if (gof_file_is_trashed (file)) |
884 | actions &= ~(GDK_ACTION_COPY | GDK_ACTION_LINK); |
885 | @@ -1595,12 +1678,16 @@ |
886 | /* check up to 100 of the paths (just in case somebody tries to |
887 | * drag around his music collection with 5000 files). |
888 | */ |
889 | + |
890 | for (lp = file_list, n = 0; lp != NULL && n < 100; lp = lp->next, ++n) |
891 | { |
892 | - uri = g_file_get_uri (lp->data); |
893 | - g_debug ("%s %s %s\n", G_STRFUNC, file->uri, uri); |
894 | - _g_free0 (uri); |
895 | + scheme = g_file_get_uri_scheme (lp->data); |
896 | + if (!g_str_has_prefix (scheme, "file")) { |
897 | + /* do not allow symbolic links from remote filesystems */ |
898 | + actions &= ~(GDK_ACTION_LINK); |
899 | + } |
900 | |
901 | + g_free (scheme); |
902 | /* we cannot drop a file on itself */ |
903 | if (G_UNLIKELY (g_file_equal (gof_file_get_target_location (file), lp->data))) |
904 | return 0; |
905 | @@ -1649,21 +1736,18 @@ |
906 | && g_file_info_get_attribute_uint32 (ofile->info, |
907 | G_FILE_ATTRIBUTE_UNIX_UID) != effective_user_id)) |
908 | { |
909 | - //printf ("%s default suggested action GDK_ACTION_COPY\n", G_STRFUNC); |
910 | /* default to copy and get outa here */ |
911 | suggested_action = GDK_ACTION_COPY; |
912 | break; |
913 | } |
914 | } |
915 | - //printf ("%s actions MOVE %d COPY %d suggested %d\n", G_STRFUNC, GDK_ACTION_MOVE, GDK_ACTION_COPY, suggested_action); |
916 | } |
917 | } |
918 | else if (!gof_file_is_folder (file) && gof_file_is_executable (file)) |
919 | { |
920 | /* determine the possible actions */ |
921 | actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE); |
922 | - } |
923 | - else |
924 | + } else |
925 | return 0; |
926 | |
927 | /* determine the preferred action based on the context */ |
928 | @@ -1914,7 +1998,7 @@ |
929 | gfiles = gof_files_get_location_list (files); |
930 | |
931 | succeed = g_app_info_launch (app_info, gfiles, G_APP_LAUNCH_CONTEXT (context), &error); |
932 | - print_error (error); |
933 | + print_error (error); /* also frees error */ |
934 | |
935 | g_list_free_full (gfiles, (GDestroyNotify) eel_g_file_unref); |
936 | g_object_unref (context); |
937 | @@ -2387,12 +2471,26 @@ |
938 | gboolean |
939 | gof_file_is_folder (GOFFile *file) |
940 | { |
941 | - /* TODO check */ |
942 | - if (file->is_directory) |
943 | - return TRUE; |
944 | - if (file->target_location != NULL |
945 | - && ( (file->file_type == G_FILE_TYPE_MOUNTABLE && g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT)) || (file->target_gof && file->target_gof->is_directory && gof_preferences_get_default ()->pref_interpret_desktop_files) )) |
946 | - return TRUE; |
947 | + /* TODO check this works for non-local files and other uri schemes*/ |
948 | + if ((file->is_directory || gof_file_get_ftype (file) == NULL) && !gof_file_is_root_network_folder (file)) |
949 | + return TRUE; |
950 | + |
951 | + if (gof_file_is_smb_share (file)) |
952 | + return TRUE; |
953 | + |
954 | + if (file->file_type == G_FILE_TYPE_MOUNTABLE && |
955 | + file->info != NULL && |
956 | + g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT)) |
957 | + |
958 | + return TRUE; |
959 | + |
960 | + if (file->target_gof && file->target_gof->is_directory) { |
961 | + /* file->target_gof is directory */ |
962 | + if (gof_preferences_get_default ()->pref_interpret_desktop_files || |
963 | + gof_file_is_network_uri_scheme (file->target_gof)) |
964 | + |
965 | + return TRUE; |
966 | + } |
967 | |
968 | return FALSE; |
969 | } |
970 | @@ -2400,13 +2498,16 @@ |
971 | const gchar * |
972 | gof_file_get_ftype (GOFFile *file) |
973 | { |
974 | - g_return_val_if_fail (file->info != NULL, NULL); |
975 | + if (file->info == NULL || gof_file_is_location_uri_default (file)) |
976 | + return NULL; |
977 | |
978 | const char *ftype = NULL; |
979 | if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)) |
980 | return g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE); |
981 | + |
982 | if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE)) |
983 | ftype = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE); |
984 | + |
985 | if (!g_strcmp0 (ftype, "application/octet-stream") && file->tagstype) |
986 | return file->tagstype; |
987 | |
988 | |
989 | === modified file 'libcore/gof-file.h' |
990 | --- libcore/gof-file.h 2014-11-21 19:24:45 +0000 |
991 | +++ libcore/gof-file.h 2015-02-07 19:01:29 +0000 |
992 | @@ -94,6 +94,7 @@ |
993 | int color; |
994 | gboolean is_mounted; |
995 | gboolean exists; |
996 | + gboolean is_connected; |
997 | |
998 | gboolean has_permissions; |
999 | guint32 permissions; |
1000 | @@ -222,6 +223,7 @@ |
1001 | gpointer callback_data); |
1002 | void gof_file_set_thumb_state (GOFFile *file, GOFFileThumbState state); |
1003 | void gof_file_add_emblem(GOFFile* file, const gchar* emblem); |
1004 | +GMount* gof_file_get_mount_at (GFile* target); |
1005 | |
1006 | /* To provide a wrapper around g_file_get_uri (not sure it is really useful tough) */ |
1007 | #define gof_file_get_uri(obj) g_file_get_uri(obj->location) |
1008 | @@ -256,6 +258,9 @@ |
1009 | gboolean gof_file_is_root_network_folder (GOFFile *file); |
1010 | gboolean gof_file_is_network_uri_scheme (GOFFile *file); |
1011 | gboolean gof_file_is_smb_uri_scheme (GOFFile *file); |
1012 | +gboolean gof_file_is_smb_share (GOFFile *file); |
1013 | +gboolean gof_file_is_smb_server (GOFFile *file); |
1014 | +gboolean gof_file_is_mountable (GOFFile *file); |
1015 | gboolean gof_file_thumb_can_frame (GOFFile *file); |
1016 | |
1017 | char *gof_file_get_display_target_uri (GOFFile *file); |
1018 | |
1019 | === modified file 'libcore/marlin-file-operations.c' |
1020 | --- libcore/marlin-file-operations.c 2015-01-16 18:03:50 +0000 |
1021 | +++ libcore/marlin-file-operations.c 2015-02-07 19:01:29 +0000 |
1022 | @@ -2058,6 +2058,7 @@ |
1023 | |
1024 | common = (CommonJob *)job; |
1025 | common->io_job = io_job; |
1026 | + |
1027 | #ifdef ENABLE_TASKVIEW |
1028 | taskview_generic_set_state (TASKVIEW_GENERIC (job->common.tv_io), TASKVIEW_RUNNING); |
1029 | #else |
1030 | @@ -2074,19 +2075,19 @@ |
1031 | for (l = job->files; l != NULL; l = l->next) { |
1032 | file = l->data; |
1033 | |
1034 | - if (job->try_trash && |
1035 | - g_file_has_uri_scheme (file, "trash")) { |
1036 | + if (job->try_trash && g_file_has_uri_scheme (file, "trash")) { |
1037 | must_confirm_delete_in_trash = TRUE; |
1038 | to_delete_files = g_list_prepend (to_delete_files, file); |
1039 | } else if (can_delete_without_confirm (file)) { |
1040 | to_delete_files = g_list_prepend (to_delete_files, file); |
1041 | } else { |
1042 | - if (job->try_trash) { |
1043 | + if (job->try_trash && |
1044 | + !g_file_has_uri_scheme (file, "smb")) { |
1045 | to_trash_files = g_list_prepend (to_trash_files, file); |
1046 | } else { |
1047 | must_confirm_delete = TRUE; |
1048 | to_delete_files = g_list_prepend (to_delete_files, file); |
1049 | - } |
1050 | + } |
1051 | } |
1052 | } |
1053 | |
1054 | @@ -2153,14 +2154,11 @@ |
1055 | inhibit_power_manager ((CommonJob *)job, _("Deleting Files")); |
1056 | } |
1057 | |
1058 | - // Start UNDO-REDO |
1059 | if (try_trash && !marlin_undo_manager_is_undo_redo (marlin_undo_manager_instance())) { |
1060 | job->common.undo_redo_data = marlin_undo_manager_data_new (MARLIN_UNDO_MOVETOTRASH, g_list_length(files)); |
1061 | - //undotest usefull ?? |
1062 | GFile* src_dir = g_file_get_parent (files->data); |
1063 | marlin_undo_manager_data_set_src_dir (job->common.undo_redo_data, src_dir); |
1064 | } |
1065 | - // End UNDO-REDO |
1066 | |
1067 | g_io_scheduler_push_job (delete_job, |
1068 | job, |
1069 | @@ -4353,6 +4351,10 @@ |
1070 | &error); |
1071 | } |
1072 | |
1073 | + /* NOTE Result is false if file being moved is a folder and the target is on a Samba share even if |
1074 | + * the file is successfully copied, so the change will not be notified to the view. |
1075 | + * The view will need to be refreshed anyway */ |
1076 | + |
1077 | if (res) { |
1078 | transfer_info->num_files ++; |
1079 | report_copy_progress (copy_job, source_info, transfer_info); |
1080 | @@ -4579,8 +4581,9 @@ |
1081 | g_error_free (error); |
1082 | goto out; |
1083 | } |
1084 | - primary = f (_("There was an Error while copying \"%s\"."), g_file_get_uri (src)); |
1085 | - secondary = f (_("There was an error copying the file into %s."), g_file_get_uri (dest_dir)); |
1086 | + |
1087 | + primary = f (_("Cannot copy \"%B\" here."), src); |
1088 | + secondary = f (_("There was an error copying the file into %B."), dest_dir); |
1089 | details = error->message; |
1090 | |
1091 | response = run_warning (job, |
1092 | @@ -5466,9 +5469,15 @@ |
1093 | not_local = FALSE; |
1094 | |
1095 | path = get_abs_path_for_symlink (src); |
1096 | - if (path == NULL) { |
1097 | + char *scheme; |
1098 | + scheme = g_file_get_uri_scheme (src); |
1099 | + |
1100 | + if (path == NULL || !g_str_has_prefix (scheme, "file")) |
1101 | not_local = TRUE; |
1102 | - } else if (g_file_make_symbolic_link (dest, |
1103 | + |
1104 | + g_free (scheme); |
1105 | + |
1106 | + if (!not_local && g_file_make_symbolic_link (dest, |
1107 | path, |
1108 | common->cancellable, |
1109 | &error)) { |
1110 | @@ -5478,6 +5487,7 @@ |
1111 | // End UNDO-REDO |
1112 | |
1113 | g_free (path); |
1114 | + |
1115 | if (debuting_files) { |
1116 | g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE)); |
1117 | } |
1118 | @@ -5946,7 +5956,7 @@ |
1119 | GFile *target_dir, |
1120 | GdkDragAction copy_action, |
1121 | GtkWidget *parent_view, |
1122 | - gpointer done_callback, |
1123 | + GCallback done_callback, |
1124 | gpointer done_callback_data) |
1125 | { |
1126 | GList *p; |
1127 | @@ -5981,6 +5991,19 @@ |
1128 | } |
1129 | |
1130 | if (copy_action == GDK_ACTION_COPY) { |
1131 | + if (g_file_has_uri_scheme (target_dir, "trash")) { |
1132 | + char *primary = f (_("Cannot copy into trash.")); |
1133 | + char *secondary = f (_("It is not permitted to copy files into the trash")); |
1134 | + eel_show_error_dialog (primary, |
1135 | + secondary, |
1136 | + parent_window); |
1137 | + |
1138 | + if (done_callback != NULL) |
1139 | + ((MarlinDeleteCallback)done_callback) (TRUE, done_callback_data); |
1140 | + |
1141 | + return; |
1142 | + } |
1143 | + |
1144 | /* done_callback is (or should be) a CopyCallBack or null in this case */ |
1145 | src_dir = g_file_get_parent (files->data); |
1146 | if (target_dir == NULL || |
1147 | |
1148 | === modified file 'libcore/marlin-file-operations.h' |
1149 | --- libcore/marlin-file-operations.h 2015-01-07 09:11:24 +0000 |
1150 | +++ libcore/marlin-file-operations.h 2015-02-07 19:01:29 +0000 |
1151 | @@ -33,7 +33,8 @@ |
1152 | typedef void (* MarlinCreateCallback) (GFile *new_file, |
1153 | gpointer callback_data); |
1154 | typedef void (* MarlinOpCallback) (gpointer callback_data); |
1155 | -typedef void (* MarlinDeleteCallback) (gboolean user_cancel, gpointer callback_data); |
1156 | +typedef void (* MarlinDeleteCallback) (gboolean user_cancel, |
1157 | + gpointer callback_data); |
1158 | typedef void (* MarlinMountCallback) (GVolume *volume, |
1159 | GObject *callback_data_object); |
1160 | typedef void (* MarlinUnmountCallback) (gpointer callback_data); |
1161 | @@ -130,7 +131,7 @@ |
1162 | GFile *target_dir, |
1163 | GdkDragAction copy_action, |
1164 | GtkWidget *parent_view, |
1165 | - gpointer done_callback, |
1166 | + GCallback done_callback, |
1167 | gpointer done_callback_data); |
1168 | |
1169 | void marlin_file_operations_move (GList *files, |
1170 | |
1171 | === modified file 'libcore/marlin-trash-monitor.c' |
1172 | --- libcore/marlin-trash-monitor.c 2013-12-28 12:07:41 +0000 |
1173 | +++ libcore/marlin-trash-monitor.c 2015-02-07 19:01:29 +0000 |
1174 | @@ -131,7 +131,6 @@ |
1175 | schedule_update_info (MarlinTrashMonitor *trash_monitor) |
1176 | { |
1177 | GFile *location; |
1178 | - |
1179 | location = g_file_new_for_uri (MARLIN_TRASH_URI); |
1180 | |
1181 | g_file_query_info_async (location, |
1182 | @@ -150,7 +149,6 @@ |
1183 | gpointer user_data) |
1184 | { |
1185 | MarlinTrashMonitor *trash_monitor; |
1186 | - |
1187 | trash_monitor = MARLIN_TRASH_MONITOR (user_data); |
1188 | |
1189 | schedule_update_info (trash_monitor); |
1190 | |
1191 | === modified file 'libcore/marlin-undostack-manager.c' |
1192 | --- libcore/marlin-undostack-manager.c 2013-08-10 20:20:23 +0000 |
1193 | +++ libcore/marlin-undostack-manager.c 2015-02-07 19:01:29 +0000 |
1194 | @@ -185,8 +185,7 @@ |
1195 | static void undo_redo_done_rename_callback (GOFFile * file, |
1196 | GFile * result_location, GError * error, gpointer callback_data); |
1197 | |
1198 | -static void undo_redo_done_delete_callback (GHashTable *debuting_uris, |
1199 | - gboolean user_cancel, gpointer callback_data); |
1200 | +static void undo_redo_done_delete_callback (gboolean user_cancel, gpointer callback_data); |
1201 | |
1202 | static void undo_redo_done_create_callback (GFile * new_file, |
1203 | gpointer callback_data); |
1204 | @@ -402,7 +401,8 @@ |
1205 | void |
1206 | marlin_undo_manager_redo (MarlinUndoManager *manager, |
1207 | GtkWidget *parent_view, |
1208 | - MarlinUndoFinishCallback cb) |
1209 | + MarlinUndoFinishCallback cb, |
1210 | + gpointer callback_data) |
1211 | { |
1212 | GList *uris; |
1213 | GOFFile *file; |
1214 | @@ -486,8 +486,6 @@ |
1215 | g_object_unref (fparent); |
1216 | break; |
1217 | case MARLIN_UNDO_MOVETOTRASH: |
1218 | - //amtest |
1219 | - //printf ("MARLIN_UNDO_MOVETRASH\n"); |
1220 | if (g_hash_table_size (action->trashed) > 0) { |
1221 | GList *uri_to_trash = g_hash_table_get_keys (action->trashed); |
1222 | uris = uri_list_to_gfile_list (uri_to_trash); |
1223 | @@ -553,7 +551,8 @@ |
1224 | void |
1225 | marlin_undo_manager_undo (MarlinUndoManager *manager, |
1226 | GtkWidget *parent_view, |
1227 | - MarlinUndoFinishCallback cb) |
1228 | + MarlinUndoFinishCallback cb, |
1229 | + gpointer done_callback_data) |
1230 | { |
1231 | GList *uris = NULL; |
1232 | GHashTable *files_to_restore; |
1233 | @@ -828,7 +827,6 @@ |
1234 | { |
1235 | GFileInfo *info; |
1236 | guint64 mtime; |
1237 | - |
1238 | info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, |
1239 | G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, FALSE, NULL); |
1240 | if (info == NULL) { |
1241 | @@ -1731,10 +1729,9 @@ |
1242 | |
1243 | /** ---------------------------------------------------------------- */ |
1244 | static void |
1245 | -undo_redo_done_delete_callback (GHashTable * |
1246 | - debuting_uris, gboolean user_cancel, gpointer callback_data) |
1247 | +undo_redo_done_delete_callback (gboolean user_cancel, gpointer callback_data) |
1248 | { |
1249 | - undo_redo_done_transfer_callback (debuting_uris, callback_data); |
1250 | + undo_redo_done_transfer_callback (NULL, callback_data); |
1251 | } |
1252 | |
1253 | /** ---------------------------------------------------------------- */ |
1254 | |
1255 | === modified file 'libcore/marlin-undostack-manager.h' |
1256 | --- libcore/marlin-undostack-manager.h 2013-08-10 20:20:23 +0000 |
1257 | +++ libcore/marlin-undostack-manager.h 2015-02-07 19:01:29 +0000 |
1258 | @@ -108,11 +108,11 @@ |
1259 | |
1260 | void |
1261 | marlin_undo_manager_undo (MarlinUndoManager* manager, |
1262 | - GtkWidget *parent_view, MarlinUndoFinishCallback cb); |
1263 | + GtkWidget *parent_view, MarlinUndoFinishCallback cb, gpointer done_callback_data); |
1264 | |
1265 | void |
1266 | marlin_undo_manager_redo (MarlinUndoManager* manager, |
1267 | - GtkWidget *parent_view, MarlinUndoFinishCallback cb); |
1268 | + GtkWidget *parent_view, MarlinUndoFinishCallback cb, gpointer done_callback_data); |
1269 | |
1270 | MarlinUndoActionData* |
1271 | marlin_undo_manager_data_new (MarlinUndoActionType type, |
1272 | |
1273 | === modified file 'libcore/pantheon-files-core-C.vapi' |
1274 | --- libcore/pantheon-files-core-C.vapi 2015-01-16 18:08:07 +0000 |
1275 | +++ libcore/pantheon-files-core-C.vapi 2015-02-07 19:01:29 +0000 |
1276 | @@ -59,7 +59,7 @@ |
1277 | static unowned GLib.List<unowned GLib.File> get_trash_dirs_for_mount (GLib.Mount mount); |
1278 | static void empty_trash_dirs (Gtk.Window? parent_window, owned GLib.List<GLib.File> dirs); |
1279 | static void empty_trash (Gtk.Widget? widget); |
1280 | - static void copy_move (GLib.List<GLib.File> files, void* relative_item_points, GLib.File target_dir, Gdk.DragAction copy_action, Gtk.Widget? parent_view = null, void* done_callback = null, void* done_callback_data = null); |
1281 | + static void copy_move (GLib.List<GLib.File> files, void* relative_item_points, GLib.File target_dir, Gdk.DragAction copy_action, Gtk.Widget? parent_view = null, GLib.Callback? done_callback = null, void* done_callback_data = null); |
1282 | static void new_file (Gtk.Widget parent_view, Gdk.Point? target_point, string parent_dir, string? target_filename, string? initial_contents, int length, Marlin.CreateCallback? create_callback = null, void* done_callback_data = null); |
1283 | static void new_file_from_template (Gtk.Widget parent_view, Gdk.Point? target_point, GLib.File parent_dir, string? target_filename, GLib.File template, Marlin.CreateCallback? create_callback = null, void* done_callback_data = null); |
1284 | } |
1285 | @@ -70,7 +70,7 @@ |
1286 | [CCode (cheader_filename = "marlin-file-operations.h", has_target = false)] |
1287 | public delegate void CreateCallback (GLib.File new_file, void* callback_data); |
1288 | [CCode (cheader_filename = "marlin-file-operations.h", has_target = false)] |
1289 | - public delegate void CopyCallBack (GLib.HashTable<GLib.File, void*> debuting_uris, void* pointer); |
1290 | + public delegate void CopyCallback (GLib.HashTable<GLib.File, void*> debuting_uris, void* pointer); |
1291 | [CCode (cheader_filename = "marlin-file-operations.h", has_target = false)] |
1292 | public delegate void DeleteCallback (bool user_cancel, void* callback_data); |
1293 | } |
1294 | @@ -102,8 +102,8 @@ |
1295 | |
1296 | [CCode (cprefix = "Eel", lower_case_cprefix = "eel_", cheader_filename = "eel-stock-dialogs.h")] |
1297 | namespace Eel { |
1298 | - public Gtk.Dialog show_warning_dialog (string primary_text, string secondary_text, Gtk.Window? parent); |
1299 | - public Gtk.Dialog show_error_dialog (string primary_text, string secondary_text, Gtk.Window? parent); |
1300 | + public unowned Gtk.Dialog show_warning_dialog (string primary_text, string secondary_text, Gtk.Window? parent); |
1301 | + public unowned Gtk.Dialog show_error_dialog (string primary_text, string secondary_text, Gtk.Window? parent); |
1302 | } |
1303 | |
1304 | [CCode (cprefix = "Eel", lower_case_cprefix = "eel_", cheader_filename = "eel-fcts.h")] |
1305 | @@ -161,19 +161,17 @@ |
1306 | } |
1307 | |
1308 | [CCode (cheader_filename = "marlin-undostack-manager.h")] |
1309 | - public delegate void UndoFinishCallback (UndoManager manager, Gtk.Widget? w); |
1310 | - /*public delegate void UndoFinishCallback (void *data);*/ |
1311 | - /*public delegate void UndoFinishCallback ();*/ |
1312 | + public delegate void UndoFinishCallback (void *data); |
1313 | |
1314 | [CCode (cheader_filename = "marlin-undostack-manager.h")] |
1315 | - public abstract class UndoManager : GLib.Object |
1316 | + public class UndoManager : GLib.Object |
1317 | { |
1318 | public static UndoManager instance (); |
1319 | |
1320 | public signal void request_menu_update (UndoMenuData data); |
1321 | |
1322 | - public void undo (UndoFinishCallback? cb); |
1323 | - public void redo (UndoFinishCallback? cb); |
1324 | + public void undo (Gtk.Widget widget, UndoFinishCallback? cb); |
1325 | + public void redo (Gtk.Widget widget, UndoFinishCallback? cb); |
1326 | } |
1327 | |
1328 | [CCode (cheader_filename = "marlin-progress-info.h")] |
1329 | @@ -232,14 +230,16 @@ |
1330 | |
1331 | public File(GLib.File location, GLib.File? dir); |
1332 | public static GOF.File @get(GLib.File location); |
1333 | - public static GOF.File get_by_uri (string uri); |
1334 | + public static GOF.File? get_by_uri (string uri); |
1335 | public static File cache_lookup (GLib.File file); |
1336 | public static bool launch_files (GLib.List<GOF.File> files, Gdk.Screen screen, GLib.AppInfo app); |
1337 | public static void list_free (GLib.List<GOF.File> files); |
1338 | + public static GLib.Mount? get_mount_at (GLib.File location); |
1339 | |
1340 | public void remove_from_caches (); |
1341 | public bool is_gone; |
1342 | public GLib.File location; |
1343 | + public GLib.File target_location; |
1344 | public GLib.File directory; /* parent directory location */ |
1345 | public GLib.Icon? icon; |
1346 | public GLib.List<string>? emblems_list; |
1347 | @@ -267,14 +267,17 @@ |
1348 | public bool is_trashed(); |
1349 | public bool is_writable (); |
1350 | public bool is_executable (); |
1351 | + public bool is_mountable (); |
1352 | public bool link_known_target; |
1353 | + public bool is_smb_share (); |
1354 | + public bool is_smb_server (); |
1355 | public uint flags; |
1356 | |
1357 | public Gdk.DragAction accepts_drop (GLib.List<GLib.File> file_list, Gdk.DragContext context, out Gdk.DragAction suggested_action_return); |
1358 | |
1359 | public unowned string get_display_name (); |
1360 | public unowned GLib.File get_target_location (); |
1361 | - public unowned string get_ftype (); |
1362 | + public unowned string? get_ftype (); |
1363 | public string? get_formated_time (string attr); |
1364 | public Gdk.Pixbuf get_icon_pixbuf (int size, bool forced_size, FileIconFlags flags); |
1365 | public void get_folder_icon_from_uri_or_path (); |
1366 | @@ -290,7 +293,7 @@ |
1367 | public bool has_permissions; |
1368 | public uint32 permissions; |
1369 | |
1370 | - public void open_single (Gdk.Screen screen, GLib.AppInfo app_info); |
1371 | + public void open_single (Gdk.Screen screen, GLib.AppInfo? app_info); |
1372 | public void update (); |
1373 | public void update_type (); |
1374 | public void update_icon (int size); |
1375 | @@ -304,6 +307,7 @@ |
1376 | public bool can_set_group (); |
1377 | public bool can_set_permissions (); |
1378 | public bool can_unmount (); |
1379 | + public GLib.Mount? mount; |
1380 | public string get_permissions_as_string (); |
1381 | public bool launch (Gdk.Screen screen, GLib.AppInfo app); |
1382 | |
1383 | @@ -314,6 +318,7 @@ |
1384 | public bool is_root_network_folder (); |
1385 | public bool is_network_uri_scheme (); |
1386 | public bool is_smb_uri_scheme (); |
1387 | + public bool is_connected; |
1388 | |
1389 | public unowned string get_display_target_uri (); |
1390 | |
1391 | |
1392 | === modified file 'libwidgets/LocationBar.vala' |
1393 | --- libwidgets/LocationBar.vala 2015-01-26 10:59:26 +0000 |
1394 | +++ libwidgets/LocationBar.vala 2015-02-07 19:01:29 +0000 |
1395 | @@ -191,6 +191,7 @@ |
1396 | escape (); |
1397 | return true; |
1398 | } |
1399 | + |
1400 | return base.key_press_event (event); |
1401 | } |
1402 | |
1403 | @@ -258,7 +259,7 @@ |
1404 | var el = get_element_from_coordinates ((int) event.x, (int) event.y); |
1405 | if (el != null) { |
1406 | selected = elements.index_of (el); |
1407 | - var newpath = sanitise_path (get_path_from_element (el)); |
1408 | + var newpath = get_path_from_element (el); |
1409 | path_changed (get_file_for_path (newpath)); |
1410 | } else |
1411 | grab_focus (); |
1412 | @@ -351,20 +352,13 @@ |
1413 | if (search_mode) |
1414 | set_entry_text (""); |
1415 | else { |
1416 | - current_path = sanitise_path (GLib.Uri.unescape_string (get_elements_path ())); |
1417 | - set_entry_text (current_path); |
1418 | + set_entry_text (GLib.Uri.unescape_string (get_elements_path ())); |
1419 | show_navigate_icon (); |
1420 | } |
1421 | + |
1422 | return base.focus_in_event (event); |
1423 | } |
1424 | |
1425 | - string sanitise_path (string path) { |
1426 | - return path.replace ("file:////", "/") |
1427 | - .replace ("file:///", "/") |
1428 | - .replace ("trash:///", "") |
1429 | - .replace ("network:///", ""); |
1430 | - } |
1431 | - |
1432 | void on_grab_focus () { |
1433 | select_region (0, 0); |
1434 | set_position (-1); |
1435 | @@ -488,15 +482,25 @@ |
1436 | return null; |
1437 | } |
1438 | |
1439 | - protected string get_path_from_element (BreadcrumbsElement el) { |
1440 | - string newpath = protocol; |
1441 | + protected string get_path_from_element (BreadcrumbsElement? el) { |
1442 | + /* return path up to the speficied element or, if the parameter is null, the whole path */ |
1443 | + string newpath = ""; |
1444 | + bool first = true; |
1445 | |
1446 | foreach (BreadcrumbsElement element in elements) { |
1447 | - if (element.display) { |
1448 | - newpath += element.text + "/"; |
1449 | - if (element == el) |
1450 | + string s = element.text; |
1451 | + if (first) { |
1452 | + if (s == "" || s == "file://") |
1453 | + newpath = "/"; |
1454 | + else |
1455 | + newpath = s; |
1456 | + |
1457 | + first = false; |
1458 | + } else |
1459 | + newpath += (s + "/"); |
1460 | + |
1461 | + if (el != null && element == el) |
1462 | break; |
1463 | - } |
1464 | } |
1465 | return newpath; |
1466 | } |
1467 | @@ -505,20 +509,13 @@ |
1468 | * Get the current path of the PathBar, based on the elements that it contains |
1469 | **/ |
1470 | public string get_elements_path () { |
1471 | - string strpath = ""; |
1472 | - strpath = protocol; |
1473 | - |
1474 | - foreach (BreadcrumbsElement element in elements) { |
1475 | - if (element.display) |
1476 | - strpath += element.text + "/"; |
1477 | - } |
1478 | - return strpath; |
1479 | + return get_path_from_element (null); |
1480 | } |
1481 | |
1482 | /** |
1483 | * Gets a properly escaped GLib.File for the given path |
1484 | **/ |
1485 | - public File get_file_for_path (string path) { |
1486 | + public File? get_file_for_path (string path) { |
1487 | string reserved_chars = (GLib.Uri.RESERVED_CHARS_GENERIC_DELIMITERS + GLib.Uri.RESERVED_CHARS_SUBCOMPONENT_DELIMITERS + " ").replace("#", ""); |
1488 | string newpath = GLib.Uri.unescape_string (path ?? ""); |
1489 | |
1490 | @@ -529,12 +526,22 @@ |
1491 | if (newpath[0] == '~') |
1492 | newpath = newpath.replace("~", Environment.get_home_dir ()); |
1493 | |
1494 | - if (!newpath.contains("://")) |
1495 | + if (!newpath.contains("://")) { |
1496 | + if (!newpath.has_prefix ("/")) |
1497 | + newpath = "/" + newpath; |
1498 | + |
1499 | newpath = Marlin.ROOT_FS_URI + newpath; |
1500 | - |
1501 | + } else { |
1502 | + string [] parts = newpath.split ("://", 3); |
1503 | + if (parts.length > 2) { |
1504 | + warning ("Invalid path"); |
1505 | + return null; |
1506 | + } |
1507 | + } |
1508 | + |
1509 | newpath = newpath.replace("ssh:", "sftp:"); |
1510 | newpath = GLib.Uri.escape_string (newpath, reserved_chars, true); |
1511 | - |
1512 | + |
1513 | File file = File.new_for_commandline_arg (newpath); |
1514 | return file; |
1515 | } |
1516 | @@ -566,24 +573,21 @@ |
1517 | load_right_click_menu (menu_x_root, menu_y_root); |
1518 | return true; |
1519 | } |
1520 | - |
1521 | return false; |
1522 | } |
1523 | |
1524 | public virtual string? update_breadcrumbs (string newpath, string breadpath) { |
1525 | string strloc; |
1526 | |
1527 | - if (Posix.strncmp (newpath, "./", 2) == 0) { |
1528 | + if (Posix.strncmp (newpath, "./", 2) == 0) |
1529 | return null; |
1530 | - } |
1531 | |
1532 | - if (newpath[0] == '/') { |
1533 | + if (newpath[0] == '/') |
1534 | strloc = newpath; |
1535 | - } else if (Posix.strncmp (newpath, "~/", 2) == 0) { |
1536 | + else if (Posix.strncmp (newpath, "~/", 2) == 0) |
1537 | strloc = Environment.get_home_dir (); |
1538 | - } else { |
1539 | + else |
1540 | strloc = breadpath + newpath; |
1541 | - } |
1542 | |
1543 | return strloc; |
1544 | } |
1545 | @@ -608,10 +612,9 @@ |
1546 | selected = -1; |
1547 | var breads = current_path.split ("/"); |
1548 | var newelements = new Gee.ArrayList<BreadcrumbsElement> (); |
1549 | - if (breads.length == 0 || breads[0] == "") { |
1550 | - var element = new BreadcrumbsElement (protocol, left_padding, right_padding); |
1551 | - newelements.add (element); |
1552 | - } |
1553 | + |
1554 | + string s = protocol == Marlin.ROOT_FS_URI ? "" : protocol; |
1555 | + newelements.add (new BreadcrumbsElement (s, left_padding, right_padding)); |
1556 | |
1557 | |
1558 | /* Add every mounted volume in our IconDirectory in order to load them properly in the pathbar if needed */ |
1559 | @@ -633,17 +636,11 @@ |
1560 | } |
1561 | |
1562 | foreach (string dir in breads) { |
1563 | - if (dir != "") { |
1564 | - var element = new BreadcrumbsElement (dir, left_padding, right_padding); |
1565 | - newelements.add (element); |
1566 | - } |
1567 | + if (dir != "") |
1568 | + newelements.add (new BreadcrumbsElement (dir, left_padding, right_padding)); |
1569 | } |
1570 | |
1571 | - if (protocol == Marlin.ROOT_FS_URI) |
1572 | - newelements[0].text = "/"; |
1573 | - |
1574 | int max_path = int.min (elements.size, newelements.size); |
1575 | - |
1576 | foreach (IconDirectory icon in icons) { |
1577 | if (icon.protocol && icon.path == protocol) { |
1578 | newelements[0].set_icon(icon.icon); |
1579 | @@ -653,7 +650,7 @@ |
1580 | bool found = true; |
1581 | int h = 0; |
1582 | |
1583 | - for (int i = 0; i < icon.exploded.length; i++) { |
1584 | + for (int i = 1; i < icon.exploded.length; i++) { |
1585 | if (icon.exploded[i] != newelements[i].text) { |
1586 | found = false; |
1587 | break; |
1588 | @@ -670,10 +667,9 @@ |
1589 | newelements[h].display_text = (icon.text_displayed != null) || !icon.break_loop; |
1590 | newelements[h].text_displayed = icon.text_displayed; |
1591 | |
1592 | - if (icon.break_loop) { |
1593 | - newelements[h].text = icon.path; |
1594 | + if (icon.break_loop) |
1595 | break; |
1596 | - } |
1597 | + |
1598 | } |
1599 | } |
1600 | } |
1601 | |
1602 | === modified file 'plugins/contractor/plugin.vala' |
1603 | --- plugins/contractor/plugin.vala 2013-05-12 05:49:12 +0000 |
1604 | +++ plugins/contractor/plugin.vala 2015-02-07 19:01:29 +0000 |
1605 | @@ -73,11 +73,14 @@ |
1606 | } else { |
1607 | files = get_file_array (gof_files); |
1608 | var mimetypes = get_mimetypes (gof_files); |
1609 | - contracts = Granite.Services.ContractorProxy.get_contracts_by_mimelist (mimetypes); |
1610 | + if (mimetypes.length > 0) |
1611 | + contracts = Granite.Services.ContractorProxy.get_contracts_by_mimelist (mimetypes); |
1612 | } |
1613 | |
1614 | assert (files != null); |
1615 | - assert (contracts != null); |
1616 | + |
1617 | + if (contracts == null) |
1618 | + return; |
1619 | |
1620 | for (int i = 0; i < contracts.size; i++) { |
1621 | var contract = contracts.get (i); |
1622 | |
1623 | === modified file 'plugins/network-places/plugin.vala' |
1624 | --- plugins/network-places/plugin.vala 2014-10-29 18:55:55 +0000 |
1625 | +++ plugins/network-places/plugin.vala 2015-02-07 19:01:29 +0000 |
1626 | @@ -17,35 +17,9 @@ |
1627 | // See src/marlin-connect-server-dialog.c |
1628 | extern void marlin_connect_server_dialog_show (Gtk.Widget widget); |
1629 | |
1630 | -public class Files.Plugins.NetworkInfobar : Gtk.InfoBar { |
1631 | - public NetworkInfobar () { |
1632 | - message_type = Gtk.MessageType.INFO; |
1633 | - add_button (_("Connect to Server…"), 0); |
1634 | - } |
1635 | - |
1636 | - public override void response (int response_id) { |
1637 | - marlin_connect_server_dialog_show (this); |
1638 | - } |
1639 | -} |
1640 | - |
1641 | public class Files.Plugins.NetworkPlaces : Marlin.Plugins.Base { |
1642 | - private NetworkInfobar? infobar = null; |
1643 | |
1644 | public override void directory_loaded (void* user_data) { |
1645 | - var file = ((Object[]) user_data)[2] as GOF.File; |
1646 | - |
1647 | - if (file.is_network_uri_scheme ()) { |
1648 | - if (infobar == null) { |
1649 | - var slot = ((Object[]) user_data)[1] as GOF.AbstractSlot; |
1650 | - return_if_fail (slot != null); |
1651 | - infobar = new NetworkInfobar (); |
1652 | - slot.add_extra_widget (infobar); |
1653 | - infobar.show_all (); |
1654 | - } |
1655 | - } else if (infobar != null) { |
1656 | - infobar.destroy (); |
1657 | - infobar = null; |
1658 | - } |
1659 | } |
1660 | |
1661 | public override void update_sidebar (Gtk.Widget widget) { |
1662 | |
1663 | === modified file 'plugins/pantheon-files-trash/plugin.vala' |
1664 | --- plugins/pantheon-files-trash/plugin.vala 2014-12-10 13:19:17 +0000 |
1665 | +++ plugins/pantheon-files-trash/plugin.vala 2015-02-07 19:01:29 +0000 |
1666 | @@ -49,7 +49,6 @@ |
1667 | if (file.location.get_uri_scheme () == "trash") { |
1668 | /* Only add infobar once */ |
1669 | if (!infobars.has_key (slot)) { |
1670 | - |
1671 | var infobar = new Gtk.InfoBar (); |
1672 | (infobar.get_content_area () as Gtk.Box).add (new Gtk.Label (_("These items may be deleted by emptying the trash."))); |
1673 | infobar.add_button (_("Empty the Trash"), 0); |
1674 | @@ -58,10 +57,11 @@ |
1675 | }); |
1676 | infobar.set_message_type (Gtk.MessageType.INFO); |
1677 | infobar.set_response_sensitive (0, !TrashMonitor.is_empty ()); |
1678 | - slot.add_extra_widget (infobar); |
1679 | infobar.show_all (); |
1680 | infobar.set_visible (!TrashMonitor.is_empty ()); |
1681 | + slot.add_extra_widget (infobar); |
1682 | infobars.@set (slot, infobar); |
1683 | + |
1684 | } |
1685 | } else { |
1686 | var infobar = infobars.@get (slot); |
1687 | |
1688 | === modified file 'src/Application.vala' |
1689 | --- src/Application.vala 2014-12-19 16:24:10 +0000 |
1690 | +++ src/Application.vala 2015-02-07 19:01:29 +0000 |
1691 | @@ -234,6 +234,19 @@ |
1692 | }); |
1693 | } |
1694 | |
1695 | + public void tab_reloaded (Marlin.View.Window source, GLib.File file) { |
1696 | + unowned List<Gtk.Window> window_list = this.get_windows (); |
1697 | + window_list.@foreach ((window) => { |
1698 | + var win = (Marlin.View.Window)window; |
1699 | + |
1700 | + if ((source.window_number != win.window_number) && |
1701 | + file.equal (win.current_tab.get_current_slot ().location)) |
1702 | + |
1703 | + win.current_tab.reload (false); |
1704 | + |
1705 | + }); |
1706 | + } |
1707 | + |
1708 | private void mount_removed_callback (VolumeMonitor monitor, Mount mount) { |
1709 | /* Notify each window */ |
1710 | foreach (var window in this.get_windows ()) { |
1711 | |
1712 | === modified file 'src/BookmarkList.vala' |
1713 | --- src/BookmarkList.vala 2014-11-13 20:24:33 +0000 |
1714 | +++ src/BookmarkList.vala 2015-02-07 19:01:29 +0000 |
1715 | @@ -83,13 +83,13 @@ |
1716 | return instance; |
1717 | } |
1718 | |
1719 | - public void insert_uri (string uri, uint index) { |
1720 | - insert_item_internal (new Bookmark.from_uri (uri, null), index); |
1721 | + public void insert_uri (string uri, uint index, string? label = null) { |
1722 | + insert_item_internal (new Bookmark.from_uri (uri, label), index); |
1723 | save_bookmarks_file (); |
1724 | } |
1725 | |
1726 | - public void insert_uri_at_end (string uri) { |
1727 | - append_internal (new Bookmark.from_uri (uri, null)); |
1728 | + public void insert_uri_at_end (string uri, string? label = null) { |
1729 | + append_internal (new Bookmark.from_uri (uri, label)); |
1730 | save_bookmarks_file (); |
1731 | } |
1732 | |
1733 | |
1734 | === modified file 'src/DndHandler.vala' |
1735 | --- src/DndHandler.vala 2014-11-13 20:24:33 +0000 |
1736 | +++ src/DndHandler.vala 2015-02-07 19:01:29 +0000 |
1737 | @@ -38,7 +38,7 @@ |
1738 | drop_target.get_target_location (), |
1739 | action, |
1740 | widget, |
1741 | - (void*)dnd_done, |
1742 | + null, |
1743 | null); |
1744 | return true; |
1745 | } else if (drop_target.is_executable ()) { |
1746 | @@ -54,8 +54,6 @@ |
1747 | return false; |
1748 | } |
1749 | |
1750 | - private void dnd_done (GLib.List<GLib.File> files, void* data) {} |
1751 | - |
1752 | public Gdk.DragAction? drag_drop_action_ask (Gtk.Widget dest_widget, |
1753 | Gtk.ApplicationWindow win, |
1754 | Gdk.DragAction possible_actions) { |
1755 | |
1756 | === modified file 'src/MimeActions.vala' |
1757 | --- src/MimeActions.vala 2014-11-13 20:24:33 +0000 |
1758 | +++ src/MimeActions.vala 2015-02-07 19:01:29 +0000 |
1759 | @@ -76,8 +76,11 @@ |
1760 | } |
1761 | |
1762 | public static List<AppInfo>? get_applications_for_file (GOF.File file) { |
1763 | + string? type = file.get_ftype (); |
1764 | + if (type == null) |
1765 | + return null; |
1766 | |
1767 | - List<AppInfo> result = AppInfo.get_all_for_type (file.get_ftype ()); |
1768 | + List<AppInfo> result = AppInfo.get_all_for_type (type); |
1769 | string uri_scheme = file.location.get_uri_scheme (); |
1770 | |
1771 | if (uri_scheme != null) { |
1772 | @@ -142,11 +145,12 @@ |
1773 | |
1774 | if (result != null) |
1775 | result = intersect_application_lists (result, one_result); |
1776 | + |
1777 | else |
1778 | result = one_result.copy (); |
1779 | |
1780 | if (result == null) |
1781 | - break; |
1782 | + return null; |
1783 | |
1784 | previous_file = file; |
1785 | } |
1786 | @@ -186,12 +190,11 @@ |
1787 | } |
1788 | |
1789 | private static void filter_non_uri_apps (List<AppInfo> apps) { |
1790 | - var non_uri_apps = new List<AppInfo> (); |
1791 | + List<AppInfo> non_uri_apps = null; |
1792 | |
1793 | foreach (var app in apps) { |
1794 | - if (!app.supports_uris ()) { |
1795 | + if (!app.supports_uris ()) |
1796 | non_uri_apps.append (app); |
1797 | - } |
1798 | } |
1799 | |
1800 | foreach (var app in non_uri_apps) { |
1801 | |
1802 | === modified file 'src/MultiLineEditableLabel.vala' |
1803 | --- src/MultiLineEditableLabel.vala 2014-12-25 08:43:14 +0000 |
1804 | +++ src/MultiLineEditableLabel.vala 2015-02-07 19:01:29 +0000 |
1805 | @@ -26,6 +26,9 @@ |
1806 | |
1807 | public override Gtk.Widget create_editable_widget () { |
1808 | textview = new Gtk.TextView (); |
1809 | + /* Block propagation of button press event to view as this would cause renaming to end */ |
1810 | + textview.button_press_event.connect_after (() => {return true;}); |
1811 | + |
1812 | scrolled_window = new Gtk.ScrolledWindow (null, null); |
1813 | scrolled_window.add (textview); |
1814 | return scrolled_window as Gtk.Widget; |
1815 | |
1816 | === modified file 'src/View/AbstractDirectoryView.vala' |
1817 | --- src/View/AbstractDirectoryView.vala 2015-01-28 09:05:34 +0000 |
1818 | +++ src/View/AbstractDirectoryView.vala 2015-02-07 19:01:29 +0000 |
1819 | @@ -198,7 +198,7 @@ |
1820 | private GLib.AppInfo default_app; |
1821 | private Gtk.TreePath? hover_path = null; |
1822 | |
1823 | - private bool selection_was_removed = false; |
1824 | + private bool can_trash_or_delete = true; |
1825 | |
1826 | /* Rapid keyboard paste support */ |
1827 | protected bool pasting_files = false; |
1828 | @@ -211,6 +211,7 @@ |
1829 | private bool updates_frozen = false; |
1830 | protected bool tree_frozen = false; |
1831 | private bool in_trash = false; |
1832 | + private bool in_network_root = false; |
1833 | protected bool is_loading; |
1834 | protected bool helpers_shown; |
1835 | private uint select_timeout_id = 0; |
1836 | @@ -429,12 +430,13 @@ |
1837 | |
1838 | protected void unfreeze_updates () { |
1839 | updates_frozen = false; |
1840 | - slot.directory.freeze_update = false;; |
1841 | + slot.directory.freeze_update = false; |
1842 | update_menu_actions (); |
1843 | size_allocate.connect (on_size_allocate); |
1844 | clipboard.changed.connect (on_clipboard_changed); |
1845 | view.enter_notify_event.connect (on_enter_notify_event); |
1846 | view.key_press_event.connect (on_view_key_press_event); |
1847 | + |
1848 | } |
1849 | |
1850 | public new void grab_focus () { |
1851 | @@ -481,21 +483,27 @@ |
1852 | return; |
1853 | |
1854 | unowned Gdk.Screen screen = Eel.gtk_widget_get_screen (this); |
1855 | - bool only_folders = selection_only_contains_folders (selection); |
1856 | - |
1857 | - if (nb_elem < 10 && (default_app == null || only_folders)) { |
1858 | + |
1859 | + if (nb_elem == 1) { |
1860 | + activate_file (selection.data, screen, flag, true); |
1861 | + return; |
1862 | + } |
1863 | + |
1864 | + if (nb_elem < 10 && (default_app == null)) { |
1865 | /* launch each selected file individually ignoring selections greater than 10 */ |
1866 | - bool only_one_file = (nb_elem == 1); |
1867 | - |
1868 | foreach (unowned GOF.File file in selection) { |
1869 | /* Prevent too rapid activation of files - causes New Tab to crash for example */ |
1870 | - GLib.Timeout.add (50, () => { |
1871 | - activate_file (file, screen, flag, only_one_file); |
1872 | - return false; |
1873 | - }); |
1874 | + if (file.is_folder ()) { |
1875 | + GLib.Timeout.add (50, () => { |
1876 | + activate_file (file, screen, flag, false); |
1877 | + return false; |
1878 | + }); |
1879 | + } else |
1880 | + file.open_single (screen, null); |
1881 | } |
1882 | - } else if (default_app != null) |
1883 | + } else if (default_app != null) { |
1884 | open_files_with (default_app, selection); |
1885 | + } |
1886 | } |
1887 | |
1888 | protected void preview_selected_items () { |
1889 | @@ -582,13 +590,12 @@ |
1890 | loaded_subdirectories = null; |
1891 | model.clear (); |
1892 | unblock_model (); |
1893 | - |
1894 | - connect_directory_handlers (new_dir); |
1895 | - update_menu_actions (); |
1896 | - model.set_sort_column_id (slot.directory.file.sort_column_id, slot.directory.file.sort_order); |
1897 | + if (new_dir.can_load); |
1898 | + connect_directory_handlers (new_dir); |
1899 | } |
1900 | |
1901 | public void reload () { |
1902 | + slot.directory.clear_directory_info (); |
1903 | change_directory (slot.directory, slot.directory); |
1904 | } |
1905 | |
1906 | @@ -631,7 +638,7 @@ |
1907 | bool only_folders = true; |
1908 | |
1909 | list.@foreach ((file) => { |
1910 | - if (!file.is_folder ()) |
1911 | + if (!(file.is_folder () || file.is_root_network_folder ())) |
1912 | only_folders = false; |
1913 | }); |
1914 | |
1915 | @@ -700,13 +707,16 @@ |
1916 | if (updates_frozen || in_trash) |
1917 | return; |
1918 | |
1919 | - debug ("activate file %s only one file %s", file.uri, only_one_file.to_string ()); |
1920 | - GLib.File location = file.location.dup (); |
1921 | - |
1922 | + GLib.File location = file.get_target_location (); |
1923 | if (screen == null) |
1924 | screen = Eel.gtk_widget_get_screen (this); |
1925 | |
1926 | - if (file.is_folder ()) { |
1927 | + default_app = Marlin.MimeActions.get_default_application_for_file (file); |
1928 | + |
1929 | + if (file.is_folder () || |
1930 | + file.get_ftype () == "inode/directory" || |
1931 | + file.is_root_network_folder ()) { |
1932 | + |
1933 | switch (flag) { |
1934 | case Marlin.OpenFlag.NEW_TAB: |
1935 | window.add_tab (location, Marlin.ViewMode.CURRENT); |
1936 | @@ -729,25 +739,27 @@ |
1937 | else if (only_one_file && default_app != null) |
1938 | file.open_single (screen, default_app); |
1939 | else |
1940 | - warning ("Unable to activate this file. Default app is %s", default_app != null ? default_app.get_name () : "null"); |
1941 | + warning ("Unable to activate this file. Default app is %s", |
1942 | + default_app != null ? default_app.get_name () : "null"); |
1943 | } |
1944 | |
1945 | private void trash_or_delete_files (GLib.List<unowned GOF.File> file_list, |
1946 | bool delete_if_already_in_trash, |
1947 | bool delete_immediately) { |
1948 | + |
1949 | GLib.List<GLib.File> locations = null; |
1950 | - |
1951 | file_list.@foreach ((file) => { |
1952 | locations.prepend (file.location); |
1953 | }); |
1954 | |
1955 | if (locations != null) { |
1956 | locations.reverse (); |
1957 | + |
1958 | if (delete_immediately) |
1959 | - Marlin.FileOperations.delete (locations, |
1960 | - window as Gtk.Window, |
1961 | - after_trash_or_delete, |
1962 | - this); |
1963 | + Marlin.FileOperations.@delete (locations, |
1964 | + window as Gtk.Window, |
1965 | + after_trash_or_delete, |
1966 | + this); |
1967 | else |
1968 | Marlin.FileOperations.trash_or_delete (locations, |
1969 | window as Gtk.Window, |
1970 | @@ -787,7 +799,10 @@ |
1971 | unselect_all (); |
1972 | select_gof_file (file_to_rename); |
1973 | |
1974 | - if (file_to_rename.is_writable ()) |
1975 | + /* Assume writability on remote locations |
1976 | + * TODO Reliably determine writability with various remote protocols. |
1977 | + */ |
1978 | + if (file_to_rename.is_writable () || !slot.directory.is_local) |
1979 | start_renaming_file (file_to_rename, false); |
1980 | else |
1981 | warning ("You do not have permission to rename this file"); |
1982 | @@ -801,17 +816,32 @@ |
1983 | |
1984 | var view = (FM.AbstractDirectoryView)data; |
1985 | var file_to_rename = GOF.File.@get (new_file); |
1986 | - /* Allow time for the file to appear in the tree model before renaming */ |
1987 | - GLib.Timeout.add (250, () => { |
1988 | + bool local = view.slot.directory.is_local; |
1989 | + if (!local) |
1990 | + view.slot.directory.need_reload (); |
1991 | + |
1992 | + /* Allow time for the file to appear in the tree model before renaming |
1993 | + * Wait longer for remote locations to allow for reload. |
1994 | + * TODO: Remove need for hard coded delay. |
1995 | + */ |
1996 | + int delay = local ? 250 : 500; |
1997 | + GLib.Timeout.add (delay, () => { |
1998 | view.rename_file (file_to_rename); |
1999 | view.slot.directory.unblock_monitor (); |
2000 | return false; |
2001 | }); |
2002 | } |
2003 | |
2004 | - public static void after_trash_or_delete (bool user_cancel, void* callback_data) { |
2005 | - FM.AbstractDirectoryView view = (FM.AbstractDirectoryView)callback_data; |
2006 | - view.selection_was_removed = false; |
2007 | + /** Must pass a pointer to an instance of FM.AbstractDirectoryView as 3rd parameter when |
2008 | + * using this callback */ |
2009 | + public static void after_trash_or_delete (bool user_cancel, void* data) { |
2010 | + var view = data as FM.AbstractDirectoryView; |
2011 | + assert (view is FM.AbstractDirectoryView); |
2012 | + |
2013 | + view.can_trash_or_delete = true; |
2014 | + |
2015 | + if (!view.slot.directory.is_local) |
2016 | + view.slot.directory.need_reload (); |
2017 | } |
2018 | |
2019 | private void trash_or_delete_selected_files (bool delete_immediately = false) { |
2020 | @@ -819,16 +849,32 @@ |
2021 | * when using keybindings. So we remember if the current selection |
2022 | * was already removed (but the view doesn't know about it yet). |
2023 | */ |
2024 | - if (selection_was_removed) |
2025 | + if (!can_trash_or_delete) |
2026 | return; |
2027 | |
2028 | unowned GLib.List<unowned GOF.File> selection = get_selected_files_for_transfer (); |
2029 | if (selection != null) { |
2030 | + can_trash_or_delete = false; |
2031 | + |
2032 | trash_or_delete_files (selection, true, delete_immediately); |
2033 | - selection_was_removed = true; |
2034 | } |
2035 | } |
2036 | |
2037 | + private void delete_selected_files () { |
2038 | + unowned GLib.List<unowned GOF.File> selection = get_selected_files_for_transfer (); |
2039 | + if (selection == null) |
2040 | + return; |
2041 | + |
2042 | + GLib.List<GLib.File> locations = null; |
2043 | + |
2044 | + selection.@foreach ((file) => { |
2045 | + locations.prepend (file.location); |
2046 | + }); |
2047 | + |
2048 | + locations.reverse (); |
2049 | + Marlin.FileOperations.@delete (locations, window as Gtk.Window, after_trash_or_delete, this); |
2050 | + } |
2051 | + |
2052 | /** Signal Handlers */ |
2053 | |
2054 | /** Menu actions */ |
2055 | @@ -920,10 +966,13 @@ |
2056 | } |
2057 | |
2058 | private void on_common_action_bookmark (GLib.SimpleAction action, GLib.Variant? param) { |
2059 | + GLib.File location; |
2060 | if (selected_files != null) |
2061 | - window.sidebar.add_uri (selected_files.data.uri); |
2062 | + location = selected_files.data.get_target_location (); |
2063 | else |
2064 | - window.sidebar.add_uri (slot.directory.file.uri); |
2065 | + location = slot.directory.file.get_target_location (); |
2066 | + |
2067 | + window.sidebar.add_uri (location.get_uri (), null); |
2068 | } |
2069 | |
2070 | /** Background actions */ |
2071 | @@ -1018,6 +1067,8 @@ |
2072 | } |
2073 | |
2074 | public static void after_pasting_files (GLib.HashTable uris, void* pointer) { |
2075 | + assert (pointer != null); |
2076 | + |
2077 | var view = pointer as FM.AbstractDirectoryView; |
2078 | if (view.paste_timer == null) |
2079 | view.paste_timer = new GLib.Timer (); |
2080 | @@ -1051,14 +1102,27 @@ |
2081 | var file = get_files_for_action ().nth_data (0); |
2082 | |
2083 | if (file != null && clipboard.get_can_paste ()) { |
2084 | - pasting_files = true; |
2085 | - prepare_to_select_added_files (); |
2086 | - /* Block the async directory file monitor to avoid generating unwanted "add-file" events */ |
2087 | - slot.directory.block_monitor (); |
2088 | + GLib.File target; |
2089 | + GLib.Callback? call_back; |
2090 | + |
2091 | if (file.is_folder () && !clipboard.has_file (file)) |
2092 | - clipboard.paste_files (file.get_target_location (), this as Gtk.Widget, after_pasting_files); |
2093 | + target = file.get_target_location (); |
2094 | else |
2095 | - clipboard.paste_files (slot.directory.location, this as Gtk.Widget, after_pasting_files); |
2096 | + target = slot.location; |
2097 | + |
2098 | + if (target.has_uri_scheme ("trash")) { |
2099 | + /* Pasting files into trash is equivalent to trash or delete action */ |
2100 | + pasting_files = false; |
2101 | + call_back = (GLib.Callback)after_trash_or_delete; |
2102 | + } else { |
2103 | + pasting_files = true; |
2104 | + prepare_to_select_added_files (); |
2105 | + /* Block the async directory file monitor to avoid generating unwanted "add-file" events */ |
2106 | + slot.directory.block_monitor (); |
2107 | + call_back = (GLib.Callback)after_pasting_files; |
2108 | + } |
2109 | + |
2110 | + clipboard.paste_files (target, this as Gtk.Widget, call_back); |
2111 | } |
2112 | } |
2113 | |
2114 | @@ -1076,7 +1140,8 @@ |
2115 | model.file_changed (file, dir); |
2116 | /* 2nd parameter is for returned request id if required - we do not use it? */ |
2117 | /* This is required if we need to dequeue the request */ |
2118 | - thumbnailer.queue_file (file, null, false); |
2119 | + if (slot.directory.is_local) |
2120 | + thumbnailer.queue_file (file, null, false); |
2121 | } |
2122 | |
2123 | private void on_directory_file_icon_changed (GOF.Directory.Async dir, GOF.File file) { |
2124 | @@ -1098,8 +1163,12 @@ |
2125 | private void on_directory_done_loading (GOF.Directory.Async dir) { |
2126 | debug ("DV directory done loading %s", dir.file.uri); |
2127 | dir.file_loaded.disconnect (on_directory_file_loaded); |
2128 | - in_trash = (dir.file.uri == Marlin.TRASH_URI); /* trash cannot be subdirectory */ |
2129 | + in_trash = slot.directory.is_trash; |
2130 | + in_network_root = slot.directory.file.is_root_network_folder (); |
2131 | thaw_tree (); |
2132 | + |
2133 | + model.set_sort_column_id (slot.directory.file.sort_column_id, slot.directory.file.sort_order); |
2134 | + |
2135 | /* This is a workround for a bug (Gtk?) in the drawing of the ListView where the columns |
2136 | * are sometimes not properly aligned when first drawn, only after redrawing the view. */ |
2137 | Idle.add (() => { |
2138 | @@ -1117,7 +1186,7 @@ |
2139 | model.set_property ("size", icon_size); |
2140 | change_zoom_level (); |
2141 | |
2142 | - if (get_realized ()) |
2143 | + if (get_realized () && slot.directory.is_local) |
2144 | load_thumbnails (slot.directory, zoom); |
2145 | } |
2146 | |
2147 | @@ -1225,7 +1294,6 @@ |
2148 | |
2149 | /** Handle clipboard signal */ |
2150 | private void on_clipboard_changed () { |
2151 | - update_menu_actions (); |
2152 | /* show possible change in appearance of cut items */ |
2153 | queue_draw (); |
2154 | } |
2155 | @@ -1238,7 +1306,6 @@ |
2156 | if (updates_frozen) |
2157 | return; |
2158 | |
2159 | - update_menu_actions (); |
2160 | window.selection_changed (get_selected_files ()); |
2161 | } |
2162 | |
2163 | @@ -1274,7 +1341,7 @@ |
2164 | GLib.StringBuilder sb = new GLib.StringBuilder (""); |
2165 | |
2166 | drag_file_list.@foreach ((file) => { |
2167 | - sb.append (file.uri); |
2168 | + sb.append (file.get_target_location ().get_uri ()); |
2169 | sb.append ("\n"); |
2170 | }); |
2171 | |
2172 | @@ -1305,15 +1372,16 @@ |
2173 | int y, |
2174 | uint timestamp) { |
2175 | /* if we don't have drop data already ... */ |
2176 | - if (!drop_data_ready) { |
2177 | - get_drop_data (context, x, y, timestamp); |
2178 | - } else |
2179 | + if (!drop_data_ready && !get_drop_data (context, x, y, timestamp)) |
2180 | + return false; |
2181 | + else |
2182 | /* We have the drop data - check whether we can drop here*/ |
2183 | check_destination_actions_and_target_file (context, x, y, timestamp); |
2184 | |
2185 | if (drag_scroll_timer_id == 0) |
2186 | start_drag_scroll_timer (context); |
2187 | |
2188 | + Gdk.drag_status (context, current_suggested_action, timestamp); |
2189 | return true; |
2190 | } |
2191 | |
2192 | @@ -1376,6 +1444,12 @@ |
2193 | if (drop_occurred) { |
2194 | drop_occurred = false; |
2195 | if (current_actions != Gdk.DragAction.DEFAULT) { |
2196 | + if (!slot.directory.is_local) { |
2197 | + /* Cannot be sure that view will automatically refresh properly |
2198 | + * so we force a refresh after a short delay */ |
2199 | + slot.directory.clear_directory_info (); |
2200 | + slot.directory.need_reload (); |
2201 | + } |
2202 | switch (info) { |
2203 | case TargetType.XDND_DIRECT_SAVE0: |
2204 | success = dnd_handler.handle_xdnddirectsave (context, |
2205 | @@ -1392,6 +1466,7 @@ |
2206 | case TargetType.TEXT_URI_LIST: |
2207 | if ((current_actions & file_drag_actions) != 0) { |
2208 | prepare_to_select_added_files (); |
2209 | + |
2210 | success = dnd_handler.handle_file_drag_actions (get_real_view (), |
2211 | window, |
2212 | context, |
2213 | @@ -1464,7 +1539,7 @@ |
2214 | return file; |
2215 | } |
2216 | |
2217 | - private void get_drop_data (Gdk.DragContext context, int x, int y, uint timestamp) { |
2218 | + private bool get_drop_data (Gdk.DragContext context, int x, int y, uint timestamp) { |
2219 | Gdk.DragAction action = Gdk.DragAction.DEFAULT; |
2220 | Gtk.TargetList? list = null; |
2221 | Gdk.Atom target = Gtk.drag_dest_find_target (get_real_view (), context, list); |
2222 | @@ -1497,9 +1572,10 @@ |
2223 | } else if (target != Gdk.Atom.NONE) |
2224 | /* request the drag data from the source */ |
2225 | Gtk.drag_get_data (get_real_view (), context, target, timestamp); /* emits "drag_data_received" */ |
2226 | + else |
2227 | + return false; |
2228 | |
2229 | - /* tell Gdk whether we can drop here */ |
2230 | - Gdk.drag_status (context, action, timestamp); |
2231 | + return true; |
2232 | } |
2233 | |
2234 | private void check_destination_actions_and_target_file (Gdk.DragContext context, int x, int y, uint timestamp) { |
2235 | @@ -1508,13 +1584,16 @@ |
2236 | string uri = file != null ? file.uri : ""; |
2237 | string current_uri = drop_target_file != null ? drop_target_file.uri : ""; |
2238 | |
2239 | + Gdk.drag_status (context, Gdk.DragAction.MOVE, timestamp); |
2240 | if (uri != current_uri) { |
2241 | drop_target_file = file; |
2242 | current_actions = Gdk.DragAction.DEFAULT; |
2243 | current_suggested_action = Gdk.DragAction.DEFAULT; |
2244 | + |
2245 | if (file != null) { |
2246 | current_actions = file.accepts_drop (drop_file_list, context, out current_suggested_action); |
2247 | highlight_drop_file (drop_target_file, current_actions, path); |
2248 | + |
2249 | if (file.is_folder () && drag_file_list.index (file) == -1) { |
2250 | cancel_timeout (ref drag_enter_timer_id); |
2251 | drag_enter_timer_id = GLib.Timeout.add_full (GLib.Priority.LOW, |
2252 | @@ -1526,7 +1605,6 @@ |
2253 | }); |
2254 | } |
2255 | } |
2256 | - Gdk.drag_status (context, current_suggested_action, timestamp); |
2257 | } |
2258 | } |
2259 | |
2260 | @@ -1584,89 +1662,130 @@ |
2261 | |
2262 | protected void show_context_menu (Gdk.Event event) { |
2263 | /* select selection or background context menu */ |
2264 | + update_menu_actions (); |
2265 | var builder = new Gtk.Builder.from_file (Config.UI_DIR + "directory_view_popup.ui"); |
2266 | - GLib.MenuModel? model; |
2267 | + GLib.MenuModel? model = null; |
2268 | |
2269 | - if (get_selected_files () != null) |
2270 | + if (get_selected_files () != null) |
2271 | model = build_menu_selection (ref builder, in_trash); |
2272 | else |
2273 | model = build_menu_background (ref builder, in_trash); |
2274 | |
2275 | - if (model != null) { |
2276 | + if (model != null && model is GLib.MenuModel) { |
2277 | /* add any additional entries from plugins */ |
2278 | var menu = new Gtk.Menu.from_model (model); |
2279 | - plugins.hook_context_menu (menu as Gtk.Widget, get_selected_files ()); |
2280 | + |
2281 | + if (!in_trash) |
2282 | + plugins.hook_context_menu (menu as Gtk.Widget, get_selected_files ()); |
2283 | + |
2284 | menu.set_screen (null); |
2285 | menu.attach_to_widget (this, null); |
2286 | Eel.pop_up_context_menu (menu, |
2287 | Eel.DEFAULT_POPUP_MENU_DISPLACEMENT, |
2288 | Eel.DEFAULT_POPUP_MENU_DISPLACEMENT, |
2289 | (Gdk.EventButton) event); |
2290 | - } else |
2291 | - warning ("Model is null"); |
2292 | + } |
2293 | + } |
2294 | + |
2295 | + private bool valid_selection_for_edit () { |
2296 | + foreach (GOF.File file in get_selected_files ()) { |
2297 | + if (file.is_root_network_folder ()) |
2298 | + return false; |
2299 | + } |
2300 | + return true; |
2301 | } |
2302 | |
2303 | private GLib.MenuModel? build_menu_selection (ref Gtk.Builder builder, bool in_trash) { |
2304 | GLib.Menu menu = new GLib.Menu (); |
2305 | |
2306 | - if (in_trash) |
2307 | + var clipboard_menu = builder.get_object ("clipboard-selection") as GLib.Menu; |
2308 | + |
2309 | + if (in_trash) { |
2310 | menu.append_section (null, builder.get_object ("popup-trash-selection") as GLib.Menu); |
2311 | - else { |
2312 | - menu.append_section (null, build_menu_open (ref builder)); |
2313 | |
2314 | - var clipboard_menu = builder.get_object ("clipboard-selection") as GLib.Menu; |
2315 | - /* Do not display the 'Paste into' menuitem if selection is not a folder. |
2316 | - * We have to hard-code the menuitem index so any change to the clipboard- |
2317 | - * selection menu definition in directory_view_popup.ui may necessitate changing |
2318 | - * the index below. |
2319 | - */ |
2320 | - if (!common_actions.get_action_enabled ("paste_into")) |
2321 | - clipboard_menu.remove (2); |
2322 | + clipboard_menu.remove (1); /* Copy */ |
2323 | + clipboard_menu.remove (1); /* Paste (index updated by previous line) */ |
2324 | |
2325 | menu.append_section (null, clipboard_menu); |
2326 | - |
2327 | - menu.append_section (null, builder.get_object ("trash") as GLib.MenuModel); |
2328 | - menu.append_section (null, builder.get_object ("rename") as GLib.MenuModel); |
2329 | + } else { |
2330 | + var open_menu = build_menu_open (ref builder); |
2331 | + if (open_menu != null) |
2332 | + menu.append_section (null, open_menu); |
2333 | + |
2334 | + if (slot.directory.file.is_smb_server ()) { |
2335 | + if (common_actions.get_action_enabled ("paste_into")) |
2336 | + menu.append_section (null, builder.get_object ("paste") as GLib.MenuModel); |
2337 | + } else if (valid_selection_for_edit ()) { |
2338 | + /* Do not display the 'Paste into' menuitem if selection is not a folder. |
2339 | + * We have to hard-code the menuitem index so any change to the clipboard- |
2340 | + * selection menu definition in directory_view_popup.ui may necessitate changing |
2341 | + * the index below. |
2342 | + */ |
2343 | + if (!common_actions.get_action_enabled ("paste_into")) |
2344 | + clipboard_menu.remove (2); |
2345 | + |
2346 | + menu.append_section (null, clipboard_menu); |
2347 | + |
2348 | + if (slot.directory.has_trash_dirs) |
2349 | + menu.append_section (null, builder.get_object ("trash") as GLib.MenuModel); |
2350 | + else |
2351 | + menu.append_section (null, builder.get_object ("delete") as GLib.MenuModel); |
2352 | + |
2353 | + menu.append_section (null, builder.get_object ("rename") as GLib.MenuModel); |
2354 | + } |
2355 | |
2356 | if (common_actions.get_action_enabled ("bookmark")) |
2357 | menu.append_section (null, builder.get_object ("bookmark") as GLib.MenuModel); |
2358 | + |
2359 | + menu.append_section (null, builder.get_object ("properties") as GLib.MenuModel); |
2360 | } |
2361 | - menu.append_section (null, builder.get_object ("properties") as GLib.MenuModel); |
2362 | + |
2363 | return menu as MenuModel; |
2364 | } |
2365 | |
2366 | private GLib.MenuModel? build_menu_background (ref Gtk.Builder builder, bool in_trash) { |
2367 | - if (in_trash) |
2368 | - return null; |
2369 | + if (in_trash) { |
2370 | + var menu = new GLib.Menu (); |
2371 | + if (common_actions.get_action_enabled ("paste_into")) { |
2372 | + menu.append_section (null, builder.get_object ("paste") as GLib.MenuModel); |
2373 | + |
2374 | + return menu as MenuModel; |
2375 | + } else |
2376 | + return null; |
2377 | + } |
2378 | |
2379 | var menu = new GLib.Menu (); |
2380 | menu.append_section (null, build_menu_open (ref builder)); |
2381 | |
2382 | - if (common_actions.get_action_enabled ("paste_into")) |
2383 | - menu.append_section (null, builder.get_object ("paste") as GLib.MenuModel); |
2384 | - |
2385 | - |
2386 | - GLib.MenuModel? template_menu = build_menu_templates (); |
2387 | - var new_menu = builder.get_object ("new") as GLib.Menu; |
2388 | - |
2389 | - if (template_menu != null) { |
2390 | - var new_submenu = builder.get_object ("new-submenu") as GLib.Menu; |
2391 | - new_submenu.append_section (null, template_menu); |
2392 | + if (!in_network_root) { |
2393 | + if (common_actions.get_action_enabled ("paste_into")) |
2394 | + menu.append_section (null, builder.get_object ("paste") as GLib.MenuModel); |
2395 | + |
2396 | + GLib.MenuModel? template_menu = build_menu_templates (); |
2397 | + var new_menu = builder.get_object ("new") as GLib.Menu; |
2398 | + |
2399 | + if (template_menu != null) { |
2400 | + var new_submenu = builder.get_object ("new-submenu") as GLib.Menu; |
2401 | + new_submenu.append_section (null, template_menu); |
2402 | + } |
2403 | + |
2404 | + menu.append_section (null, new_menu as GLib.MenuModel); |
2405 | + |
2406 | + menu.append_section (null, builder.get_object ("sort-by") as GLib.MenuModel); |
2407 | } |
2408 | |
2409 | - menu.append_section (null, new_menu as GLib.MenuModel); |
2410 | - menu.append_section (null, builder.get_object ("sort-by") as GLib.MenuModel); |
2411 | - |
2412 | if (common_actions.get_action_enabled ("bookmark")) |
2413 | menu.append_section (null, builder.get_object ("bookmark") as GLib.MenuModel); |
2414 | |
2415 | menu.append_section (null, builder.get_object ("hidden") as GLib.MenuModel); |
2416 | menu.append_section (null, builder.get_object ("properties") as GLib.MenuModel); |
2417 | + |
2418 | return menu as MenuModel; |
2419 | } |
2420 | |
2421 | private GLib.MenuModel build_menu_open (ref Gtk.Builder builder) { |
2422 | var menu = new GLib.Menu (); |
2423 | + |
2424 | string label = _("Invalid"); |
2425 | unowned GLib.List<unowned GOF.File> selection = get_files_for_action (); |
2426 | unowned GOF.File selected_file = selection.data; |
2427 | @@ -1684,8 +1803,14 @@ |
2428 | |
2429 | GLib.MenuModel? app_submenu = build_submenu_open_with_applications (ref builder, selection); |
2430 | |
2431 | - if (app_submenu != null) |
2432 | - menu.append_submenu (_("Open in"), app_submenu); |
2433 | + if (app_submenu != null && app_submenu.get_n_items () > 0) { |
2434 | + if (selected_file.is_folder () || selected_file.is_root_network_folder ()) |
2435 | + label = _("Open in"); |
2436 | + else |
2437 | + label = _("Open with"); |
2438 | + |
2439 | + menu.append_submenu (label, app_submenu); |
2440 | + } |
2441 | |
2442 | return menu as MenuModel; |
2443 | } |
2444 | @@ -1696,32 +1821,39 @@ |
2445 | var open_with_submenu = new GLib.Menu (); |
2446 | int index = -1; |
2447 | |
2448 | - if (common_actions.get_action_enabled ("open_in")) |
2449 | + if (common_actions.get_action_enabled ("open_in")) { |
2450 | open_with_submenu.append_section (null, builder.get_object ("open-in") as GLib.MenuModel); |
2451 | |
2452 | + if (!selection.data.is_mountable () && !selection.data.is_root_network_folder ()) |
2453 | + open_with_submenu.append_section (null, builder.get_object ("open-in-terminal") as GLib.MenuModel); |
2454 | + else |
2455 | + return open_with_submenu; |
2456 | + } |
2457 | + |
2458 | open_with_apps = Marlin.MimeActions.get_applications_for_files (selection); |
2459 | filter_default_app_from_open_with_apps (); |
2460 | filter_this_app_from_open_with_apps (); |
2461 | |
2462 | - if (open_with_apps.length () > 0) { |
2463 | + if (open_with_apps != null) { |
2464 | var apps_section = new GLib.Menu (); |
2465 | string last_label = ""; |
2466 | open_with_apps.@foreach ((app) => { |
2467 | - var label = app.get_display_name (); |
2468 | - |
2469 | - /* The following mainly applies to Nautilus, whose display name is also "Files" */ |
2470 | - if (label == "Files") { |
2471 | - label = app.get_executable (); |
2472 | - label = label[0].toupper ().to_string () + label.substring (1); |
2473 | - } |
2474 | - |
2475 | - /* Do no show same name twice - some apps have more than one .desktop file |
2476 | - * with the same name (e.g. Nautilus) |
2477 | - */ |
2478 | - if (label != last_label) { |
2479 | - index++; |
2480 | - apps_section.append (label, "selection.open_with_app::" + index.to_string ()); |
2481 | - last_label = label.dup (); |
2482 | + if (app != null && app is AppInfo) { |
2483 | + var label = app.get_display_name (); |
2484 | + /* The following mainly applies to Nautilus, whose display name is also "Files" */ |
2485 | + if (label == "Files") { |
2486 | + label = app.get_executable (); |
2487 | + label = label[0].toupper ().to_string () + label.substring (1); |
2488 | + } |
2489 | + |
2490 | + /* Do not show same name twice - some apps have more than one .desktop file |
2491 | + * with the same name (e.g. Nautilus) |
2492 | + */ |
2493 | + if (label != last_label) { |
2494 | + index++; |
2495 | + apps_section.append (label, "selection.open_with_app::" + index.to_string ()); |
2496 | + last_label = label.dup (); |
2497 | + } |
2498 | } |
2499 | }); |
2500 | |
2501 | @@ -1781,21 +1913,26 @@ |
2502 | return; |
2503 | |
2504 | unowned GLib.List<unowned GOF.File> selection = get_files_for_action (); |
2505 | + unowned GOF.File file; |
2506 | + |
2507 | uint selection_count = selection.length (); |
2508 | bool more_than_one_selected = (selection_count > 1); |
2509 | - bool single_folder = true; /* background is a folder */ |
2510 | + bool single_folder = false; |
2511 | bool only_folders = selection_only_contains_folders (selection); |
2512 | bool can_rename = false; |
2513 | |
2514 | update_default_app (selection); |
2515 | |
2516 | if (selection_count > 0) { |
2517 | - unowned GOF.File? file = selection.data; |
2518 | + file = selection.data; |
2519 | if (file != null) { |
2520 | single_folder = (!more_than_one_selected && file.is_folder ()); |
2521 | can_rename = file.is_writable (); |
2522 | } else |
2523 | critical ("File in selection is null"); |
2524 | + } else { |
2525 | + file = slot.directory.file; |
2526 | + single_folder = (!more_than_one_selected && file.is_folder ()); |
2527 | } |
2528 | |
2529 | update_paste_action_enabled (single_folder); |
2530 | @@ -1806,8 +1943,18 @@ |
2531 | action_set_enabled (selection_actions, "rename", selection_count == 1 && can_rename); |
2532 | action_set_enabled (selection_actions, "open", selection_count == 1); |
2533 | action_set_enabled (selection_actions, "cut", selection_count > 0); |
2534 | + action_set_enabled (selection_actions, "trash", slot.directory.has_trash_dirs); |
2535 | + |
2536 | + /* Both folder and file can be bookmarked if local, but only remote folders can be bookmarked |
2537 | + * because remote file bookmarks do not work correctly for unmounted locations */ |
2538 | + bool can_bookmark = (!more_than_one_selected || single_folder) && |
2539 | + (slot.directory.is_local || |
2540 | + (file.get_ftype () != null && file.get_ftype () == "inode/directory") || |
2541 | + file.is_smb_server ()); |
2542 | + |
2543 | + action_set_enabled (common_actions, "bookmark", can_bookmark); |
2544 | /* TODO inhibit copy for unreadable files see bug #1392465*/ |
2545 | - action_set_enabled (common_actions, "copy", true); |
2546 | + action_set_enabled (common_actions, "copy", !in_trash); |
2547 | action_set_enabled (common_actions, "bookmark", !more_than_one_selected); |
2548 | } |
2549 | |
2550 | @@ -1915,13 +2062,19 @@ |
2551 | unowned GLib.List<AppInfo> l = open_with_apps; |
2552 | |
2553 | while (l != null) { |
2554 | - exec_name = l.data.get_executable (); |
2555 | + if (l.data is AppInfo) { |
2556 | + exec_name = l.data.get_executable (); |
2557 | |
2558 | - if (exec_name != null && (exec_name == APP_NAME || exec_name == TERMINAL_NAME)) { |
2559 | + if (exec_name != null && (exec_name == APP_NAME || exec_name == TERMINAL_NAME)) { |
2560 | + open_with_apps.delete_link (l); |
2561 | + break; |
2562 | + } |
2563 | + } else { |
2564 | open_with_apps.delete_link (l); |
2565 | - break; |
2566 | + l = open_with_apps; |
2567 | + if (l == null) |
2568 | + break; |
2569 | } |
2570 | - |
2571 | l = l.next; |
2572 | } |
2573 | } |
2574 | @@ -1936,7 +2089,7 @@ |
2575 | if (id2 != null) { |
2576 | unowned GLib.List<AppInfo> l = open_with_apps; |
2577 | |
2578 | - while (l != null) { |
2579 | + while (l != null && l.data is AppInfo) { |
2580 | id1 = l.data.get_id (); |
2581 | |
2582 | if (id1 != null && id1 == id2) { |
2583 | @@ -1975,10 +2128,13 @@ |
2584 | * all items have been added and we've perhaps scrolled to the file remembered |
2585 | * the last time */ |
2586 | |
2587 | - if (thumbnail_source_id != 0 || !(slot is GOF.AbstractSlot) || slot.directory == null) |
2588 | + if (thumbnail_source_id != 0 || |
2589 | + !(slot is GOF.AbstractSlot) || |
2590 | + slot.directory == null || |
2591 | + !slot.directory.is_local) |
2592 | + |
2593 | return; |
2594 | |
2595 | - cancel_thumbnailing (); |
2596 | thumbnail_source_id = GLib.Timeout.add (175, () => { |
2597 | if (!(slot is GOF.AbstractSlot) || slot.directory == null) |
2598 | return false; |
2599 | @@ -1986,6 +2142,8 @@ |
2600 | if (slot.directory.is_loading ()) |
2601 | return true; |
2602 | |
2603 | + cancel_thumbnailing (); |
2604 | + |
2605 | /* compute visible item range */ |
2606 | Gtk.TreePath start_path, end_path, path; |
2607 | Gtk.TreeIter iter; |
2608 | @@ -2036,7 +2194,7 @@ |
2609 | } |
2610 | |
2611 | private void load_thumbnails (GOF.Directory.Async dir, Marlin.ZoomLevel zoom) { |
2612 | - /* Async function checks dir is not loading */ |
2613 | + /* Async function checks dir is not loading and dir is local */ |
2614 | dir.queue_load_thumbnails (Marlin.zoom_level_to_icon_size (zoom)); |
2615 | } |
2616 | |
2617 | @@ -2143,6 +2301,7 @@ |
2618 | return; |
2619 | |
2620 | notify_selection_changed (); |
2621 | + update_menu_actions (); |
2622 | } |
2623 | |
2624 | protected virtual bool on_view_key_press_event (Gdk.EventKey event) { |
2625 | @@ -2157,6 +2316,7 @@ |
2626 | bool other_mod_pressed = (((mods & ~Gdk.ModifierType.SHIFT_MASK) & ~Gdk.ModifierType.CONTROL_MASK) != 0); |
2627 | bool only_control_pressed = control_pressed && !other_mod_pressed; /* Shift can be pressed */ |
2628 | bool only_alt_pressed = alt_pressed && ((mods & ~Gdk.ModifierType.MOD1_MASK) == 0); |
2629 | + bool in_trash = slot.location.has_uri_scheme ("trash"); |
2630 | |
2631 | switch (event.keyval) { |
2632 | case Gdk.Key.F10: |
2633 | @@ -2167,7 +2327,7 @@ |
2634 | break; |
2635 | |
2636 | case Gdk.Key.F2: |
2637 | - if (no_mods) { |
2638 | + if (no_mods && selection_actions.get_action_enabled ("rename")) { |
2639 | rename_selected_file (); |
2640 | return true; |
2641 | } |
2642 | @@ -2176,17 +2336,21 @@ |
2643 | case Gdk.Key.Delete: |
2644 | case Gdk.Key.KP_Delete: |
2645 | if (no_mods) { |
2646 | - trash_or_delete_selected_files (false); |
2647 | + /* If already in trash, permanently delete the file */ |
2648 | + trash_or_delete_selected_files (in_trash); |
2649 | return true; |
2650 | } else if (only_shift_pressed) { |
2651 | trash_or_delete_selected_files (true); |
2652 | return true; |
2653 | + } else if (only_shift_pressed && !renaming) { |
2654 | + delete_selected_files (); |
2655 | + return true; |
2656 | } |
2657 | break; |
2658 | |
2659 | case Gdk.Key.space: |
2660 | if (view_has_focus ()) { |
2661 | - if (only_shift_pressed) |
2662 | + if (only_shift_pressed && !in_trash) |
2663 | activate_selected_items (Marlin.OpenFlag.NEW_TAB); |
2664 | else if (no_mods) |
2665 | preview_selected_items (); |
2666 | @@ -2199,7 +2363,9 @@ |
2667 | |
2668 | case Gdk.Key.Return: |
2669 | case Gdk.Key.KP_Enter: |
2670 | - if (only_shift_pressed) |
2671 | + if (in_trash) |
2672 | + return false; |
2673 | + else if (only_shift_pressed) |
2674 | activate_selected_items (Marlin.OpenFlag.NEW_TAB); |
2675 | else if (no_mods) |
2676 | activate_selected_items (Marlin.OpenFlag.DEFAULT); |
2677 | @@ -2209,14 +2375,14 @@ |
2678 | return true; |
2679 | |
2680 | case Gdk.Key.T: |
2681 | - if (only_control_pressed) { |
2682 | + if (only_control_pressed && !in_trash) { |
2683 | open_selected_in_terminal (); |
2684 | return true; |
2685 | } |
2686 | break; |
2687 | |
2688 | case Gdk.Key.N: |
2689 | - if (only_control_pressed) { |
2690 | + if (only_control_pressed && !in_trash) { |
2691 | activate_selected_items (Marlin.OpenFlag.NEW_TAB); |
2692 | return true; |
2693 | } |
2694 | @@ -2224,7 +2390,16 @@ |
2695 | |
2696 | case Gdk.Key.c: |
2697 | if (only_control_pressed) { |
2698 | - common_actions.activate_action ("copy", null); |
2699 | + /* Should not copy files in the trash - cut instead */ |
2700 | + if (in_trash) { |
2701 | + Eel.show_warning_dialog (_("Cannot copy files that are in the trash"), |
2702 | + _("Cutting the selection instead"), |
2703 | + window as Gtk.Window); |
2704 | + |
2705 | + selection_actions.activate_action ("cut", null); |
2706 | + } else |
2707 | + common_actions.activate_action ("copy", null); |
2708 | + |
2709 | return true; |
2710 | } |
2711 | break; |
2712 | @@ -2235,7 +2410,6 @@ |
2713 | action_set_enabled (common_actions, "paste_into", true); |
2714 | unselect_all (); |
2715 | common_actions.activate_action ("paste_into", null); |
2716 | - update_menu_actions (); |
2717 | return true; |
2718 | } |
2719 | break; |
2720 | @@ -2331,8 +2505,15 @@ |
2721 | (path == null && hover_path != null) || |
2722 | (path != null && hover_path != null && path.compare (hover_path) != 0)) { |
2723 | |
2724 | - window.item_hovered (file); |
2725 | - hover_path = path; |
2726 | + /* cannot get file info while network disconnected */ |
2727 | + if (slot.directory.is_local || slot.directory.check_network ()) { |
2728 | + /* cannot get file info while network disconnected */ |
2729 | + window.item_hovered (file); |
2730 | + hover_path = path; |
2731 | + } else { |
2732 | + slot.reload (); |
2733 | + slot.directory.need_reload (); |
2734 | + } |
2735 | } |
2736 | |
2737 | return false; |
2738 | @@ -2388,13 +2569,13 @@ |
2739 | } |
2740 | |
2741 | protected void on_name_editing_canceled () { |
2742 | - if (!renaming) |
2743 | - return; |
2744 | + if (!renaming) |
2745 | + return; |
2746 | |
2747 | - renaming = false; |
2748 | - name_renderer.editable = false; |
2749 | - unfreeze_updates (); |
2750 | - grab_focus (); |
2751 | + renaming = false; |
2752 | + name_renderer.editable = false; |
2753 | + unfreeze_updates (); |
2754 | + grab_focus (); |
2755 | } |
2756 | |
2757 | protected void on_name_edited (string path_string, string new_name) { |
2758 | @@ -2416,9 +2597,11 @@ |
2759 | new_name); |
2760 | dialog.run (); |
2761 | dialog.destroy (); |
2762 | - on_name_editing_canceled (); |
2763 | + new_name = ""; |
2764 | } |
2765 | + } |
2766 | |
2767 | + if (new_name != "") { |
2768 | var path = new Gtk.TreePath.from_string (path_string); |
2769 | Gtk.TreeIter? iter = null; |
2770 | model.get_iter (out iter, path); |
2771 | @@ -2437,6 +2620,9 @@ |
2772 | } |
2773 | |
2774 | on_name_editing_canceled (); |
2775 | + |
2776 | + if (!slot.directory.is_local && new_name != original_name) |
2777 | + slot.directory.need_reload (); |
2778 | } |
2779 | |
2780 | public virtual bool on_view_draw (Cairo.Context cr) { |
2781 | @@ -2738,8 +2924,6 @@ |
2782 | warning ("Could not set file attributes - %s", e.message); |
2783 | } |
2784 | }); |
2785 | - |
2786 | - update_menu_actions_sort (); |
2787 | } |
2788 | |
2789 | protected void cancel_timeout (ref uint id) { |
2790 | @@ -2810,9 +2994,6 @@ |
2791 | protected abstract void freeze_tree (); |
2792 | protected abstract void thaw_tree (); |
2793 | |
2794 | - |
2795 | - |
2796 | - |
2797 | /** Unimplemented methods |
2798 | * fm_directory_view_parent_set () - purpose unclear |
2799 | */ |
2800 | |
2801 | === modified file 'src/View/ColumnView.vala' |
2802 | --- src/View/ColumnView.vala 2014-12-26 14:20:20 +0000 |
2803 | +++ src/View/ColumnView.vala 2015-02-07 19:01:29 +0000 |
2804 | @@ -137,7 +137,7 @@ |
2805 | cancel_await_double_click (); |
2806 | |
2807 | if (selected_folder != null) |
2808 | - load_root_location (selected_folder.location); |
2809 | + load_root_location (selected_folder.get_target_location ()); |
2810 | |
2811 | result = true; |
2812 | } |
2813 | |
2814 | === modified file 'src/View/DiskRenderer.vala' |
2815 | --- src/View/DiskRenderer.vala 2014-12-19 17:34:11 +0000 |
2816 | +++ src/View/DiskRenderer.vala 2015-02-07 19:01:29 +0000 |
2817 | @@ -23,10 +23,14 @@ |
2818 | public uint64 free_space { set; get; } |
2819 | public uint64 disk_size { set; get; } |
2820 | |
2821 | + /* padding to the right of the disk usage graphic */ |
2822 | + public uint rpad {set; get;} |
2823 | + |
2824 | /* offset to left align disk usage graphic with the text */ |
2825 | private int offset = 2; |
2826 | |
2827 | public CellRendererDisk () { |
2828 | + rpad = 0; |
2829 | } |
2830 | |
2831 | /** |
2832 | @@ -54,6 +58,7 @@ |
2833 | Gtk.StateFlags state; |
2834 | Gtk.StyleContext context = widget.get_parent ().get_style_context (); |
2835 | cr.set_line_width (1.0); |
2836 | + uint width = area.width - rpad; |
2837 | if (widget.get_state_flags () != Gtk.StateFlags.BACKDROP) { |
2838 | state = Gtk.StateFlags.NORMAL; |
2839 | state |= widget.get_state_flags (); |
2840 | @@ -62,7 +67,7 @@ |
2841 | Gdk.cairo_set_source_rgba (cr, color_border); |
2842 | cr.rectangle (area.x, |
2843 | area.y + area.height - 3, |
2844 | - area.width, |
2845 | + width, |
2846 | 4); |
2847 | cr.fill (); |
2848 | } |
2849 | @@ -71,14 +76,14 @@ |
2850 | Gdk.cairo_set_source_rgba (cr, context.get_color (state)); |
2851 | cr.rectangle (area.x + 1, |
2852 | area.y + area.height - 2, |
2853 | - area.width - 2, |
2854 | + width - 2, |
2855 | 2); |
2856 | cr.fill (); |
2857 | |
2858 | Gdk.cairo_set_source_rgba (cr, context.get_background_color(state)); |
2859 | cr.rectangle (area.x + 1, |
2860 | area.y + area.height - 2, |
2861 | - area.width - (int)(((double)free_space)/((double)disk_size)*((double)area.width - 2)), |
2862 | + width - (int)(((double)free_space)/((double)disk_size)*((double)area.width - 2)), |
2863 | 2); |
2864 | cr.stroke (); |
2865 | } |
2866 | |
2867 | === modified file 'src/View/LocationBar.vala' |
2868 | --- src/View/LocationBar.vala 2015-01-24 13:51:05 +0000 |
2869 | +++ src/View/LocationBar.vala 2015-02-07 19:01:29 +0000 |
2870 | @@ -92,8 +92,8 @@ |
2871 | bread.search_mode = true; |
2872 | } |
2873 | |
2874 | - private void on_path_changed (File file) { |
2875 | - if (win.freeze_view_changes) |
2876 | + private void on_path_changed (File? file) { |
2877 | + if (file == null || win.freeze_view_changes) |
2878 | return; |
2879 | |
2880 | win.grab_focus (); |
2881 | @@ -136,14 +136,15 @@ |
2882 | { |
2883 | this.win = win; |
2884 | /* FIXME the string split of the path url is kinda too basic, we should use the Gile to split our uris and determine the protocol (if any) with g_uri_parse_scheme or g_file_get_uri_scheme */ |
2885 | - add_icon ({ "afp://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, _("AFP")}); |
2886 | - add_icon ({ "dav://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, _("DAV")}); |
2887 | - add_icon ({ "davs://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, _("DAVS")}); |
2888 | - add_icon ({ "ftp://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, _("FTP")}); |
2889 | - add_icon ({ "network://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, _("Network")}); |
2890 | - add_icon ({ "sftp://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, _("SFTP")}); |
2891 | - add_icon ({ "smb://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, _("SMB")}); |
2892 | - add_icon ({ "trash://", Marlin.ICON_TRASH_SYMBOLIC, true, null, null, null, true, _("Trash")}); |
2893 | + add_icon ({ "afp://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, Marlin.PROTOCOL_NAME_AFP}); |
2894 | + add_icon ({ "dav://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, Marlin.PROTOCOL_NAME_DAV}); |
2895 | + add_icon ({ "davs://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true,Marlin.PROTOCOL_NAME_DAVS}); |
2896 | + add_icon ({ "ftp://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, Marlin.PROTOCOL_NAME_FTP}); |
2897 | + add_icon ({ "network://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, Marlin.PROTOCOL_NAME_NETWORK}); |
2898 | + add_icon ({ "sftp://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true, Marlin.PROTOCOL_NAME_SFTP}); |
2899 | + add_icon ({ "smb://", Marlin.ICON_FOLDER_REMOTE_SYMBOLIC, true, null, null, null, true,Marlin.PROTOCOL_NAME_SMB}); |
2900 | + add_icon ({ "trash://", Marlin.ICON_TRASH_SYMBOLIC, true, null, null, null, true, Marlin.PROTOCOL_NAME_TRASH}); |
2901 | + |
2902 | |
2903 | /* music */ |
2904 | string dir; |
2905 | @@ -233,12 +234,12 @@ |
2906 | completed.connect (() => { |
2907 | string path = ""; |
2908 | string newpath = update_breadcrumbs (get_file_for_path (text).get_uri (), path); |
2909 | - |
2910 | + |
2911 | foreach (BreadcrumbsElement element in elements) { |
2912 | if (!element.hidden) |
2913 | path += element.text + "/"; |
2914 | } |
2915 | - |
2916 | + |
2917 | if (path != newpath) |
2918 | change_breadcrumbs (newpath); |
2919 | |
2920 | @@ -320,9 +321,12 @@ |
2921 | } |
2922 | |
2923 | public void on_need_completion () { |
2924 | - File file = get_file_for_path (text); |
2925 | - |
2926 | - // don't use get_basename (), it will return "folder" for "/folder/" |
2927 | + File? file = get_file_for_path (text); |
2928 | + |
2929 | + if (file == null) |
2930 | + return; |
2931 | + |
2932 | + /* don't use get_basename (), it will return "folder" for "/folder/" */ |
2933 | int last_slash = text.last_index_of_char ('/'); |
2934 | if (last_slash > -1 && last_slash < text.length) |
2935 | to_search = text.slice (last_slash + 1, text.length); |
2936 | @@ -338,20 +342,19 @@ |
2937 | return; |
2938 | |
2939 | files = GOF.Directory.Async.from_gfile (file); |
2940 | - if (files.file.exists) { |
2941 | + if (files.file.exists) |
2942 | files.load (on_file_loaded); |
2943 | - } |
2944 | } |
2945 | |
2946 | private void on_files_loaded_menu () { |
2947 | - // First the "Open in new tab" menuitem is added to the menu. |
2948 | + /* First the "Open in new tab" menuitem is added to the menu. */ |
2949 | var menuitem_newtab = new Gtk.MenuItem.with_label (_("Open in New Tab")); |
2950 | menu.append (menuitem_newtab); |
2951 | menuitem_newtab.activate.connect (() => { |
2952 | - win.add_tab (File.new_for_uri (current_right_click_path), Marlin.ViewMode.CURRENT); |
2953 | + win.add_tab (File.new_for_commandline_arg (current_right_click_path), Marlin.ViewMode.CURRENT); |
2954 | }); |
2955 | |
2956 | - // Then the "Open with" menuitem is added to the menu. |
2957 | + /* Then the "Open with" menuitem is added to the menu. */ |
2958 | var menu_open_with = new Gtk.MenuItem.with_label (_("Open with")); |
2959 | menu.append (menu_open_with); |
2960 | |
2961 | @@ -432,12 +435,15 @@ |
2962 | } |
2963 | |
2964 | protected override void load_right_click_menu (double x, double y) { |
2965 | + if (current_right_click_root == null) |
2966 | + return; |
2967 | + |
2968 | menu_x_root = x; |
2969 | menu_y_root = y; |
2970 | menu = new Gtk.Menu (); |
2971 | menu.cancel.connect (() => { reset_elements_states (); }); |
2972 | menu.deactivate.connect (() => { reset_elements_states (); }); |
2973 | - /* current_right_click_root is parent of the directory named on the breadcrumb. */ |
2974 | + /* current_right_click_root is parent of the directory named on the breadcrumb. */ |
2975 | var directory = File.new_for_uri (current_right_click_root); |
2976 | files_menu = GOF.Directory.Async.from_gfile (directory); |
2977 | files_menu.done_loading.connect (on_files_loaded_menu); |
2978 | |
2979 | === modified file 'src/View/OverlayBar.vala' |
2980 | --- src/View/OverlayBar.vala 2015-01-16 17:08:14 +0000 |
2981 | +++ src/View/OverlayBar.vala 2015-02-07 19:01:29 +0000 |
2982 | @@ -130,11 +130,10 @@ |
2983 | } else if (!goffile.is_folder ()) { |
2984 | |
2985 | /* if we have an image, see if we can get its resolution */ |
2986 | - var type = goffile.get_ftype (); |
2987 | - if (type.substring (0, 6) == "image/" && !(type in SKIP_IMAGES)) { |
2988 | + string? type = goffile.get_ftype (); |
2989 | + if (type != null && type.substring (0, 6) == "image/" && !(type in SKIP_IMAGES)) { |
2990 | load_resolution.begin (goffile); |
2991 | } |
2992 | - |
2993 | status = "%s (%s)".printf (goffile.formated_type, format_size ((int64) PropertiesWindow.file_real_size (goffile))); |
2994 | } else { |
2995 | status = "%s - %s".printf (goffile.info.get_name (), goffile.formated_type); |
2996 | |
2997 | === modified file 'src/View/PropertiesWindow.vala' |
2998 | --- src/View/PropertiesWindow.vala 2015-01-17 04:26:34 +0000 |
2999 | +++ src/View/PropertiesWindow.vala 2015-02-07 19:01:29 +0000 |
3000 | @@ -1362,16 +1362,22 @@ |
3001 | } |
3002 | |
3003 | public static uint64 file_real_size (GOF.File gof) { |
3004 | + if (!gof.is_connected) |
3005 | + return 0; |
3006 | + |
3007 | uint64 file_size = gof.size; |
3008 | - try { |
3009 | - var info = gof.location.query_info (FileAttribute.STANDARD_ALLOCATED_SIZE, FileQueryInfoFlags.NONE); |
3010 | - uint64 allocated_size = info.get_attribute_uint64 (FileAttribute.STANDARD_ALLOCATED_SIZE); |
3011 | - // Check for sparse file, allocated size will be smaller, for normal files allocated size |
3012 | - // includes overhead size so we don't use it for those here |
3013 | - if (allocated_size < file_size && !gof.is_directory) |
3014 | - file_size = allocated_size; |
3015 | - } catch (Error err) { |
3016 | - warning ("%s", err.message); |
3017 | + if (gof.location is GLib.File) { |
3018 | + try { |
3019 | + var info = gof.location.query_info (FileAttribute.STANDARD_ALLOCATED_SIZE, FileQueryInfoFlags.NONE); |
3020 | + uint64 allocated_size = info.get_attribute_uint64 (FileAttribute.STANDARD_ALLOCATED_SIZE); |
3021 | + // Check for sparse file, allocated size will be smaller, for normal files allocated size |
3022 | + // includes overhead size so we don't use it for those here |
3023 | + if (allocated_size < file_size && !gof.is_directory) |
3024 | + file_size = allocated_size; |
3025 | + } catch (Error err) { |
3026 | + warning ("%s", err.message); |
3027 | + gof.is_connected = false; |
3028 | + } |
3029 | } |
3030 | return file_size; |
3031 | } |
3032 | |
3033 | === modified file 'src/View/Resources.vala' |
3034 | --- src/View/Resources.vala 2014-11-13 20:24:33 +0000 |
3035 | +++ src/View/Resources.vala 2015-02-07 19:01:29 +0000 |
3036 | @@ -52,4 +52,56 @@ |
3037 | public const string NETWORK_URI = "network:///"; |
3038 | |
3039 | public const string OPEN_IN_TERMINAL_DESKTOP_ID = "open-pantheon-terminal-here.desktop"; |
3040 | + |
3041 | + public const string PROTOCOL_NAME_AFP = _("AFP"); |
3042 | + public const string PROTOCOL_NAME_DAV = _("DAV"); |
3043 | + public const string PROTOCOL_NAME_DAVS = _("DAVS"); |
3044 | + public const string PROTOCOL_NAME_FTP = _("FTP"); |
3045 | + public const string PROTOCOL_NAME_NETWORK = _("Network"); |
3046 | + public const string PROTOCOL_NAME_SFTP = _("SFTP"); |
3047 | + public const string PROTOCOL_NAME_SMB = _("SMB"); |
3048 | + public const string PROTOCOL_NAME_TRASH = _("Trash"); |
3049 | + |
3050 | + public string protocol_to_name (string protocol) { |
3051 | + /* Deal with protocol with or without : or / characters at the end */ |
3052 | + string s = protocol.delimit (":/", ' ').chomp (); |
3053 | + |
3054 | + switch (s) { |
3055 | + case "trash": |
3056 | + return Marlin.PROTOCOL_NAME_TRASH; |
3057 | + case "network": |
3058 | + return Marlin.PROTOCOL_NAME_NETWORK; |
3059 | + case "smb": |
3060 | + return Marlin.PROTOCOL_NAME_SMB; |
3061 | + case "ftp": |
3062 | + return Marlin.PROTOCOL_NAME_FTP; |
3063 | + case "sftp": |
3064 | + return Marlin.PROTOCOL_NAME_SFTP; |
3065 | + case "afp": |
3066 | + return Marlin.PROTOCOL_NAME_AFP; |
3067 | + case "dav": |
3068 | + return Marlin.PROTOCOL_NAME_DAV; |
3069 | + case "davs": |
3070 | + return Marlin.PROTOCOL_NAME_DAVS; |
3071 | + default: |
3072 | + return protocol; |
3073 | + } |
3074 | + } |
3075 | + |
3076 | + public string get_smb_share_from_uri (string uri) { |
3077 | + if (!(Uri.parse_scheme (uri) == "smb")) |
3078 | + return (uri); |
3079 | + |
3080 | + string [] uri_parts = uri.split (Path.DIR_SEPARATOR_S); |
3081 | + |
3082 | + if (uri_parts.length < 4) |
3083 | + return uri; |
3084 | + else { |
3085 | + var sb = new StringBuilder (); |
3086 | + for (int i = 0; i < 4; i++) |
3087 | + sb.append (uri_parts [i] + Path.DIR_SEPARATOR_S); |
3088 | + |
3089 | + return sb.str; |
3090 | + } |
3091 | + } |
3092 | } |
3093 | |
3094 | === modified file 'src/View/Sidebar.vala' |
3095 | --- src/View/Sidebar.vala 2015-01-27 14:20:52 +0000 |
3096 | +++ src/View/Sidebar.vala 2015-02-07 19:01:29 +0000 |
3097 | @@ -33,9 +33,8 @@ |
3098 | |
3099 | private const int MAX_BOOKMARKS_DROPPED = 100; |
3100 | private const int ROOT_INDENTATION_XPAD = 2; |
3101 | - private const int EJECT_BUTTON_XPAD = 12; |
3102 | - private const int TEXT_XPAD = 5; |
3103 | - private const int ICON_XPAD = 6; |
3104 | + private const int EJECT_BUTTON_XPAD = 6; |
3105 | + private const int ICON_XPAD = 6 + ROOT_INDENTATION_XPAD; |
3106 | private const int PROP_0 = 0; |
3107 | private const int PROP_ZOOM_LEVEL = 1; |
3108 | |
3109 | @@ -55,6 +54,7 @@ |
3110 | Gtk.IconTheme theme; |
3111 | GLib.Icon eject_icon; |
3112 | |
3113 | + int eject_button_size = 20; |
3114 | uint n_builtins_before; |
3115 | string last_selected_uri; |
3116 | string slot_location; |
3117 | @@ -125,7 +125,7 @@ |
3118 | |
3119 | /* TODO Make it an option in Settings whether or not to show |
3120 | * bookmarks pointing to non-existent (or unmounted) files. */ |
3121 | - bool display_all_bookmarks = false; |
3122 | + bool display_all_bookmarks = true; |
3123 | |
3124 | /* Remember vertical adjustment value when lose focus */ |
3125 | double adjustment_val = 0.0; |
3126 | @@ -160,32 +160,30 @@ |
3127 | tree_view.set_size_request (Preferences.settings.get_int ("minimum-sidebar-width"), -1); |
3128 | tree_view.set_headers_visible (false); |
3129 | |
3130 | - var col = new Gtk.TreeViewColumn (); |
3131 | + var cab = new Gtk.CellAreaBox (); |
3132 | + var col = new Gtk.TreeViewColumn.with_area (cab); |
3133 | col.max_width = -1; |
3134 | col.expand = true; |
3135 | col.spacing = 3; |
3136 | |
3137 | var crt = new Gtk.CellRendererText (); |
3138 | - col.pack_start(crt, false); |
3139 | - col.set_cell_data_func (crt, root_indent_cell_data_func); |
3140 | - |
3141 | - crt = new Gtk.CellRendererText (); |
3142 | this.indent_renderer = crt; |
3143 | - col.pack_start(crt, false); |
3144 | + cab.pack_start(crt, false, false, false); |
3145 | col.set_cell_data_func (crt, indent_cell_data_func); |
3146 | |
3147 | var crpb = new Gtk.CellRendererPixbuf (); |
3148 | this.icon_cell_renderer = crpb; |
3149 | crpb.follow_state = true; |
3150 | crpb.stock_size = Gtk.IconSize.MENU; |
3151 | - col.pack_start(crpb, false); |
3152 | + cab.pack_start(crpb, false, false, false); |
3153 | col.set_attributes (crpb, "gicon", Column.ICON); |
3154 | col.set_cell_data_func (crpb, icon_cell_data_func); |
3155 | |
3156 | var crd = new Marlin.CellRendererDisk (); |
3157 | crd.ellipsize = Pango.EllipsizeMode.END; |
3158 | crd.ellipsize_set = true; |
3159 | - col.pack_start (crd, true); |
3160 | + crd.rpad = 12; |
3161 | + cab.pack_start (crd, true, false, false); |
3162 | col.set_attributes (crd, |
3163 | "text", Column.NAME, |
3164 | "visible", Column.EJECT, |
3165 | @@ -196,9 +194,9 @@ |
3166 | eject_spinner_cell_renderer = crs; |
3167 | crs.mode = Gtk.CellRendererMode.ACTIVATABLE; |
3168 | crs.icon_size = Gtk.IconSize.MENU; |
3169 | - crs.xpad = EJECT_BUTTON_XPAD; |
3170 | + crs.xpad = 0; |
3171 | crs.xalign = (float)1.0; |
3172 | - col.pack_start (crs, false); |
3173 | + cab.pack_start (crs, false, false, false); |
3174 | col.set_attributes (crs, |
3175 | "gicon", Column.EJECT_ICON, |
3176 | "visible", Column.EJECT, |
3177 | @@ -212,7 +210,7 @@ |
3178 | name_renderer.ellipsize_set = true; |
3179 | name_renderer.edited.connect (edited); |
3180 | name_renderer.editing_canceled.connect (editing_canceled); |
3181 | - col.pack_start (name_renderer,true); |
3182 | + cab.pack_start (name_renderer,true, false, false); |
3183 | col.set_attributes (name_renderer, |
3184 | "text", Column.NAME, |
3185 | "visible", Column.NO_EJECT, |
3186 | @@ -225,11 +223,16 @@ |
3187 | cre.is_category_expander = true; |
3188 | /* this is required to align the eject buttons to the right */ |
3189 | int exp_size = cre.get_arrow_size (tree_view); |
3190 | - cre.xpad = (16 - exp_size).abs () + EJECT_BUTTON_XPAD - 2; |
3191 | - cre.xalign = (float)1.0; |
3192 | - col.pack_end (cre, false); |
3193 | + Gtk.icon_size_lookup (Gtk.IconSize.MENU, out eject_button_size, null); |
3194 | + cre.xpad = int.max ((eject_button_size - exp_size)/2, 0); |
3195 | + cab.pack_start (cre, false, false, false); |
3196 | col.set_cell_data_func (cre, expander_cell_data_func); |
3197 | |
3198 | + crt = new Gtk.CellRendererText (); |
3199 | + crt.xpad = EJECT_BUTTON_XPAD; |
3200 | + crt.xalign = (float)1.0; |
3201 | + cab.pack_start(crt, false, false, false); |
3202 | + |
3203 | tree_view.append_column (col); |
3204 | tree_view.tooltip_column = Column.TOOLTIP; |
3205 | tree_view.model = this.store; |
3206 | @@ -249,7 +252,9 @@ |
3207 | tree_view.enable_model_drag_source (Gdk.ModifierType.BUTTON1_MASK, |
3208 | source_targets, |
3209 | Gdk.DragAction.MOVE); |
3210 | - Gtk.drag_dest_set (tree_view, Gtk.DestDefaults.MOTION, drop_targets, |
3211 | + Gtk.drag_dest_set (tree_view, |
3212 | + Gtk.DestDefaults.MOTION, |
3213 | + drop_targets, |
3214 | Gdk.DragAction.MOVE | Gdk.DragAction.COPY | Gdk.DragAction.LINK); |
3215 | } |
3216 | |
3217 | @@ -564,13 +569,17 @@ |
3218 | if (volume != null) |
3219 | continue; |
3220 | |
3221 | + |
3222 | var root = mount.get_default_location (); |
3223 | if (root.is_native ()) { |
3224 | string scheme = root.get_uri_scheme (); |
3225 | - if (scheme == "archive") { |
3226 | + if (scheme == "archive" ) { |
3227 | network_mounts.prepend (mount); |
3228 | continue; |
3229 | } |
3230 | + } else { |
3231 | + network_mounts.prepend (mount); |
3232 | + continue; |
3233 | } |
3234 | |
3235 | add_place (Marlin.PlaceType.MOUNTED_VOLUME, |
3236 | @@ -602,16 +611,23 @@ |
3237 | network_mounts.reverse (); |
3238 | foreach (Mount mount in network_mounts) { |
3239 | var root = mount.get_default_location (); |
3240 | + /* get_smb_share_from_uri will return the uri unaltered if does not have |
3241 | + * the smb scheme so we need not test. This is required because the mount |
3242 | + * does not return the true root location of the share but the location used |
3243 | + * when creating the mount. |
3244 | + */ |
3245 | + string uri = Marlin.get_smb_share_from_uri (root.get_uri ()); |
3246 | + |
3247 | add_place (Marlin.PlaceType.BUILT_IN, |
3248 | iter, |
3249 | mount.get_name (), |
3250 | mount.get_icon (), |
3251 | - root.get_uri (), |
3252 | + uri, |
3253 | null, |
3254 | null, |
3255 | mount, |
3256 | 0, |
3257 | - root.get_parse_name ()); |
3258 | + uri); |
3259 | } |
3260 | |
3261 | /* Add Entire Network BUILTIN */ |
3262 | @@ -913,7 +929,7 @@ |
3263 | null, |
3264 | File.new_for_uri (drop_uri), |
3265 | real_action, |
3266 | - null, null, null); |
3267 | + this, null, null); |
3268 | return true; |
3269 | case TargetType.GTK_TREE_MODEL_ROW: |
3270 | return false; |
3271 | @@ -964,12 +980,13 @@ |
3272 | if (can_accept_file_as_bookmark (file)) |
3273 | uris.prepend (file.get_uri ()); |
3274 | }); |
3275 | + |
3276 | if (uris != null) |
3277 | bookmarks.insert_uris (uris, position); |
3278 | } |
3279 | |
3280 | - public void add_uri (string uri) { |
3281 | - bookmarks.insert_uri_at_end (uri); |
3282 | + public void add_uri (string uri, string? label = null) { |
3283 | + bookmarks.insert_uri_at_end (uri, label); |
3284 | } |
3285 | |
3286 | private bool drag_scroll_timer () { |
3287 | @@ -1388,22 +1405,13 @@ |
3288 | cell.set_visible (!store.iter_has_child (iter)); |
3289 | } |
3290 | |
3291 | - private void root_indent_cell_data_func (Gtk.CellLayout layout, |
3292 | - Gtk.CellRenderer cell, |
3293 | - Gtk.TreeModel model, |
3294 | - Gtk.TreeIter iter) { |
3295 | - cell.set_visible (true); |
3296 | - cell.xpad = ROOT_INDENTATION_XPAD; |
3297 | - } |
3298 | - |
3299 | private void indent_cell_data_func (Gtk.CellLayout layout, |
3300 | Gtk.CellRenderer cell, |
3301 | Gtk.TreeModel model, |
3302 | Gtk.TreeIter iter) { |
3303 | var path = store.get_path (iter); |
3304 | var depth = path.get_depth (); |
3305 | - cell.set_visible (depth > 1); |
3306 | - cell.xpad = ICON_XPAD; |
3307 | + cell.xpad = depth > 1 ? ICON_XPAD : ROOT_INDENTATION_XPAD; |
3308 | } |
3309 | |
3310 | private void expander_cell_data_func (Gtk.CellLayout layout, |
3311 | @@ -1460,8 +1468,9 @@ |
3312 | Gtk.CellRenderer renderer, |
3313 | Gtk.TreeModel model, |
3314 | Gtk.TreeIter iter) { |
3315 | + |
3316 | + var crt = renderer as Gtk.CellRendererText; |
3317 | Marlin.PlaceType type; |
3318 | - Gtk.CellRendererText crt = renderer as Gtk.CellRendererText; |
3319 | model.@get (iter, Column.ROW_TYPE, out type, -1); |
3320 | |
3321 | if (type == Marlin.PlaceType.PERSONAL_CATEGORY || |
3322 | @@ -1682,7 +1691,7 @@ |
3323 | mount.unmount_with_operation.end (res); |
3324 | } |
3325 | catch (GLib.Error error) { |
3326 | - warning ("Error while unmounting"); |
3327 | + debug ("Error while unmounting"); |
3328 | } |
3329 | finish_eject_or_unmount (row_ref); |
3330 | }); |
3331 | @@ -1690,17 +1699,16 @@ |
3332 | |
3333 | private void empty_trash_on_mount (Mount? mount, Gtk.TreeRowReference? row_ref = null) { |
3334 | if (Marlin.FileOperations.has_trash_files (mount)) { |
3335 | - unowned GLib.List<unowned GLib.File>? dirs = Marlin.FileOperations.get_trash_dirs_for_mount (mount); |
3336 | - /* Marlin.FileOperations will show a confirm dialog according to settings */ |
3337 | - if (dirs != null) |
3338 | - Marlin.FileOperations.empty_trash_dirs (null, dirs.copy ()); |
3339 | + unowned GLib.List<unowned GLib.File>? dirs = Marlin.FileOperations.get_trash_dirs_for_mount (mount); |
3340 | + /* Marlin.FileOperations will show a confirm dialog according to settings */ |
3341 | + if (dirs != null) |
3342 | + Marlin.FileOperations.empty_trash_dirs (null, dirs.copy ()); |
3343 | } |
3344 | } |
3345 | |
3346 | private bool over_eject_button (int x, int y, out Gtk.TreePath p) { |
3347 | unowned Gtk.TreeViewColumn column; |
3348 | int width, x_offset, hseparator; |
3349 | - int eject_button_size; |
3350 | bool show_eject; |
3351 | Gtk.TreeIter iter; |
3352 | Gtk.TreePath path; |
3353 | @@ -1723,8 +1731,7 @@ |
3354 | column.cell_set_cell_data (store, iter, false, false); |
3355 | column.cell_get_position (eject_spinner_cell_renderer, out x_offset, out width); |
3356 | |
3357 | - eject_button_size = 20; |
3358 | - x_offset += width - hseparator - EJECT_BUTTON_XPAD - eject_button_size; |
3359 | + x_offset += width - hseparator - eject_button_size; |
3360 | if (cell_x - x_offset >= 0 && cell_x - x_offset <= eject_button_size) |
3361 | return true; |
3362 | } |
3363 | |
3364 | === modified file 'src/View/Slot.vala' |
3365 | --- src/View/Slot.vala 2015-01-25 20:40:53 +0000 |
3366 | +++ src/View/Slot.vala 2015-02-07 19:01:29 +0000 |
3367 | @@ -60,6 +60,7 @@ |
3368 | set_up_directory (_location); |
3369 | connect_slot_signals (); |
3370 | make_view (); |
3371 | + connect_dir_view_signals (); |
3372 | } |
3373 | |
3374 | ~Slot () { |
3375 | @@ -107,12 +108,16 @@ |
3376 | |
3377 | if (mode == Marlin.ViewMode.MILLER_COLUMNS) |
3378 | autosize_slot (); |
3379 | + |
3380 | + set_view_updates_frozen (false); |
3381 | }); |
3382 | |
3383 | if (mode == Marlin.ViewMode.MILLER_COLUMNS) |
3384 | directory.track_longest_name = true; |
3385 | |
3386 | - directory.need_reload.connect (ctab.reload); |
3387 | + directory.need_reload.connect (() => { |
3388 | + ctab.reload (); |
3389 | + }); |
3390 | } |
3391 | |
3392 | private void schedule_path_change_request (GLib.File loc, int flag, bool make_root) { |
3393 | @@ -123,7 +128,7 @@ |
3394 | } |
3395 | |
3396 | private void on_path_change_request (GLib.File loc, int flag, bool make_root) { |
3397 | - if (flag == 0) { |
3398 | + if (flag == 0) { /* make view in existing container */ |
3399 | if (dir_view is FM.ColumnView) |
3400 | miller_slot_request (loc, make_root); |
3401 | else |
3402 | @@ -166,18 +171,13 @@ |
3403 | |
3404 | public override void user_path_change_request (GLib.File loc, bool allow_mode_change = true) { |
3405 | assert (loc != null); |
3406 | - |
3407 | - if (!location.equal (loc)) { |
3408 | - var old_dir = directory; |
3409 | - set_up_directory (loc); |
3410 | - dir_view.change_directory (old_dir, directory); |
3411 | - /* ViewContainer takes care of updating appearance |
3412 | - * If allow_mode_change is false View Container will not automagically |
3413 | - * switch to icon view for icon folders (needed for Miller View) */ |
3414 | - ctab.slot_path_changed (loc, allow_mode_change); |
3415 | - } else { |
3416 | - ctab.reload (); |
3417 | - } |
3418 | + var old_dir = directory; |
3419 | + set_up_directory (loc); |
3420 | + dir_view.change_directory (old_dir, directory); |
3421 | + /* ViewContainer takes care of updating appearance |
3422 | + * If allow_mode_change is false View Container will not automagically |
3423 | + * switch to icon view for icon folders (needed for Miller View) */ |
3424 | + ctab.slot_path_changed (directory.location, allow_mode_change); |
3425 | } |
3426 | |
3427 | protected override void make_view () { |
3428 | @@ -203,7 +203,7 @@ |
3429 | if (mode != Marlin.ViewMode.MILLER_COLUMNS) |
3430 | content_box.pack_start (dir_view, true, true, 0); |
3431 | |
3432 | - connect_dir_view_signals (); |
3433 | + set_view_updates_frozen (true); |
3434 | } |
3435 | |
3436 | public void set_view_updates_frozen (bool freeze) { |
3437 | @@ -275,8 +275,7 @@ |
3438 | } |
3439 | |
3440 | public override void reload () { |
3441 | - if (dir_view != null) |
3442 | - dir_view.reload (); |
3443 | + user_path_change_request (location, false); |
3444 | } |
3445 | |
3446 | public override void cancel () { |
3447 | |
3448 | === modified file 'src/View/ViewContainer.vala' |
3449 | --- src/View/ViewContainer.vala 2015-01-11 10:25:14 +0000 |
3450 | +++ src/View/ViewContainer.vala 2015-02-07 19:01:29 +0000 |
3451 | @@ -45,10 +45,19 @@ |
3452 | return slot != null ? slot.uri : null; |
3453 | } |
3454 | } |
3455 | + |
3456 | + public GOF.AbstractSlot? slot { |
3457 | + get { |
3458 | + return get_current_slot (); |
3459 | + } |
3460 | + } |
3461 | + |
3462 | public OverlayBar overlay_statusbar; |
3463 | private Browser browser; |
3464 | private GLib.List<GLib.File>? selected_locations = null; |
3465 | |
3466 | + private bool ready = false; |
3467 | + |
3468 | public signal void tab_name_changed (string tab_name); |
3469 | public signal void loading (bool is_loading); |
3470 | /* To maintain compatibility with existing plugins */ |
3471 | @@ -137,7 +146,6 @@ |
3472 | |
3473 | public void change_view_mode (Marlin.ViewMode mode, GLib.File? loc = null) { |
3474 | if (mode != view_mode) { |
3475 | - |
3476 | if (loc == null) /* Only untrue on container creation */ |
3477 | loc = this.location; |
3478 | |
3479 | @@ -158,15 +166,19 @@ |
3480 | else |
3481 | view = new Slot (loc, this, mode); |
3482 | |
3483 | + content = view.get_content_box (); |
3484 | + |
3485 | view_mode = mode; |
3486 | - set_up_current_slot (); |
3487 | overlay_statusbar.showbar = view_mode != Marlin.ViewMode.LIST; |
3488 | overlay_statusbar.reset_selection (); |
3489 | + |
3490 | + load_slot_directory (view); |
3491 | window.update_top_menu (); |
3492 | } |
3493 | } |
3494 | |
3495 | public void user_path_change_request (GLib.File loc) { |
3496 | + loading (true); |
3497 | view.user_path_change_request (loc); |
3498 | } |
3499 | |
3500 | @@ -197,28 +209,39 @@ |
3501 | } |
3502 | |
3503 | private void set_up_current_slot () { |
3504 | - var slot = get_current_slot (); |
3505 | - assert (slot != null); |
3506 | - assert (slot.directory != null); |
3507 | - |
3508 | - content = view.get_content_box (); |
3509 | - load_slot_directory (slot); |
3510 | + ready = false; |
3511 | + load_slot_directory (get_current_slot ()); |
3512 | } |
3513 | |
3514 | - public void load_slot_directory (GOF.AbstractSlot slot) { |
3515 | - can_show_folder = true; |
3516 | - loading (true); |
3517 | - /* Allow time for the window to update before starting to load directory so that |
3518 | - * the window is displayed more quickly |
3519 | - * when starting the application in, or switching view to, a folder that contains |
3520 | - * a large number of files. Also ensures infobars are added correctly by plugins. |
3521 | - */ |
3522 | - Timeout.add (100, () => { |
3523 | - slot.directory.load (); |
3524 | - plugin_directory_loaded (); |
3525 | - return false; |
3526 | - }); |
3527 | + public void load_slot_directory (GOF.AbstractSlot? slot) { |
3528 | + if (slot == null) |
3529 | + return; |
3530 | + |
3531 | refresh_slot_info (slot); |
3532 | + |
3533 | + /* Allow time for the window to update before trying to load directory so that |
3534 | + * the window is displayed more quickly when starting the application in, |
3535 | + * or switching view to, a folder that contains a large number of files. |
3536 | + * Also ensures infobars are added correctly by plugins. Only checking in idle |
3537 | + * time allows pathbar animation to complete smoothly. |
3538 | + |
3539 | + * Wait until directory is flagged ready to allow time for network folders to be found |
3540 | + * and accessed. |
3541 | + |
3542 | + * Do not try and load directory that is not flagged 'can load'. |
3543 | + */ |
3544 | + Idle.add (() => { |
3545 | + if (!slot.directory.is_ready) |
3546 | + return true; |
3547 | + |
3548 | + if (slot.directory.can_load) { |
3549 | + slot.directory.load (); |
3550 | + plugin_directory_loaded (); |
3551 | + } else |
3552 | + directory_done_loading (slot); |
3553 | + |
3554 | + return false; |
3555 | + }); |
3556 | } |
3557 | |
3558 | private void plugin_directory_loaded () { |
3559 | @@ -235,7 +258,6 @@ |
3560 | public void refresh_slot_info (GOF.AbstractSlot aslot) { |
3561 | GLib.File loc = aslot.directory.file.location; |
3562 | update_tab_name (loc); |
3563 | - |
3564 | browser.record_uri (loc.get_parse_name ()); /* will ignore null changes */ |
3565 | |
3566 | window.loading_uri (loc.get_uri ()); |
3567 | @@ -246,56 +268,78 @@ |
3568 | } |
3569 | |
3570 | public void update_tab_name (GLib.File loc) { |
3571 | - var slot_path = loc.get_path (); |
3572 | - |
3573 | - if (slot_path == Environment.get_home_dir ()) |
3574 | + string? slot_path = loc.get_path (); |
3575 | + tab_name = "-----"; |
3576 | + if (slot_path == null) { |
3577 | + string [] uri_parts = loc.get_uri ().split (Path.DIR_SEPARATOR_S); |
3578 | + uint index = uri_parts.length - 1; |
3579 | + string s; |
3580 | + while (index >= 0) { |
3581 | + s = uri_parts [index]; |
3582 | + if (s.length >= 1) { |
3583 | + if (index == 0) { |
3584 | + tab_name = Marlin.protocol_to_name (s); |
3585 | + } else |
3586 | + tab_name = s; |
3587 | + break; |
3588 | + } |
3589 | + index--; |
3590 | + } |
3591 | + } else if (slot_path == Environment.get_home_dir ()) |
3592 | tab_name = _("Home"); |
3593 | else if (slot_path == "/") |
3594 | tab_name = _("File System"); |
3595 | - else if (loc.query_exists ()) { |
3596 | + else { |
3597 | try { |
3598 | var info = loc.query_info (FileAttribute.STANDARD_DISPLAY_NAME, FileQueryInfoFlags.NONE); |
3599 | tab_name = info.get_attribute_string (FileAttribute.STANDARD_DISPLAY_NAME); |
3600 | } |
3601 | catch (GLib.Error e) { |
3602 | warning ("Could not get location display name. %s", e.message); |
3603 | + tab_name = loc.get_basename (); |
3604 | + can_show_folder = false; |
3605 | } |
3606 | - |
3607 | - } else { |
3608 | - tab_name = _("This folder does not exist"); |
3609 | - can_show_folder = false; |
3610 | } |
3611 | |
3612 | + if (tab_name == "-----") |
3613 | + tab_name = loc.get_uri (); |
3614 | + |
3615 | if (Posix.getuid() == 0) |
3616 | tab_name = tab_name + " " + _("(as Administrator)"); |
3617 | - |
3618 | } |
3619 | |
3620 | public void directory_done_loading (GOF.AbstractSlot slot) { |
3621 | - FileInfo file_info; |
3622 | - |
3623 | loading (false); |
3624 | - |
3625 | - try { |
3626 | - file_info = slot.location.query_info ("standard::*,access::*", FileQueryInfoFlags.NONE); |
3627 | - |
3628 | - /* If not readable, alert the user */ |
3629 | - if (slot.directory.permission_denied) { |
3630 | - content = new Granite.Widgets.Welcome (_("This does not belong to you."), |
3631 | + can_show_folder = true; |
3632 | + |
3633 | + if (slot.directory.permission_denied) { |
3634 | + content = new Granite.Widgets.Welcome (_("This does not belong to you."), |
3635 | _("You don't have permission to view this folder.")); |
3636 | + can_show_folder = false; |
3637 | + } else if (!slot.directory.can_load) { |
3638 | + content = new Granite.Widgets.Welcome (_("Unable to mount folder."), |
3639 | + _("The server for this folder could not be located.")); |
3640 | + can_show_folder = false; |
3641 | + } else if (!slot.directory.file.exists) { |
3642 | + content = new DirectoryNotFound (slot.directory, this); |
3643 | can_show_folder = false; |
3644 | - } else if (file_info.get_file_type () == FileType.DIRECTORY && selected_locations != null) { |
3645 | + } else if (selected_locations != null) { |
3646 | view.select_glib_files (selected_locations, null); |
3647 | selected_locations = null; |
3648 | - } |
3649 | - } catch (Error err) { |
3650 | - /* query_info will throw an exception if it cannot find the file */ |
3651 | - if (err is IOError.NOT_MOUNTED) |
3652 | - slot.reload (); |
3653 | + } else if (slot.directory.selected_file != null) { |
3654 | + if (slot.directory.selected_file.query_exists ()) |
3655 | + focus_location_if_in_current_directory (slot.directory.selected_file); |
3656 | else { |
3657 | - content = new DirectoryNotFound (slot.directory, this); |
3658 | + content = new Granite.Widgets.Welcome (_("File not found."), |
3659 | + _("The file selected no longer exists.")); |
3660 | can_show_folder = false; |
3661 | } |
3662 | + slot.directory.selected_file = null; |
3663 | + } |
3664 | + |
3665 | + if (can_show_folder) { |
3666 | + ready = true; |
3667 | + content = view.get_content_box (); |
3668 | } |
3669 | } |
3670 | |
3671 | @@ -353,11 +397,8 @@ |
3672 | } |
3673 | } |
3674 | |
3675 | - if (loc != null) { |
3676 | + if (loc != null) |
3677 | user_path_change_request (loc); |
3678 | - slot_path_changed (loc); |
3679 | - refresh_slot_info (get_current_slot ()); |
3680 | - } |
3681 | } |
3682 | |
3683 | public void focus_location_if_in_current_directory (GLib.File? file, |
3684 | @@ -382,16 +423,19 @@ |
3685 | return path; |
3686 | } |
3687 | |
3688 | - public void reload () { |
3689 | - if (!can_show_folder) /* Try to display folder again */ |
3690 | - content = view.get_content_box (); |
3691 | - |
3692 | - loading (true); |
3693 | + public void reload (bool propagate = true) { |
3694 | /* Allow time for the signal to propagate and the tab label to redraw */ |
3695 | - Timeout.add (10, () => { |
3696 | + Idle.add (() => { |
3697 | var slot = view.get_current_slot (); |
3698 | slot.reload (); |
3699 | load_slot_directory (slot); |
3700 | + /* For remote folders, make sure any other windows showing the same folder are |
3701 | + * also refreshed. Prevent infinite loop with propagate - when called from application, |
3702 | + * propagate will be false. |
3703 | + */ |
3704 | + if (propagate) |
3705 | + ((Marlin.Application)(window.application)).tab_reloaded (window, slot.location); |
3706 | + |
3707 | return false; |
3708 | }); |
3709 | } |
3710 | |
3711 | === modified file 'src/View/Window.vala' |
3712 | --- src/View/Window.vala 2015-01-26 10:59:26 +0000 |
3713 | +++ src/View/Window.vala 2015-02-07 19:01:29 +0000 |
3714 | @@ -58,6 +58,7 @@ |
3715 | public Granite.Widgets.DynamicNotebook tabs; |
3716 | public Marlin.Places.Sidebar sidebar; |
3717 | public ViewContainer? current_tab = null; |
3718 | + public uint window_number; |
3719 | |
3720 | public void set_can_go_forward (bool can) { |
3721 | top_menu.set_can_go_forward (can); |
3722 | @@ -73,6 +74,7 @@ |
3723 | public signal void selection_changed (GLib.List<GOF.File> gof_file); |
3724 | public signal void loading_uri (string location); |
3725 | public signal void folder_deleted (GLib.File location); |
3726 | + public signal void tab_reloaded (GLib.File location); |
3727 | |
3728 | [Signal (action=true)] |
3729 | public virtual signal void go_up () { |
3730 | @@ -86,7 +88,7 @@ |
3731 | |
3732 | public Window (Marlin.Application app, Gdk.Screen myscreen) { |
3733 | /* Capture application window_count and active_window before they can change */ |
3734 | - var window_number = app.window_count; |
3735 | + window_number = app.window_count; |
3736 | application = app; |
3737 | screen = myscreen; |
3738 | is_first_window = (window_number == 0); |
3739 | @@ -332,6 +334,9 @@ |
3740 | } |
3741 | |
3742 | public void change_tab (int offset) { |
3743 | + if (freeze_view_changes) |
3744 | + return; |
3745 | + |
3746 | ViewContainer? old_tab = current_tab; |
3747 | current_tab = (tabs.get_tab_by_index (offset)).page as ViewContainer; |
3748 | |
3749 | @@ -582,12 +587,18 @@ |
3750 | |
3751 | private void action_undo (GLib.SimpleAction action, GLib.Variant? param) { |
3752 | update_undo_actions (); |
3753 | - undo_manager.undo (null); |
3754 | + undo_manager.undo (this, after_undo_redo); |
3755 | + } |
3756 | + |
3757 | + public static void after_undo_redo (void *data) { |
3758 | + var window = data as Marlin.View.Window; |
3759 | + if (!window.current_tab.slot.directory.is_local) |
3760 | + window.current_tab.reload (); |
3761 | } |
3762 | |
3763 | private void action_redo (GLib.SimpleAction action, GLib.Variant? param) { |
3764 | update_undo_actions (); |
3765 | - undo_manager.redo (null); |
3766 | + undo_manager.redo (this, after_undo_redo); |
3767 | } |
3768 | |
3769 | private void change_state_select_all (GLib.SimpleAction action) { |
3770 | @@ -600,6 +611,7 @@ |
3771 | } |
3772 | } |
3773 | |
3774 | + |
3775 | public void change_state_show_hidden (GLib.SimpleAction action) { |
3776 | bool state = !action.state.get_boolean (); |
3777 | action.set_state (new GLib.Variant.boolean (state)); |
3778 | @@ -835,6 +847,14 @@ |
3779 | private bool valid_location (GLib.File location) { |
3780 | GLib.FileInfo? info = null; |
3781 | |
3782 | + string scheme = location.get_uri_scheme (); |
3783 | + if (scheme == "smb" || |
3784 | + scheme == "ftp" || |
3785 | + scheme == "network") |
3786 | + /* Do not restore remote and network locations */ |
3787 | + //return false; |
3788 | + return true; |
3789 | + |
3790 | try { |
3791 | info = location.query_info ("standard::*", GLib.FileQueryInfoFlags.NONE); |
3792 | } |
3793 | @@ -886,6 +906,10 @@ |
3794 | } |
3795 | |
3796 | public void update_top_menu () { |
3797 | + if (freeze_view_changes) |
3798 | + return; |
3799 | + |
3800 | + |
3801 | if (current_tab != null) { |
3802 | top_menu.set_back_menu (current_tab.get_go_back_path_list ()); |
3803 | top_menu.set_forward_menu (current_tab.get_go_forward_path_list ()); |
3804 | @@ -916,13 +940,9 @@ |
3805 | } |
3806 | |
3807 | public void file_path_change_request (GLib.File loc) { |
3808 | - FileType type = loc.query_file_type (GLib.FileQueryInfoFlags.NONE); |
3809 | - |
3810 | - if (type == FileType.DIRECTORY || type == FileType.UNKNOWN) |
3811 | - /* ViewContainer deals with non-existent or unmounted directories */ |
3812 | - current_tab.user_path_change_request (loc); |
3813 | - else |
3814 | - current_tab.focus_location (loc); |
3815 | + /* ViewContainer deals with non-existent or unmounted directories |
3816 | + * and locations that are not directories */ |
3817 | + current_tab.user_path_change_request (loc); |
3818 | } |
3819 | |
3820 | public void uri_path_change_request (string uri) { |
3821 | |
3822 | === modified file 'src/View/directory_view_popup.ui' |
3823 | --- src/View/directory_view_popup.ui 2014-12-21 10:42:13 +0000 |
3824 | +++ src/View/directory_view_popup.ui 2015-02-07 19:01:29 +0000 |
3825 | @@ -25,6 +25,13 @@ |
3826 | </item> |
3827 | </menu> |
3828 | |
3829 | + <menu id='delete'> |
3830 | + <item> |
3831 | + <attribute name='label' translatable='yes'>Delete permanently</attribute> |
3832 | + <attribute name='action'>selection.delete</attribute> |
3833 | + </item> |
3834 | + </menu> |
3835 | + |
3836 | <menu id='new'> |
3837 | <submenu id='new-submenu'> |
3838 | <attribute name='label' translatable='yes'>New</attribute> |
3839 | @@ -59,6 +66,9 @@ |
3840 | <attribute name='action'>common.open_in</attribute> |
3841 | <attribute name="target">WINDOW</attribute> |
3842 | </item> |
3843 | + </menu> |
3844 | + |
3845 | + <menu id='open-in-terminal'> |
3846 | <item> |
3847 | <attribute name='label' translatable='yes'>Terminal</attribute> |
3848 | <attribute name='action'>common.open_in</attribute> |
3849 | |
3850 | === modified file 'src/ZeitgeistManager.vala' |
3851 | --- src/ZeitgeistManager.vala 2014-07-22 02:54:40 +0000 |
3852 | +++ src/ZeitgeistManager.vala 2015-02-07 19:01:29 +0000 |
3853 | @@ -12,7 +12,7 @@ |
3854 | try { |
3855 | info = file.query_info_async.end (res); |
3856 | } catch (Error e) { |
3857 | - warning ("Fetching file info folder loggin to zeitgeist failed: %s", e.message); |
3858 | + debug ("Fetching file info folder loggin to zeitgeist failed: %s", e.message); |
3859 | return; |
3860 | } |
3861 | var log = Zeitgeist.Log.get_default (); |
3862 | |
3863 | === modified file 'src/gtk+-3.0.vapi' |
3864 | --- src/gtk+-3.0.vapi 2014-10-02 20:52:33 +0000 |
3865 | +++ src/gtk+-3.0.vapi 2015-02-07 19:01:29 +0000 |
3866 | @@ -999,13 +999,28 @@ |
3867 | public virtual signal void remove_editable (Gtk.CellRenderer p0, Gtk.CellEditable p1); |
3868 | } |
3869 | [CCode (cheader_filename = "gtk/gtk.h", type_id = "gtk_cell_area_box_get_type ()")] |
3870 | - public class CellAreaBox : Gtk.CellArea, Gtk.CellLayout, Gtk.Buildable, Gtk.Orientable { |
3871 | + /* We have to remove the reference to the Gtk.CellLayout interface in order to use the new pack_ |
3872 | + * start and pack_end functions of Gtk.CellAreaBox, which have a different signature. |
3873 | + * Instead we add the other CellLayout methods explicitly */ |
3874 | + //public class CellAreaBox : Gtk.CellArea, Gtk.CellLayout, Gtk.Buildable, Gtk.Orientable { |
3875 | + public class CellAreaBox : Gtk.CellArea, Gtk.Buildable, Gtk.Orientable { |
3876 | [CCode (has_construct_function = false, type = "GtkCellArea*")] |
3877 | public CellAreaBox (); |
3878 | public int get_spacing (); |
3879 | public void set_spacing (int spacing); |
3880 | public int spacing { get; set; } |
3881 | + public void pack_start (Gtk.CellRenderer renderer, bool expand, bool align, bool fixed); |
3882 | + public void pack_end (Gtk.CellRenderer renderer, bool expand, bool align, bool fixed); |
3883 | + public void add_attribute (Gtk.CellRenderer cell, string attribute, int column); |
3884 | + public void clear (); |
3885 | + public void clear_attributes (Gtk.CellRenderer cell); |
3886 | + public unowned Gtk.CellArea get_area (); |
3887 | + public GLib.List<weak Gtk.CellRenderer> get_cells (); |
3888 | + public void reorder (Gtk.CellRenderer cell, int position); |
3889 | + public void set_attributes (Gtk.CellRenderer cell, ...); |
3890 | + public void set_cell_data_func (Gtk.CellRenderer cell, owned Gtk.CellLayoutDataFunc func); |
3891 | } |
3892 | + |
3893 | [CCode (cheader_filename = "gtk/gtk.h", type_id = "gtk_cell_area_context_get_type ()")] |
3894 | public class CellAreaContext : GLib.Object { |
3895 | [CCode (has_construct_function = false)] |
3896 | |
3897 | === modified file 'src/marlin-clipboard-manager.c' |
3898 | --- src/marlin-clipboard-manager.c 2014-12-26 12:01:20 +0000 |
3899 | +++ src/marlin-clipboard-manager.c 2015-02-07 19:01:29 +0000 |
3900 | @@ -279,8 +279,6 @@ |
3901 | |
3902 | result = NULL; |
3903 | for (i=0; lines[i] != NULL; i++) { |
3904 | - //printf ("lines %d: %s\n", i, lines[i]); |
3905 | - //result = g_list_prepend (result, g_strdup (lines[i])); |
3906 | result = g_list_prepend (result, g_file_new_for_uri (lines[i])); |
3907 | } |
3908 | return g_list_reverse (result); |
3909 | @@ -291,11 +289,8 @@ |
3910 | GtkSelectionData *selection_data, |
3911 | gpointer user_data) |
3912 | { |
3913 | - //amtest |
3914 | - printf("%s\n", G_STRFUNC); |
3915 | MarlinClipboardPasteRequest *request = user_data; |
3916 | MarlinClipboardManager *manager = MARLIN_CLIPBOARD_MANAGER (request->manager); |
3917 | - //MarlinApplication *application; |
3918 | gboolean path_copy = TRUE; |
3919 | GList *file_list = NULL; |
3920 | char **lines; |
3921 | @@ -329,24 +324,25 @@ |
3922 | /* perform the action if possible */ |
3923 | if (G_LIKELY (file_list != NULL)) |
3924 | { |
3925 | - //application = marlin_application_get (); |
3926 | - //TODO |
3927 | if (G_LIKELY (path_copy)) |
3928 | { |
3929 | - //marlin_application_copy_into (application, request->widget, file_list, request->target_file, request->new_files_closure); |
3930 | - printf ("marlin_application_copy_into\n"); |
3931 | - /*marlin_file_operations_copy (file_list, NULL, request->target_file, |
3932 | - NULL, NULL, NULL);*/ |
3933 | - marlin_file_operations_copy_move (file_list, NULL, request->target_file, |
3934 | - GDK_ACTION_COPY, NULL, request->new_files_closure, request->widget); |
3935 | - |
3936 | + marlin_file_operations_copy_move (file_list, |
3937 | + NULL, |
3938 | + request->target_file, |
3939 | + GDK_ACTION_COPY, |
3940 | + request->widget, |
3941 | + request->new_files_closure, |
3942 | + request->widget); |
3943 | } else { |
3944 | - printf ("marlin_application_move_into\n"); |
3945 | - //marlin_application_move_into (application, request->widget, file_list, request->target_file, request->new_files_closure); |
3946 | - marlin_file_operations_copy_move (file_list, NULL, request->target_file, |
3947 | - GDK_ACTION_MOVE, NULL, request->new_files_closure, request->widget); |
3948 | + marlin_file_operations_copy_move (file_list, |
3949 | + NULL, |
3950 | + request->target_file, |
3951 | + GDK_ACTION_MOVE, |
3952 | + request->widget, |
3953 | + request->new_files_closure, |
3954 | + request->widget); |
3955 | } |
3956 | - //g_object_unref (G_OBJECT (application)); |
3957 | + |
3958 | g_list_free_full (file_list, g_object_unref); |
3959 | |
3960 | /* clear the clipboard if it contained "cutted data" |
3961 | @@ -729,10 +725,9 @@ |
3962 | marlin_clipboard_manager_paste_files (MarlinClipboardManager *manager, |
3963 | GFile *target_file, |
3964 | GtkWidget *widget, |
3965 | - MarlinCopyCallback *new_files_closure) |
3966 | + GCallback *new_files_closure) |
3967 | { |
3968 | MarlinClipboardPasteRequest *request; |
3969 | - |
3970 | g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager)); |
3971 | g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget)); |
3972 | |
3973 | |
3974 | === modified file 'src/marlin-clipboard-manager.h' |
3975 | --- src/marlin-clipboard-manager.h 2014-12-26 12:01:20 +0000 |
3976 | +++ src/marlin-clipboard-manager.h 2015-02-07 19:01:29 +0000 |
3977 | @@ -59,7 +59,7 @@ |
3978 | void marlin_clipboard_manager_paste_files (MarlinClipboardManager *manager, |
3979 | GFile *target_file, |
3980 | GtkWidget *widget, |
3981 | - MarlinCopyCallback *new_files_closure); |
3982 | + GCallback *new_files_closure); |
3983 | |
3984 | |
3985 | #endif /* !__MARLIN_CLIPBOARD_MANAGER_H__ */ |
3986 | |
3987 | === modified file 'src/marlin.vapi' |
3988 | --- src/marlin.vapi 2015-01-18 20:46:03 +0000 |
3989 | +++ src/marlin.vapi 2015-02-07 19:01:29 +0000 |
3990 | @@ -41,7 +41,7 @@ |
3991 | public bool has_file (GOF.File file); |
3992 | public void copy_files (GLib.List files); |
3993 | public void cut_files (GLib.List files); |
3994 | - public void paste_files (GLib.File target, Gtk.Widget widget, CopyCallBack? new_file_closure); |
3995 | + public void paste_files (GLib.File target, Gtk.Widget widget, GLib.Callback? new_file_closure); |
3996 | public signal void changed (); |
3997 | } |
3998 |
Here are my remarks.
1) when files starts displaying network (because it was the last folder opened in the last session) it slows down the startup even if no network can be found c9DXGlcvF5: Connection refused
Also it logs the following in the console:
[_LOG_LEVEL_WARN 13:55:55.176021] Couldn't connect to accessibility bus: Failed to connect to socket /tmp/dbus-
2) When I double click on Windows Network with no server, this is written to the console: info_get_ attribute_ string: assertion 'G_IS_FILE_INFO (info)' failed async.vala: 99: mount_mountable failed: Failed to retrieve share list from server: No such file or directory r.vala: 15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted info_get_ attribute_ string: assertion 'G_IS_FILE_INFO (info)' failed r.vala: 15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted r.vala: 15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted info_get_ attribute_ string: assertion 'G_IS_FILE_INFO (info)' failed
[_LOG_LEVEL_FATAL 14:03:35.490005] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 14:03:35.490060] Files will not function properly.
[_LOG_LEVEL_WARN 14:03:38.563686] gof-directory-
[_LOG_LEVEL_WARN 14:03:38.579646] ZeitgeistManage
[_LOG_LEVEL_FATAL 14:03:38.692921] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 14:03:38.693035] Files will not function properly.
[_LOG_LEVEL_WARN 14:03:38.715301] ZeitgeistManage
[_LOG_LEVEL_WARN 14:03:38.716851] ZeitgeistManage
[_LOG_LEVEL_FATAL 14:03:47.079300] [GLib-GIO] g_file_
[_LOG_LEVEL_FATAL 14:03:47.079349] Files will not function properly.
3) The size of Windows Network is 0 bytes when there is no sever. Shouldn't it be left blank or named "0 server"? i.imgur. com/zXgD1Fn. png
http://