Merge lp:~jeremywootten/pantheon-files/fix-network-browsing into lp:~elementary-apps/pantheon-files/trunk

Proposed by Jeremy Wootten
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
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.COPY). To move the file into the folder, drag while holding down the Shift key. To get a choice menu, drag using the secondary (right) button (The option to link is not supported on smb).

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)

To post a comment you must log in.
Revision history for this message
PerfectCarl (name-is-carl) wrote :

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
  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-c9DXGlcvF5: Connection refused

2) When I double click on Windows Network with no server, this is written to the console:
[_LOG_LEVEL_FATAL 14:03:35.490005] [GLib-GIO] g_file_info_get_attribute_string: assertion 'G_IS_FILE_INFO (info)' failed
[_LOG_LEVEL_FATAL 14:03:35.490060] Files will not function properly.
[_LOG_LEVEL_WARN 14:03:38.563686] gof-directory-async.vala:99: mount_mountable failed: Failed to retrieve share list from server: No such file or directory
[_LOG_LEVEL_WARN 14:03:38.579646] ZeitgeistManager.vala:15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted
[_LOG_LEVEL_FATAL 14:03:38.692921] [GLib-GIO] g_file_info_get_attribute_string: assertion 'G_IS_FILE_INFO (info)' failed
[_LOG_LEVEL_FATAL 14:03:38.693035] Files will not function properly.
[_LOG_LEVEL_WARN 14:03:38.715301] ZeitgeistManager.vala:15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted
[_LOG_LEVEL_WARN 14:03:38.716851] ZeitgeistManager.vala:15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted
[_LOG_LEVEL_FATAL 14:03:47.079300] [GLib-GIO] g_file_info_get_attribute_string: assertion 'G_IS_FILE_INFO (info)' failed
[_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"?
http://i.imgur.com/zXgD1Fn.png

Revision history for this message
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://i.imgur.com/9BX6AAE.png

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.imgur.com/SVMLJ3g.png
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_info_get_attribute_string: assertion 'G_IS_FILE_INFO (info)' failed
[_LOG_LEVEL_FATAL 14:33:06.441324] Files will not function properly.
[_LOG_LEVEL_FATAL 14:33:12.247325] [GLib-GIO] g_app_info_get_all_for_type: assertion 'content_type != NULL' failed
[_LOG_LEVEL_FATAL 14:33:12.247420] Files will not function properly.
[_LOG_LEVEL_WARN 14:33:12.255183] plugin.vala:96: GDBus.Error:org.elementary.ContractorError.NoMimetypesGiven: No mimetypes were provided.
[_LOG_LEVEL_FATAL 14:33:14.680825] [GLib-GIO] g_file_info_get_attribute_string: assertion 'G_IS_FILE_INFO (info)' failed
[_LOG_LEVEL_FATAL 14:33:14.680882] Files will not function properly.
[_LOG_LEVEL_WARN 14:33:48.733310] ZeitgeistManager.vala:15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted
[_LOG_LEVEL_WARN 14:34:07.293536] ZeitgeistManager.vala:15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted
[_LOG_LEVEL_WARN 14:34:07.293807] ZeitgeistManager.vala:15: Fetching file info folder loggin to zeitgeist failed: The specified location is not mounted

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://i.imgur.com/2sopzoJ.png
http://i.imgur.com/y16DOEH.png
   Note that the button label is changing. Not sure that this is a good idea

Revision history for this message
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-async.vala:193: directory monitor failed: Operation not supported by backend smb://192.168.0.10/sdcard/
[_LOG_LEVEL_WARN 14:49:55.875373] gof-directory-async.vala:193: directory monitor failed: Operation not supported by backend smb://192.168.0.10/sdcard/Download

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

Revision history for this message
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/")

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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://bugs.launchpad.net/pantheon-files/+bug/1400731

6) and 7) and 8) Server connection dialog is outside scope of branch
-> https://bugs.launchpad.net/pantheon-files/+bug/1400735
-> https://bugs.launchpad.net/pantheon-files/+bug/1400738
-> https://bugs.launchpad.net/pantheon-files/+bug/1400740

14) Do you mean servers are not remembered in the "Connect to Server" dialog? This is outside the scope of the branch
-> https://bugs.launchpad.net/pantheon-files/+bug/1400734

All those issues can be found there : https://bugs.launchpad.net/pantheon-files/+bugs?field.tag=networking
(two older issues are not mine)

Revision history for this message
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.

Revision history for this message
PerfectCarl (name-is-carl) wrote :

A blueprint has been filed with suggestion to improve how Files handle network folders
https://blueprints.launchpad.net/pantheon-files/+spec/improve-network-folders

> 4)
See https://blueprints.launchpad.net/pantheon-files/+spec/remember-server-connections
and https://blueprints.launchpad.net/pantheon-files/+spec/files-ftp-bookmark

> 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?

Revision history for this message
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"

Revision history for this message
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).

Revision history for this message
PerfectCarl (name-is-carl) wrote :

The smb.conf changes has been filed as lp:1401693

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
PerfectCarl (name-is-carl) wrote :

20) the mount buttons are cut off in the side bar.
http://i.imgur.com/VKoQ5OT.png

21) the application displays the following warnings:
[_LOG_LEVEL_WARN 13:41:07.172015] gof-directory-async.vala:166: mount_mountable failed: Location is already mounted
[_LOG_LEVEL_WARN 13:41:07.827718] gof-directory-async.vala:166: mount_mountable failed: Location is already mounted
[_LOG_LEVEL_WARN 13:41:07.834263] The specified location is not mounted [code 16]
[_LOG_LEVEL_WARN 13:41:08.686950] gof-directory-async.vala:166: mount_mountable failed: Location is already mounted
[_LOG_LEVEL_WARN 13:41:09.826451] gof-directory-async.vala:166: mount_mountable failed: Location is already mounted
[_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://imgur.com/sOtOYwF

Revision history for this message
PerfectCarl (name-is-carl) wrote :

23) My samba domain (WORKGROUP) and samba share are compressable.
http://imgur.com/EFadkb6

Revision history for this message
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

Revision history for this message
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;ZZZZ@192.168.56.101/shared/
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)

http://i.imgur.com/OYheNHK.png

Revision history for this message
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/CRAN-virtualbox/ the shares are displayed.

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-async.vala:166: mount_mountable failed: Location is already mounted
[_LOG_LEVEL_WARN 14:08:48.812230] AbstractDirectoryView.vala:790: You do not have permission to rename this file
[_LOG_LEVEL_WARN 14:09:03.537883] AbstractDirectoryView.vala:790: You do not have permission to rename this file

http://i.imgur.com/zujvoOo.png

When I refresh the folder (Ctrl+R) the folder are displayed (and I can rename them)

Revision history for this message
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://i.imgur.com/4cAOUBi.png

After I work in that folder, the right click menu gets updated.

Revision history for this message
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.

Revision history for this message
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_get_parse_name: assertion 'G_IS_FILE (file)' failed
[_LOG_LEVEL_FATAL 14:24:10.443504] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.443544] marlin_places_sidebar_real_add_place: assertion 'tooltip != NULL' failed
[_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_get_parse_name: assertion 'G_IS_FILE (file)' failed
[_LOG_LEVEL_FATAL 14:24:10.520987] Files will not function properly.
[_LOG_LEVEL_FATAL 14:24:10.521017] marlin_places_sidebar_real_add_place: assertion 'tooltip != NULL' failed
[_LOG_LEVEL_FATAL 14:24:10.521043] Files will not function properly.
[_LOG_LEVEL_WARN 14:24:11.760355] gof-directory-async.vala:166: mount_mountable failed: Location is already mounted
Segmentation fault (core dumped)

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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://bugs.launchpad.net/elementaryos/+bug/1405878

30) I still have the following critical warnings:
[_LOG_LEVEL_FATAL 23:16:12.363828] gof_file_is_mountable: assertion 'file->info != NULL' failed
[_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-VIRTUALBOX\share) but only a folder (\\CRAN-VIRTUALBOX\share\folder) to the bookmark?
I find adding at a samba share useful.

Revision history for this message
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.

Revision history for this message
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?

Revision history for this message
Jeremy Wootten (jeremywootten) wrote :

There are these related bugs:

https://bugs.launchpad.net/pantheon-files/+bug/1386335
https://bugs.launchpad.net/pantheon-files/+bug/1400731

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.

Revision history for this message
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.

Revision history for this message
PerfectCarl (name-is-carl) wrote :

# Test with a SMB share from a luna VM (samba = 2:3.6.3-2ubuntu2.11)

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://i.imgur.com/EmDkwJw.png

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://ele.slack.com/files/carl/F039M70J0/files-network-icons.webm
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://i.imgur.com/laMnyGh.png

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.

Revision history for this message
PerfectCarl (name-is-carl) wrote :

47) message displayed in the console with a typo:
PropertiesWindow.vala:270: selection sizr update

Revision history for this message
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?

Revision history for this message
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.

Revision history for this message
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?

Revision history for this message
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?

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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"?

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
PerfectCarl (name-is-carl) wrote :
Download full text (4.0 KiB)

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_info_get_attribute_string: assertion 'G_IS_FILE_INFO (info)' failed
[_LOG_LEVEL_FATAL 22:00:42.019547] Files will not function properly.
[_LOG_LEVEL_FATAL 22:00:42.020752] [GLib-GIO] g_file_info_get_attribute_string: assertion 'G_IS_FILE_INFO (info)' failed
[_LOG_LEVEL_FATAL 22:00:42.020802] Files will not function properly.
[_LOG_LEVEL_FATAL 22:00:42.020901] [GLib-GIO] g_file_info_get_attribute_string: assertion 'G_IS_FILE_INFO (info)' failed
[_LOG_LEVEL_FATAL 22:00:42.020930] Files will not function properly.

Here's the full stacktrace:

#0 g_logv (log_domain=0x7ffff5ab1198 "GLib-GIO", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>,
    args=args@entry=0x7fffffffcf08) at /build/buildd/glib2.0-2.42.1/./glib/gmessages.c:1046
#1 0x00007ffff524ab32 in g_log (log_domain=log_domain@entry=0x7ffff5ab1198 "GLib-GIO",
    log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x7ffff529244d "%s: assertion '%s' failed")
    at /build/buildd/glib2.0-2.42.1/./glib/gmessages.c:1079
#2 0x00007ffff524ab59 in g_return_if_fail_warning (log_domain=log_domain@entry=0x7ffff5ab1198 "GLib-GIO",
    pretty_function=pretty_function@entry=0x7ffff5ab85c0 <__FUNCTION__.13927> "g_file_info_get_attribute_string",
    expression=expression@entry=0x7ffff5ab46df "G_IS_FILE_INFO (info)") at /build/buildd/glib2.0-2.42.1/./glib/gmessages.c:1088
#3 0x00007ffff5a086fc in g_file_info_get_attribute_string (info=0x0, attribute=0x7ffff7bcab49 "standard::target-uri")
    at /build/buildd/glib2.0-2.42.1/./gio/gfileinfo.c:887
#4 0x00007ffff7baf2d2 in gof_file_is_location_uri_default (file=0xc15e70)
    at /home/cran/Documents/Projects/elementary/pantheon-files/fix-crash-when-trashing/libcore/gof-file.c:201
#5 0x00007ffff7baf3ef in gof_file_is_root_network_folder (file=0xc15e70)
    at /home/cran/Documents/Projects/elementary/pantheon-files/fix-crash-when-trashing/libcore/gof-file.c:231
#6 0x00007ffff7baf359 in gof_file_is_remote_uri_scheme (file=0xc15e70)
    at /home/cran/Documents/Projects/elementary/pantheon-files/fix-crash-when-trashing/libcore/gof-file.c:218
#7 0x00007ffff7baf509 in gof_file_get_folder_icon_from_uri_or_path (file=0xc15e70)
    at /home/cran/Documents/Projects/elementary/pantheon-files/fix-crash-when-trashing/libcore/gof-file.c:257
#8 0x000000000042fa25 in marlin_bookmark_get_icon (self=self@entry=0xc174e0)
    at /home/cran/Documents/Projects/elementary/pantheon-files/fix-crash-when-trashing/src/Bookmark.vala:83
#9 0x000000000046e1a3 in marlin_places_sidebar_add_bookmark (index=7, bm=0xc174e0, iter=<synthetic pointer>, self=0x9fc520)
    at /home/cran/Documents/Projects/elementary/pantheon-files/fix-crash-when-trashing/src/View/Sidebar.vala:642
#10 marlin_places_sidebar_update_places (self=0x9fc520)
    at /home/cran/Documents/Projects/elementary/pantheon-files/fix-crash-when-trashing/src/View/Sidebar.vala:454
#11 0x00007ffff57761c8 in g_closure_invoke (closure=0xa08060, return_value=0x0, n_param_values=1, param_values=0x7fffffffd600,
...

Read more...

Revision history for this message
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.

Revision history for this message
Danielle Foré (danrabbit) wrote :

When attempting to merge trunk I get:
Text conflict in libcore/gof-directory-async.vala
Text conflict in libwidgets/LocationBar.vala
Text conflict in src/View/LocationBar.vala
3 conflicts encountered.

If we can get those conflicts solved, Cody and I would like to merge this within the next day or so

Revision history for this message
Danielle Foré (danrabbit) wrote :

Right click a breadcrumb in a local path > Open in New Tab > "Unable to mount folder"

Revision history for this message
Danielle Foré (danrabbit) wrote :

Something with the files open is broken. Clicking a local .jpg image gives me a "folder does not exist" error

Revision history for this message
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.

Revision history for this message
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.'

Revision history for this message
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.

Revision history for this message
Danielle Foré (danrabbit) wrote :

Cody and I have been testing, so we're gonna merge. Thanks Jeremy!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches

to status/vote changes: