Merge lp:~jeremywootten/pantheon-files/all-views-vala into lp:~elementary-apps/pantheon-files/trunk

Proposed by Jeremy Wootten
Status: Merged
Approved by: Danielle Foré
Approved revision: 1661
Merged at revision: 1643
Proposed branch: lp:~jeremywootten/pantheon-files/all-views-vala
Merge into: lp:~elementary-apps/pantheon-files/trunk
Diff against target: 49548 lines (+17157/-29587)
96 files modified
.bzrignore (+1/-1)
AUTHORS (+2/-0)
CMakeLists.txt (+1/-1)
data/schemas/org.pantheon.files.gschema.xml (+2/-2)
libcore/AbstractSlot.vala (+76/-0)
libcore/CMakeLists.txt (+3/-2)
libcore/Plugin.vala (+1/-1)
libcore/PluginManager.vala (+1/-1)
libcore/eel-stock-dialogs.c (+0/-3)
libcore/fm-list-model.c (+60/-139)
libcore/fm-list-model.h (+3/-9)
libcore/gof-abstract-slot.c (+0/-47)
libcore/gof-abstract-slot.h (+0/-56)
libcore/gof-directory-async.vala (+45/-23)
libcore/gof-file.c (+46/-26)
libcore/gof-file.h (+4/-3)
libcore/marlin-file-changes-queue.c (+0/-3)
libcore/marlin-file-operations.c (+6/-10)
libcore/marlin-icon-info.c (+9/-5)
libcore/pantheon-files-core-C.vapi (+86/-16)
libwidgets/Animations.vala (+12/-3)
libwidgets/BreadcrumbsElements.vala (+1/-1)
libwidgets/LocationBar.vala (+15/-13)
plugins/network-places/plugin.vala (+12/-7)
plugins/pantheon-files-ctags/plugin.vala (+183/-55)
plugins/pantheon-files-trash/plugin.vala (+19/-15)
src/AbstractEditableLabel.vala (+96/-0)
src/Application.vala (+47/-76)
src/BookmarkList.vala (+5/-0)
src/CMakeLists.txt (+26/-20)
src/DndHandler.vala (+272/-0)
src/MimeActions.vala (+19/-11)
src/MultiLineEditableLabel.vala (+196/-0)
src/SingleLineEditableLabel.vala (+104/-0)
src/TextRenderer.vala (+307/-0)
src/View/AbstractDirectoryView.vala (+2778/-0)
src/View/AbstractTreeView.vala (+280/-0)
src/View/Browser.vala (+3/-18)
src/View/Chrome/ColorWidget.vala (+0/-183)
src/View/Chrome/TopMenu.vala (+81/-35)
src/View/Chrome/ViewSwicher.vala (+36/-27)
src/View/ColumnView.vala (+140/-0)
src/View/DbusTags.vala (+0/-100)
src/View/DirectoryNotFound.vala (+1/-1)
src/View/IconView.vala (+318/-0)
src/View/ListView.vala (+235/-0)
src/View/LocationBar.vala (+27/-44)
src/View/Miller.vala (+396/-0)
src/View/OverlayBar.vala (+58/-53)
src/View/PropertiesWindow.vala (+5/-4)
src/View/Resources.vala (+4/-1)
src/View/Sidebar.vala (+44/-40)
src/View/Slot.vala (+276/-0)
src/View/ViewContainer.vala (+246/-296)
src/View/ViewMode.vala (+0/-17)
src/View/Window.vala (+614/-604)
src/View/directory_view_popup.ui (+138/-0)
src/eel-editable-label.c (+0/-4417)
src/eel-editable-label.h (+0/-146)
src/exo-icon-view.c (+0/-11753)
src/exo-icon-view.h (+0/-347)
src/exo-tree-view.c (+0/-674)
src/exo-tree-view.h (+0/-91)
src/fm-abstract-icon-view.c (+0/-870)
src/fm-abstract-icon-view.h (+0/-60)
src/fm-columns-view.c (+0/-858)
src/fm-columns-view.h (+0/-60)
src/fm-directory-view-ui.xml (+0/-194)
src/fm-directory-view.c (+0/-4059)
src/fm-directory-view.h (+0/-446)
src/fm-icon-view-ui.xml (+0/-37)
src/fm-icon-view.c (+0/-147)
src/fm-icon-view.h (+0/-51)
src/fm-list-view.c (+0/-999)
src/fm-list-view.h (+0/-60)
src/gof-window-slot.c (+0/-315)
src/gof-window-slot.h (+0/-88)
src/gtk+-3.0.deps (+7/-0)
src/gtk+-3.0.vapi (+9698/-0)
src/main.vala (+1/-0)
src/marlin-cell-renderer-text-ellipsized.c (+0/-73)
src/marlin-cell-renderer-text-ellipsized.h (+0/-58)
src/marlin-clipboard-manager.c (+10/-0)
src/marlin-clipboard-manager.h (+3/-0)
src/marlin-dnd.c (+0/-230)
src/marlin-dnd.h (+0/-43)
src/marlin-enum-types.h (+2/-4)
src/marlin-global-preferences.h (+1/-0)
src/marlin-icon-renderer.c (+13/-4)
src/marlin-text-renderer.c (+0/-869)
src/marlin-text-renderer.h (+0/-76)
src/marlin-view-window.h (+25/-13)
src/marlin-window-columns.c (+0/-310)
src/marlin-window-columns.h (+0/-84)
src/marlin.vapi (+57/-104)
src/pantheon-files-ui.xml (+0/-75)
To merge this branch: bzr merge lp:~jeremywootten/pantheon-files/all-views-vala
Reviewer Review Type Date Requested Status
PerfectCarl (community) Approve
Danielle Foré ux Approve
Jeremy Wootten Pending
Cody Garver Pending
Review via email: mp+240371@code.launchpad.net

This proposal supersedes a proposal from 2014-09-18.

Commit message

* Convert Slot, Miller, DirectoryView, ListView, ColumnView and IconView to Vala.
* Use standard Gtk.TreeView and Gtk.IconView
* Address a number of bugs associated with the views.
* Some changes made to ViewContainer - new slots only made when view mode changes, not when location changes.
* For improved accessibility, helper emblem appears in all views but only when the icons are above a minimum size.
* Implemented bookmarking from context menu
* Implemented "Open in New Window" in context menu
* Context menu and app menu reimplemented without deprecated code.

Description of the change

Convert Slot, Miller, DirectoryView, ListView, ColumnView and IconView to Vala.
Use standard Gtk.TreeView and Gtk.IconView
Address a number of bugs associated with the views.
Some changes made to ViewContainer - new slots only made when view mode changes, not when location changes.

Use of standard Gtk widgets means that multi-item dragging is only possible with the secondary button or by using primary button in combination with a modifier key.

Automatic switching to icon-view in certain directories not (yet) implemented. Is this desirable?

Rename by clicking on name implemented. Is this desirable?

In list and column view, right clicking on any blank space treats it as background allowing operations on root directory even when window filled with rows. Blank space click initiates rubber-banding, not drag.

For improved accessibility, helper emblem appears in all views but only when the icons are above a minimum size. Is this desirable?

Items activate only when clicked on icon. Clicking on name starts on renaming, clicking on blank space selects only. Is this desirable?

Implemented bookmarking from context menu
Implemented "Open in New Window" in context menu

Context menu and app menu reimplemented without deprecated code.

Design input required
Code will be cleaned up before merge

To post a comment you must log in.
Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

I can see a menubar and a GNOME-style appmenu. That's a pretty undesirable change.

I get an "edit"/I-beam anytime I'm anywhere over the icon view. That should probably only happen over the label (which btw, clicking the label to edit is awesome).

When there's no back/forward history the respective button should become insensitive (regression)

There's no visible rubberband for selection files/folders in any view (regression)

No Ctrl F to search (regression)

"Open In" menu is interesting. Does this show the same items as "Open With" would, but with the addition of "New Tab" and "New Window"? It's possible to get both menus which shows the duplicate entry "Terminal". I think we should choose one or the other.

I like that the selection emblems automatically appear based on icon size. That's a pretty cool feature.

Not being able to drag multiple items is a regression and a pretty big one. I think this is pretty important functionality we need to preserve.

I don't think we need to automatically switch to a certain view when in a directory. Users should be in control of the view imo.

I can no longer show hidden files by right clicking in the view (regression)

If I have a selection and then right click on the view, the selection should be lost. It's kind of confusing to see context items for the selection when I haven't right clicked on the selection.

Submenus don't need ellipsis. (Open with, Open in, New etc)

I don't think we need to specify "Empty Folder" since there isn't really a way to create a populated folder. "Folder" is probably okay here.

review: Needs Fixing
Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

I can't appear to open anything (file or folder) in the column view other than the first folder. Clicking another folder selects and opens the top folder.

clicking the object to go in and the title to rename seems to be a lot less awesome in the column and list view where the actual icon is very small. In these views, I would expect clicking empty rows or the title to still open the directory.

I like the way the list view is intelligent about which context menu to pull up depending on where you click.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

I also get a "Run" menu item on folders which appears to do nothing.

Revision history for this message
Cody Garver (codygarver) wrote : Posted in a previous version of this proposal

Nodes in Entire Network should not be editable

The "Empty Trash" button is sorta selected on hover. It's not bad but I don't know if it's desired by design.

review: Needs Fixing
Revision history for this message
Cody Garver (codygarver) wrote : Posted in a previous version of this proposal

Disregard what I said about Empty Trash, apparently it already behaved like that in trunk

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

I don't get the I-beam cursor anymore. But I also don't get it when hovering a file/folder name. If we get the cursor showing properly, I wonder if we really need to have a menu item for this.

The way labels are wrapped in the icon view is kind of weird. I'm getting stuff like "Document \n s"

I think the Open/Open in/Open with items should be back at the top of the context menu.

I'm now unable to drag and drop any items in icon view.

Items open on click instead of on release.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

Labels in icon view seem to be centered vertically instead of aligned to the top.

Whenever I select something using an emblem, it launches

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

typing to select a folder/file in a directory no longer works

Revision history for this message
Shawn McTear (syst3mfailur3) wrote : Posted in a previous version of this proposal

> I don't get the I-beam cursor anymore. But I also don't get it when hovering a
> file/folder name. If we get the cursor showing properly, I wonder if we really
> need to have a menu item for this.
>

If the menu item for renaming is removed, maybe adding a tooltip when hovering over the file/folder name to also specify that its editable would be a good idea.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> typing to select a folder/file in a directory no longer works

It should be working in list and column views. It is not working in icon view because Gtk.IconView does not support it - another unforeseen side-effect of switching.

It is possible to apparently 'lock up' the function in list and column view by pressing a second key, creating a non-existent filename, very quickly, before the overlay appears. You have to click on the view again to restart it. This occurs in trunk as well.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> I don't get the I-beam cursor anymore. But I also don't get it when hovering a
> file/folder name. If we get the cursor showing properly, I wonder if we really
> need to have a menu item for this.
>
> The way labels are wrapped in the icon view is kind of weird. I'm getting
> stuff like "Document \n s"
>
> I think the Open/Open in/Open with items should be back at the top of the
> context menu.
>
> I'm now unable to drag and drop any items in icon view.
>
> Items open on click instead of on release.

The cursor doesn't change over the filenames in trunk, but I can try and arrange this.
Items were intended to open on click unless a drag is started - it was working - I'll look into this regression.
I'll address the formating issues you mention

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

Let's kill the font zoom thing. We can discuss that in another branch. In the interest of making a review as easy as possible, I think we should avoid introducing new features in this branch. I think it'd be better to keep the diff as small as possible.

Not being able to type to find a file in the directory is a pretty big regression.

Also, Ctrl + F to search seems to not work again

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

OK, no problem - I'll revert the font zooming in the next revision. Sorry about the re-regression with Ctrl + F, I didn't notice that. I'll not be able to work on this for a couple of days though.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

Looks like I also can't copy folders

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

When you rename something, the name isn't save on lose focus. It's only saved if you press enter.

We should truncate (ellipsis in the center) file/folder names after like 3 lines.

Clicking the title to rename seems to be broken. On releasing the button the entry loses focus

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

The click-to-rename area seems to extend the height of whatever the tallest label is. I'm not sure this is good as it means you can't start a rubberband from a seemingly blank space

Revision history for this message
Sam Hewitt (snwh) wrote : Posted in a previous version of this proposal

I've found a few regressions with this branch as well:

1. The navigation arrows are not using their symbolic variants.
2. Long folder/file strings seem to be cut off.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Revision 1564 fixes:
Name saved when focus lost during renaming
Single click to rename
Does not rename when click on blank part of name in icon view
The cursor changes when over the editable part of the name label
The height of the name label is limited to three lines.

The following problems remain due mainly to limitations of the Gtk.IconView:
Rubberbanding is not initiated by clicking on the blank part of the name renderer (unlike in Gtk.TreeView)
The editable widget used by Gtk.Iconview is a simple one line Entry which is limited in size to the width of the items in the view. This causes only part of the name to be visible when renaming.
The default Gtk.CellRendererText in IconView will either ellipsize on one line or will wrap over several lines - but not both.

To solve these issues will probably meaning implementing our own rubberbanding and name renderer and editable widget.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> I've found a few regressions with this branch as well:
>
> 1. The navigation arrows are not using their symbolic variants.
> 2. Long folder/file strings seem to be cut off.

1. OK, will change.
2. Which view are you referring to? Do you mean during renaming? In Column view there is a maximum width set for autosizing which could cause truncation of long names but the limit can be exceeded manually.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Revision 1566 is an attempt to give visual feedback by means of the cursor on the effect of (primary) clicking at the cursor position. In particular this helps in icon view to distinguish the blank area in the name label (no rubberbanding) from the blank space between the items (will rubberband). A different cursor is also used depending on whether clicking (or double clicking) will activate the item or just select it.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

It sounds like there's going to be a *lot* of work to get the views back to where the exo views are.

Is it possible to split this into multiple merges so we can at least get some of your work merged in?

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> It sounds like there's going to be a *lot* of work to get the views back to
> where the exo views are.
>
> Is it possible to split this into multiple merges so we can at least get some
> of your work merged in?

I wish! I originally was going to change one file to Vala but could get it to work without changing another file to Vala and that wouldn't work without changing ... etc. I was hoping that it would be possible to use the Gtk TreeView and IconView widgets to produce something usable even if not exactly the same as the Exo views. This would make the code lighter and easier to maintain. The Gtk IconView is the main problem - it needs a lot of customisation to behave like the exo view, although it would still be easier (probably) than converting the Exo IconView (ca 12000 lines of code) to Vala.

I will have another look at getting the Exo IconView to work in this branch but it would need some new functions.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

The last two revisions implement much of the functionality of the original customised text renderer and editable label but using Vala to create customised Gtk.CellRendererText and to add Gtk.Editable and Gtk.CellEditable interfaces to Gtk.TextView.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

[ 39%] Built target pantheon-files-widgets
make[2]: *** No rule to make target `../src/EditableLabel.vala', needed by `src/pantheon-files_valac.stamp'. Stop.
make[1]: *** [src/CMakeFiles/../pantheon-files.dir/all] Error 2
make: *** [all] Error 2

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Oops - sorry - forgot to add the new file to bzr.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

When you drag something over a folder, it changes to the "folder-drag-accept" icon as expected, but it gets stuck there and doesn't change back if you drag the something away and don't complete the drop there.

If you put the label in edit mode, then click away, the label text will still be selected.

Is there a way to remove that blue coloration from selected items since you already have the grey box?

Revision history for this message
Cody Garver (codygarver) wrote : Posted in a previous version of this proposal

* Should be "elementary Developers" not "Elementary Developers"

* Warnings:

src/TextRenderer.vala:173.103-173.118: warning: use of possibly unassigned parameter `y_offset'
        public override void get_size (Gtk.Widget widget, Gdk.Rectangle? cell_area, out int x_offset, out int y_offset, out int width, out int height) {
                                                                                                      ^^^^^^^^^^^^^^^^
src/TextRenderer.vala:173.85-173.100: warning: use of possibly unassigned parameter `x_offset'
        public override void get_size (Gtk.Widget widget, Gdk.Rectangle? cell_area, out int x_offset, out int y_offset, out int width, out int height) {
                                                                                    ^^^^^^^^^^^^^^^^

review: Needs Fixing
Revision history for this message
Cody Garver (codygarver) wrote : Posted in a previous version of this proposal

There are .vala files listed in the .bzrignore

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

The last five revisions address the follow issues:
1) Rubberbanding from any visually blank area on Icon view
2) Drop highlighting works correctly according to whether the target has write permissions and unhighlights correctly
3) Compilation warnings removed
4) .bzrignore files removed
5) When editing a long file name a smaller font is used if it would not otherwise fit in the available area.
6) Ctrl-Z restores original file name during renaming (but does not end rename)
7) Folder names with dots are fully selected during rename
8) ViewContainer now correctly forgets selected files after selecting them after a view change.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

9) Editable widget correctly hides when loses focus

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

A number of other bugs/wish-list items have now been fixed.

review: Needs Resubmitting
Revision history for this message
Cody Garver (codygarver) wrote : Posted in a previous version of this proposal

I get a target reticle cursor when I'm not hovering over a file in Grid view

Right click > Copy is grayed out for me for every file in Grid view

Some, seemingly random files are italicized in Grid view

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> I get a target reticle cursor when I'm not hovering over a file in Grid view
>
> Right click > Copy is grayed out for me for every file in Grid view
>
> Some, seemingly random files are italicized in Grid view

The target reticle cursor is used to indicate that rubberbanding is active. The arrow cursor indicates that selection (or row expansion) will occur but not activation. The hand cursor indicates activation will occur, the I-bar cursor indicates that renaming will occur.

It will be a design team decision whether any of this cursor shape feedback is necessary or desirable - it is easily disabled.

The italicized filenames indicate that you do not have write permission for the file and cannot rename it. Again, a design team decision whether it is desirable to have some visual feedback as to the read/write permissions.

I am not getting a greyed out Copy in the Icon View context menu usually, but I have found a bug that causes this if you right click while in renaming. I'll fix this. Thanks.

Revision history for this message
Viko Adi Rahmawan (vikoadi) wrote : Posted in a previous version of this proposal

Hello Jeremy,
this is a few regression a found during testing:
1. pressing enter key now open folder in a new tab,
2. and after that the notebook get the focus, so pressing left/right key will change tab.
3. F2 key doesn't rename, delete key doesn't delete file.
4. cannot undo file renaming
5. when i undo file deletion, it doesn't get focused
6. if i search for file and press enter, the file is selected but i cant navigate to other file by arrow key
7. Renaming file in miller column and List, even if its short name, reduce the font size

I will install this branch and see what more that i can get,
Viko

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Thanks for these Viko - a fresh pair of eyes can always find new things!

On 11 October 2014 10:40, Viko Adi Rahmawan <email address hidden> wrote:

> Hello Jeremy,
> this is a few regression a found during testing:
> 1. pressing enter key now open folder in a new tab,
> 2. and after that the notebook get the focus, so pressing left/right key
> will change tab.
> 3. F2 key doesn't rename, delete key doesn't delete file.
> 4. cannot undo file renaming
> 5. when i undo file deletion, it doesn't get focused
> 6. if i search for file and press enter, the file is selected but i cant
> navigate to other file by arrow key
> 7. Renaming file in miller column and List, even if its short name, reduce
> the font size
>
> I will install this branch and see what more that i can get,
> Viko
>
> --
>
> https://code.launchpad.net/~jeremywootten/pantheon-files/all-views-vala/+merge/235107
> You are the owner of lp:~jeremywootten/pantheon-files/all-views-vala.
>

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> Hello Jeremy,
> this is a few regression a found during testing:
> 1. pressing enter key now open folder in a new tab, --- FIXED
> 2. and after that the notebook get the focus, so pressing left/right key will
> change tab. --- FIXED
> 3. F2 key doesn't rename, delete key doesn't delete file. --- FIXED
> 4. cannot undo file renaming --- Trunk does not have this feature?
> 5. when i undo file deletion, it doesn't get focused --- Trunk does not have this feature?
> 6. if i search for file and press enter, the file is selected but i cant
> navigate to other file by arrow key --- FIXED
> 7. Renaming file in miller column and List, even if its short name, reduce the
> font size --- FIXED
>
> I will install this branch and see what more that i can get,
> Viko

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> Hello Jeremy,
> this is a few regression a found during testing:
> 1. pressing enter key now open folder in a new tab, --- FIXED
> 2. and after that the notebook get the focus, so pressing left/right key will
> change tab. --- FIXED
> 3. F2 key doesn't rename, delete key doesn't delete file. --- FIXED
> 4. cannot undo file renaming --- Trunk does not have this feature?
> 5. when i undo file deletion, it doesn't get focused --- Trunk does not have this feature?
> 6. if i search for file and press enter, the file is selected but i cant
> navigate to other file by arrow key --- FIXED
> 7. Renaming file in miller column and List, even if its short name, reduce the
> font size --- FIXED
>
> I will install this branch and see what more that i can get,
> Viko

Revision history for this message
Viko Adi Rahmawan (vikoadi) wrote : Posted in a previous version of this proposal

just one more thing Jeremy,
1. in Icon View open a folder with rather large number of item, scroll to the bottom, start rubberbanding from last row to the last row-1, I get scrolled up to the top and dont want to scrolled down by placing mouse in the bottom
Other than that everything seems solid for me.
Thanks

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Thanks again Viko. The last revision fixes this.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

I don't really like that the icon spacing changes per-folder. I feel like this is a regression.

There's no longer any border around entries for renaming. I think this makes it unclear that you've actually entered renaming mode.

When there's a long label, renaming makes the text small. I'm not sure I like that. Either way, it's an unnecessary change from trunk. It should be proposed in its own branch.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

While renaming in column or list views,text should be centered vertically on the line instead of aligned to top.

Revision history for this message
Viko Adi Rahmawan (vikoadi) wrote : Posted in a previous version of this proposal

Been using this all day and feels solid enough for me
great work Jeremy

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

Here's a relatively play-by-play look at me testing this for a bit today:

1. Icons in the icon view feel weirdly spaced out; I do like that they evenly space out, though. There just feels like there's a LOT of padding around each icon. Much lower information density.

2. Clicking things in the miller view just doesn't work... Oh, I have to click the icon. Clicking next to the text doesn't do anything and clicking right on it renames, which I guess is nice. I'd recommend only showing the rename if you click the text once it's already selected, otherwise it's a tiny target for browsing (which is a 1000x more important). Clicking the blank space anywhere on the line should open that folder. This is a regression.

3. There's a flash of "Loading..." every time I click a folder anywhere (even on the local SSD), which feels really, really glitchy. I'd consider it a regression.

4. Some things are italic and I don't know why. (Folders in /, things in the trash, maybe more.) I'd consider this a regression as well, or at least a weird design decision that isn't obvious.

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

As a note to number 4 from above, our interface font doesn't even have a true italic and I don't think we use italics anywhere else in the entire OS.

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

5. I feel like the list view should work similarly to how I described the miller view above: clicking anywhere on the line should to the "default" action, which in that case is opening the file or folder. The icon is too tiny of a target.

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

6. If I have a file highlighted in the icon view, then go to ctrl+click+drag to rubberband select another file, it looks like it starts rubberband selecting but then also starts dragging the highlighted file (even when not click+dragging near it).

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

7. Hitting F2 to rename doesn't work anymore.

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

After a while of having Files open, it will no longer open any files. Clicking an image does nothing, right-clicking and telling it to open in a different app does nothing. I'm not 100% sure this is the fault of this branch as I seem to recall this happening in the past.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

I have made the item width in icon view a fixed ratio of the icon size rather than automatic. The column and row spacings are also scaled to the icon size. I have set the ratios to give a more compact view.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

In order to distinguish more clearly, in icon view, the text of a selected item and a renaming item I have reimplemented the black border when renaming and also stopped displaying the text in the "selected" state when the item is selected so that there is a distinct state change when renaming (when the text becomes selected).

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Italic font for unwritable files reverted. Small font for editing long filenames reverted.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> 7. Hitting F2 to rename doesn't work anymore.

I cannot reproduce this - F2 functionality was restore in version 1596 of this branch.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> 7. Hitting F2 to rename doesn't work anymore.

I cannot reproduce this - F2 functionality was restore in version 1596 of this branch.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> After a while of having Files open, it will no longer open any files. Clicking
> an image does nothing, right-clicking and telling it to open in a different
> app does nothing. I'm not 100% sure this is the fault of this branch as I seem
> to recall this happening in the past.

Is this cleared by changing to another view and back? It sounds like the view has become stuck in the "updates_frozen" state somehow. There was a bug like this in an earlier version due to renaming not unfreezing the view in some circumstances but that was fixed.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> 6. If I have a file highlighted in the icon view, then go to ctrl+click+drag
> to rubberband select another file, it looks like it starts rubberband
> selecting but then also starts dragging the highlighted file (even when not
> click+dragging near it).

I cannot reproduce this in the later versions. Dragging with the primary button (left) should only occur when the cursor is in the hand shape (on the icon). However, dragging with the secondary button (right) occurs on both the icon and the text at the moment.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

> 5. I feel like the list view should work similarly to how I described the
> miller view above: clicking anywhere on the line should to the "default"
> action, which in that case is opening the file or folder. The icon is too tiny
> of a target.

An aim of this branch was to make the different views behave consistently - a lot more of the code is now shared. So clicking on an icon activates in all three views, clicking on the name text starts renaming in all three views, click on a blank area starts rubberbanding in all three view etc.

One of the bugs that this branch was intended to fix was the inability to access background functions in list and column views when the window was filled with files. This is why the rows do not activate everywhere. The blank part of a row in list and column views behaves pretty much like the background in icon view. Note also that in column view clicking on the far right of the row behaves like the extra columns in list view - i.e. clicking here deselects all selected files and does not select that row.

I accept that there may be a discoverability issue here and I am open to suggestions. The size of the target could be an issue at some zoom-levels. I already hide the selection helpers on small icons to ameliorate this. Another possibility would be to make the whole row activatable at small icon sizes but I feel that could be confusing.

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

Hey Jeremy, I get this when trying to make the latest revision:

make[2]: *** No rule to make target `../src/AbstractEditableLabel.vala', needed by `src/pantheon-files_valac.stamp'. Stop.
make[1]: *** [src/CMakeFiles/../pantheon-files.dir/all] Error 2
make: *** [all] Error 2

Am I doing something wrong? :P

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

As usual, I forgot to add the new files to bzr - sorry. Now added.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Rev 1608 was necessary to get the vertical centering of the editable widget in list and column view. Prior to that I was using the same widget (a Gtk.TextView subclass) for both in the interest of simplicity and code economy, but I couldn't find an obvious way of having Gtk.TextView display vertically centred text. Now I use a different subclass of AbstractEditableLabel depending on the view, one of which displays a Gtk.TextView and the other a Gtk.Entry. This way at least DirectoryView remains agnostic as to which type of view it is. However, you do lose text wrapping in list and column view (not a regression as it is the same in trunk).

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

Jeremy can you resolve conflicts with trunk? :)

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Conflicts with current trunk now resolved.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

Can we change the border on the editable label back to just 1px width?

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

The original C code used an 'illegal' fractional value for an int parameter, which I rounded up in Vala. I've now rounded it down and this has restored the 1px outline.

Revision history for this message
PerfectCarl (name-is-carl) wrote : Posted in a previous version of this proposal

Here's the issues I found:

 1) the empty trash is not always displayed.
    Open the app, go to trash, close the app.
    Re-open the app. The trash folder is opened by default but the top banner is missing

Revision history for this message
PerfectCarl (name-is-carl) wrote : Posted in a previous version of this proposal

Here's the issues I found:

  2) not a regression per se, but this still applies
     https://bugs.launchpad.net/pantheon-files/+bug/1383019

  3) when I search for a file (Ctrl+F) I once add in the console
    [_LOG_LEVEL_FATAL 21:05:42.727155] [GLib-GIO] g_file_equal: assertion 'G_IS_FILE (file2)' failed
    [_LOG_LEVEL_FATAL 21:05:42.727236] Files will not function properly.
    I cannot reproduce it though.

Suggestion seeing the amount of work that went into this branch maybe a version bump (from 0.2) would be fair.

  4) minor warnings that can be ironed out easily

/home/cran/Projects/elementary/pantheon-files/all-views-vala/src/TextRenderer.vala:129.17-129.53: warning: local variable `context' declared but never used
            var context = widget.get_style_context ();
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/cran/Projects/elementary/pantheon-files/all-views-vala/src/TextRenderer.vala:130.17-130.49: warning: local variable `state' declared but never used
            var state = widget.get_state_flags ();

   5) suspicious warning
/home/cran/Projects/elementary/pantheon-files/all-views-vala/src/View/Miller.vala:181.34-181.34: warning: if-statement without body
            if (increment != 0.0);

Revision history for this message
PerfectCarl (name-is-carl) wrote : Posted in a previous version of this proposal

6) the files can't be opened with double click anymore. When double clicking a file, the file is getting renamed (if you clicked on the name) or do nothing if you clicked somewhere in the line.

When I single clicked the icon, the file is opened though.
But that's not convenient as the icon is very small: I am on the wrong end of Fitts law here

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

It was intentional that only clicking on the icon activates in all three views (in order to have consistent behaviour, shared code between the views and allow other desired behaviours when clicking on different parts of the filename column, especially in column view). But as several people have commented adversely, I will rethink this.

Revision history for this message
Viko Adi Rahmawan (vikoadi) wrote : Posted in a previous version of this proposal

Hey Jeremy,
other regression i found:
- cannot type z in rename box
- cannot open menu using menu key
thanks

Revision history for this message
Cody Garver (codygarver) wrote : Posted in a previous version of this proposal

Regression: can't drag files out of file-roller (opened archive) and onto empty space to extract them (in any view)

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

Vika, I can confirm the "Z" key issue. Very odd.

Revision history for this message
Cassidy James Blaede (cassidyjames) wrote : Posted in a previous version of this proposal

Regression: back/forward buttons (like those on mice) don't work inside of the views, but they still do in other parts of the chrome (when the mouse cursor is over the sidebar or headerbar)

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

To avoid unnecessary difficulties in reviewing this branch I have made it respond like trunk to button presses in list and column views. i.e. clicking anywhere on the row will activate it (no single-click rename, blank part of row does not behave like background). However, rubberbanding works in column view unlike trunk.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

"Z" key issue solved. Silly error in implementing Ctrl-Z to undo while renaming.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Dragging and dropping between applications (including FileRoller) now enabled.
Implemented/reinstated the following trunk behaviours:
1) Select on right click
2) Automagically switch to icon view for icon folders
3) Menu key activates context menu
4) Trash infobar shows in column view

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

Extra mouse button actions should now work. Fixed bug preventing display of some thumbnails

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

The latest revisions fix a number of bugs including some causing crashing under rapid random mouse clicking. Most debugging code has been removed and the format consistency of the code improved.

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

Hey, nice work.
All the bugs I previously found are fixed now.

I found the the following

1) those critical warnings should be investigated:

[_LOG_LEVEL_FATAL 17:34:16.757606] gof_file_update_icon_internal: assertion 'size >= 1' failed
[_LOG_LEVEL_FATAL 17:34:16.757701] Files will not function properly.
[_LOG_LEVEL_FATAL 17:34:16.758011] gof_file_update_icon_internal: assertion 'size >= 1' failed
[_LOG_LEVEL_FATAL 17:34:16.758082] Files will not function properly.
[_LOG_LEVEL_FATAL 17:34:16.758735] gof_file_update_icon_internal: assertion 'size >= 1' failed
[_LOG_LEVEL_FATAL 17:34:16.758800] Files will not function properly.
[_LOG_LEVEL_FATAL 17:34:16.759305] gof_file_update_icon_internal: assertion 'size >= 1' failed
[_LOG_LEVEL_FATAL 17:34:16.759363] Files will not function properly.
[_LOG_LEVEL_FATAL 17:34:16.759540] gof_file_update_icon_internal: assertion 'size >= 1' failed
[_LOG_LEVEL_FATAL 17:34:16.759596] Files will not function properly.
[_LOG_LEVEL_FATAL 17:34:16.762127] gof_file_update_icon_internal: assertion 'size >= 1' failed
[_LOG_LEVEL_FATAL 17:34:16.762194] Files will not function properly.
[_LOG_LEVEL_FATAL 17:35:06.832406] gof_file_update_icon_internal: assertion 'size >= 1' failed
[_LOG_LEVEL_FATAL 17:35:06.832465] Files will not function properly.
[_LOG_LEVEL_FATAL 17:35:06.832664] gof_file_update_icon_internal: assertion 'size >= 1' failed
[_LOG_LEVEL_FATAL 17:35:06.832707] Files will not function properly.

2) minor code warnings: some async methods should be called as method_async.begin ()

/home/cran/Projects/elementary/pantheon-files/all-views-vala/plugins/pantheon-files-ctags/plugin.vala:171.41-171.60: warning: implicit .begin is deprecated
/home/cran/Projects/elementary/pantheon-files/all-views-vala/plugins/pantheon-files-ctags/plugin.vala:183.51-183.72: warning: implicit .begin is deprecated
/home/cran/Projects/elementary/pantheon-files/all-views-vala/plugins/pantheon-files-ctags/plugin.vala:232.13-232.34: warning: implicit .begin is deprecated
/home/cran/Projects/elementary/pantheon-files/all-views-vala/plugins/pantheon-files-ctags/plugin.vala:242.13-242.21: warning: implicit .begin is deprecated
/home/cran/Projects/elementary/pantheon-files/all-views-vala/src/ZeitgeistManager.vala:10.13-10.33: warning: implicit .begin is deprecated

3) not sure it is related to this branch but, when trying to rename a file with an existing file name, I get this pretty ugly looking dialog.
http://i.imgur.com/St2YXBq.png

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

Thanks Carl,

I put those extra assertions in and I thought I had tracked down the only
source of these warnings (its quiet on my machine) but I must have missed
something.

The warnings about async methods are in trunk so not related to this
branch,

I'll check out the dialog - thanks.

On 2 November 2014 16:47, PerfectCarl <email address hidden> wrote:

> Hey, nice work.
> All the bugs I previously found are fixed now.
>
> I found the the following
>
> 1) those critical warnings should be investigated:
>
> [_LOG_LEVEL_FATAL 17:34:16.757606] gof_file_update_icon_internal:
> assertion 'size >= 1' failed
> [_LOG_LEVEL_FATAL 17:34:16.757701] Files will not function properly.
> [_LOG_LEVEL_FATAL 17:34:16.758011] gof_file_update_icon_internal:
> assertion 'size >= 1' failed
> [_LOG_LEVEL_FATAL 17:34:16.758082] Files will not function properly.
> [_LOG_LEVEL_FATAL 17:34:16.758735] gof_file_update_icon_internal:
> assertion 'size >= 1' failed
> [_LOG_LEVEL_FATAL 17:34:16.758800] Files will not function properly.
> [_LOG_LEVEL_FATAL 17:34:16.759305] gof_file_update_icon_internal:
> assertion 'size >= 1' failed
> [_LOG_LEVEL_FATAL 17:34:16.759363] Files will not function properly.
> [_LOG_LEVEL_FATAL 17:34:16.759540] gof_file_update_icon_internal:
> assertion 'size >= 1' failed
> [_LOG_LEVEL_FATAL 17:34:16.759596] Files will not function properly.
> [_LOG_LEVEL_FATAL 17:34:16.762127] gof_file_update_icon_internal:
> assertion 'size >= 1' failed
> [_LOG_LEVEL_FATAL 17:34:16.762194] Files will not function properly.
> [_LOG_LEVEL_FATAL 17:35:06.832406] gof_file_update_icon_internal:
> assertion 'size >= 1' failed
> [_LOG_LEVEL_FATAL 17:35:06.832465] Files will not function properly.
> [_LOG_LEVEL_FATAL 17:35:06.832664] gof_file_update_icon_internal:
> assertion 'size >= 1' failed
> [_LOG_LEVEL_FATAL 17:35:06.832707] Files will not function properly.
>
> 2) minor code warnings: some async methods should be called as
> method_async.begin ()
>
> /home/cran/Projects/elementary/pantheon-files/all-views-vala/plugins/pantheon-files-ctags/plugin.vala:171.41-171.60:
> warning: implicit .begin is deprecated
> /home/cran/Projects/elementary/pantheon-files/all-views-vala/plugins/pantheon-files-ctags/plugin.vala:183.51-183.72:
> warning: implicit .begin is deprecated
> /home/cran/Projects/elementary/pantheon-files/all-views-vala/plugins/pantheon-files-ctags/plugin.vala:232.13-232.34:
> warning: implicit .begin is deprecated
> /home/cran/Projects/elementary/pantheon-files/all-views-vala/plugins/pantheon-files-ctags/plugin.vala:242.13-242.21:
> warning: implicit .begin is deprecated
> /home/cran/Projects/elementary/pantheon-files/all-views-vala/src/ZeitgeistManager.vala:10.13-10.33:
> warning: implicit .begin is deprecated
>
> 3) not sure it is related to this branch but, when trying to rename a file
> with an existing file name, I get this pretty ugly looking dialog.
> http://i.imgur.com/St2YXBq.png
>
>
> --
>
> https://code.launchpad.net/~jeremywootten/pantheon-files/all-views-vala/+merge/240371
> You are the owner of lp:~jeremywootten/pantheon-files/all-views-vala.
>

1649. By Jeremy Wootten

Fix rename error dialog and handling

Revision history for this message
PerfectCarl (name-is-carl) wrote : Posted in a previous version of this proposal

When right clicking on a header I have the new file menu.
Shouldn't we have an option to add columns instead ?

PS : congrats, all the issue I reported are fixed in this version.

PS2: I did post a review message that got deleted it seems. Damn.

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

The suggestion to have an option to add columns in list view is similar to Bug #1181547. Its a good idea - but will need another branch.

1650. By Jeremy Wootten

Reinstate interactive search in tree views; not implemented in Gtk.IconView

Revision history for this message
PerfectCarl (name-is-carl) wrote : Posted in a previous version of this proposal

Okay, here goes the code review.
The QA will go in another comment (or I'll annotate the bug report directly).

So all in all, a good merge, congrats.

1) minor: remove the commented code
- in the cmakelist file
- in the code (mostly g_message )
- remove unused files from filesystem (see comments)

2) deal with the FIXME
- this is not a good thing to leave a FIXME without a bug report on lp.

note: the merge display is truncated by launchpad. I have to do it offline then.

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

Damn, I commented the wrong merge proposal.

Please find my code comments there : https://code.launchpad.net/~jeremywootten/pantheon-files/all-views-vala/+merge/235107

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

remove src/views/View/ViewMode.vala

I would suggest to add you to the AUTHORS file and about dialog as this merge is really non trivial and qualifies as being on the pantheon (ah ah) wall of fame !

Also because this merge is long, I had to take the review offline and annotate the code in a separate branch.
Nothing major though : spotted commented code and public event handlers.

You can make a diff from your branch and mine (https://code.launchpad.net/~name-is-carl/pantheon-files/all-views-vala-review) and see the issues.

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

Went through and verified that all the currently linked reports are indeed fixed by this branch :)

I did notice a couple of problems though:

1. In Icon view, right click and rename a folder (not the first one)
2. Move your cursor over the text box
3. See the the first folder in the view becomes highlighted

1. In Icon view, right click a folder and Open In > New Window
2. Close the new window
3. Right click the same folder
4. See that both "Open With" and "Open In" are now present.

1651. By Jeremy Wootten

Merged trunk

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

Rev 1650 reverts the new "find on typing" function and re-instates the native interactive search in list and column view. Because Gtk.IconView does not have a native interactive search, a different solution will have to be found for this view, for example that implemented in lp:~jeremywootten/pantheon-files/all-views-vala-local-only-search, which uses the location bar find function to rapidly search just the current directory, providing a similar functionality to the native Gtk interactive search, except that at the moment, the found files are not selected (this could be implemented). If acceptable, it might be better to use the same function for all three views, for a consistent user experience.

1652. By Jeremy Wootten

Remove unnecessary comment lines, remove redundant files, remove or revise TODOs and FIXMEs, update about dialog authors list and AUTHORS file

1653. By Jeremy Wootten

Do not select current directory during actions on background

1654. By Jeremy Wootten

Allow 'Open with' menu action for background

1655. By Jeremy Wootten

Select focussed location

1656. By Jeremy Wootten

Eat motion events while renaming

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

When I go to paste something into the current view, it now reads "Paste into Folder". Can we revert this to just "Paste"? This is kind of disorienting. I'd only expect to see that string if I'd right clicked on a folder not the current view.

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

Can we get type-to-search back in Icon view? Slow completion is better than no completion :/

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

Oh sorry I didn't realize the search thing was fixed in another branch. Please disregard that comment.

1657. By Jeremy Wootten

Merge 'Open in' and 'Open with' submenus; Fix 'Open with other application' menuitem; improve app list filtering

1658. By Jeremy Wootten

Limit background clipboard actions to 'Paste'; only show paste action menuitem when pasting is possible

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

Small issue, but wouldn't consider it a merge blocker (it can be addressed after the merge if necessary). Similar to before with the open with:

Right click a folder
Open in new window
close new window
Right click same folder
Open in shows Terminal twice.

I think this branch is ready to be merged :) Great work Jeremy!

review: Approve (ux)
Revision history for this message
Cody Garver (codygarver) wrote :

Forgive me if any of these are not relevant to this branch, here are my issues:

* Don't change the tab name to "Loading..." for local browsing, but would be nice to keep the spinner if possible; no big deal if it's not easy to implement

Icon View:
 * Third line of long folder and filenames is cropped at the bottom, as is selection highlight

List View:
 * No where to right click on to paste in a full directory
 * Hand cursor appears when you hover over a file or folder icon, but not when you hover on the name (or its + or - icon)

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

I can revert the change that changes the tab name to "loading" but keep the spinner.

The issue of long file names in icon view has come up before. I do not think it a problem that they are truncated when not renaming - that happens in all the views. The perceived problem is the truncation when renaming - which is a regression compared with trunk. This is because trunk uses a custom icon view that does not manage the editable widget, which then overlays as much of the view as needed to display the whole name. This branch uses a standard Gtk.IconView which handles the renaming and limits display of the editable widget to the available cell area (set to 3 lines at the moment). It is by no means trivial to change this. Perhaps a separate rename dialog could be shown? It is always possible to zoom in or switch to list view to edit a long name.

The ability to access the the background in List View with a full directory was implemented in an earlier revision of this branch but was reverted in order to reduce the number of changes introduced. Trunk has the same limitation. This can be re-introduced in a later branch.

The hand cursor is intended to indicate that dragging is possible. If you try and drag on the name you get rubberbanding instead. The code is quite flexible with regards to what actions occur and what cursor is shown when hovering or clicking on different regions of the row so if an agreed specification can be drawn up this can be implemented

Revision history for this message
Cody Garver (codygarver) wrote :

The only issue I named that is definitely blocking the merge of this branch is the "Loading..." change, it was confirmed with Dan before posting my review.

The truncation is a problem but I suppose we should just make a report and deal with it some other time.

Accessing the background in a list view pre-dates this code, so ignore it.

We should hear from Dan about the hand cursor though.

Revision history for this message
Cody Garver (codygarver) wrote :

Oh and theres a lot of commented out code that should be removed but you may already know this

1659. By Jeremy Wootten

Remove commented out code

1660. By Jeremy Wootten

Do not show 'Loading ...' on tab

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

I think all the commented code that was added by this branch has now been removed.

I have stopped "Loading ..." being displayed on the tab during loading of the directory but the spinner still appears. A small visual flaw with this is that the name of the tab shifts to the right slightly when the spinner is hidden, since nothing takes the spinner's place and the available space is redistributed. One solution to this would be to display an icon for the directory (or a blank icon) in place of the spinner. DynamicNotebook already permits this.

1661. By Jeremy Wootten

Ensure gof_file_icon_update_internal is not called with invalid pix_size

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

If we showed a blank icon it would also make sure the labels are centered since they are already offset a bit by the tab close button. I'm +1 to that solution.

Revision history for this message
PerfectCarl (name-is-carl) wrote : Posted in a previous version of this proposal

There is still commented code in that branch.

But to be fair this is a minor inconvenience and the codebase of pantheon-files is old enough that it could filed as a separate issue.

Besides, the work of everyone (maintainer and reviewer) will be simpler if that gigantic merge request is not stalled any longer.

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

There is still commented code in that branch.

But to be fair this is a minor inconvenience and the codebase of pantheon-files is old enough that it could filed as a separate issue.

Besides, the work of everyone (maintainer and reviewer) will be simpler if that gigantic merge request is not stalled any longer.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-08-06 10:29:32 +0000
3+++ .bzrignore 2014-11-22 15:05:49 +0000
4@@ -1,1 +1,1 @@
5-build
6+
7
8=== modified file 'AUTHORS'
9--- AUTHORS 2011-07-02 12:29:59 +0000
10+++ AUTHORS 2014-11-22 15:05:49 +0000
11@@ -3,3 +3,5 @@
12 Robert Roth
13 Vadim Rutkovsky
14 Rico Tzschichholz
15+Mario Guerriero <mario@elementaryos.org>
16+Jeremy Wootten <jeremy@elementaryos.org>
17
18=== modified file 'CMakeLists.txt'
19--- CMakeLists.txt 2014-10-05 06:30:05 +0000
20+++ CMakeLists.txt 2014-11-22 15:05:49 +0000
21@@ -22,7 +22,7 @@
22
23 find_package (Vala REQUIRED)
24 include (ValaVersion)
25-ensure_vala_version ("0.26.0" MINIMUM)
26+ensure_vala_version ("0.26" MINIMUM)
27 include (ValaPrecompile)
28
29 IF (LIB_ONLY)
30
31=== modified file 'data/schemas/org.pantheon.files.gschema.xml'
32--- data/schemas/org.pantheon.files.gschema.xml 2014-10-18 17:39:19 +0000
33+++ data/schemas/org.pantheon.files.gschema.xml 2014-11-22 15:05:49 +0000
34@@ -127,7 +127,7 @@
35 </key>
36 <key type="a(uss)" name="tab-info-list">
37 <summary>Details of open tabs</summary>
38- <default>[(0,"","")]</default>
39+ <default>[(0,'','')]</default>
40 <description>Array of tab info: View mode, root uri, tip uri (for Miller view)</description>
41 </key>
42 </schema>
43@@ -176,4 +176,4 @@
44 </key>
45 </schema>
46
47-</schemalist>
48\ No newline at end of file
49+</schemalist>
50
51=== added file 'libcore/AbstractSlot.vala'
52--- libcore/AbstractSlot.vala 1970-01-01 00:00:00 +0000
53+++ libcore/AbstractSlot.vala 2014-11-22 15:05:49 +0000
54@@ -0,0 +1,76 @@
55+/***
56+ Copyright (C) 2014 elementary Developers and Jeremy Wootten
57+
58+ This program is free software: you can redistribute it and/or modify it
59+ under the terms of the GNU Lesser General Public License version 3, as published
60+ by the Free Software Foundation.
61+
62+ This program is distributed in the hope that it will be useful, but
63+ WITHOUT ANY WARRANTY; without even the implied warranties of
64+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
65+ PURPOSE. See the GNU General Public License for more details.
66+
67+ You should have received a copy of the GNU General Public License along
68+ with this program. If not, see <http://www.gnu.org/licenses/>.
69+
70+ Authors : Lucas Baudin <xapantu@gmail.com>
71+ Jeremy Wootten <jeremywootten@gmail.com>
72+***/
73+
74+namespace GOF {
75+ public abstract class AbstractSlot : GLib.Object {
76+
77+ GOF.Directory.Async _directory;
78+ public GOF.Directory.Async? directory {
79+ get {
80+ AbstractSlot? current = get_current_slot ();
81+ if (current != null)
82+ return current._directory;
83+ else
84+ return null;
85+ }
86+
87+ protected set {_directory = value;}
88+ }
89+ public GLib.File location {
90+ get {return directory.location;}
91+ }
92+ public string uri {
93+ get { return directory.file.uri;}
94+ }
95+ protected Gtk.Box extra_location_widgets;
96+ protected Gtk.Box content_box;
97+ protected int slot_number;
98+ protected int width;
99+
100+ public void add_extra_widget (Gtk.Widget widget) {
101+ extra_location_widgets.pack_start (widget);
102+ }
103+
104+ protected void init () {
105+ content_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
106+ extra_location_widgets = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
107+ (content_box as Gtk.Box).pack_start (extra_location_widgets, false, false, 0);
108+ slot_number = -1;
109+ }
110+
111+ public abstract unowned GLib.List<unowned GOF.File>? get_selected_files ();
112+ public abstract void set_active_state (bool set_active);
113+ public abstract unowned AbstractSlot? get_current_slot ();
114+ public abstract void reload ();
115+ public abstract void grab_focus ();
116+ public abstract void user_path_change_request (GLib.File loc, bool allow_mode_change = true);
117+ public abstract void select_first_for_empty_selection ();
118+ public abstract void select_glib_files (GLib.List<GLib.File> locations, GLib.File? focus_location);
119+ protected abstract void make_view ();
120+ public abstract void cancel ();
121+
122+ public virtual void zoom_out () {}
123+ public virtual void zoom_in () {}
124+ public virtual void zoom_normal () {}
125+ public virtual bool set_all_selected (bool all_selected) {return false;}
126+ public virtual Gtk.Widget get_content_box () {return content_box as Gtk.Widget;}
127+ public virtual string? get_root_uri () {return directory.file.uri;}
128+ public virtual string? get_tip_uri () {return null;}
129+ }
130+}
131
132=== modified file 'libcore/CMakeLists.txt'
133--- libcore/CMakeLists.txt 2014-08-05 22:40:12 +0000
134+++ libcore/CMakeLists.txt 2014-11-22 15:05:49 +0000
135@@ -15,6 +15,7 @@
136
137 vala_precompile(VALA_C ${PKGNAME}
138 AbstractSidebar.vala
139+ AbstractSlot.vala
140 gof-callwhenready.vala
141 gof-directory-async.vala
142 gof-preferences.vala
143@@ -60,7 +61,7 @@
144 marlin-progress-info.c
145 marlin-progress-info-manager.c
146 marlin-exec.c
147- gof-abstract-slot.c
148+# gof-abstract-slot.c
149 gof-file.c
150 marlin-icon-info.c
151 marlin-trash-monitor.c
152@@ -80,7 +81,7 @@
153 eel-string.h
154 eel-ui.h
155 eel-vfs-extensions.h
156- gof-abstract-slot.h
157+# gof-abstract-slot.h
158 gof-file.h
159 marlin-exec.h
160 marlin-file-conflict-dialog.h
161
162=== modified file 'libcore/Plugin.vala'
163--- libcore/Plugin.vala 2014-09-05 08:54:51 +0000
164+++ libcore/Plugin.vala 2014-11-22 15:05:49 +0000
165@@ -1,6 +1,6 @@
166 public abstract class Marlin.Plugins.Base {
167 public virtual void directory_loaded (void* data) { }
168- public virtual void context_menu (Gtk.Widget? widget, List<GOF.File> files) { }
169+ public virtual void context_menu (Gtk.Widget? widget, List<unowned GOF.File> files) { }
170 public virtual void ui (Gtk.UIManager? widget) { }
171 public virtual void update_sidebar (Gtk.Widget widget) { }
172 public virtual void update_file_info (GOF.File file) { }
173
174=== modified file 'libcore/PluginManager.vala'
175--- libcore/PluginManager.vala 2014-09-05 08:54:51 +0000
176+++ libcore/PluginManager.vala 2014-11-22 15:05:49 +0000
177@@ -169,7 +169,7 @@
178 }
179 }
180
181- public void hook_context_menu (Gtk.Widget menu, List<GOF.File> files) {
182+ public void hook_context_menu (Gtk.Widget menu, List<unowned GOF.File> files) {
183 drop_menu_references (menu);
184
185 if (menu is Gtk.Menu)
186
187=== modified file 'libcore/eel-stock-dialogs.c'
188--- libcore/eel-stock-dialogs.c 2013-09-09 03:08:16 +0000
189+++ libcore/eel-stock-dialogs.c 2014-11-22 15:05:49 +0000
190@@ -55,7 +55,6 @@
191 GtkWindow *parent)
192 {
193 GtkWidget *dialog;
194-
195 dialog = gtk_message_dialog_new (parent, 0, type, buttons_type, NULL);
196 g_object_set (dialog, "text", primary_text, "secondary-text", secondary_text, NULL);
197
198@@ -78,11 +77,9 @@
199 GtkWindow *parent)
200 {
201 GtkDialog *dialog;
202-
203 dialog = show_message_dialog (primary_text, secondary_text, type,
204 GTK_BUTTONS_OK, NULL, parent);
205 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
206-
207 return dialog;
208 }
209
210
211=== modified file 'libcore/fm-list-model.c'
212--- libcore/fm-list-model.c 2013-09-09 03:08:16 +0000
213+++ libcore/fm-list-model.c 2014-11-22 15:05:49 +0000
214@@ -37,6 +37,7 @@
215 enum {
216 PROP_0,
217 PROP_HAS_CHILD,
218+ PROP_SIZE,
219 };
220
221 static GQuark attribute_name_q,
222@@ -67,14 +68,11 @@
223
224 int stamp;
225 gboolean has_child;
226-
227- //GQuark sort_attribute;
228 gint sort_id;
229+ gint icon_size;
230 GtkSortType order;
231
232 gboolean sort_directories_first;
233-
234- //GPtrArray *columns;
235 };
236
237 typedef struct FileEntry FileEntry;
238@@ -98,7 +96,6 @@
239 static void
240 file_entry_free (FileEntry *file_entry)
241 {
242- //gof_file_unref (file_entry->file);
243 if (file_entry->reverse_map) {
244 g_hash_table_destroy (file_entry->reverse_map);
245 file_entry->reverse_map = NULL;
246@@ -117,14 +114,12 @@
247 static GtkTreeModelFlags
248 fm_list_model_get_flags (GtkTreeModel *tree_model)
249 {
250- //return GTK_TREE_MODEL_ITERS_PERSIST;
251 return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
252 }
253
254 static int
255 fm_list_model_get_n_columns (GtkTreeModel *tree_model)
256 {
257- //return FM_LIST_MODEL_NUM_COLUMNS + FM_LIST_MODEL (tree_model)->details->columns->len;
258 return FM_LIST_MODEL_NUM_COLUMNS;
259 }
260
261@@ -134,6 +129,8 @@
262 switch (index) {
263 case FM_LIST_MODEL_FILE_COLUMN:
264 return GOF_TYPE_FILE;
265+ case FM_LIST_MODEL_PIXBUF:
266+ return GDK_TYPE_PIXBUF;
267 default:
268 if (index < FM_LIST_MODEL_NUM_COLUMNS) {
269 return G_TYPE_STRING;
270@@ -141,13 +138,6 @@
271 return G_TYPE_INVALID;
272 }
273 }
274- /*default:
275- if (index < FM_LIST_MODEL_NUM_COLUMNS + FM_LIST_MODEL (tree_model)->details->columns->len) {
276- return G_TYPE_STRING;
277- } else {
278- return G_TYPE_INVALID;
279- }
280- }*/
281 }
282
283 static void
284@@ -157,6 +147,7 @@
285 if (iter != NULL) {
286 iter->stamp = model->details->stamp;
287 iter->user_data = ptr;
288+ } else {
289 }
290 }
291
292@@ -204,7 +195,7 @@
293 g_return_val_if_fail (iter->stamp == model->details->stamp, NULL);
294
295 if (g_sequence_iter_is_end (iter->user_data)) {
296- /* FIXME is this right? */
297+ /* is this right? */
298 return NULL;
299 }
300
301@@ -275,6 +266,15 @@
302 g_value_set_string(value, file->formated_modified);
303 break;
304
305+ case FM_LIST_MODEL_PIXBUF:
306+ g_value_init (value, GDK_TYPE_PIXBUF);
307+ if (file != NULL) {
308+ gof_file_update_icon (file, model->details->icon_size);
309+ if (file->pix != NULL)
310+ g_value_set_object(value, file->pix);
311+ }
312+ break;
313+
314 }
315 }
316
317@@ -409,6 +409,8 @@
318 FileEntry *file_entry;
319 GSequenceIter *ptr, *parent_ptr;
320
321+ g_assert (file != NULL);
322+
323 parent_ptr = NULL;
324 if (directory) {
325 parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map,
326@@ -417,6 +419,7 @@
327
328 if (parent_ptr) {
329 file_entry = g_sequence_get (parent_ptr);
330+ g_assert (file_entry != NULL);
331 ptr = g_hash_table_lookup (file_entry->reverse_map, file);
332 } else {
333 ptr = g_hash_table_lookup (model->details->top_reverse_map, file);
334@@ -588,7 +591,6 @@
335 }
336
337 /* Let the world know about our new order */
338-
339 g_assert (new_order != NULL);
340
341 has_iter = FALSE;
342@@ -672,10 +674,7 @@
343 FileEntry *dummy_file_entry;
344 GtkTreeIter iter;
345 GtkTreePath *path;
346-
347 dummy_file_entry = g_new0 (FileEntry, 1);
348- //amtest
349- //dummy_file_entry->file = parent_entry->file;
350 dummy_file_entry->parent = parent_entry;
351 dummy_file_entry->ptr = g_sequence_insert_sorted (parent_entry->files, dummy_file_entry,
352 fm_list_model_file_entry_compare_func, model);
353@@ -696,7 +695,7 @@
354 FileEntry *file_entry;
355 GSequenceIter *ptr, *parent_ptr;
356 GSequence *files;
357- gboolean replace_dummy;
358+ gboolean replaced_dummy;
359 GHashTable *parent_hash;
360
361 g_return_val_if_fail (file != NULL, FALSE);
362@@ -711,25 +710,21 @@
363 }
364
365 if (ptr != NULL) {
366- g_warning ("file already in tree (parent_ptr: %p)!!!\n", parent_ptr);
367+ g_debug ("file already in tree (parent_ptr: %p)!!!\n", parent_ptr);
368 return FALSE;
369 }
370
371 file_entry = g_new0 (FileEntry, 1);
372- file_entry->file = file;
373+ file_entry->file = file; /* Does not increase reference count */
374 file_entry->parent = NULL;
375 file_entry->subdirectory = NULL;
376 file_entry->files = NULL;
377
378 files = model->details->files;
379 parent_hash = model->details->top_reverse_map;
380-
381- //SPOTTED!
382- //#if 0
383- replace_dummy = FALSE;
384+ replaced_dummy = FALSE;
385
386 if (parent_ptr != NULL) {
387- //log_printf (LOG_LEVEL_UNDEFINED, "parent_ptr != NULL %s\n", file->name);
388 file_entry->parent = g_sequence_get (parent_ptr);
389 /* At this point we set loaded. Either we saw
390 * "done" and ignored it waiting for this, or we do this
391@@ -738,30 +733,25 @@
392 file_entry->parent->loaded = 1;
393 parent_hash = file_entry->parent->reverse_map;
394 files = file_entry->parent->files;
395- if (g_sequence_get_length (files) == 1) {
396+ if (g_sequence_get_length (files) == 1) { /* maybe the dummy row */
397 GSequenceIter *dummy_ptr = g_sequence_get_iter_at_pos (files, 0);
398 FileEntry *dummy_entry = g_sequence_get (dummy_ptr);
399- if (dummy_entry->file == NULL) {
400- /* replace the dummy loading entry */
401- //model->details->stamp++;
402+ if (dummy_entry->file == NULL) { /* it is the dummy row - replace it */
403 g_sequence_remove (dummy_ptr);
404-
405- replace_dummy = TRUE;
406+ replaced_dummy = TRUE;
407 }
408 }
409 }
410- //#endif
411 file_entry->ptr = g_sequence_insert_sorted (files, file_entry,
412 fm_list_model_file_entry_compare_func, model);
413
414 g_hash_table_insert (parent_hash, file, file_entry->ptr);
415
416- //#if 0
417 iter.stamp = model->details->stamp;
418 iter.user_data = file_entry->ptr;
419
420 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
421- if (replace_dummy) {
422+ if (replaced_dummy) {
423 gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
424 } else {
425 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
426@@ -769,18 +759,11 @@
427
428 if (gof_file_is_folder (file)) {
429 file_entry->files = g_sequence_new ((GDestroyNotify)file_entry_free);
430-
431 add_dummy_row (model, file_entry);
432-
433 gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
434 path, &iter);
435 }
436 gtk_tree_path_free (path);
437-
438- //file_entry_free (file_entry);
439- //g_object_unref(file);
440- //#endif
441-
442 return TRUE;
443 }
444
445@@ -916,11 +899,7 @@
446 */
447 add_dummy_row (model, parent_file_entry);
448 }
449- /* FIXME we don't need to unref file here - clean up this part */
450- /*if (file_entry->file != NULL) {
451- //log_printf (LOG_LEVEL_UNDEFINED, "remove file %s\n", file_entry->file->name);
452- g_object_unref (file_entry->file);
453- }*/
454+ /* We don't need to unref file here - we did not add a ref */
455
456 if (file_entry->subdirectory != NULL) {
457 g_signal_emit (model,
458@@ -937,15 +916,6 @@
459 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
460
461 gtk_tree_path_free (path);
462-
463- if (parent_file_entry && g_sequence_get_length (parent_file_entry->files) == 0) {
464- parent_iter.stamp = model->details->stamp;
465- parent_iter.user_data = parent_file_entry->ptr;
466- path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &parent_iter);
467- gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
468- path, &parent_iter);
469- gtk_tree_path_free (path);
470- }
471 }
472
473 void
474@@ -1014,19 +984,21 @@
475 return (file);
476 }
477
478-void fm_list_model_get_directory_file (FMListModel *model, GtkTreePath *path, GOFDirectoryAsync **directory, GOFFile **file)
479+gboolean
480+fm_list_model_get_directory_file (FMListModel *model, GtkTreePath *path, GOFDirectoryAsync **directory, GOFFile **file)
481 {
482 GtkTreeIter iter;
483 FileEntry *file_entry;
484
485 *directory = NULL;
486 *file = NULL;
487- if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) {
488- return;
489+ if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) {;
490+ return FALSE;
491 }
492 file_entry = g_sequence_get (iter.user_data);
493 *directory = file_entry->subdirectory;
494 *file = file_entry->file;
495+ return TRUE;
496 }
497
498 gboolean
499@@ -1047,24 +1019,11 @@
500
501 file_entry->subdirectory = gof_directory_async_from_file (file_entry->file);
502
503- /* FIXME not sure the hash lookup is really needed gof_driectory_async_get_for_file is a always a new object */
504- if (g_hash_table_lookup (model->details->directory_reverse_map,
505- file_entry->subdirectory) != NULL) {
506- g_object_unref (file_entry->subdirectory);
507- g_warning ("Already in directory_reverse_map, failing\n");
508- return FALSE;
509- }
510-
511 g_hash_table_insert (model->details->directory_reverse_map,
512 file_entry->subdirectory, file_entry->ptr);
513 file_entry->reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
514
515- /* Return a ref too */
516- //gof_directory_ref (file_entry->subdirectory);
517- *directory = file_entry->subdirectory;
518-
519- //load_dir_async (file_entry->subdirectory);
520-
521+ *directory = file_entry->subdirectory; /* AbstractDirectoryView will maintain another reference on this */
522 return TRUE;
523 }
524
525@@ -1087,8 +1046,6 @@
526 file_entry->subdirectory);
527 file_entry->loaded = 0;
528
529- g_message ("remove all children\n");
530- //log_printf (LOG_LEVEL_UNDEFINED, "remove all children %d\n", g_sequence_get_length (file_entry->files));
531 /* Remove all children */
532 while (g_sequence_get_length (file_entry->files) > 0) {
533 child_ptr = g_sequence_get_begin_iter (file_entry->files);
534@@ -1107,7 +1064,7 @@
535 list_model_signals[SUBDIRECTORY_UNLOADED], 0,
536 file_entry->subdirectory);
537
538- g_object_unref (file_entry->subdirectory);
539+ g_object_unref (file_entry->subdirectory); /* AbstractDirectoryView will also release its reference */
540 file_entry->subdirectory = NULL;
541 g_assert (g_hash_table_size (file_entry->reverse_map) == 0);
542 g_hash_table_destroy (file_entry->reverse_map);
543@@ -1146,18 +1103,8 @@
544 fm_list_model_dispose (GObject *object)
545 {
546 FMListModel *model;
547- //int i;
548-
549 model = FM_LIST_MODEL (object);
550
551- /*if (model->details->columns) {
552- for (i = 0; i < model->details->columns->len; i++) {
553- g_object_unref (model->details->columns->pdata[i]);
554- }
555- g_ptr_array_free (model->details->columns, TRUE);
556- model->details->columns = NULL;
557- }*/
558-
559 if (model->details->files) {
560 g_sequence_free (model->details->files);
561 model->details->files = NULL;
562@@ -1196,7 +1143,6 @@
563 model->details->stamp = g_random_int ();
564 model->details->sort_id = FM_LIST_MODEL_FILENAME;
565 model->details->order = GTK_SORT_ASCENDING;
566- //model->details->columns = g_ptr_array_new ();
567 }
568
569 static void
570@@ -1222,6 +1168,13 @@
571 FALSE,
572 G_PARAM_READWRITE));
573
574+ g_object_class_install_property (object_class,
575+ PROP_SIZE,
576+ g_param_spec_int ("size", "size", "icon size",
577+ 16, 128,
578+ 32,
579+ G_PARAM_READWRITE));
580+
581
582 list_model_signals[SUBDIRECTORY_UNLOADED] =
583 g_signal_new ("subdirectory_unloaded",
584@@ -1272,11 +1225,14 @@
585 g_return_if_fail (FM_IS_LIST_MODEL (model));
586
587 model->details->has_child = has_child;
588- /*if (model->details->has_child != has_child)
589- {
590- model->details->has_child = has_child;
591- //g_object_notify (G_OBJECT (tree_view), "single-click");
592- }*/
593+}
594+
595+static void
596+fm_list_model_set_icon_size (FMListModel *model, gint size)
597+{
598+ g_return_if_fail (FM_IS_LIST_MODEL (model));
599+
600+ model->details->icon_size = size;
601 }
602
603 static void
604@@ -1293,6 +1249,10 @@
605 g_value_set_boolean (value, model->details->has_child);
606 break;
607
608+ case PROP_SIZE:
609+ g_value_set_int (value, model->details->icon_size);
610+ break;
611+
612 default:
613 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
614 break;
615@@ -1313,57 +1273,16 @@
616 fm_list_model_set_has_child (model, g_value_get_boolean (value));
617 break;
618
619+ case PROP_SIZE:
620+ fm_list_model_set_icon_size (model, g_value_get_int (value));
621+ break;
622+
623 default:
624 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
625 break;
626 }
627 }
628
629-
630-#if 0
631-void
632-fm_list_model_subdirectory_done_loading (FMListModel *model, NautilusDirectory *directory)
633-{
634- GtkTreeIter iter;
635- GtkTreePath *path;
636- FileEntry *file_entry, *dummy_entry;
637- GSequenceIter *parent_ptr, *dummy_ptr;
638- GSequence *files;
639-
640- if (model == NULL || model->details->directory_reverse_map == NULL) {
641- return;
642- }
643- parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map,
644- directory);
645- if (parent_ptr == NULL) {
646- return;
647- }
648-
649- file_entry = g_sequence_get (parent_ptr);
650- files = file_entry->files;
651-
652- /* Only swap loading -> empty if we saw no files yet at "done",
653- * otherwise, toggle loading at first added file to the model.
654- */
655- if (!nautilus_directory_is_not_empty (directory) &&
656- g_sequence_get_length (files) == 1) {
657- dummy_ptr = g_sequence_get_iter_at_pos (file_entry->files, 0);
658- dummy_entry = g_sequence_get (dummy_ptr);
659- if (dummy_entry->file == NULL) {
660- /* was the dummy file */
661- file_entry->loaded = 1;
662-
663- iter.stamp = model->details->stamp;
664- iter.user_data = dummy_ptr;
665-
666- path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
667- gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
668- gtk_tree_path_free (path);
669- }
670- }
671-}
672-#endif
673-
674 const gchar *
675 fm_list_model_get_string_from_column_id (gint id)
676 {
677@@ -1376,6 +1295,8 @@
678 return "type";
679 case FM_LIST_MODEL_MODIFIED:
680 return "modified";
681+ case FM_LIST_MODEL_PIXBUF:
682+ return "pixbuf";
683 }
684
685 g_return_val_if_reached ("name");
686@@ -1390,7 +1311,7 @@
687 { "name", FM_LIST_MODEL_FILENAME },
688 { "size", FM_LIST_MODEL_SIZE },
689 { "type", FM_LIST_MODEL_TYPE },
690- { "modified", FM_LIST_MODEL_MODIFIED },
691+ { "modified", FM_LIST_MODEL_MODIFIED},
692 };
693
694 gint
695
696=== modified file 'libcore/fm-list-model.h'
697--- libcore/fm-list-model.h 2014-08-05 22:40:12 +0000
698+++ libcore/fm-list-model.h 2014-11-22 15:05:49 +0000
699@@ -28,6 +28,7 @@
700 #include <gtk/gtk.h>
701 #include <gdk/gdk.h>
702 #include "gof-file.h"
703+#include "../src/marlin-enum-types.h"
704 #include "pantheon-files-core.h"
705
706 #ifndef FM_LIST_MODEL_H
707@@ -48,6 +49,7 @@
708 enum {
709 FM_LIST_MODEL_FILE_COLUMN,
710 FM_LIST_MODEL_COLOR,
711+ FM_LIST_MODEL_PIXBUF,
712 FM_LIST_MODEL_FILENAME,
713 FM_LIST_MODEL_SIZE,
714 FM_LIST_MODEL_TYPE,
715@@ -88,19 +90,11 @@
716
717 GOFFile * fm_list_model_file_for_path (FMListModel *model, GtkTreePath *path);
718 GOFFile * fm_list_model_file_for_iter (FMListModel *model, GtkTreeIter *iter);
719-void fm_list_model_get_directory_file (FMListModel *model, GtkTreePath *path,
720+gboolean fm_list_model_get_directory_file (FMListModel *model, GtkTreePath *path,
721 GOFDirectoryAsync **directory, GOFFile **file);
722 gboolean fm_list_model_load_subdirectory (FMListModel *model, GtkTreePath *path, GOFDirectoryAsync **directory);
723 void fm_list_model_unload_subdirectory (FMListModel *model, GtkTreeIter *iter);
724
725-/*int fm_list_model_add_column (FMListModel *model,
726- NautilusColumn *column);*/
727-/*int fm_list_model_get_column_number (FMListModel *model,
728- const char *column_name);*/
729-
730-/*void fm_list_model_subdirectory_done_loading (FMListModel *model,
731- NautilusDirectory *directory);*/
732-
733 const gchar *fm_list_model_get_string_from_column_id (gint id);
734 gint fm_list_model_get_column_id_from_string (const gchar *colstr);
735
736
737=== removed file 'libcore/gof-abstract-slot.c'
738--- libcore/gof-abstract-slot.c 2011-09-08 02:01:24 +0000
739+++ libcore/gof-abstract-slot.c 1970-01-01 00:00:00 +0000
740@@ -1,47 +0,0 @@
741-/*
742- * Copyright (C) 2011, Lucas Baudin <xapantu@gmail.com>
743- *
744- * Marlin is free software; you can redistribute it and/or
745- * modify it under the terms of the GNU General Public License as
746- * published by the Free Software Foundation; either version 2 of the
747- * License, or (at your option) any later version.
748- *
749- * Marlin is distributed in the hope that it will be useful,
750- * but WITHOUT ANY WARRANTY; without even the implied warranty of
751- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
752- * General Public License for more details.
753- *
754- * You should have received a copy of the GNU General Public License
755- * along with this program; if not, write to the Free Software
756- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
757- *
758- */
759-
760-/*
761- * The AbstractSlot may be used by the plugins. They are needed because GOFWindowSlot
762- * are in src (ok, they shouldn't, but it would require much work to move them, FIXME).
763- **/
764-
765-#include "gof-abstract-slot.h"
766-
767-G_DEFINE_ABSTRACT_TYPE (GOFAbstractSlot, gof_abstract_slot, G_TYPE_OBJECT)
768-
769-
770-/**
771- * Add a widget in the top part of the slot.
772- **/
773-void gof_abstract_slot_add_extra_widget (GOFAbstractSlot* slot, GtkWidget* widget)
774-{
775- gtk_box_pack_start(GTK_BOX (slot->extra_location_widgets), widget, FALSE, FALSE, 0);
776- gtk_widget_show_all(slot->extra_location_widgets);
777-}
778-
779-static void
780-gof_abstract_slot_class_init (GOFAbstractSlotClass *klass)
781-{
782-}
783-
784-static void
785-gof_abstract_slot_init (GOFAbstractSlot *self)
786-{
787-}
788
789=== removed file 'libcore/gof-abstract-slot.h'
790--- libcore/gof-abstract-slot.h 2011-09-08 02:01:24 +0000
791+++ libcore/gof-abstract-slot.h 1970-01-01 00:00:00 +0000
792@@ -1,56 +0,0 @@
793-/*
794- * Copyright (C) 2011, Lucas Baudin <xapantu@gmail.com>
795- *
796- * Marlin is free software; you can redistribute it and/or
797- * modify it under the terms of the GNU General Public License as
798- * published by the Free Software Foundation; either version 2 of the
799- * License, or (at your option) any later version.
800- *
801- * Marlin is distributed in the hope that it will be useful,
802- * but WITHOUT ANY WARRANTY; without even the implied warranty of
803- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
804- * General Public License for more details.
805- *
806- * You should have received a copy of the GNU General Public License
807- * along with this program; if not, write to the Free Software
808- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
809- *
810- */
811-
812-#ifndef _GOF_ABSTRACT_SLOT_H
813-#define _GOF_ABSTRACT_SLOT_H
814-
815-#include <gtk/gtk.h>
816-
817-G_BEGIN_DECLS
818-
819-#define GOF_TYPE_ABSTRACT_SLOT gof_abstract_slot_get_type()
820-#define GOF_ABSTRACT_SLOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOF_TYPE_ABSTRACT_SLOT, GOFAbstractSlot))
821-#define GOF_ABSTRACT_SLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOF_TYPE_ABSTRACT_SLOT, GOFAbstractSlotClass))
822-#define GOF_IS_ABSTRACT_SLOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOF_TYPE_ABSTRACT_SLOT))
823-#define GOF_IS_ABSTRACT_SLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOF_TYPE_ABSTRACT_SLOT))
824-#define GOF_ABSTRACT_SLOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GOF_TYPE_ABSTRACT_SLOT, GOFAbstractSlotClass))
825-
826-typedef struct _GOFAbstractSlot GOFAbstractSlot;
827-typedef struct _GOFAbstractSlotClass GOFAbstractSlotClass;
828-
829-struct _GOFAbstractSlot
830-{
831- GObject parent;
832-
833- GtkWidget* extra_location_widgets;
834-};
835-
836-struct _GOFAbstractSlotClass
837-{
838- GObjectClass parent_class;
839-};
840-
841-GType gof_abstract_slot_get_type (void) G_GNUC_CONST;
842-
843-GOFAbstractSlot *gof_abstract_slot_new (void);
844-void gof_abstract_slot_add_extra_widget (GOFAbstractSlot* slot, GtkWidget* widget);
845-
846-G_END_DECLS
847-
848-#endif /* _GOF_ABSTRACT_SLOT_H */
849
850=== modified file 'libcore/gof-directory-async.vala'
851--- libcore/gof-directory-async.vala 2014-10-29 15:02:53 +0000
852+++ libcore/gof-directory-async.vala 2014-11-22 15:05:49 +0000
853@@ -23,6 +23,7 @@
854 public class GOF.Directory.Async : Object {
855 public GLib.File location;
856 public GOF.File file;
857+ public int icon_size = 32;
858
859 /* we're looking for particular path keywords like *\/icons* .icons ... */
860 public bool uri_contain_keypath_icons;
861@@ -84,10 +85,9 @@
862
863 this.add_toggle_ref ((ToggleNotify) toggle_ref_notify);
864 this.unref ();
865- debug ("dir %s ref_count %u", this.file.uri, this.ref_count);
866
867+ debug ("created dir %s ref_count %u", this.file.uri, this.ref_count);
868 file_hash = new HashTable<GLib.File,GOF.File> (GLib.File.hash, GLib.File.equal);
869-
870 uri_contain_keypath_icons = "/icons" in file.uri || "/.icons" in file.uri;
871 }
872
873@@ -101,6 +101,7 @@
874 dir.remove_dir_from_cache ();
875
876 dir.remove_toggle_ref ((ToggleNotify) toggle_ref_notify);
877+ } else {
878 }
879 }
880
881@@ -114,7 +115,7 @@
882 }
883 }
884
885- private void clear_directory_info () {
886+ public void clear_directory_info () {
887 if (idle_consume_changes_id != 0) {
888 Source.remove ((uint) idle_consume_changes_id);
889 idle_consume_changes_id = 0;
890@@ -156,11 +157,13 @@
891 bool show_hidden = Preferences.get_default ().pref_show_hidden_files;
892
893 foreach (GOF.File gof in file_hash.get_values ()) {
894- if (gof.info != null && (!gof.is_hidden || show_hidden)) {
895- if (track_longest_name)
896- update_longest_file_name (gof);
897+ if (gof != null) {
898+ if (gof.info != null && (!gof.is_hidden || show_hidden)) {
899+ if (track_longest_name)
900+ update_longest_file_name (gof);
901
902- file_loaded (gof);
903+ file_loaded (gof);
904+ }
905 }
906 }
907
908@@ -187,8 +190,8 @@
909 }
910 }
911 }
912-
913- done_loading ();
914+ if (!cancellable.is_cancelled ())
915+ done_loading ();
916 }
917
918 public void update_desktop_files () {
919@@ -275,17 +278,21 @@
920 file.is_mounted = false;
921 }
922
923- //TODO send err code
924- done_loading ();
925+ if (!cancellable.is_cancelled ())
926+ done_loading ();
927 }
928
929- public GOF.File? file_hash_lookup_location (GLib.File location) {
930- GOF.File? result = file_hash.lookup (location);
931- return result;
932+ public GOF.File? file_hash_lookup_location (GLib.File? location) {
933+ if (location != null && location is GLib.File) {
934+ GOF.File? result = file_hash.lookup (location);
935+ return result;
936+ } else {
937+ return null;
938+ }
939 }
940
941 public void file_hash_add_file (GOF.File gof) {
942- file_hash.insert (gof.location, gof);
943+ file_hash.insert (gof.location, gof);
944 }
945
946 /* TODO move this to GOF.File */
947@@ -326,8 +333,9 @@
948
949 gof.update ();
950
951- if (gof.info != null && (!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files))
952+ if (gof.info != null && (!gof.is_hidden || Preferences.get_default ().pref_show_hidden_files)) {
953 file_added (gof);
954+ }
955
956 if (!gof.is_hidden && gof.is_folder ()) {
957 /* add to sorted_dirs */
958@@ -523,7 +531,7 @@
959 dir.file_hash.remove (gof.location);
960 }
961
962- public static Async? cache_lookup (GLib.File *file) {
963+ public static Async? cache_lookup (GLib.File? file) {
964 Async? cached_dir = null;
965
966 if (directory_cache == null) {
967@@ -532,6 +540,9 @@
968 return null;
969 }
970
971+ if (file == null)
972+ return null;
973+
974 dir_cache_lock.@lock ();
975 cached_dir = directory_cache.lookup (file);
976
977@@ -575,6 +586,14 @@
978 return file.directory;
979 }
980
981+ public bool is_loading () {
982+ return this.state == State.LOADING;
983+ }
984+
985+ public bool is_loaded () {
986+ return this.state == State.LOADED;
987+ }
988+
989 public bool is_empty () {
990 uint file_hash_count = 0;
991
992@@ -605,7 +624,6 @@
993 }
994
995 /* Thumbnail loading */
996- public int icon_size;
997 private uint timeout_thumbsq = 0;
998 private bool thumbs_stop;
999 private bool thumbs_thread_running;
1000@@ -619,7 +637,6 @@
1001 this.unref ();
1002 return null;
1003 }
1004-
1005 thumbs_thread_running = true;
1006 thumbs_stop = false;
1007
1008@@ -666,16 +683,21 @@
1009 }
1010
1011 public void queue_load_thumbnails (int size) {
1012+ icon_size = size;
1013+ if (this.state == State.LOADING)
1014+ return;
1015+
1016 /* Do not interrupt loading thumbs at same size for this folder */
1017- if (icon_size == size && thumbs_thread_running)
1018+ if ((icon_size == size) && thumbs_thread_running)
1019 return;
1020
1021 icon_size = size;
1022 thumbs_stop = true;
1023
1024 /* Wait for thumbnail thread to stop then start a new thread */
1025- if (timeout_thumbsq == 0) {
1026- timeout_thumbsq = Timeout.add (40, queue_thumbs_timeout_cb);
1027- }
1028+ if (timeout_thumbsq != 0)
1029+ GLib.Source.remove (timeout_thumbsq);
1030+
1031+ timeout_thumbsq = Timeout.add (40, queue_thumbs_timeout_cb);
1032 }
1033 }
1034
1035=== modified file 'libcore/gof-file.c'
1036--- libcore/gof-file.c 2014-10-16 16:26:45 +0000
1037+++ libcore/gof-file.c 2014-11-22 15:05:49 +0000
1038@@ -111,6 +111,7 @@
1039 file->directory = g_object_ref (dir);
1040 else
1041 file->directory = NULL;
1042+
1043 file->basename = g_file_get_basename (file->location);
1044 //file->parent_dir = g_file_enumerator_get_container (enumerator);
1045
1046@@ -137,7 +138,7 @@
1047 void
1048 gof_file_icon_changed (GOFFile *file)
1049 {
1050- GOFDirectoryAsync *dir;
1051+ GOFDirectoryAsync *dir = NULL;
1052
1053 /* get the DirectoryAsync associated to the file */
1054 dir = gof_directory_async_cache_lookup (file->directory);
1055@@ -320,9 +321,10 @@
1056
1057 gof_file_update_formated_type (file);
1058 /* update icon */
1059- //g_message ("%s build new icon", G_STRFUNC);
1060 file->icon = g_content_type_get_icon (ftype);
1061- gof_file_update_icon_internal (file, file->pix_size);
1062+ if (file->pix_size > 1)
1063+ gof_file_update_icon_internal (file, file->pix_size);
1064+
1065 gof_file_icon_changed (file);
1066 }
1067
1068@@ -358,7 +360,6 @@
1069
1070 if (file->file_type == G_FILE_TYPE_SHORTCUT || file->file_type == G_FILE_TYPE_MOUNTABLE) {
1071 const char *target_uri = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
1072- /*g_message ("%s target uri: %s", G_STRFUNC, target_uri);*/
1073 if (target_uri != NULL) {
1074 file->target_location = g_file_new_for_uri (target_uri);
1075 gof_file_target_location_update (file);
1076@@ -471,7 +472,6 @@
1077 file->icon = g_content_type_get_icon (ftype);
1078 }
1079
1080-
1081 file->utf8_collation_key = g_utf8_collate_key_for_filename (gof_file_get_display_name (file), -1);
1082 /* mark the thumb flags as state none, we'll load the thumbs once the directory
1083 * would be loaded on a thread */
1084@@ -504,7 +504,6 @@
1085 if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT)) {
1086 file->can_unmount = g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT);
1087 }
1088-
1089 gof_file_update_trash_info (file);
1090 gof_file_update_emblem (file);
1091 }
1092@@ -512,6 +511,8 @@
1093 static MarlinIconInfo *
1094 gof_file_get_special_icon (GOFFile *file, int size, GOFFileIconFlags flags)
1095 {
1096+ g_return_val_if_fail (size >= 1, NULL);
1097+
1098 if (file->custom_icon_name != NULL) {
1099 if (g_path_is_absolute (file->custom_icon_name))
1100 return marlin_icon_info_lookup_from_path (file->custom_icon_name, size);
1101@@ -540,6 +541,7 @@
1102 GIcon *gicon;
1103
1104 g_return_val_if_fail (file, NULL);
1105+ g_return_val_if_fail (size >= 1, NULL);
1106
1107 icon = gof_file_get_special_icon (file, size, flags);
1108 if (icon != NULL && !marlin_icon_info_is_fallback (icon))
1109@@ -574,6 +576,7 @@
1110 {
1111 GdkPixbuf *pix;
1112 MarlinIconInfo *temp_nicon;
1113+ g_return_val_if_fail (size >= 1, NULL);
1114
1115 pix = marlin_icon_info_get_pixbuf_force_size (nicon, size, force_size);
1116 if (pix == NULL) {
1117@@ -592,7 +595,7 @@
1118 {
1119 MarlinIconInfo *nicon;
1120 GdkPixbuf *pix;
1121-
1122+ g_return_val_if_fail (size >= 1, NULL);
1123 nicon = gof_file_get_icon (file, size, flags);
1124 //nicon = gof_file_get_icon (file, size, 0);
1125
1126@@ -601,8 +604,6 @@
1127 if (nicon)
1128 g_object_unref (nicon);
1129 //pix = gdk_pixbuf_new_from_file_at_size ("/usr/share/icons/hicolor/scalable/apps/marlin.svg", size, size, NULL);
1130- /*if (pix && nicon)
1131- g_message ("%s ref count %u %u", G_STRFUNC, G_OBJECT (nicon)->ref_count, G_OBJECT (pix)->ref_count);*/
1132
1133 return pix;
1134 }
1135@@ -610,8 +611,7 @@
1136 static void
1137 gof_file_update_icon_internal (GOFFile *file, gint size)
1138 {
1139- /*g_message ("%s %s %d", G_STRFUNC, file->uri, file->flags);*/
1140-
1141+ g_return_if_fail (size >= 1);
1142 /* destroy pixbuff if already present */
1143 _g_object_unref0 (file->pix);
1144 //g_clear_object (&file->pix);
1145@@ -620,17 +620,17 @@
1146 file->pix_size = size;
1147 }
1148
1149-/* This function is used by the icon renderer and only by it.
1150+/* This function is used by the icon renderer and fm-list-model.
1151 * Store the pixbuf and update it only for size change.
1152 */
1153 void gof_file_update_icon (GOFFile *file, gint size)
1154 {
1155- if (size <=0)
1156+ if (size <= 1)
1157 return;
1158+
1159 if (!(file->pix == NULL || file->pix_size != size))
1160 return;
1161
1162- //g_message ("%s %s %d %d", G_STRFUNC, file->uri, file->flags, size);
1163 gof_file_update_icon_internal (file, size);
1164 }
1165
1166@@ -650,8 +650,10 @@
1167 g_list_free (file->emblems_list);
1168 file->emblems_list = NULL;
1169 }
1170+
1171 if(plugins != NULL)
1172 marlin_plugin_manager_update_file_info (plugins, file);
1173+
1174 if(gof_file_is_symlink(file) || (file->is_desktop && file->target_gof))
1175 {
1176 gof_file_add_emblem(file, "emblem-symbolic-link");
1177@@ -668,7 +670,6 @@
1178 else
1179 gof_file_add_emblem (file, "emblem-unreadable");
1180 }
1181-
1182 /* TODO update signal on real change */
1183 //g_warning ("update emblem %s", file.uri);
1184 if (file->emblems_list != NULL)
1185@@ -750,7 +751,6 @@
1186 }
1187 print_error (err);
1188 }
1189-
1190 return info;
1191 }
1192
1193@@ -784,10 +784,15 @@
1194 gchar *base_name;
1195 gchar *md5_hash;
1196
1197+ /* Silently ignore invalid requests */
1198+ if (file->pix_size <= 1)
1199+ return;
1200+
1201 if (gof_file_get_thumbnail_path (file) == NULL) {
1202 /* get the thumbnail path from md5 filename */
1203 md5_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, file->uri, -1);
1204 base_name = g_strdup_printf ("%s.png", md5_hash);
1205+ /* TODO Use $XDG_CACHE_HOME specified thumbnail directory instead of hard coding - when Tumbler does*/
1206 file->thumbnail_path = g_build_filename (g_get_home_dir (), ".thumbnails",
1207 "normal", base_name, NULL);
1208 g_free (base_name);
1209@@ -857,6 +862,8 @@
1210
1211 file->sort_column_id = FM_LIST_MODEL_FILENAME;
1212 file->sort_order = GTK_SORT_ASCENDING;
1213+
1214+ file->is_expanded = FALSE;
1215 }
1216
1217 static void gof_file_finalize (GObject* obj) {
1218@@ -1332,11 +1339,14 @@
1219
1220 if (file->target_gof)
1221 return gof_file_is_executable (file->target_gof);
1222- if (file->info == NULL)
1223+ if (file->info == NULL) {
1224 return FALSE;
1225+ }
1226
1227- if (gof_file_is_desktop_file (file))
1228+ if (gof_file_is_desktop_file (file)) {
1229 return TRUE;
1230+ }
1231+
1232 if (g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
1233 {
1234 /* get the content type of the file */
1235@@ -1353,7 +1363,8 @@
1236 * g_content_type_can_be_executable() for unix because it also returns
1237 * true for "text/plain" and we don't want that */
1238 if (g_content_type_is_a (content_type, "application/x-executable")
1239- || g_content_type_is_a (content_type, "application/x-shellscript"))
1240+ || g_content_type_is_a (content_type, "application/x-shellscript")
1241+ || g_content_type_is_a (content_type, "application/octet-stream"))
1242 {
1243 can_execute = TRUE;
1244 }
1245@@ -1411,6 +1422,12 @@
1246 return _g_object_ref0 (cached_file);
1247 }
1248
1249+void
1250+gof_file_set_expanded (GOFFile *file, gboolean expanded) {
1251+ g_return_if_fail (file != NULL || !file->is_directory);
1252+ file->is_expanded = expanded;
1253+}
1254+
1255 GOFFile*
1256 gof_file_get (GFile *location)
1257 {
1258@@ -1418,6 +1435,8 @@
1259 GOFFile *file = NULL;
1260 GOFDirectoryAsync *dir = NULL;
1261
1262+g_return_val_if_fail (location != NULL && G_IS_FILE (location), NULL);
1263+
1264 if ((parent = g_file_get_parent (location)) != NULL) {
1265 dir = gof_directory_async_cache_lookup (parent);
1266 if (dir != NULL) {
1267@@ -1639,7 +1658,7 @@
1268 //printf ("%s actions MOVE %d COPY %d suggested %d\n", G_STRFUNC, GDK_ACTION_MOVE, GDK_ACTION_COPY, suggested_action);
1269 }
1270 }
1271- else if (gof_file_is_executable (file))
1272+ else if (!gof_file_is_folder (file) && gof_file_is_executable (file))
1273 {
1274 /* determine the possible actions */
1275 actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
1276@@ -1689,7 +1708,6 @@
1277 }
1278
1279 if (error != NULL) {
1280- g_message ("Could not start application on terminal: %s", error->message);
1281 g_error_free (error);
1282 }
1283
1284@@ -1881,7 +1899,7 @@
1285 }
1286
1287 gboolean
1288-gof_files_launch_with (GList *files, GdkScreen *screen, GAppInfo* app_info)
1289+gof_file_launch_files (GList *files, GdkScreen *screen, GAppInfo* app_info)
1290 {
1291 GdkAppLaunchContext *context;
1292 gboolean succeed;
1293@@ -2087,11 +2105,14 @@
1294 //marlin_file_changes_queue_file_added (new_file);
1295 if (error == NULL)
1296 gof_file_update_existing (op->file, new_file);
1297- /*else
1298- marlin_dialogs_show_error (NULL, error, "Failed to rename %s", op->file->name);*/
1299+ else
1300+ marlin_dialogs_show_error (NULL,
1301+ error,
1302+ "Failed to rename %s",
1303+ g_file_get_parse_name (op->file->location));
1304
1305 //g_warning ("%s %u", G_STRFUNC, G_OBJECT (op->file)->ref_count);
1306- gof_file_operation_complete (op, NULL, error);
1307+ gof_file_operation_complete (op, new_file, error);
1308 if (new_file != NULL) {
1309 g_object_unref (new_file);
1310 } else {
1311@@ -2111,7 +2132,6 @@
1312 //char *new_file_name;
1313 //gboolean success, name_changed;
1314 GError *error;
1315-
1316 //g_warning ("%s %u", G_STRFUNC, G_OBJECT (file)->ref_count);
1317 g_return_if_fail (GOF_IS_FILE (file));
1318 g_return_if_fail (new_name != NULL);
1319
1320=== modified file 'libcore/gof-file.h'
1321--- libcore/gof-file.h 2013-08-10 20:20:23 +0000
1322+++ libcore/gof-file.h 2014-11-22 15:05:49 +0000
1323@@ -84,6 +84,7 @@
1324 gboolean is_directory;
1325 gboolean is_hidden;
1326 gboolean is_desktop;
1327+ gboolean is_expanded;
1328 GIcon *icon;
1329 gchar *custom_icon_name;
1330 GdkPixbuf *pix;
1331@@ -200,6 +201,7 @@
1332 gchar *gof_file_get_formated_time (GOFFile *file, const char *attr);
1333 gboolean gof_file_is_symlink (GOFFile *file);
1334 gboolean gof_file_is_desktop_file (GOFFile *file);
1335+void gof_file_set_expanded (GOFFile *file, gboolean expanded);
1336 gchar *gof_file_list_to_string (GList *list, gsize *len);
1337
1338 gboolean gof_file_same_filesystem (GOFFile *file_a, GOFFile *file_b);
1339@@ -208,10 +210,9 @@
1340 GdkDragContext *context,
1341 GdkDragAction *suggested_action_return);
1342 void gof_file_open_single (GOFFile *file, GdkScreen *screen, GAppInfo *app_info);
1343-//gboolean gof_file_launch_with (GOFFile *file, GdkScreen *screen, GAppInfo* app_info);
1344-gboolean gof_files_launch_with (GList *files, GdkScreen *screen, GAppInfo* app_info);
1345+gboolean gof_file_launch_files (GList *files, GdkScreen *screen, GAppInfo* app_info);
1346+gboolean gof_file_launch (GOFFile *file, GdkScreen *screen, GAppInfo *app_info);
1347 gboolean gof_file_execute (GOFFile *file, GdkScreen *screen, GList *file_list, GError **error);
1348-gboolean gof_file_launch (GOFFile *file, GdkScreen *screen, GAppInfo *app_info);
1349 GAppInfo *gof_file_get_default_handler (GOFFile *file);
1350
1351 void gof_file_icon_changed (GOFFile *file);
1352
1353=== modified file 'libcore/marlin-file-changes-queue.c'
1354--- libcore/marlin-file-changes-queue.c 2014-08-05 22:40:12 +0000
1355+++ libcore/marlin-file-changes-queue.c 2014-11-22 15:05:49 +0000
1356@@ -86,7 +86,6 @@
1357 {
1358 MarlinFileChange *new_item;
1359 MarlinFileChangesQueue *queue;
1360-
1361 queue = marlin_file_changes_queue_get();
1362
1363 new_item = g_new0 (MarlinFileChange, 1);
1364@@ -146,7 +145,6 @@
1365 MarlinFileChange *result;
1366
1367 g_assert (queue != NULL);
1368-
1369 /* dequeue the tail item while locking down the list */
1370 g_mutex_lock (&queue->mutex);
1371
1372@@ -205,7 +203,6 @@
1373 MarlinFileChangesQueue *queue;
1374 gboolean flush_needed;
1375
1376-
1377 additions = NULL;
1378 changes = NULL;
1379 deletions = NULL;
1380
1381=== modified file 'libcore/marlin-file-operations.c'
1382--- libcore/marlin-file-operations.c 2014-09-06 06:20:16 +0000
1383+++ libcore/marlin-file-operations.c 2014-11-22 15:05:49 +0000
1384@@ -2801,7 +2801,6 @@
1385 int response;
1386
1387 dirs = g_queue_new ();
1388-
1389 retry:
1390 error = NULL;
1391 info = g_file_query_info (file,
1392@@ -3605,7 +3604,6 @@
1393 }
1394 return CREATE_DEST_DIR_FAILED;
1395 }
1396- marlin_file_changes_queue_file_added (*dest);
1397
1398 // Start UNDO-REDO
1399 marlin_undo_manager_data_add_origin_target_pair (job->undo_redo_data, src, *dest);
1400@@ -4363,7 +4361,7 @@
1401 if (copy_job->is_move) {
1402 marlin_file_changes_queue_file_moved (src, dest);
1403 } else {
1404- marlin_file_changes_queue_file_added (dest);
1405+ marlin_file_changes_queue_file_added (dest);
1406 }
1407
1408 /* If copying a trusted desktop file to the desktop,
1409@@ -4573,8 +4571,8 @@
1410 g_error_free (error);
1411 goto out;
1412 }
1413- primary = f (_("Error while copying \"%B\"."), src);
1414- secondary = f (_("There was an error copying the file into %F."), dest_dir);
1415+ primary = f (_("There was an Error while copying \"%s\"."), g_file_get_uri (src));
1416+ secondary = f (_("There was an error copying the file into %s."), g_file_get_uri (dest_dir));
1417 details = error->message;
1418
1419 response = run_warning (job,
1420@@ -5087,7 +5085,7 @@
1421 if (job->skip_all_error) {
1422 goto out;
1423 }
1424- primary = f (_("Error while moving \"%B\"."), src);
1425+ primary = f (_("Error while moving \"%F\"."), src);
1426 secondary = f (_("There was an error moving the file into %F."), dest_dir);
1427 details = error->message;
1428
1429@@ -5352,7 +5350,6 @@
1430 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
1431
1432 inhibit_power_manager ((CommonJob *)job, _("Moving Files"));
1433-
1434 // Start UNDO-REDO
1435 if (!marlin_undo_manager_is_undo_redo (marlin_undo_manager_instance())) {
1436 if (g_file_has_uri_scheme (g_list_first(files)->data, "trash")) {
1437@@ -5476,8 +5473,7 @@
1438 if (debuting_files) {
1439 g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
1440 }
1441-
1442- marlin_file_changes_queue_file_added (dest);
1443+ marlin_file_changes_queue_file_added (dest);
1444 /*if (position) {
1445 //marlin_file_changes_queue_schedule_position_set (dest, *position, common->screen_num);
1446 } else {
1447@@ -6210,7 +6206,7 @@
1448
1449 if (res) {
1450 job->created_file = g_object_ref (dest);
1451- marlin_file_changes_queue_file_added (dest);
1452+ marlin_file_changes_queue_file_added (dest);
1453 /*if (job->has_position) {
1454 //marlin_file_changes_queue_schedule_position_set (dest, job->position, common->screen_num);
1455 } else {
1456
1457=== modified file 'libcore/marlin-icon-info.c'
1458--- libcore/marlin-icon-info.c 2014-07-09 10:25:45 +0000
1459+++ libcore/marlin-icon-info.c 2014-11-22 15:05:49 +0000
1460@@ -378,7 +378,6 @@
1461 GdkPixbuf *pixbuf;
1462 char *str_icon = g_icon_to_string (icon);
1463
1464- g_message ("%s stream %s\n", G_STRFUNC, str_icon);
1465 pixbuf = gdk_pixbuf_new_from_file (str_icon, NULL);
1466
1467 if (pixbuf != NULL) {
1468@@ -404,6 +403,8 @@
1469 //GdkPixbuf *scaled_pixbuf = NULL;
1470
1471 g_return_val_if_fail (icon && G_IS_ICON (icon), NULL);
1472+ size = MAX (1, size);
1473+
1474 if (G_IS_LOADABLE_ICON (icon)) {
1475 LoadableIconKey lookup_key;
1476 LoadableIconKey *key;
1477@@ -456,12 +457,14 @@
1478 char *str_icon = g_icon_to_string (icon);
1479 gint width, height;
1480
1481- if (gdk_pixbuf_get_file_info (str_icon, &width, &height) != NULL)
1482+ gdk_pixbuf_get_file_info (str_icon, &width, &height);
1483+ if ((width >= 1 || width == -1) && (height >= 1 || height == -1))
1484 pixbuf = gdk_pixbuf_new_from_file_at_size (str_icon, MIN (width, size), MIN (height, size), NULL);
1485+
1486 /*icon_info = g_object_new (MARLIN_TYPE_ICON_INFO, NULL);
1487 icon_info->pixbuf = pixbuf;*/
1488- icon_info = marlin_icon_info_new_for_pixbuf (pixbuf);
1489 if (pixbuf != NULL) {
1490+ icon_info = marlin_icon_info_new_for_pixbuf (pixbuf);
1491 key = loadable_icon_key_new (icon, size);
1492 g_hash_table_insert (loadable_icon_cache, key, g_object_ref (icon_info));
1493 g_free (str_icon);
1494@@ -555,7 +558,7 @@
1495 {
1496 GIcon *icon;
1497 MarlinIconInfo *info;
1498-
1499+ g_return_val_if_fail (size >= 1, NULL);
1500 icon = g_themed_icon_new (name);
1501 info = marlin_icon_info_lookup (icon, size);
1502 g_object_unref (icon);
1503@@ -570,6 +573,7 @@
1504 GIcon *icon;
1505 MarlinIconInfo *info;
1506
1507+ g_return_val_if_fail (size >= 1, NULL);
1508 icon_file = g_file_new_for_path (path);
1509 icon = g_file_icon_new (icon_file);
1510 info = marlin_icon_info_lookup (icon, size);
1511@@ -745,4 +749,4 @@
1512 g_object_unref (icon_file);
1513 g_object_unref (icon);
1514 loadable_icon_key_free (lookup_key);
1515-}
1516\ No newline at end of file
1517+}
1518
1519=== modified file 'libcore/pantheon-files-core-C.vapi'
1520--- libcore/pantheon-files-core-C.vapi 2014-10-18 21:27:05 +0000
1521+++ libcore/pantheon-files-core-C.vapi 2014-11-22 15:05:49 +0000
1522@@ -14,20 +14,62 @@
1523 {
1524 public class ListModel : GLib.Object, Gtk.TreeModel, Gtk.TreeDragDest, Gtk.TreeSortable
1525 {
1526+ [CCode (cprefix = "FM_LIST_MODEL_", cheader_filename = "fm-list-model.h")]
1527+ public enum ColumnID {
1528+ FILE_COLUMN,
1529+ COLOR,
1530+ PIXBUF,
1531+ FILENAME,
1532+ SIZE,
1533+ TYPE,
1534+ MODIFIED,
1535+ NUM_COLUMNS
1536+ }
1537+
1538 public bool load_subdirectory(Gtk.TreePath path, out GOF.Directory.Async dir);
1539+ public bool unload_subdirectory(Gtk.TreeIter iter);
1540 public void add_file(GOF.File file, GOF.Directory.Async dir);
1541- public GOF.File file_for_path(Gtk.TreePath path);
1542+ public void remove_file (GOF.File file, GOF.Directory.Async dir);
1543+ public void file_changed (GOF.File file, GOF.Directory.Async dir);
1544+ public unowned GOF.File file_for_path(Gtk.TreePath path);
1545+ public static GLib.Type get_type ();
1546+ public bool get_first_iter_for_file (GOF.File file, out Gtk.TreeIter iter);
1547+ public bool get_tree_iter_from_file (GOF.File file, GOF.Directory.Async directory, out Gtk.TreeIter iter);
1548+ public bool get_directory_file (Gtk.TreePath path, out unowned GOF.Directory.Async directory, out unowned GOF.File file);
1549+ public GOF.File file_for_iter (Gtk.TreeIter iter);
1550+ public void clear ();
1551+ public signal void subdirectory_unloaded (GOF.Directory.Async directory);
1552 }
1553 }
1554
1555-[CCode (cprefix = "MarlinFileOperations", lower_case_cprefix = "marlin_file_operations_", cheader_filename = "marlin-file-operations.h")]
1556-namespace Marlin.FileOperations {
1557- static bool has_trash_files (GLib.Mount mount);
1558- static int prompt_empty_trash (Gtk.Window? parent_window);
1559- static GLib.List<GLib.File> get_trash_dirs_for_mount (GLib.Mount mount);
1560- static void empty_trash_dirs (Gtk.Window? parent_window, owned GLib.List<GLib.File> dirs);
1561- static void empty_trash (Gtk.Widget? widget);
1562- 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);
1563+
1564+namespace Marlin {
1565+ [CCode (cprefix = "MarlinFileOperations", lower_case_cprefix = "marlin_file_operations_", cheader_filename = "marlin-file-operations.h")]
1566+ namespace FileOperations {
1567+ static void new_folder(Gtk.Widget? parent_view, Gdk.Point? target_point, GLib.File file, Marlin.CreateCallback? create_callback = null, void* data_callback = null);
1568+ static void new_folder_with_name(Gtk.Widget? parent_view, Gdk.Point? target_point, GLib.File file, string name, Marlin.CreateCallback? create_callback = null, void* data_callback = null);
1569+ static void new_folder_with_name_recursive(Gtk.Widget? parent_view, Gdk.Point? target_point, GLib.File file, string name, Marlin.CreateCallback? create_callback = null, void* data_callback = null);
1570+ static void mount_volume (Gtk.Window? parent_window, GLib.Volume volume, bool allow_autorun);
1571+ static void mount_volume_full (Gtk.Window? parent_window, GLib.Volume volume, bool allow_autorun, Marlin.MountCallback? mount_callback, GLib.Object? callback_data_object);
1572+ static void unmount_mount_full (Gtk.Window? parent_window, GLib.Mount mount, bool eject, bool check_trash, Marlin.UnmountCallback? unmount_callback, void* callback_data);
1573+ static void trash_or_delete (GLib.List<GLib.File> locations, Gtk.Window window, void* callback, void* callback_data);
1574+ static void @delete (GLib.List<GLib.File> locations, Gtk.Window window, void* callback, void* callback_data);
1575+ static bool has_trash_files (GLib.Mount mount);
1576+ static int prompt_empty_trash (Gtk.Window? parent_window);
1577+ static GLib.List<GLib.File> get_trash_dirs_for_mount (GLib.Mount mount);
1578+ static void empty_trash_dirs (Gtk.Window? parent_window, owned GLib.List<GLib.File> dirs);
1579+ static void empty_trash (Gtk.Widget? widget);
1580+ 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);
1581+ 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);
1582+ 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);
1583+ }
1584+ [CCode (cheader_filename = "marlin-file-operations.h", has_target = false)]
1585+ public delegate void MountCallback (GLib.Volume volume, void* callback_data_object);
1586+ [CCode (cheader_filename = "marlin-file-operations.h", has_target = false)]
1587+ public delegate void UnmountCallback (void* callback_data);
1588+ [CCode (cheader_filename = "marlin-file-operations.h", has_target = false)]
1589+ public delegate void CreateCallback (GLib.File new_file, void* callback_data);
1590+
1591 }
1592
1593 [CCode (cprefix = "EelGtk", lower_case_cprefix = "eel_gtk_window_", cheader_filename = "eel-gtk-extensions.h")]
1594@@ -36,6 +78,11 @@
1595 public void set_initial_geometry_from_string (Gtk.Window win, string geometry, uint w, uint h, bool ignore_position, int left_offset, int top_offset);
1596 }
1597
1598+[CCode (cprefix = "EelGtk", lower_case_cprefix = "eel_gtk_widget_", cheader_filename = "eel-gtk-extensions.h")]
1599+namespace EelGtk.Widget {
1600+ public Gdk.Screen get_screen ();
1601+}
1602+
1603 [CCode (cprefix = "EelGFile", lower_case_cprefix = "eel_g_file_", cheader_filename = "eel-gio-extensions.h")]
1604 namespace EelGFile {
1605 public static GLib.List<GLib.File> list_new_from_string (string filelist);
1606@@ -46,6 +93,7 @@
1607 public void pop_up_context_menu (Gtk.Menu menu, int16 offset_x, int16 offset_y, Gdk.EventButton event);
1608 public void gtk_widget_set_shown (Gtk.Widget widget, bool shown);
1609 public Gtk.MenuItem gtk_menu_append_separator (Gtk.Menu menu);
1610+ public unowned Gdk.Screen gtk_widget_get_screen (Gtk.Widget? widget);
1611 public const int16 DEFAULT_POPUP_MENU_DISPLACEMENT;
1612 }
1613
1614@@ -65,9 +113,16 @@
1615 public string format_size (uint64 size);
1616 }
1617
1618+[CCode (cprefix = "Eel", lower_case_cprefix = "eel_", cheader_filename = "eel-string.h")]
1619+namespace Eel {
1620+ public string? str_double_underscores (string? str);
1621+}
1622+
1623 [CCode (cprefix = "EelPango", lower_case_cprefix = "eel_pango_", cheader_filename = "eel-pango-extensions.h")]
1624 namespace EelPango {
1625 public unowned Pango.AttrList attr_list_small();
1626+ public unowned Pango.AttrList attr_list_small_italic();
1627+ public unowned Pango.AttrList attr_list_italic();
1628 public unowned Pango.AttrList attr_list_big();
1629 }
1630
1631@@ -82,6 +137,8 @@
1632 public Gdk.Pixbuf get_pixbuf_nodefault();
1633 public Gdk.Pixbuf get_pixbuf_at_size(int size);
1634 public static void clear_caches ();
1635+ public static void remove_cache (string path, int size);
1636+ public static void infos_caches ();
1637 }
1638 [CCode (cheader_filename = "marlin-trash-monitor.h")]
1639 public abstract class TrashMonitor : GLib.Object
1640@@ -152,7 +209,7 @@
1641 public void changes_consume_changes (bool consume_all);
1642 }
1643
1644-[CCode (cprefix = "GOF", lower_case_cprefix = "gof_")]
1645+[CCode (cprefix = "GOF", lower_case_cprefix = "gof_", ref_function = "gof_file_ref", unref_function = "gof_file_unref")]
1646 namespace GOF {
1647
1648 [CCode (cheader_filename = "gof-file.h")]
1649@@ -171,9 +228,11 @@
1650 public const string GIO_DEFAULT_ATTRIBUTES;
1651
1652 public File(GLib.File location, GLib.File? dir);
1653- public static GOF.File get(GLib.File location);
1654+ public static GOF.File @get(GLib.File location);
1655 public static GOF.File get_by_uri (string uri);
1656 public static File cache_lookup (GLib.File file);
1657+ public static bool launch_files (GLib.List<GOF.File> files, Gdk.Screen screen, GLib.AppInfo app);
1658+ public static void list_free (GLib.List<GOF.File> files);
1659
1660 public void remove_from_caches ();
1661 public bool is_gone;
1662@@ -192,14 +251,19 @@
1663 public string tagstype;
1664 public Gdk.Pixbuf pix;
1665 public int pix_size;
1666+ public int sort_column_id;
1667+ public Gtk.SortType sort_order;
1668
1669 public GLib.FileType file_type;
1670 public bool is_hidden;
1671 public bool is_directory;
1672 public bool is_desktop;
1673+ public void set_expanded (bool expanded);
1674 public bool is_folder();
1675 public bool is_symlink();
1676 public bool is_trashed();
1677+ public bool is_writable ();
1678+ public bool is_executable ();
1679 public bool link_known_target;
1680 public uint flags;
1681
1682@@ -223,6 +287,7 @@
1683 public bool has_permissions;
1684 public uint32 permissions;
1685
1686+ public void open_single (Gdk.Screen screen, GLib.AppInfo app_info);
1687 public void update ();
1688 public void update_type ();
1689 public void update_icon (int size);
1690@@ -250,18 +315,23 @@
1691 public unowned string get_display_target_uri ();
1692
1693 public GLib.AppInfo get_default_handler ();
1694+
1695+ public static string list_to_string (GLib.List<GOF.File> list, out long len);
1696+
1697+ public bool execute (Gdk.Screen screen, GLib.List<GLib.File>? files, out GLib.Error error);
1698+ public void rename (string new_name, GOF.FileOperationCallback callback);
1699+
1700+ public GOF.File @ref ();
1701+ public GOF.File unref ();
1702 }
1703
1704+ public delegate void FileOperationCallback (GOF.File file, GLib.File? result_location, GLib.Error? error, void* callback_data);
1705+
1706 [CCode (cheader_filename = "gof-file.h")]
1707 public enum FileIconFlags
1708 {
1709 NONE,
1710 USE_THUMBNAILS
1711 }
1712-
1713- [CCode (cheader_filename = "gof-abstract-slot.h")]
1714- public class AbstractSlot : GLib.Object {
1715- public void add_extra_widget(Gtk.Widget widget);
1716- }
1717 }
1718
1719
1720=== modified file 'libwidgets/Animations.vala'
1721--- libwidgets/Animations.vala 2014-05-05 06:19:28 +0000
1722+++ libwidgets/Animations.vala 2014-11-22 15:05:49 +0000
1723@@ -1,6 +1,12 @@
1724 namespace Marlin.Animation {
1725
1726+ private static uint timeout_source_id = 0;
1727 public static void smooth_adjustment_to (Gtk.Adjustment adj, int final) {
1728+ if (timeout_source_id > 0) {
1729+ GLib.Source.remove (timeout_source_id);
1730+ timeout_source_id = 0;
1731+ }
1732+
1733 var initial = adj.value;
1734 var to_do = final - initial;
1735
1736@@ -11,14 +17,17 @@
1737 var newvalue = 0;
1738 var old_adj_value = adj.value;
1739
1740- Timeout.add (1000 / 60, () => {
1741- /* If the user moves it at the same time, just stop the animation */
1742- if(old_adj_value != adj.value)
1743+ timeout_source_id = Timeout.add (1000 / 60, () => {
1744+ /* If the user move it at the same time, just stop the animation */
1745+ if (old_adj_value != adj.value) {
1746+ timeout_source_id = 0;
1747 return false;
1748+ }
1749
1750 if (newvalue >= to_do - 10) {
1751 /* to be sure that there is not a little problem */
1752 adj.value = final;
1753+ timeout_source_id = 0;
1754 return false;
1755 }
1756
1757
1758=== modified file 'libwidgets/BreadcrumbsElements.vala'
1759--- libwidgets/BreadcrumbsElements.vala 2014-05-24 03:48:19 +0000
1760+++ libwidgets/BreadcrumbsElements.vala 2014-11-22 15:05:49 +0000
1761@@ -188,4 +188,4 @@
1762
1763 return x;
1764 }
1765-}
1766\ No newline at end of file
1767+}
1768
1769=== modified file 'libwidgets/LocationBar.vala'
1770--- libwidgets/LocationBar.vala 2014-07-30 02:20:58 +0000
1771+++ libwidgets/LocationBar.vala 2014-11-22 15:05:49 +0000
1772@@ -185,14 +185,13 @@
1773 escape ();
1774 return true;
1775 }
1776-
1777 return base.key_press_event (event);
1778 }
1779
1780 public override bool button_press_event (Gdk.EventButton event) {
1781 if (is_focus)
1782 return base.button_press_event (event);
1783-
1784+
1785 foreach (BreadcrumbsElement element in elements)
1786 element.pressed = false;
1787
1788@@ -242,7 +241,7 @@
1789 var el = get_element_from_coordinates ((int) event.x, (int) event.y);
1790 if (el != null) {
1791 selected = elements.index_of (el);
1792- var newpath = get_path_from_element (el);
1793+ var newpath = sanitise_path (get_path_from_element (el));
1794 path_changed (get_file_for_path (newpath));
1795 } else
1796 grab_focus ();
1797@@ -318,14 +317,18 @@
1798 if (search_mode)
1799 set_entry_text ("");
1800 else
1801- set_entry_text (GLib.Uri.unescape_string (get_elements_path ()
1802- .replace ("file:////", "/")
1803- .replace ("file:///", "/")
1804- .replace ("trash:///", "")
1805- .replace ("network:///", "")));
1806+ set_entry_text (sanitise_path (GLib.Uri.unescape_string (get_elements_path ())));
1807+
1808
1809 return base.focus_in_event (event);
1810 }
1811+
1812+ string sanitise_path (string path) {
1813+ return path.replace ("file:////", "/")
1814+ .replace ("file:///", "/")
1815+ .replace ("trash:///", "")
1816+ .replace ("network:///", "");
1817+ }
1818
1819 void on_grab_focus () {
1820 select_region (0, 0);
1821@@ -398,7 +401,7 @@
1822 icons.append (icon);
1823 }
1824
1825- public void complete () {
1826+ public void complete () {
1827 if (text_completion.length == 0)
1828 return;
1829
1830@@ -428,6 +431,7 @@
1831
1832 public void set_entry_cursor (Gdk.Cursor? cursor) {
1833 /* Only child 13 needs to be modified for the cursor - there may be a better way to do this */
1834+ /* TODO - this doesn't work ? */
1835 get_window ().get_children ().nth_data (13).set_cursor (cursor ?? new Gdk.Cursor (Gdk.CursorType.XTERM));
1836 }
1837
1838@@ -439,7 +443,7 @@
1839 secondary_icon_tooltip_text = tooltip;
1840 }
1841 }
1842-
1843+
1844 public double get_all_breadcrumbs_width (out int breadcrumbs_count) {
1845 double total_width = 0.0;
1846 breadcrumbs_count = 0;
1847@@ -478,7 +482,6 @@
1848 break;
1849 }
1850 }
1851-
1852 return newpath;
1853 }
1854
1855@@ -493,7 +496,6 @@
1856 if (element.display)
1857 strpath += element.text + "/";
1858 }
1859-
1860 return strpath;
1861 }
1862
1863@@ -530,7 +532,7 @@
1864 * @param event a button event to compute the coords of the new menu.
1865 *
1866 **/
1867- private bool select_bread_from_coord (Gdk.EventButton event) {
1868+ private bool select_bread_from_coord (Gdk.EventButton event) {
1869 var el = get_element_from_coordinates ((int) event.x, (int) event.y);
1870
1871 if (el != null) {
1872
1873=== modified file 'plugins/network-places/plugin.vala'
1874--- plugins/network-places/plugin.vala 2014-09-24 07:03:11 +0000
1875+++ plugins/network-places/plugin.vala 2014-11-22 15:05:49 +0000
1876@@ -29,17 +29,22 @@
1877 }
1878
1879 public class Files.Plugins.NetworkPlaces : Marlin.Plugins.Base {
1880+ private NetworkInfobar? infobar = null;
1881+
1882 public override void directory_loaded (void* user_data) {
1883 var file = ((Object[]) user_data)[2] as GOF.File;
1884- return_if_fail (file != null);
1885
1886 if (file.is_network_uri_scheme ()) {
1887- var slot = ((Object[]) user_data)[1] as GOF.AbstractSlot;
1888- return_if_fail (slot != null);
1889-
1890- var infobar = new NetworkInfobar ();
1891- slot.add_extra_widget (infobar);
1892- infobar.show_all ();
1893+ if (infobar == null) {
1894+ var slot = ((Object[]) user_data)[1] as GOF.AbstractSlot;
1895+ return_if_fail (slot != null);
1896+ infobar = new NetworkInfobar ();
1897+ slot.add_extra_widget (infobar);
1898+ infobar.show_all ();
1899+ }
1900+ } else if (infobar != null) {
1901+ infobar.destroy ();
1902+ infobar = null;
1903 }
1904 }
1905
1906
1907=== modified file 'plugins/pantheon-files-ctags/plugin.vala'
1908--- plugins/pantheon-files-ctags/plugin.vala 2014-08-05 20:46:06 +0000
1909+++ plugins/pantheon-files-ctags/plugin.vala 2014-11-22 15:05:49 +0000
1910@@ -17,7 +17,6 @@
1911
1912 [DBus (name = "org.elementary.pantheonfiles.db")]
1913 interface MarlinDaemon : Object {
1914- //public abstract async HashTable<string,Variant> get_uri_infos_from_directory (string directory) throws IOError;
1915 public abstract async Variant get_uri_infos (string raw_uri) throws IOError;
1916 public abstract async bool record_uris (Variant[] entries, string directory) throws IOError;
1917
1918@@ -26,7 +25,6 @@
1919
1920 public class Marlin.Plugins.CTags : Marlin.Plugins.Base {
1921 private MarlinDaemon daemon;
1922- //GOF.Directory.Async directory;
1923 GOF.File directory;
1924 private bool is_user_dir;
1925 private bool ignore_dir;
1926@@ -50,41 +48,6 @@
1927 }
1928 }
1929
1930- /*private void file_info_update (HashTable<string,Variant> rc, GOF.File file)
1931- {
1932- Variant row = rc.lookup (file.uri);
1933- if (row == null)
1934- return;
1935-
1936- VariantIter iter = row.iterator ();
1937- warning ("iter n_children %d", (int) iter.n_children ());
1938- assert (iter.n_children () == 1);
1939- VariantIter row_iter = iter.next_value ().iterator ();
1940- warning ("row_iter n_children %d", (int) row_iter.n_children ());
1941-
1942- if (row_iter.n_children () == 2) {
1943- unowned string type = row_iter.next_value ().get_string ();
1944- int n = int.parse (row_iter.next_value ().get_string ());
1945- file.tagstype = type;
1946- //file.color = Preferences.tags_colors[n];
1947- file.update_type ();
1948- message ("grrrrrr %s %d", type, n);
1949- }
1950- }*/
1951-
1952- /*private async void files_infos_updates ()
1953- {
1954- var rc = yield daemon.get_uri_infos_from_directory (directory.file.uri);
1955-
1956- foreach (var file in directory.unknown_types) {
1957- message ("unknown %s %s", file.name, file.ftype);
1958- file_info_update (rc, file);
1959- }
1960- directory.unknown_types = null;
1961-
1962-
1963- }*/
1964-
1965 /* Arbitrary user dir list */
1966 private const string users_dirs[2] = {
1967 "file:///home",
1968@@ -112,7 +75,7 @@
1969 }
1970
1971 public override void directory_loaded (void* user_data) {
1972- message ("CANCEL");
1973+ debug ("CANCEL");
1974 cancellable.cancel ();
1975
1976
1977@@ -124,15 +87,12 @@
1978 unknowns.clear ();
1979 cancellable.reset ();
1980
1981- //directory = GOF.Directory.Async.from_file(((Object[])user_data)[2] as GOF.File);
1982 directory = ((Object[]) user_data)[2] as GOF.File;
1983- //warning ("CTags Plugin dir %s", directory.file.uri);
1984- warning ("CTags Plugin dir %s", directory.uri);
1985+ debug ("CTags Plugin dir %s", directory.uri);
1986 is_user_dir = f_is_user_dir (directory.uri);
1987 ignore_dir = f_ignore_dir (directory.uri);
1988 }
1989
1990- //private Variant add_entry (string uri, string content_type, int modified_time)
1991 private Variant add_entry (GOF.File gof) {
1992 char* ptr_arr[4];
1993 ptr_arr[0] = gof.uri;
1994@@ -147,12 +107,11 @@
1995 Variant[] entries = null;
1996 GOF.File gof;
1997 while ((gof = knowns.pop_head ()) != null) {
1998- //warning ("--- known %s", gof.name);
1999 entries += add_entry (gof);
2000 }
2001
2002 if (entries != null) {
2003- warning ("--- known entries %d", entries.length);
2004+ debug ("--- known entries %d", entries.length);
2005 try {
2006 yield daemon.record_uris (entries, directory.uri);
2007 } catch (Error err) {
2008@@ -165,7 +124,7 @@
2009 GOF.File gof = null;
2010
2011 var count = unknowns.get_length ();
2012- message ("unknows queue nb: %u", count);
2013+ debug ("unknowns queue length: %u", count);
2014 if (count > 10) {
2015 /* query info the whole dir, we can clear the whole unknowns queue */
2016 unknowns.clear ();
2017@@ -188,11 +147,8 @@
2018 }
2019 } else {
2020 while ((gof = unknowns.pop_head ()) != null) {
2021- //warning ("--- unknown %s", gof.name);
2022 try {
2023- //var info = gof.location.query_info (FileAttribute.STANDARD_CONTENT_TYPE, 0);
2024 var info = yield gof.location.query_info_async (FileAttribute.STANDARD_CONTENT_TYPE, 0, 0, cancellable);
2025- warning ("--- unknown query_info %s", gof.info.get_name ());
2026 add_to_knowns_queue (gof, info);
2027 } catch (Error err2) {
2028 warning ("query_info failed: %s %s", err2.message, gof.uri);
2029@@ -200,7 +156,6 @@
2030
2031 }
2032 }
2033- idle_consume_unknowns = 0;
2034 }
2035
2036 private void add_to_knowns_queue (GOF.File file, FileInfo info) {
2037@@ -214,6 +169,7 @@
2038 }
2039 t_consume_knowns = Timeout.add (300, () => {
2040 consume_knowns_queue ();
2041+ t_consume_knowns = 0;
2042 return false;
2043 });
2044 }
2045@@ -225,25 +181,24 @@
2046 if (idle_consume_unknowns == 0)
2047 idle_consume_unknowns = Idle.add (() => {
2048 consume_unknowns_queue ();
2049+ idle_consume_unknowns = 0;
2050 return false;
2051 });
2052 }
2053 }
2054
2055 private async void rreal_update_file_info (GOF.File file) {
2056- //warning ("ctags update %s", file.name);
2057 try {
2058 var rc = yield daemon.get_uri_infos (file.uri);
2059
2060 VariantIter iter = rc.iterator ();
2061- //warning ("iter n_children %d", (int) iter.n_children ());
2062+ debug ("iter n_children %d", (int) iter.n_children ());
2063 assert (iter.n_children () == 1);
2064 VariantIter row_iter = iter.next_value ().iterator ();
2065- //warning ("row_iter n_children %d", (int) row_iter.n_children ());
2066+ debug ("row_iter n_children %d", (int) row_iter.n_children ());
2067
2068 if (row_iter.n_children () == 3) {
2069 uint64 modified = int64.parse (row_iter.next_value ().get_string ());
2070- //message ("%s %d %d", file.name, (int) file.info.get_attribute_uint64 (FileAttribute.TIME_MODIFIED), (int) modified);
2071 unowned string type = row_iter.next_value ().get_string ();
2072 file.color = int.parse (row_iter.next_value ().get_string ());
2073 /* check modified time field only on user dirs. We don't want to query again and
2074@@ -259,7 +214,6 @@
2075 file.update_type ();
2076 }
2077 }
2078- //message ("grrrrrr %s %s %d %s", myfile.name, type, n, myfile.ftype);
2079 } else {
2080 add_to_unknowns_queue (file);
2081 }
2082@@ -277,9 +231,183 @@
2083 /*if (file.ftype == "application/octet-stream")*/
2084 rreal_update_file_info (file);
2085 }
2086+
2087+ public override void context_menu (Gtk.Widget? widget, GLib.List<unowned GOF.File> selected_files) {
2088+ if (selected_files.length () < 1 || widget == null)
2089+ return;
2090+
2091+ var menu = widget as Gtk.Menu;
2092+ var color_menu_item = new ColorWidget ();
2093+ color_menu_item.color_changed.connect ((ncolor) => {
2094+ set_color (selected_files, ncolor);
2095+ });
2096+
2097+ add_menuitem (menu, new Gtk.SeparatorMenuItem ());
2098+ add_menuitem (menu, color_menu_item);
2099+ }
2100+
2101+ private void add_menuitem (Gtk.Menu menu, Gtk.MenuItem menu_item) {
2102+ menu.append (menu_item);
2103+ menu_item.show ();
2104+ }
2105+
2106+ private async void set_color (GLib.List<unowned GOF.File> files, int n) throws IOError {
2107+ Variant[] entries = null;
2108+ foreach (unowned GOF.File file in files) {
2109+ file.color = n;
2110+ entries += add_entry (file);
2111+ }
2112+
2113+ if (entries != null) {
2114+ try {
2115+ yield daemon.record_uris (entries, ((GOF.File) files.data).uri);
2116+ } catch (Error err) {
2117+ warning ("%s", err.message);
2118+ }
2119+ }
2120+ }
2121+
2122+ private class ColorWidget : Gtk.MenuItem {
2123+ private new bool has_focus;
2124+ private int height;
2125+ public signal void color_changed (int ncolor);
2126+ public ColorWidget () {
2127+ set_size_request (150, 20);
2128+ height = 20;
2129+
2130+ button_press_event.connect (button_pressed_cb);
2131+ draw.connect (on_draw);
2132+
2133+ select.connect (() => {
2134+ has_focus = true;
2135+ });
2136+
2137+ deselect.connect (() => {
2138+ has_focus = false;
2139+ });
2140+ }
2141+
2142+ private bool button_pressed_cb (Gdk.EventButton event) {
2143+ determine_button_pressed_event (event);
2144+ return true;
2145+ }
2146+
2147+ private void determine_button_pressed_event (Gdk.EventButton event) {
2148+ int i;
2149+ int btnw = 10;
2150+ int btnh = 10;
2151+ int y0 = (height - btnh) /2;
2152+ int x0 = btnw+5;
2153+ int xpad = 9;
2154+
2155+ if (event.y >= y0 && event.y <= y0+btnh)
2156+ for (i=1; i<=10; i++) {
2157+ if (event.x>= xpad+x0*i && event.x <= xpad+x0*i+btnw) {
2158+ color_changed (i-1);
2159+ break;
2160+ }
2161+ }
2162+ }
2163+
2164+ protected bool on_draw (Cairo.Context cr) {
2165+ int i;
2166+ int btnw = 10;
2167+ int btnh = 10;
2168+ int y0 = (height - btnh) /2;
2169+ int x0 = btnw+5;
2170+ int xpad = 9;
2171+
2172+ for (i=1; i<=10; i++) {
2173+ if (i==1)
2174+ DrawCross (cr,xpad + x0*i, y0+1, btnw-2, btnh-2);
2175+ else {
2176+ DrawRoundedRectangle (cr,xpad + x0*i, y0, btnw, btnh, "stroke", i-1);
2177+ DrawRoundedRectangle (cr,xpad + x0*i, y0, btnw, btnh, "fill", i-1);
2178+ DrawGradientOverlay (cr,xpad + x0*i, y0, btnw, btnh);
2179+ }
2180+ }
2181+
2182+ return true;
2183+ }
2184+
2185+ private void DrawCross (Cairo.Context cr, int x, int y, int w, int h) {
2186+ cr.new_path ();
2187+ cr.set_line_width (2.0);
2188+ cr.move_to (x, y);
2189+ cr.rel_line_to (w, h);
2190+ cr.move_to (x, y+h);
2191+ cr.rel_line_to (w, -h);
2192+ cr.set_source_rgba (0,0,0,0.6);
2193+ cr.stroke();
2194+
2195+ cr.close_path ();
2196+ }
2197+
2198+ /*
2199+ * Create a rounded rectangle using the Bezier curve.
2200+ * Adapted from http://cairographics.org/cookbook/roundedrectangles/
2201+ */
2202+ private void DrawRoundedRectangle (Cairo.Context cr, int x, int y, int w, int h, string style, int color) {
2203+ int radius_x=2;
2204+ int radius_y=2;
2205+ double ARC_TO_BEZIER = 0.55228475;
2206+
2207+ if (radius_x > w - radius_x)
2208+ radius_x = w / 2;
2209+
2210+ if (radius_y > h - radius_y)
2211+ radius_y = h / 2;
2212+
2213+ /* approximate (quite close) the arc using a bezier curve */
2214+ double ca = ARC_TO_BEZIER * radius_x;
2215+ double cb = ARC_TO_BEZIER * radius_y;
2216+
2217+ cr.new_path ();
2218+ cr.set_line_width (0.7);
2219+ cr.set_tolerance (0.1);
2220+ cr.move_to (x + radius_x, y);
2221+ cr.rel_line_to (w - 2 * radius_x, 0.0);
2222+ cr.rel_curve_to (ca, 0.0, radius_x, cb, radius_x, radius_y);
2223+ cr.rel_line_to (0, h - 2 * radius_y);
2224+ cr.rel_curve_to (0.0, cb, ca - radius_x, radius_y, -radius_x, radius_y);
2225+ cr.rel_line_to (-w + 2 * radius_x, 0);
2226+ cr.rel_curve_to (-ca, 0, -radius_x, -cb, -radius_x, -radius_y);
2227+ cr.rel_line_to (0, -h + 2 * radius_y);
2228+ cr.rel_curve_to (0.0, -cb, radius_x - ca, -radius_y, radius_x, -radius_y);
2229+
2230+ switch (style) {
2231+ default:
2232+ case "fill":
2233+ Gdk.RGBA rgba = Gdk.RGBA ();
2234+ rgba.parse (GOF.Preferences.TAGS_COLORS[color]);
2235+ Gdk.cairo_set_source_rgba (cr, rgba);
2236+ cr.fill ();
2237+ break;
2238+ case "stroke":
2239+ cr.set_source_rgba (0,0,0,0.5);
2240+ cr.stroke ();
2241+ break;
2242+ }
2243+
2244+ cr.close_path ();
2245+ }
2246+
2247+ /*
2248+ * Draw the overlaying gradient
2249+ */
2250+ private void DrawGradientOverlay (Cairo.Context cr, int x, int y, int w, int h) {
2251+ var radial = new Cairo.Pattern.radial (w, h, 1, 0.0, 0.0, 0.0);
2252+ radial.add_color_stop_rgba (0, 0.3, 0.3, 0.3,0.0);
2253+ radial.add_color_stop_rgba (1, 0.0, 0.0, 0.0,0.5);
2254+
2255+ cr.set_source (radial);
2256+ cr.rectangle (x,y,w,h);
2257+ cr.fill ();
2258+ }
2259+ }
2260 }
2261
2262-
2263 public Marlin.Plugins.Base module_init () {
2264 return new Marlin.Plugins.CTags ();
2265 }
2266+
2267
2268=== modified file 'plugins/pantheon-files-trash/plugin.vala'
2269--- plugins/pantheon-files-trash/plugin.vala 2014-01-07 22:25:32 +0000
2270+++ plugins/pantheon-files-trash/plugin.vala 2014-11-22 15:05:49 +0000
2271@@ -30,22 +30,26 @@
2272
2273 public override void directory_loaded (void* user_data) {
2274 GOF.File file = ((Object[]) user_data)[2] as GOF.File;
2275+ /* Ignore directories other than trash and ignore reloading trash */
2276 if (file.location.get_uri_scheme () == "trash") {
2277- assert (((Object[]) user_data)[1] is GOF.AbstractSlot);
2278- GOF.AbstractSlot slot = ((Object[]) user_data)[1] as GOF.AbstractSlot;
2279-
2280- infobar = new Gtk.InfoBar ();
2281- (infobar.get_content_area () as Gtk.Box).add (new Gtk.Label (_("These items may be deleted by emptying the trash.")));
2282- infobar.add_button (_("Empty the Trash"), 0);
2283- infobar.response.connect ((self, response) => {
2284- Marlin.FileOperations.empty_trash (self);
2285- });
2286- infobar.set_message_type (Gtk.MessageType.INFO);
2287-
2288- infobar.set_response_sensitive (0, !TrashMonitor.is_empty ());
2289-
2290- slot.add_extra_widget (infobar);
2291- infobar.show_all ();
2292+ /* Only add infobar once */
2293+ if (infobar == null || infobar.get_parent () == null) {
2294+ assert (((Object[]) user_data)[1] is GOF.AbstractSlot);
2295+ GOF.AbstractSlot slot = ((Object[]) user_data)[1] as GOF.AbstractSlot;
2296+ infobar = new Gtk.InfoBar ();
2297+ (infobar.get_content_area () as Gtk.Box).add (new Gtk.Label (_("These items may be deleted by emptying the trash.")));
2298+ infobar.add_button (_("Empty the Trash"), 0);
2299+ infobar.response.connect ((self, response) => {
2300+ Marlin.FileOperations.empty_trash (self);
2301+ });
2302+ infobar.set_message_type (Gtk.MessageType.INFO);
2303+ infobar.set_response_sensitive (0, !TrashMonitor.is_empty ());
2304+ slot.add_extra_widget (infobar);
2305+ infobar.show_all ();
2306+ }
2307+ } else if (infobar != null) {
2308+ infobar.destroy ();
2309+ infobar = null;
2310 }
2311 }
2312 }
2313
2314=== added file 'src/AbstractEditableLabel.vala'
2315--- src/AbstractEditableLabel.vala 1970-01-01 00:00:00 +0000
2316+++ src/AbstractEditableLabel.vala 2014-11-22 15:05:49 +0000
2317@@ -0,0 +1,96 @@
2318+/*
2319+ Copyright (C) 2014 elementary Developers
2320+
2321+ This program is free software: you can redistribute it and/or modify it
2322+ under the terms of the GNU Lesser General Public License version 3, as published
2323+ by the Free Software Foundation.
2324+
2325+ This program is distributed in the hope that it will be useful, but
2326+ WITHOUT ANY WARRANTY; without even the implied warranties of
2327+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2328+ PURPOSE. See the GNU General Public License for more details.
2329+
2330+ You should have received a copy of the GNU General Public License along
2331+ with this program. If not, see <http://www.gnu.org/licenses/>.
2332+
2333+ Authors : Jeremy Wootten <jeremy@elementary.org>
2334+*/
2335+
2336+
2337+namespace Marlin {
2338+ public abstract class AbstractEditableLabel : Gtk.Frame, Gtk.Editable, Gtk.CellEditable {
2339+
2340+ public bool editing_canceled { get; set; }
2341+ public bool small_size { get; set; }
2342+ public float yalign {get; set;}
2343+ public float xalign {get; set;}
2344+ public string original_name;
2345+ public bool draw_outline {get; set;}
2346+
2347+ public Gtk.Widget editable_widget;
2348+
2349+ public AbstractEditableLabel () {
2350+ editable_widget = create_editable_widget ();
2351+ add (editable_widget);
2352+ show_all ();
2353+ get_real_editable ().key_press_event.connect (on_key_press_event);
2354+ }
2355+
2356+ public bool on_key_press_event (Gdk.EventKey event) {
2357+ bool control_pressed = ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0);
2358+ switch (event.keyval) {
2359+ case Gdk.Key.Return:
2360+ case Gdk.Key.KP_Enter:
2361+ editing_canceled = false;
2362+ editing_done ();
2363+ remove_widget ();
2364+ break;
2365+
2366+ case Gdk.Key.Escape:
2367+ editing_canceled = true;
2368+ editing_done ();
2369+ remove_widget ();
2370+ break;
2371+
2372+ case Gdk.Key.z:
2373+ if (control_pressed)
2374+ set_text (original_name);
2375+ else
2376+ return false;
2377+
2378+ break;
2379+
2380+ default:
2381+ return false;
2382+ }
2383+ return true;
2384+ }
2385+
2386+
2387+ public virtual void set_text (string text) {
2388+ original_name = text;
2389+ }
2390+
2391+ public virtual void set_line_wrap (bool wrap) {}
2392+ public virtual void set_line_wrap_mode (Pango.WrapMode mode) {}
2393+ public virtual void set_justify (Gtk.Justification jtype) {}
2394+ public virtual void set_padding (int xpad, int ypad) {}
2395+
2396+ public abstract new void set_size_request (int width, int height);
2397+ public abstract Gtk.Widget create_editable_widget ();
2398+ public abstract string get_text ();
2399+ public abstract void select_region (int start_pos, int end_pos);
2400+ public abstract void do_delete_text (int start_pos, int end_pos);
2401+ public abstract void do_insert_text (string new_text, int new_text_length, ref int position);
2402+ public abstract string get_chars (int start_pos, int end_pos);
2403+ public abstract int get_position ();
2404+ public abstract bool get_selection_bounds (out int start_pos, out int end_pos);
2405+ public abstract void set_position (int position);
2406+ public abstract Gtk.Widget get_real_editable ();
2407+
2408+
2409+ /** CellEditable interface */
2410+ /* modified gtk+-3.0.vapi required */
2411+ public virtual void start_editing (Gdk.Event? event) {}
2412+ }
2413+}
2414
2415=== modified file 'src/Application.vala'
2416--- src/Application.vala 2014-06-06 18:51:14 +0000
2417+++ src/Application.vala 2014-11-22 15:05:49 +0000
2418@@ -26,14 +26,15 @@
2419
2420 private VolumeMonitor volume_monitor;
2421 private Marlin.Progress.UIHandler progress_handler;
2422- private Marlin.Clipboard.Manager clipboard;
2423+ private Marlin.ClipboardManager clipboard;
2424 private Marlin.Thumbnailer thumbnailer;
2425
2426 private const int MARLIN_ACCEL_MAP_SAVE_DELAY = 15;
2427- private bool save_of_accel_map_requested = false;
2428
2429 public int window_count { get; private set; }
2430
2431+ bool quitting = false;
2432+
2433 construct {
2434 /* Needed by Glib.Application */
2435 this.application_id = "org.pantheon.files"; //Ensures an unique instance.
2436@@ -83,7 +84,6 @@
2437 message ("Report any issues/bugs you might find to http://bugs.launchpad.net/pantheon-files");
2438
2439 init_schemas ();
2440- init_gtk_accels ();
2441
2442 Gtk.IconTheme.get_default ().changed.connect (() => {
2443 Marlin.IconInfo.clear_caches ();
2444@@ -91,11 +91,9 @@
2445
2446 Notify.init (Config.GETTEXT_PACKAGE);
2447 this.progress_handler = new Marlin.Progress.UIHandler ();
2448- this.clipboard = new Marlin.Clipboard.Manager.get_for_display (Gdk.Display.get_default ());
2449+ this.clipboard = new Marlin.ClipboardManager.get_for_display (Gdk.Display.get_default ());
2450 this.thumbnailer = Marlin.Thumbnailer.get ();
2451
2452- tags = new Marlin.View.Tags ();
2453-
2454 plugins = new Marlin.PluginManager (Config.PLUGIN_DIR);
2455
2456 /* TODO move the volume manager here? */
2457@@ -112,6 +110,10 @@
2458 this.window_removed.connect (() => {window_count--;});
2459 }
2460
2461+ public unowned Marlin.ClipboardManager get_clipboard_manager () {
2462+ return this.clipboard;
2463+ }
2464+
2465 public override int command_line (ApplicationCommandLine cmd) {
2466 this.hold ();
2467 int result = _command_line (cmd);
2468@@ -195,7 +197,7 @@
2469
2470 /* Open application */
2471 if (create_new_window)
2472- create_window (File.new_for_path (Environment.get_home_dir ()), Gdk.Screen.get_default ());
2473+ create_window ();
2474 else if (open_in_tab)
2475 open_tabs (files);
2476 else
2477@@ -212,34 +214,23 @@
2478 }
2479
2480 public new void quit () {
2481- foreach (var window in this.get_windows ())
2482- window.destroy ();
2483- }
2484-
2485- public void create_window (File location, Gdk.Screen screen) {
2486- open_window (location, screen);
2487+ /* Protect against holding Ctrl-Q down */
2488+ if (quitting)
2489+ return;
2490+
2491+ quitting = true;
2492+ unowned List<Gtk.Window> window_list = this.get_windows ();
2493+ window_list.@foreach ((window) => {
2494+ ((Marlin.View.Window)window).quit ();
2495+ });
2496+
2497+ base.quit ();
2498 }
2499
2500 private void mount_removed_callback (VolumeMonitor monitor, Mount mount) {
2501- /* Check and see if any of the open windows are displaying contents from the unmounted mount */
2502- unowned List<Gtk.Window> window_list = this.get_windows ();
2503- File root = mount.get_root ();
2504-
2505- /* Check each slot from each window, loading home for current tabs and closing the rest */
2506- foreach (Gtk.Window window in window_list) {
2507- var marlin_window = window as Marlin.View.Window;
2508- List<Gtk.Widget> pages = marlin_window.tabs.get_children ();
2509-
2510- foreach (var page in pages) {
2511- var view_container = page as Marlin.View.ViewContainer;
2512- File location = view_container.slot.location;
2513- if (location == null || location.has_prefix (root) || location.equal (root)) {
2514- if (view_container == marlin_window.current_tab)
2515- view_container.path_changed (File.new_for_path (Environment.get_home_dir ()));
2516- else
2517- marlin_window.remove_tab (view_container);
2518- }
2519- }
2520+ /* Notify each window */
2521+ foreach (var window in this.get_windows ()) {
2522+ ((Marlin.View.Window)window).mount_removed (mount);
2523 }
2524 }
2525
2526@@ -252,44 +243,13 @@
2527
2528 /* Bind settings with GOFPreferences */
2529 Preferences.settings.bind ("show-hiddenfiles",
2530- GOF.Preferences.get_default (), "show-hidden-files", 0);
2531+ GOF.Preferences.get_default (), "show-hidden-files", GLib.SettingsBindFlags.DEFAULT);
2532 Preferences.settings.bind ("confirm-trash",
2533- GOF.Preferences.get_default (), "confirm-trash", 0);
2534+ GOF.Preferences.get_default (), "confirm-trash", GLib.SettingsBindFlags.DEFAULT);
2535 Preferences.settings.bind ("date-format",
2536- GOF.Preferences.get_default (), "date-format", 0);
2537+ GOF.Preferences.get_default (), "date-format", GLib.SettingsBindFlags.DEFAULT);
2538 Preferences.settings.bind ("interpret-desktop-files",
2539- GOF.Preferences.get_default (), "interpret-desktop-files", 0);
2540- }
2541-
2542- /* Load accelerator map, and register save callback */
2543- private void init_gtk_accels () {
2544- string accel_map_filename = Marlin.get_accel_map_file ();
2545- if (accel_map_filename != null) {
2546- Gtk.AccelMap.load (accel_map_filename);
2547- }
2548-
2549- Gtk.AccelMap.get ().changed.connect (() => {
2550- if (!save_of_accel_map_requested) {
2551- save_of_accel_map_requested = true;
2552- Timeout.add_seconds (MARLIN_ACCEL_MAP_SAVE_DELAY,
2553- save_accel_map);
2554- }
2555- });
2556- }
2557-
2558- private bool save_accel_map () {
2559- if (save_of_accel_map_requested) {
2560- string accel_map_filename = Marlin.get_accel_map_file ();
2561- if (accel_map_filename != null)
2562- Gtk.AccelMap.save (accel_map_filename);
2563- save_of_accel_map_requested = false;
2564- }
2565-
2566- return false;
2567- }
2568-
2569- private void open_window (File location, Gdk.Screen screen = Gdk.Screen.get_default ()) {
2570- (add_view_window (screen)).add_tab (location);
2571+ GOF.Preferences.get_default (), "interpret-desktop-files", GLib.SettingsBindFlags.DEFAULT);
2572 }
2573
2574 private void open_windows (File[]? files) {
2575@@ -302,6 +262,24 @@
2576 }
2577 }
2578
2579+ public void create_window (File location = File.new_for_path (Environment.get_home_dir ()),
2580+ Gdk.Screen screen = Gdk.Screen.get_default (),
2581+ Marlin.ViewMode viewmode = Marlin.ViewMode.PREFERRED) {
2582+
2583+ open_window (location, screen, viewmode);
2584+ }
2585+
2586+ private void open_window (File? location, Gdk.Screen screen = Gdk.Screen.get_default (), Marlin.ViewMode viewmode = Marlin.ViewMode.PREFERRED) {
2587+ (add_view_window (screen)).add_tab (location, viewmode);
2588+ }
2589+
2590+ private Marlin.View.Window add_view_window (Gdk.Screen screen) {
2591+ var window = new Marlin.View.Window (this, screen);
2592+ this.add_window (window as Gtk.Window);
2593+ plugins.interface_loaded (window as Gtk.Widget);
2594+ return window;
2595+ }
2596+
2597 private void open_tabs (File[]? files, Gdk.Screen screen = Gdk.Screen.get_default ()) {
2598 Marlin.View.Window window = null;
2599
2600@@ -316,22 +294,15 @@
2601 if (!Preferences.settings.get_boolean ("restore-tabs") || window.restore_tabs () < 1) {
2602 /* Open a tab pointing at the default location if no tabs restored*/
2603 var location = File.new_for_path (Environment.get_home_dir ());
2604- window.add_tab (location);
2605+ window.add_tab (location, Marlin.ViewMode.PREFERRED);
2606 }
2607 } else {
2608 /* Open tabs at each requested location */
2609 foreach (var file in files)
2610- window.add_tab (file);
2611+ window.add_tab (file, Marlin.ViewMode.PREFERRED);
2612 }
2613 }
2614
2615- private Marlin.View.Window add_view_window (Gdk.Screen screen) {
2616- var window = new Marlin.View.Window (this, screen, true);
2617- this.add_window (window as Gtk.Window);
2618- plugins.interface_loaded (window as Gtk.Widget);
2619- return window;
2620- }
2621-
2622 private bool windows_exist () {
2623 unowned List<weak Gtk.Window> windows = this.get_windows ();
2624 return (windows != null && windows.data != null);
2625
2626=== modified file 'src/BookmarkList.vala'
2627--- src/BookmarkList.vala 2014-07-22 02:54:40 +0000
2628+++ src/BookmarkList.vala 2014-11-22 15:05:49 +0000
2629@@ -88,6 +88,11 @@
2630 save_bookmarks_file ();
2631 }
2632
2633+ public void insert_uri_at_end (string uri) {
2634+ append_internal (new Bookmark.from_uri (uri, null));
2635+ save_bookmarks_file ();
2636+ }
2637+
2638 public void insert_uris (GLib.List<string> uris, uint index) {
2639 uris.@foreach ((uri) => {
2640 insert_item_internal (new Bookmark.from_uri (uri, null), index);
2641
2642=== modified file 'src/CMakeLists.txt'
2643--- src/CMakeLists.txt 2014-08-05 22:40:12 +0000
2644+++ src/CMakeLists.txt 2014-11-22 15:05:49 +0000
2645@@ -48,31 +48,40 @@
2646 Bookmark.vala
2647 BookmarkList.vala
2648 ConnectServerOperation.vala
2649+ DndHandler.vala
2650+ AbstractEditableLabel.vala
2651+ SingleLineEditableLabel.vala
2652+ MultiLineEditableLabel.vala
2653 main.vala
2654 marlin-deep-count.vala
2655 MimeActions.vala
2656 ProgressInfoWidget.vala
2657 ProgressUIHandler.vala
2658+ TextRenderer.vala
2659 QuicklistHandler.vala
2660+ View/ColumnView.vala
2661+ View/AbstractTreeView.vala
2662+ View/IconView.vala
2663+ View/ListView.vala
2664 ZeitgeistManager.vala
2665 View/DiskRenderer.vala
2666 View/IconSpinnerRenderer.vala
2667 View/DirectoryNotFound.vala
2668+ View/AbstractDirectoryView.vala
2669 View/SearchResults.vala
2670 View/Window.vala
2671 View/Resources.vala
2672- View/DbusTags.vala
2673 View/ViewContainer.vala
2674 View/OverlayBar.vala
2675 View/PropertiesWindow.vala
2676 View/Browser.vala
2677- View/ViewMode.vala
2678 View/LocationBar.vala
2679 View/Sidebar.vala
2680+ View/Slot.vala
2681+ View/Miller.vala
2682 View/Chrome/TopMenu.vala
2683 View/Chrome/ButtonWithMenu.vala
2684 View/Chrome/ViewSwicher.vala
2685- View/Chrome/ColorWidget.vala
2686 View/Chrome/XsEntry.vala
2687 View/Chrome/ImgEventBox.vala
2688 PACKAGES
2689@@ -104,35 +113,45 @@
2690 Bookmark.vala
2691 BookmarkList.vala
2692 ConnectServerOperation.vala
2693+ DndHandler.vala
2694+ AbstractEditableLabel.vala
2695+ SingleLineEditableLabel.vala
2696+ MultiLineEditableLabel.vala
2697 main.vala
2698 marlin-deep-count.vala
2699 MimeActions.vala
2700 ProgressInfoWidget.vala
2701 ProgressUIHandler.vala
2702+ TextRenderer.vala
2703+ View/ColumnView.vala
2704+ View/AbstractTreeView.vala
2705+ View/IconView.vala
2706+ View/ListView.vala
2707 ZeitgeistManager.vala
2708 View/SearchResults.vala
2709 View/DiskRenderer.vala
2710 View/DirectoryNotFound.vala
2711+ View/AbstractDirectoryView.vala
2712 View/Window.vala
2713 View/Resources.vala
2714- View/DbusTags.vala
2715 View/ViewContainer.vala
2716 View/IconSpinnerRenderer.vala
2717 View/OverlayBar.vala
2718 View/PropertiesWindow.vala
2719 View/Browser.vala
2720- View/ViewMode.vala
2721 View/LocationBar.vala
2722 View/Sidebar.vala
2723+ View/Slot.vala
2724+ View/Miller.vala
2725 View/Chrome/TopMenu.vala
2726 View/Chrome/ButtonWithMenu.vala
2727 View/Chrome/ViewSwicher.vala
2728- View/Chrome/ColorWidget.vala
2729 View/Chrome/XsEntry.vala
2730 View/Chrome/ImgEventBox.vala
2731 PACKAGES
2732 gtk+-3.0
2733 gio-2.0
2734+ pango
2735 posix
2736 gee-0.8
2737 granite
2738@@ -154,23 +173,10 @@
2739 ENDIF (WITH_UNITY AND UNITY_FOUND)
2740
2741 add_executable (../pantheon-files
2742- eel-editable-label.c
2743 marlin-enum-types.c
2744- marlin-dnd.c
2745 marlin-clipboard-manager.c
2746- marlin-window-columns.c
2747- gof-window-slot.c
2748 marlin-thumbnailer.c
2749 marlin-icon-renderer.c
2750- marlin-text-renderer.c
2751- marlin-cell-renderer-text-ellipsized.c
2752- fm-directory-view.c
2753- exo-icon-view.c
2754- exo-tree-view.c
2755- fm-abstract-icon-view.c
2756- fm-icon-view.c
2757- fm-list-view.c
2758- fm-columns-view.c
2759 marlin-connect-server-dialog.c
2760 ${VALA_C} )
2761
2762@@ -184,5 +190,5 @@
2763 ENDIF (WITH_UNITY AND UNITY_FOUND)
2764
2765 install (TARGETS ../pantheon-files RUNTIME DESTINATION bin)
2766-install (FILES pantheon-files-ui.xml fm-directory-view-ui.xml fm-icon-view-ui.xml DESTINATION ${UI_DIR})
2767+install (FILES View/directory_view_popup.ui DESTINATION ${UI_DIR})
2768 include (Tests)
2769
2770=== added file 'src/DndHandler.vala'
2771--- src/DndHandler.vala 1970-01-01 00:00:00 +0000
2772+++ src/DndHandler.vala 2014-11-22 15:05:49 +0000
2773@@ -0,0 +1,272 @@
2774+/*
2775+ * DndHandler.vala
2776+ *
2777+ * Copyright 2014 jeremy <jeremy@jeremy-MM061>
2778+ *
2779+ * This program is free software; you can redistribute it and/or modify
2780+ * it under the terms of the GNU General Public License as published by
2781+ * the Free Software Foundation; either version 2 of the License, or
2782+ * (at your option) any later version.
2783+ *
2784+ * This program is distributed in the hope that it will be useful,
2785+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2786+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2787+ * GNU General Public License for more details.
2788+ *
2789+ * You should have received a copy of the GNU General Public License
2790+ * along with this program; if not, write to the Free Software
2791+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
2792+ * MA 02110-1301, USA.
2793+ *
2794+ *
2795+ */
2796+
2797+namespace FM {
2798+ public class DndHandler : GLib.Object {
2799+ Gdk.DragAction chosen = Gdk.DragAction.DEFAULT;
2800+
2801+ public DndHandler () {}
2802+
2803+ public bool dnd_perform (Gtk.Widget widget,
2804+ GOF.File drop_target,
2805+ GLib.List<GLib.File> drop_file_list,
2806+ Gdk.DragAction action) {
2807+
2808+ if (drop_target.is_folder ()) {
2809+ Marlin.FileOperations.copy_move (drop_file_list,
2810+ null,
2811+ drop_target.get_target_location (),
2812+ action,
2813+ widget,
2814+ (void*)dnd_done,
2815+ null);
2816+ return true;
2817+ } else if (drop_target.is_executable ()) {
2818+ GLib.Error error;
2819+ if (!drop_target.execute (widget.get_screen (), drop_file_list, out error)) {
2820+ Eel.show_error_dialog (_("Failed to execute \"%s\"").printf (drop_target.get_display_name ()),
2821+ error.message,
2822+ null);
2823+ return false;
2824+ } else
2825+ return true;
2826+ }
2827+ return false;
2828+ }
2829+
2830+ private void dnd_done (GLib.List<GLib.File> files, void* data) {}
2831+
2832+ public Gdk.DragAction? drag_drop_action_ask (Gtk.Widget dest_widget,
2833+ Gtk.ApplicationWindow win,
2834+ Gdk.DragAction possible_actions) {
2835+ this.chosen = Gdk.DragAction.DEFAULT;
2836+ add_action (win);
2837+ var ask_menu = build_menu (possible_actions);
2838+ ask_menu.set_screen (dest_widget.get_screen ());
2839+ ask_menu.show_all ();
2840+ var loop = new GLib.MainLoop (null, false);
2841+
2842+ ask_menu.deactivate.connect (() => {
2843+ if (loop.is_running ())
2844+ loop.quit ();
2845+
2846+ remove_action (win);
2847+ });
2848+
2849+ ask_menu.popup (null, null, null, 0, Gdk.CURRENT_TIME);
2850+ loop.run ();
2851+ Gtk.grab_remove (ask_menu);
2852+
2853+ return this.chosen;
2854+ }
2855+
2856+ private void add_action (Gtk.ApplicationWindow win) {
2857+ var action = new GLib.SimpleAction ("choice", GLib.VariantType.STRING);
2858+ action.activate.connect (this.on_choice);
2859+
2860+ win.add_action (action);
2861+ }
2862+
2863+ private void remove_action (Gtk.ApplicationWindow win) {
2864+ win.remove_action ("choice");
2865+ }
2866+
2867+ private Gtk.Menu build_menu (Gdk.DragAction possible_actions) {
2868+ var menu = new Gtk.Menu ();
2869+
2870+ build_and_append_menu_item (menu, _("Move Here"), Gdk.DragAction.MOVE, possible_actions);
2871+ build_and_append_menu_item (menu, _("Copy Here"), Gdk.DragAction.COPY, possible_actions);
2872+ build_and_append_menu_item (menu, _("Link Here"), Gdk.DragAction.LINK, possible_actions);
2873+
2874+ menu.append (new Gtk.SeparatorMenuItem ());
2875+ menu.append (new Gtk.MenuItem.with_label (_("Cancel")));
2876+
2877+ return menu;
2878+ }
2879+
2880+ private void build_and_append_menu_item (Gtk.Menu menu, string label, Gdk.DragAction? action, Gdk.DragAction possible_actions) {
2881+ if ((possible_actions & action) != 0) {
2882+ var item = new Gtk.MenuItem.with_label (label);
2883+
2884+ item.activate.connect (() => {
2885+ this.chosen = action;
2886+ });
2887+
2888+ menu.append (item);
2889+ }
2890+ }
2891+
2892+ public void on_choice (GLib.Variant? param) {
2893+ if (param == null || !param.is_of_type (GLib.VariantType.STRING)) {
2894+ critical ("Invalid variant type in DndHandler Menu");
2895+ return;
2896+ }
2897+
2898+ string choice = param.get_string ();
2899+
2900+ switch (choice) {
2901+ case "move":
2902+ this.chosen = Gdk.DragAction.MOVE;
2903+ break;
2904+ case "copy":
2905+ this.chosen = Gdk.DragAction.COPY;
2906+ break;
2907+ case "link":
2908+ this.chosen = Gdk.DragAction.LINK;
2909+ break;
2910+ case "background": /* not implemented yet */
2911+ case "cancel":
2912+ default:
2913+ this.chosen = Gdk.DragAction.DEFAULT;
2914+ break;
2915+ }
2916+ }
2917+
2918+ public string? get_source_filename (Gdk.DragContext context) {
2919+ uchar []? data = null;
2920+ Gdk.Atom property_name = Gdk.Atom.intern_static_string ("XdndDirectSave0");
2921+ Gdk.Atom property_type = Gdk.Atom.intern_static_string ("text/plain");
2922+
2923+ bool exists = Gdk.property_get (context.get_source_window (),
2924+ property_name,
2925+ property_type,
2926+ 0, /* offset into property to start getting */
2927+ 1024, /* max bytes of data to retrieve */
2928+ 0, /* do not delete after retrieving */
2929+ null, null, /* actual property type and format got disregarded */
2930+ out data
2931+ );
2932+
2933+ if (exists && data != null) {
2934+ string name = data_to_string (data);
2935+ if (GLib.Path.DIR_SEPARATOR.to_string () in name) {
2936+ warning ("invalid source filename");
2937+ return null; /* not a valid filename */
2938+ } else
2939+ return name;
2940+ } else {
2941+ warning ("source file does not exist");
2942+ return null;
2943+ }
2944+ }
2945+
2946+ public void set_source_uri (Gdk.DragContext context, string uri) {
2947+ debug ("DNDHANDLER: set source uri to %s", uri);
2948+ Gdk.Atom property_name = Gdk.Atom.intern_static_string ("XdndDirectSave0");
2949+ Gdk.Atom property_type = Gdk.Atom.intern_static_string ("text/plain");
2950+
2951+ Gdk.property_change (context.get_source_window (),
2952+ property_name,
2953+ property_type,
2954+ 8,
2955+ Gdk.PropMode.REPLACE,
2956+ uri.data,
2957+ uri.length);
2958+ }
2959+
2960+ public bool handle_xdnddirectsave (Gdk.DragContext context,
2961+ GOF.File drop_target,
2962+ Gtk.SelectionData selection) {
2963+ bool success = false;
2964+
2965+ if (selection.get_length () == 1 && selection.get_format () == 8) {
2966+ uchar result = selection.get_data ()[0];
2967+
2968+ switch (result) {
2969+ case 'F':
2970+ /* No fallback for XdndDirectSave stage (3), result "F" ("Failed") yet */
2971+ break;
2972+ case 'S':
2973+ /* XdndDirectSave "Success" */
2974+ success = true;
2975+ break;
2976+ default:
2977+ warning ("Unhandled XdndDirectSave result %s", result.to_string ());
2978+ break;
2979+ }
2980+ }
2981+
2982+ if (!success)
2983+ set_source_uri (context, "");
2984+
2985+ return success;
2986+ }
2987+
2988+ public bool handle_netscape_url (Gdk.DragContext context, GOF.File drop_target, Gtk.SelectionData selection) {
2989+ string [] parts = (selection.get_text ()).split ("\n");
2990+
2991+ /* _NETSCAPE_URL looks like this: "$URL\n$TITLE" - should be 2 parts */
2992+ if (parts.length != 2)
2993+ return false;
2994+
2995+ /* NETSCAPE URLs are not currently handled. No current bug reports */
2996+ return false;
2997+ }
2998+
2999+ public bool handle_file_drag_actions (Gtk.Widget dest_widget,
3000+ Gtk.ApplicationWindow win,
3001+ Gdk.DragContext context,
3002+ GOF.File drop_target,
3003+ GLib.List<GLib.File> drop_file_list,
3004+ Gdk.DragAction possible_actions,
3005+ Gdk.DragAction suggested_action,
3006+ uint32 timestamp) {
3007+ bool success = false;
3008+ Gdk.DragAction action = suggested_action;
3009+
3010+ if ((possible_actions & Gdk.DragAction.ASK) != 0)
3011+ action = drag_drop_action_ask (dest_widget, win, possible_actions);
3012+
3013+ if (action != Gdk.DragAction.DEFAULT) {
3014+ success = dnd_perform (dest_widget,
3015+ drop_target,
3016+ drop_file_list,
3017+ action);
3018+ }
3019+ return success;
3020+ }
3021+
3022+
3023+ public bool selection_data_is_uri_list (Gtk.SelectionData selection_data, uint info, out string? text) {
3024+ text = null;
3025+
3026+ if (info == AbstractDirectoryView.TargetType.TEXT_URI_LIST &&
3027+ selection_data.get_format () == 8 &&
3028+ selection_data.get_length () > 0) {
3029+
3030+ text = data_to_string (selection_data.get_data_with_length ());
3031+ }
3032+ debug ("DNDHANDLER selection data is uri list returning %s", (text != null).to_string ());
3033+ return (text != null);
3034+ }
3035+
3036+ private string data_to_string (uchar [] cdata) {
3037+ var sb = new StringBuilder ("");
3038+
3039+ foreach (uchar u in cdata)
3040+ sb.append_c ((char)u);
3041+
3042+ return sb.str;
3043+ }
3044+ }
3045+}
3046
3047=== modified file 'src/MimeActions.vala'
3048--- src/MimeActions.vala 2014-01-11 14:48:15 +0000
3049+++ src/MimeActions.vala 2014-11-22 15:05:49 +0000
3050@@ -23,6 +23,7 @@
3051 public class Marlin.MimeActions {
3052
3053 public static AppInfo? get_default_application_for_file (GOF.File file) {
3054+
3055 AppInfo app = file.get_default_handler ();
3056
3057 if (app == null) {
3058@@ -35,16 +36,20 @@
3059 return app;
3060 }
3061
3062- public static AppInfo? get_default_application_for_files (List<GOF.File> files) {
3063+ public static AppInfo? get_default_application_for_files (GLib.List<unowned GOF.File> files) {
3064 assert (files != null);
3065+ /* Need to make a new list to avoid corrupting the selection */
3066+ unowned GLib.List<GOF.File> sorted_files = null;
3067+ files.@foreach ((file) => {
3068+ sorted_files.prepend (file);
3069+ });
3070
3071- List<GOF.File> sorted_files = files.copy ();
3072 sorted_files.sort (file_compare_by_mime_type);
3073
3074- AppInfo app = null;
3075- GOF.File previous_file = null;
3076+ AppInfo? app = null;
3077+ GOF.File? previous_file = null;
3078
3079- foreach (var file in sorted_files) {
3080+ foreach (GOF.File file in sorted_files) {
3081 if (previous_file == null) {
3082 app = get_default_application_for_file (file);
3083 previous_file = file;
3084@@ -67,11 +72,11 @@
3085
3086 previous_file = file;
3087 }
3088-
3089 return app;
3090 }
3091
3092 public static List<AppInfo>? get_applications_for_file (GOF.File file) {
3093+
3094 List<AppInfo> result = AppInfo.get_all_for_type (file.get_ftype ());
3095 string uri_scheme = file.location.get_uri_scheme ();
3096
3097@@ -109,16 +114,19 @@
3098 return result;
3099 }
3100
3101- public static List<AppInfo>? get_applications_for_files (List<GOF.File> files) {
3102+ public static List<AppInfo>? get_applications_for_files (GLib.List<unowned GOF.File> files) {
3103 assert (files != null);
3104-
3105- List<GOF.File> sorted_files = files.copy ();
3106+ /* Need to make a new list to avoid corrupting the selection */
3107+ unowned GLib.List<GOF.File> sorted_files = null;
3108+ files.@foreach ((file) => {
3109+ sorted_files.prepend (file);
3110+ });
3111 sorted_files.sort (file_compare_by_mime_type);
3112
3113 List<AppInfo> result = null;
3114- GOF.File previous_file = null;
3115+ unowned GOF.File previous_file = null;
3116
3117- foreach (var file in sorted_files) {
3118+ foreach (unowned GOF.File file in sorted_files) {
3119 if (previous_file == null) {
3120 result = get_applications_for_file (file);
3121 previous_file = file;
3122
3123=== added file 'src/MultiLineEditableLabel.vala'
3124--- src/MultiLineEditableLabel.vala 1970-01-01 00:00:00 +0000
3125+++ src/MultiLineEditableLabel.vala 2014-11-22 15:05:49 +0000
3126@@ -0,0 +1,196 @@
3127+/*
3128+ Copyright (C) 2014 elementary Developers
3129+
3130+ This program is free software: you can redistribute it and/or modify it
3131+ under the terms of the GNU Lesser General Public License version 3, as published
3132+ by the Free Software Foundation.
3133+
3134+ This program is distributed in the hope that it will be useful, but
3135+ WITHOUT ANY WARRANTY; without even the implied warranties of
3136+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3137+ PURPOSE. See the GNU General Public License for more details.
3138+
3139+ You should have received a copy of the GNU General Public License along
3140+ with this program. If not, see <http://www.gnu.org/licenses/>.
3141+
3142+ Authors : Jeremy Wootten <jeremy@elementary.org>
3143+*/
3144+
3145+namespace Marlin {
3146+ public class MultiLineEditableLabel : AbstractEditableLabel {
3147+
3148+ protected Gtk.ScrolledWindow scrolled_window;
3149+ protected Gtk.TextView textview;
3150+
3151+ public MultiLineEditableLabel () {}
3152+
3153+ public override Gtk.Widget create_editable_widget () {
3154+ textview = new Gtk.TextView ();
3155+ scrolled_window = new Gtk.ScrolledWindow (null, null);
3156+ scrolled_window.add (textview);
3157+ return scrolled_window as Gtk.Widget;
3158+ }
3159+
3160+ public override Gtk.Widget get_real_editable () {
3161+ return textview;
3162+ }
3163+
3164+ public override void set_text (string text) {
3165+ textview.get_buffer ().set_text (text);
3166+ original_name = text;
3167+ }
3168+
3169+ public override void set_line_wrap (bool wrap) {
3170+ if (!wrap)
3171+ textview.set_wrap_mode (Gtk.WrapMode.NONE);
3172+ else
3173+ textview.set_wrap_mode (Gtk.WrapMode.CHAR);
3174+ }
3175+
3176+ public override void set_line_wrap_mode (Pango.WrapMode mode) {
3177+ switch (mode) {
3178+ case Pango.WrapMode.CHAR:
3179+ textview.set_wrap_mode (Gtk.WrapMode.CHAR);
3180+ break;
3181+
3182+ case Pango.WrapMode.WORD:
3183+ textview.set_wrap_mode (Gtk.WrapMode.WORD);
3184+ break;
3185+
3186+ case Pango.WrapMode.WORD_CHAR:
3187+ textview.set_wrap_mode (Gtk.WrapMode.WORD_CHAR);
3188+ break;
3189+
3190+ default:
3191+ break;
3192+ }
3193+ }
3194+
3195+ public override void set_justify (Gtk.Justification jtype) {
3196+ textview.justification = jtype;
3197+ }
3198+
3199+ public override void set_padding (int xpad, int ypad) {
3200+ textview.set_margin_start (xpad);
3201+ textview.set_margin_end (xpad);
3202+ textview.set_margin_top (ypad);
3203+ textview.set_margin_bottom (ypad);
3204+ }
3205+
3206+ public override string get_text () {
3207+ var buffer = textview.get_buffer ();
3208+ Gtk.TextIter? start = null;
3209+ Gtk.TextIter? end = null;
3210+ buffer.get_start_iter (out start);
3211+ buffer.get_end_iter (out end);
3212+ return buffer.get_text (start, end, false);
3213+ }
3214+
3215+ /** Gtk.Editable interface */
3216+
3217+ public override void select_region (int start_pos, int end_pos) {
3218+ var buffer = textview.get_buffer ();
3219+ Gtk.TextIter? ins = null;
3220+ Gtk.TextIter? bound = null;
3221+
3222+ buffer.get_iter_at_offset (out ins, start_pos);
3223+
3224+ if (end_pos > 0)
3225+ buffer.get_iter_at_offset (out bound, end_pos);
3226+ else
3227+ buffer.get_end_iter (out bound);
3228+
3229+ buffer.select_range (ins, bound);
3230+ textview.grab_focus ();
3231+ }
3232+
3233+ public override void do_delete_text (int start_pos, int end_pos) {
3234+ var buffer = textview.get_buffer ();
3235+ Gtk.TextIter? start = null;
3236+ Gtk.TextIter? end = null;
3237+
3238+ buffer.get_iter_at_offset (out start, start_pos);
3239+
3240+ if (end_pos > 0)
3241+ buffer.get_iter_at_offset (out end, end_pos);
3242+ else
3243+ buffer.get_end_iter (out end);
3244+
3245+ buffer.delete_range (start, end);
3246+ }
3247+
3248+ public override void do_insert_text (string new_text, int new_text_length, ref int position) {
3249+ var buffer = textview.get_buffer ();
3250+ Gtk.TextIter? pos = null;
3251+
3252+ buffer.get_iter_at_offset (out pos, position);
3253+ buffer.insert (ref pos, new_text, new_text_length);
3254+ }
3255+
3256+ public override string get_chars (int start_pos, int end_pos) {
3257+ var buffer = textview.get_buffer ();
3258+ Gtk.TextIter? start = null;
3259+ Gtk.TextIter? end = null;
3260+
3261+ buffer.get_iter_at_offset (out start, start_pos);
3262+
3263+ if (end_pos > 0)
3264+ buffer.get_iter_at_offset (out end, end_pos);
3265+ else
3266+ buffer.get_end_iter (out end);
3267+
3268+ return buffer.get_text (start, end, false);
3269+ }
3270+
3271+ public override int get_position () {
3272+ var buffer = textview.get_buffer ();
3273+ var mark = buffer.get_insert ();
3274+ Gtk.TextIter? iter = null;
3275+ buffer.get_iter_at_mark (out iter, mark);
3276+
3277+ return iter.get_offset ();
3278+ }
3279+
3280+ public override bool get_selection_bounds (out int start_pos, out int end_pos) {
3281+ var buffer = textview.get_buffer ();
3282+ Gtk.TextIter? start = null;
3283+ Gtk.TextIter? end = null;
3284+
3285+ buffer.get_selection_bounds (out start, out end);
3286+ start_pos = start.get_offset ();
3287+ end_pos = end.get_offset ();
3288+
3289+ return start_pos != end_pos;
3290+ }
3291+
3292+ public override void set_position (int position) {
3293+ var buffer = textview.get_buffer ();
3294+ Gtk.TextIter? iter = null;
3295+ buffer.get_start_iter (out iter);
3296+ iter.set_offset (position);
3297+ buffer.place_cursor (iter);
3298+ }
3299+
3300+ public override bool draw (Cairo.Context cr) {
3301+ bool result = base.draw (cr);
3302+ if (draw_outline) {
3303+ Gtk.Allocation allocation;
3304+ Gdk.RGBA color;
3305+ Gdk.Rectangle outline;
3306+
3307+ get_allocation (out allocation);
3308+ color = get_style_context ().get_color (get_state_flags ());
3309+ Gdk.cairo_set_source_rgba (cr, color);
3310+ cr.set_line_width (1.0);
3311+ outline = {0, 0, allocation.width, allocation.height};
3312+ Gdk.cairo_rectangle (cr, outline);
3313+ cr.stroke ();
3314+ }
3315+ return result;
3316+ }
3317+
3318+ public override void set_size_request (int width, int height) {
3319+ textview.set_size_request (width, height);
3320+ }
3321+ }
3322+}
3323
3324=== added file 'src/SingleLineEditableLabel.vala'
3325--- src/SingleLineEditableLabel.vala 1970-01-01 00:00:00 +0000
3326+++ src/SingleLineEditableLabel.vala 2014-11-22 15:05:49 +0000
3327@@ -0,0 +1,104 @@
3328+/*
3329+ Copyright (C) 2014 elementary Developers
3330+
3331+ This program is free software: you can redistribute it and/or modify it
3332+ under the terms of the GNU Lesser General Public License version 3, as published
3333+ by the Free Software Foundation.
3334+
3335+ This program is distributed in the hope that it will be useful, but
3336+ WITHOUT ANY WARRANTY; without even the implied warranties of
3337+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3338+ PURPOSE. See the GNU General Public License for more details.
3339+
3340+ You should have received a copy of the GNU General Public License along
3341+ with this program. If not, see <http://www.gnu.org/licenses/>.
3342+
3343+ Authors : Jeremy Wootten <jeremy@elementary.org>
3344+*/
3345+
3346+namespace Marlin {
3347+ public class SingleLineEditableLabel : AbstractEditableLabel {
3348+
3349+ protected Gtk.Entry textview;
3350+
3351+ public SingleLineEditableLabel () {}
3352+
3353+ public override Gtk.Widget create_editable_widget () {
3354+ textview = new Gtk.Entry ();
3355+ return textview as Gtk.Widget;
3356+ }
3357+
3358+ public override Gtk.Widget get_real_editable () {
3359+ return textview;
3360+ }
3361+
3362+ public override void set_text (string text) {
3363+ textview.set_text (text);
3364+ original_name = text;
3365+ }
3366+
3367+
3368+ public override void set_justify (Gtk.Justification jtype) {
3369+ switch (jtype) {
3370+ case Gtk.Justification.LEFT:
3371+ textview.set_alignment (0.0f);
3372+ break;
3373+
3374+ case Gtk.Justification.CENTER:
3375+ textview.set_alignment (0.5f);
3376+ break;
3377+
3378+ case Gtk.Justification.RIGHT:
3379+ textview.set_alignment (1.0f);
3380+ break;
3381+
3382+ default:
3383+ textview.set_alignment (0.5f);
3384+ break;
3385+ }
3386+ }
3387+
3388+ public override string get_text () {
3389+ return textview.get_text ();
3390+ }
3391+
3392+ /** Gtk.Editable interface */
3393+
3394+ public override void select_region (int start_pos, int end_pos) {
3395+ textview.select_region (start_pos, end_pos);
3396+ textview.grab_focus ();
3397+ }
3398+
3399+ public override void do_delete_text (int start_pos, int end_pos) {
3400+ textview.delete_text (start_pos, end_pos);
3401+ }
3402+
3403+ public override void do_insert_text (string new_text, int new_text_length, ref int position) {
3404+ textview.insert_text (new_text, new_text_length, ref position);
3405+ }
3406+
3407+ public override string get_chars (int start_pos, int end_pos) {
3408+ return textview.get_chars (start_pos, end_pos);
3409+ }
3410+
3411+ public override int get_position () {
3412+ return textview.get_position ();
3413+ }
3414+
3415+ public override bool get_selection_bounds (out int start_pos, out int end_pos) {
3416+ int start, end;
3417+ bool result = textview.get_selection_bounds (out start, out end);
3418+ start_pos = start;
3419+ end_pos = end;
3420+ return result;
3421+ }
3422+
3423+ public override void set_position (int position) {
3424+ textview.set_position (position);
3425+ }
3426+
3427+ public override void set_size_request (int width, int height) {
3428+ textview.set_size_request (width, height);
3429+ }
3430+ }
3431+}
3432
3433=== added file 'src/TextRenderer.vala'
3434--- src/TextRenderer.vala 1970-01-01 00:00:00 +0000
3435+++ src/TextRenderer.vala 2014-11-22 15:05:49 +0000
3436@@ -0,0 +1,307 @@
3437+/*
3438+ Copyright (C) 2014 elementary Developers
3439+
3440+ This program is free software: you can redistribute it and/or modify it
3441+ under the terms of the GNU Lesser General Public License version 3, as published
3442+ by the Free Software Foundation.
3443+
3444+ This program is distributed in the hope that it will be useful, but
3445+ WITHOUT ANY WARRANTY; without even the implied warranties of
3446+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3447+ PURPOSE. See the GNU General Public License for more details.
3448+
3449+ You should have received a copy of the GNU General Public License along
3450+ with this program. If not, see <http://www.gnu.org/licenses/>.
3451+
3452+ Authors : Jeremy Wootten <jeremy@elementary.org>
3453+*/
3454+
3455+namespace Marlin {
3456+ public class TextRenderer: Gtk.CellRendererText {
3457+
3458+ const int MAX_LINES = 3;
3459+ const uint BORDER_RADIUS = 6;
3460+
3461+ public Marlin.ZoomLevel zoom_level {get; set;}
3462+ public bool follow_state {get; set;}
3463+ public new string background { set; private get;}
3464+ public GOF.File file {set; private get;}
3465+ public int text_width;
3466+ public int text_height;
3467+
3468+ int char_width;
3469+ int char_height;
3470+ int focus_border_width;
3471+ Pango.Layout layout;
3472+ Gtk.Widget widget;
3473+ Marlin.AbstractEditableLabel? entry = null;
3474+
3475+ public TextRenderer (Marlin.ViewMode viewmode) {
3476+ this.mode = Gtk.CellRendererMode.EDITABLE;
3477+
3478+ if (viewmode == Marlin.ViewMode.ICON)
3479+ entry = new Marlin.MultiLineEditableLabel ();
3480+ else
3481+ entry = new Marlin.SingleLineEditableLabel ();
3482+
3483+ entry.editing_done.connect (on_entry_editing_done);
3484+ entry.get_real_editable ().focus_out_event.connect_after (on_entry_focus_out_event);
3485+ }
3486+
3487+
3488+ public override void render (Cairo.Context cr,
3489+ Gtk.Widget widget,
3490+ Gdk.Rectangle background_area,
3491+ Gdk.Rectangle cell_area,
3492+ Gtk.CellRendererState flags) {
3493+ set_widget (widget);
3494+ Gtk.StateFlags state = widget.get_state_flags ();
3495+
3496+ if ((flags & Gtk.CellRendererState.SELECTED) == Gtk.CellRendererState.SELECTED)
3497+ state |= Gtk.StateFlags.SELECTED;
3498+ else if ((flags & Gtk.CellRendererState.PRELIT) == Gtk.CellRendererState.PRELIT)
3499+ state = Gtk.StateFlags.PRELIGHT;
3500+ else
3501+ state = widget.get_sensitive () ? Gtk.StateFlags.NORMAL : Gtk.StateFlags.INSENSITIVE;
3502+
3503+ set_up_layout (text, cell_area);
3504+
3505+ var style_context = widget.get_parent ().get_style_context ();
3506+ style_context.save ();
3507+ style_context.set_state (state);
3508+
3509+ if (follow_state || background != null)
3510+ draw_focus (cr, cell_area, flags, style_context, state);
3511+
3512+ int x_offset, y_offset;
3513+ get_offsets (cell_area, text_width, text_height, xalign, out x_offset, out y_offset);
3514+
3515+ /* Adjust text offsets for best appearance in each view */
3516+ if (xalign == 0.5f) { /* Icon view */
3517+ x_offset = (cell_area.width - this.wrap_width) / 2;
3518+ y_offset += focus_border_width + (int)ypad;
3519+ } else {
3520+ x_offset += focus_border_width + 2 * (int)xpad;
3521+ y_offset += focus_border_width;
3522+ }
3523+
3524+ style_context.render_layout (cr,
3525+ cell_area.x + x_offset,
3526+ cell_area.y + y_offset,
3527+ layout);
3528+
3529+ style_context.restore ();
3530+ }
3531+
3532+ public void set_up_layout (string? text, Gdk.Rectangle cell_area) {
3533+ /* render small/normal text depending on the zoom_level */
3534+ if (text == null)
3535+ text= " ";
3536+
3537+ bool small = this.zoom_level < Marlin.ZoomLevel.NORMAL;
3538+ if (small)
3539+ layout.set_attributes (EelPango.attr_list_small ());
3540+ else
3541+ layout.set_attributes (null);
3542+
3543+ if (wrap_width < 0) {
3544+ layout.set_width (cell_area.width * Pango.SCALE);
3545+ layout.set_height (- 1);
3546+ } else {
3547+ layout.set_width (wrap_width * Pango.SCALE);
3548+ layout.set_wrap (this.wrap_mode);
3549+ layout.set_height (- MAX_LINES);
3550+ }
3551+
3552+ layout.set_ellipsize (Pango.EllipsizeMode.END);
3553+
3554+ if (xalign == 0.5f)
3555+ layout.set_alignment (Pango.Alignment.CENTER);
3556+
3557+ layout.set_text (text, -1);
3558+
3559+ /* calculate the real text dimension */
3560+ int width, height;
3561+ layout.get_pixel_size (out width, out height);
3562+ text_width = width;
3563+ text_height = height;
3564+ }
3565+
3566+ /* Needs patched gtk+-3.0.vapi file - incorrect function signature up to version 0.25.4 */
3567+ public override unowned Gtk.CellEditable? start_editing (Gdk.Event? event,
3568+ Gtk.Widget widget,
3569+ string path,
3570+ Gdk.Rectangle background_area,
3571+ Gdk.Rectangle cell_area,
3572+ Gtk.CellRendererState flags) {
3573+
3574+ if (!visible || mode != Gtk.CellRendererMode.EDITABLE)
3575+ return null;
3576+
3577+ float xalign, yalign;
3578+ get_alignment (out xalign, out yalign);
3579+
3580+ entry.set_text (text);
3581+ entry.set_line_wrap (true);
3582+ entry.set_line_wrap_mode (wrap_mode);
3583+
3584+ if (wrap_width > 0) { /* Icon view */
3585+ entry.set_justify (Gtk.Justification.CENTER);
3586+ entry.draw_outline = true;
3587+ } else { /*List and Column views */
3588+ entry.set_justify (Gtk.Justification.LEFT);
3589+ entry.draw_outline = false;
3590+ }
3591+
3592+ entry.yalign = this.yalign;
3593+ entry.set_padding ((int)xpad, (int)ypad);
3594+ entry.set_size_request (wrap_width, -1);
3595+ entry.set_position (-1);
3596+ entry.set_data ("marlin-text-renderer-path", path.dup ());
3597+ entry.show_all ();
3598+
3599+ return entry as Gtk.CellEditable;
3600+ }
3601+
3602+ private void set_widget (Gtk.Widget? _widget) {
3603+ Pango.FontMetrics metrics;
3604+ Pango.Context context;
3605+ int focus_padding;
3606+ int focus_line_width;
3607+
3608+ if (_widget == widget)
3609+ return;
3610+
3611+ /* disconnect from the previously set widget */
3612+ if (widget != null)
3613+ disconnect_widget_signals ();
3614+
3615+ widget = _widget;
3616+
3617+ if (widget != null) {
3618+ connect_widget_signals ();
3619+ context = widget.get_pango_context ();
3620+ layout = new Pango.Layout (context);
3621+ layout.set_auto_dir (false);
3622+ layout.set_single_paragraph_mode (true);
3623+ metrics = context.get_metrics (layout.get_font_description (), context.get_language ());
3624+ char_width = (metrics.get_approximate_char_width () + 512 ) >> 10;
3625+ char_height = (metrics.get_ascent () + metrics.get_descent () + 512) >> 10;
3626+ if (wrap_width < 0)
3627+ (this as Gtk.CellRenderer).set_fixed_size (-1, char_height);
3628+
3629+ widget.style_get ("focus-padding", out focus_padding, "focus-line-width", out focus_line_width);
3630+ focus_border_width = int.max (focus_padding + focus_line_width, 2);
3631+ } else {
3632+ layout = null;
3633+ char_width = 0;
3634+ char_height = 0;
3635+ }
3636+ }
3637+
3638+ private void connect_widget_signals () {
3639+ widget.destroy.connect (invalidate);
3640+ widget.style_set.connect (invalidate);
3641+ }
3642+
3643+ private void disconnect_widget_signals () {
3644+ widget.destroy.disconnect (invalidate);
3645+ widget.style_set.disconnect (invalidate);
3646+ }
3647+
3648+ private void invalidate () {
3649+ set_widget (null);
3650+ }
3651+
3652+ private void on_entry_editing_done () {
3653+ bool cancelled = entry.editing_canceled;
3654+ base.stop_editing (cancelled);
3655+
3656+ entry.hide ();
3657+
3658+ if (!cancelled) {
3659+ string text = entry.get_text ();
3660+ string path = entry.get_data ("marlin-text-renderer-path");
3661+ edited (path, text);
3662+ }
3663+ }
3664+
3665+ private bool on_entry_focus_out_event (Gdk.Event event) {
3666+ on_entry_editing_done ();
3667+ return false;
3668+ }
3669+
3670+ private void draw_focus (Cairo.Context cr,
3671+ Gdk.Rectangle cell_area,
3672+ Gtk.CellRendererState flags,
3673+ Gtk.StyleContext style_context,
3674+ Gtk.StateFlags state) {
3675+ bool selected = false;
3676+ float x;
3677+ int x_offset, y_offset, focus_rect_width, focus_rect_height;
3678+
3679+ if (follow_state)
3680+ selected = ((flags & Gtk.CellRendererState.SELECTED) == Gtk.CellRendererState.SELECTED);
3681+
3682+ focus_rect_width = text_width + 4 * this.focus_border_width;
3683+ focus_rect_height = text_height + 2 * this.focus_border_width;
3684+
3685+ if (widget.get_direction () == Gtk.TextDirection.RTL)
3686+ x = 1.0f - xalign;
3687+ else
3688+ x = xalign;
3689+
3690+ get_offsets (cell_area, focus_rect_width, focus_rect_height, x, out x_offset, out y_offset);
3691+
3692+ /* render the background if selected or colorized */
3693+ if (selected || this.background != null) {
3694+ int x0 = cell_area.x + x_offset + (int)xpad;
3695+ int y0 = cell_area.y + y_offset + (int)ypad;
3696+ int x1 = x0 + focus_rect_width;
3697+ int y1 = y0 + focus_rect_height;
3698+
3699+ cr.move_to (x0 + BORDER_RADIUS, y0);
3700+ cr.line_to (x1 - BORDER_RADIUS, y0);
3701+ cr.curve_to (x1 - BORDER_RADIUS, y0, x1, y0, x1, y0 + BORDER_RADIUS);
3702+ cr.line_to (x1, y1 - BORDER_RADIUS);
3703+ cr.curve_to (x1, y1 - BORDER_RADIUS, x1, y1, x1 - BORDER_RADIUS, y1);
3704+ cr.line_to (x0 + BORDER_RADIUS, y1);
3705+ cr.curve_to (x0 + BORDER_RADIUS, y1, x0, y1, x0, y1 - BORDER_RADIUS);
3706+ cr.line_to (x0, y0 + BORDER_RADIUS);
3707+ cr.curve_to (x0, y0 + BORDER_RADIUS, x0, y0, x0 + BORDER_RADIUS, y0);
3708+
3709+ Gdk.RGBA color ={};
3710+ if (background != null && !selected) {
3711+ if (!color.parse (background)) {
3712+ critical ("Can't parse this color value: %s", background);
3713+ color = style_context.get_background_color (state);
3714+ }
3715+ } else
3716+ color = style_context.get_background_color (state);
3717+
3718+ Gdk.cairo_set_source_rgba (cr, color);
3719+ cr.fill ();
3720+ }
3721+ /* draw the focus indicator */
3722+ if (follow_state && (flags & Gtk.CellRendererState.FOCUSED) != 0)
3723+ style_context.render_focus (cr,
3724+ cell_area.x + x_offset,
3725+ cell_area.y + y_offset,
3726+ focus_rect_width,
3727+ focus_rect_height);
3728+ }
3729+
3730+ private void get_offsets (Gdk.Rectangle cell_area,
3731+ int width,
3732+ int height,
3733+ float x,
3734+ out int x_offset,
3735+ out int y_offset) {
3736+ x_offset = (int)(x * (cell_area.width - width - 2 * (int)xpad));
3737+ x_offset = int.max (x_offset, 0);
3738+
3739+ y_offset = (int)(yalign * (cell_area.height - height - 2 * (int)ypad));
3740+ y_offset = int.max (y_offset, 0);
3741+ }
3742+ }
3743+}
3744
3745=== added file 'src/View/AbstractDirectoryView.vala'
3746--- src/View/AbstractDirectoryView.vala 1970-01-01 00:00:00 +0000
3747+++ src/View/AbstractDirectoryView.vala 2014-11-22 15:05:49 +0000
3748@@ -0,0 +1,2778 @@
3749+/*
3750+ Copyright (C) 2014 elementary Developers
3751+
3752+ This program is free software: you can redistribute it and/or modify it
3753+ under the terms of the GNU Lesser General Public License version 3, as published
3754+ by the Free Software Foundation.
3755+
3756+ This program is distributed in the hope that it will be useful, but
3757+ WITHOUT ANY WARRANTY; without even the implied warranties of
3758+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3759+ PURPOSE. See the GNU General Public License for more details.
3760+
3761+ You should have received a copy of the GNU General Public License along
3762+ with this program. If not, see <http://www.gnu.org/licenses/>.
3763+
3764+ Authors : Jeremy Wootten <jeremy@elementary.org>
3765+*/
3766+
3767+/** Implementations of AbstractDirectoryView are
3768+ * IconView
3769+ * ListView
3770+ * ColumnView
3771+**/
3772+
3773+namespace FM {
3774+ public abstract class AbstractDirectoryView : Gtk.ScrolledWindow {
3775+
3776+ public enum TargetType {
3777+ STRING,
3778+ TEXT_URI_LIST,
3779+ XDND_DIRECT_SAVE0,
3780+ NETSCAPE_URL
3781+ }
3782+
3783+ protected enum ClickZone {
3784+ EXPANDER,
3785+ HELPER,
3786+ ICON,
3787+ NAME,
3788+ BLANK_PATH,
3789+ BLANK_NO_PATH,
3790+ INVALID
3791+ }
3792+
3793+ const int MAX_TEMPLATES = 32;
3794+
3795+ const Gtk.TargetEntry [] drag_targets = {
3796+ {"text/plain", 0, TargetType.STRING},
3797+ {"text/uri-list", 0, TargetType.TEXT_URI_LIST}
3798+ };
3799+
3800+ const Gtk.TargetEntry [] drop_targets = {
3801+ {"text/uri-list", 0, TargetType.TEXT_URI_LIST},
3802+ {"XdndDirectSave0", 0, TargetType.TEXT_URI_LIST},
3803+ {"_NETSCAPE_URL", 0, TargetType.TEXT_URI_LIST}
3804+ };
3805+
3806+ const Gdk.DragAction file_drag_actions = (Gdk.DragAction.COPY | Gdk.DragAction.MOVE | Gdk.DragAction.LINK);
3807+
3808+ /* Menu Handling */
3809+ const GLib.ActionEntry [] selection_entries = {
3810+ {"open", on_selection_action_open_executable},
3811+ {"open_with_app", on_selection_action_open_with_app, "s"},
3812+ {"open_with_default", on_selection_action_open_with_default},
3813+ {"open_with_other_app", on_selection_action_open_with_other_app},
3814+ {"rename", on_selection_action_rename},
3815+ {"cut", on_selection_action_cut},
3816+ {"trash", on_selection_action_trash},
3817+ {"delete", on_selection_action_delete},
3818+ {"restore", on_selection_action_restore}
3819+ };
3820+
3821+ const GLib.ActionEntry [] background_entries = {
3822+ {"new", on_background_action_new, "s"},
3823+ {"create_from", on_background_action_create_from, "s"},
3824+ {"sort_by", on_background_action_sort_by_changed, "s", "'name'"},
3825+ {"reverse", on_background_action_reverse_changed, null, "false"},
3826+ {"show_hidden", null, null, "false", change_state_show_hidden}
3827+ };
3828+
3829+ const GLib.ActionEntry [] common_entries = {
3830+ {"copy", on_common_action_copy},
3831+ {"paste_into", on_common_action_paste_into},
3832+ {"open_in", on_common_action_open_in, "s"},
3833+ {"bookmark", on_common_action_bookmark},
3834+ {"properties", on_common_action_properties}
3835+ };
3836+
3837+ GLib.SimpleActionGroup common_actions;
3838+ GLib.SimpleActionGroup selection_actions;
3839+ GLib.SimpleActionGroup background_actions;
3840+
3841+ private Marlin.ZoomLevel _zoom_level;
3842+ public Marlin.ZoomLevel zoom_level {
3843+ get {
3844+ return _zoom_level;
3845+ }
3846+
3847+ set {
3848+ if (value <= maximum_zoom &&
3849+ value >= minimum_zoom &&
3850+ value != _zoom_level) {
3851+
3852+ _zoom_level = value;
3853+ on_zoom_level_changed (value);
3854+ }
3855+ }
3856+ }
3857+
3858+ public int icon_size {
3859+ get {
3860+ return Marlin.zoom_level_to_icon_size (_zoom_level);
3861+ }
3862+ }
3863+
3864+ protected Marlin.ZoomLevel minimum_zoom = Marlin.ZoomLevel.SMALLEST;
3865+ protected Marlin.ZoomLevel maximum_zoom = Marlin.ZoomLevel.LARGEST;
3866+
3867+ /* drag support */
3868+ uint drag_scroll_timer_id = 0;
3869+ uint drag_timer_id = 0;
3870+ int drag_x = 0;
3871+ int drag_y = 0;
3872+ int drag_button;
3873+ protected int drag_delay = Gtk.Settings.get_default ().gtk_menu_popup_delay;
3874+
3875+ Gdk.DragAction current_suggested_action = Gdk.DragAction.DEFAULT;
3876+ Gdk.DragAction current_actions = Gdk.DragAction.DEFAULT;
3877+
3878+ unowned GLib.List<unowned GOF.File> drag_file_list = null;
3879+ GOF.File? drop_target_file = null;
3880+
3881+
3882+ /* drop site support */
3883+ bool _drop_highlight;
3884+ bool drop_highlight {
3885+ get {
3886+ return _drop_highlight;
3887+ }
3888+
3889+ set {
3890+ if (value != _drop_highlight) {
3891+ if (value)
3892+ Gtk.drag_highlight (this);
3893+ else
3894+ Gtk.drag_unhighlight (this);
3895+ }
3896+ _drop_highlight = value;
3897+ }
3898+ }
3899+
3900+ private bool drop_data_ready = false; /* whether the drop data was received already */
3901+ private bool drop_occurred = false; /* whether the data was dropped */
3902+ private bool drag_has_begun = false;
3903+ protected bool dnd_disabled = false;
3904+ private void* drag_data;
3905+ private GLib.List<GLib.File> drop_file_list = null; /* the list of URIs that are contained in the drop data */
3906+
3907+ /* support for generating thumbnails */
3908+ uint thumbnail_request = 0;
3909+ uint thumbnail_source_id = 0;
3910+ Marlin.Thumbnailer thumbnailer = null;
3911+
3912+ /* TODO Support for preview see bug #1380139 */
3913+ private string? previewer = null;
3914+
3915+ /* Rename support */
3916+ protected Gtk.TreeViewColumn name_column;
3917+ protected Marlin.TextRenderer? name_renderer = null;
3918+ unowned Marlin.AbstractEditableLabel? editable_widget = null;
3919+ public string original_name = "";
3920+
3921+ /* Support for zoom by smooth scrolling */
3922+ private double total_delta_y = 0.0;
3923+
3924+ /* UI options for button press handling */
3925+ protected bool single_click_rename = false;
3926+ protected bool activate_on_blank = true;
3927+ protected bool right_margin_unselects_all = false;
3928+ public bool single_click_mode {get; set;}
3929+ protected bool should_activate = false;
3930+ protected bool should_scroll = true;
3931+ protected uint click_zone = ClickZone.ICON;
3932+ protected uint previous_click_zone = ClickZone.ICON;
3933+
3934+ /* Cursors for different areas */
3935+ private Gdk.Cursor editable_cursor;
3936+ private Gdk.Cursor activatable_cursor;
3937+ private Gdk.Cursor blank_cursor;
3938+ private Gdk.Cursor selectable_cursor;
3939+
3940+ private GLib.List<GLib.AppInfo> open_with_apps;
3941+ protected GLib.List<GOF.Directory.Async>? loaded_subdirectories = null;
3942+ protected GLib.List<unowned GOF.File> selected_files = null ;
3943+ private GLib.List<unowned GOF.File>? templates = null;
3944+
3945+ private GLib.AppInfo default_app;
3946+ private Gtk.TreePath selection_before_delete;
3947+ private Gtk.TreePath? hover_path = null;
3948+
3949+ private bool selection_was_removed = false;
3950+ public bool select_added_files = false;
3951+ protected bool renaming = false;
3952+ private bool updates_frozen = false;
3953+ protected bool tree_frozen = false;
3954+ private bool in_trash = false;
3955+ protected bool is_loading;
3956+ protected bool helpers_shown;
3957+ private uint select_timeout_id = 0;
3958+
3959+ private Gtk.Widget view;
3960+ private unowned Marlin.ClipboardManager clipboard;
3961+ protected FM.ListModel model;
3962+ protected Marlin.IconRenderer icon_renderer;
3963+ protected unowned Marlin.View.Slot slot;
3964+ protected unowned Marlin.View.Window window; /*For convenience - this can be derived from slot */
3965+ protected static DndHandler dnd_handler = new FM.DndHandler ();
3966+
3967+ public signal void path_change_request (GLib.File location, int flag = 0, bool new_root = true);
3968+
3969+
3970+ public AbstractDirectoryView (Marlin.View.Slot _slot) {
3971+ slot = _slot;
3972+ window = _slot.window;
3973+ editable_cursor = new Gdk.Cursor (Gdk.CursorType.XTERM);
3974+ activatable_cursor = new Gdk.Cursor (Gdk.CursorType.HAND1);
3975+ selectable_cursor = new Gdk.Cursor (Gdk.CursorType.ARROW);
3976+ blank_cursor = new Gdk.Cursor (Gdk.CursorType.CROSSHAIR);
3977+ clipboard = ((Marlin.Application)(window.application)).get_clipboard_manager ();
3978+ icon_renderer = new Marlin.IconRenderer ();
3979+ thumbnailer = Marlin.Thumbnailer.get ();
3980+ model = GLib.Object.@new (FM.ListModel.get_type (), null) as FM.ListModel;
3981+ Preferences.settings.bind ("single-click", this, "single_click_mode", SettingsBindFlags.GET);
3982+
3983+ /* Currently, "single-click rename" is disabled, matching existing UI
3984+ * Currently, "activate on blank" is enabled, matching existing UI
3985+ * Currently, "right margin unselects all" is disabled, matching existing UI
3986+ */
3987+
3988+ set_up__menu_actions ();
3989+ set_up_directory_view ();
3990+ view = create_view ();
3991+
3992+ if (view != null) {
3993+ add (view);
3994+ show_all ();
3995+ connect_drag_drop_signals (view);
3996+ view.add_events (Gdk.EventMask.POINTER_MOTION_MASK);
3997+ view.motion_notify_event.connect (on_motion_notify_event);
3998+ view.leave_notify_event.connect (on_leave_notify_event);
3999+ view.enter_notify_event.connect (on_enter_notify_event);
4000+ view.key_press_event.connect (on_view_key_press_event);
4001+ view.button_press_event.connect (on_view_button_press_event);
4002+ view.button_release_event.connect (on_view_button_release_event);
4003+ view.draw.connect (on_view_draw);
4004+ }
4005+
4006+ freeze_tree (); /* speed up loading of icon view. Thawed when directory loaded */
4007+ set_up_zoom_level ();
4008+ change_zoom_level ();
4009+ }
4010+
4011+ ~AbstractDirectoryView () {
4012+ loaded_subdirectories.@foreach ((dir) => {
4013+ remove_subdirectory (dir);
4014+ });
4015+ }
4016+
4017+ protected virtual void set_up_name_renderer () {
4018+ name_renderer.editable = false;
4019+ name_renderer.follow_state = true;
4020+ name_renderer.edited.connect (on_name_edited);
4021+ name_renderer.editing_canceled.connect (on_name_editing_canceled);
4022+ name_renderer.editing_started.connect (on_name_editing_started);
4023+ }
4024+
4025+ private void set_up_directory_view () {
4026+ set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
4027+ set_shadow_type (Gtk.ShadowType.NONE);
4028+
4029+ size_allocate.connect_after (on_size_allocate);
4030+ button_press_event.connect (on_button_press_event);
4031+ popup_menu.connect (on_popup_menu);
4032+
4033+ unrealize.connect (() => {
4034+ clipboard.changed.disconnect (on_clipboard_changed);
4035+ });
4036+
4037+ realize.connect (() => {
4038+ clipboard.changed.connect (on_clipboard_changed);
4039+ on_clipboard_changed ();
4040+ });
4041+
4042+ scroll_event.connect (on_scroll_event);
4043+
4044+ get_vadjustment ().value_changed.connect ((alloc) => {
4045+ schedule_thumbnail_timeout ();
4046+ });
4047+
4048+ (GOF.Preferences.get_default ()).notify["show-hidden-files"].connect (on_show_hidden_files_changed);
4049+ (GOF.Preferences.get_default ()).notify["interpret-desktop-files"].connect (on_interpret_desktop_files_changed);
4050+
4051+ connect_directory_handlers (slot.directory);
4052+
4053+ model.row_deleted.connect (on_row_deleted);
4054+ model.row_deleted.connect_after (after_restore_selection);
4055+
4056+ model.sort_column_changed.connect (on_sort_column_changed);
4057+ model.set_sort_column_id (slot.directory.file.sort_column_id, slot.directory.file.sort_order);
4058+
4059+ }
4060+
4061+ private void set_up__menu_actions () {
4062+ selection_actions = new GLib.SimpleActionGroup ();
4063+ selection_actions.add_action_entries (selection_entries, this);
4064+ insert_action_group ("selection", selection_actions);
4065+
4066+ background_actions = new GLib.SimpleActionGroup ();
4067+ background_actions.add_action_entries (background_entries, this);
4068+ insert_action_group ("background", background_actions);
4069+
4070+ common_actions = new GLib.SimpleActionGroup ();
4071+ common_actions.add_action_entries (common_entries, this);
4072+ insert_action_group ("common", common_actions);
4073+
4074+ action_set_state (background_actions, "show_hidden", Preferences.settings.get_boolean ("show-hiddenfiles"));
4075+ }
4076+
4077+ public void zoom_in () {
4078+ zoom_level = zoom_level + 1;
4079+ }
4080+
4081+ public void zoom_out () {
4082+ zoom_level = zoom_level - 1;
4083+ }
4084+
4085+ private void set_up_zoom_level () {
4086+ zoom_level = get_set_up_zoom_level ();
4087+ model.set_property ("size", icon_size);
4088+ }
4089+
4090+ public void zoom_normal () {
4091+ zoom_level = get_normal_zoom_level ();
4092+ }
4093+
4094+ public void select_first_for_empty_selection () {
4095+ if (selected_files == null)
4096+ set_cursor (new Gtk.TreePath.from_indices (0), false, true, true);
4097+ }
4098+
4099+ public void select_glib_files (GLib.List<GLib.File> location_list, GLib.File? focus_location) {
4100+ updates_frozen = true;
4101+ unselect_all ();
4102+ GLib.List<GOF.File>? file_list = null;
4103+
4104+ if (focus_location == null)
4105+ focus_location = location_list.first ().data;
4106+
4107+ location_list.@foreach ((loc) => {
4108+ file_list.prepend (GOF.File.@get (loc));
4109+ });
4110+
4111+ /* Because the Icon View disconnects the model while loading, we need to wait until
4112+ * the tree is thawed and the model reconnected before selecting the files */
4113+ select_timeout_id = GLib.Timeout.add (100, () => {
4114+ if (tree_frozen)
4115+ return true;
4116+
4117+ file_list.@foreach ((file) => {
4118+ var iter = Gtk.TreeIter ();
4119+
4120+ if (model.get_first_iter_for_file (file, out iter)) {
4121+ Gtk.TreePath path = model.get_path (iter);
4122+ if (path != null) {
4123+ if (focus_location == file.location)
4124+ set_cursor (path, false, true, false); /* set cursor and select */
4125+ else
4126+ select_path (path);
4127+ }
4128+ }
4129+ });
4130+ select_timeout_id = 0;
4131+ return false;
4132+ });
4133+
4134+ updates_frozen = false;
4135+ update_selected_files ();
4136+ notify_selection_changed ();
4137+ grab_focus ();
4138+ }
4139+
4140+ public unowned GLib.List<GLib.AppInfo> get_open_with_apps () {
4141+ return open_with_apps;
4142+ }
4143+
4144+ public unowned GLib.AppInfo get_default_app () {
4145+ return default_app;
4146+ }
4147+
4148+ public void set_updates_frozen (bool freeze) {
4149+ if (freeze && !updates_frozen)
4150+ freeze_updates ();
4151+ else if (!freeze && updates_frozen)
4152+ unfreeze_updates ();
4153+ }
4154+
4155+ public bool get_updates_frozen () {
4156+ return updates_frozen;
4157+ }
4158+
4159+ protected void freeze_updates () {
4160+ updates_frozen = true;
4161+ slot.directory.freeze_update = true;
4162+ action_set_enabled (selection_actions, "cut", false);
4163+ action_set_enabled (common_actions, "copy", false);
4164+ action_set_enabled (common_actions, "paste_into", false);
4165+ action_set_enabled (window.win_actions, "select_all", false);
4166+
4167+ size_allocate.disconnect (on_size_allocate);
4168+ clipboard.changed.disconnect (on_clipboard_changed);
4169+ view.enter_notify_event.disconnect (on_enter_notify_event);
4170+ view.key_press_event.disconnect (on_view_key_press_event);
4171+ }
4172+
4173+ protected void unfreeze_updates () {
4174+ updates_frozen = false;
4175+ slot.directory.freeze_update = false;;
4176+ update_menu_actions ();
4177+ size_allocate.connect (on_size_allocate);
4178+ clipboard.changed.connect (on_clipboard_changed);
4179+ view.enter_notify_event.connect (on_enter_notify_event);
4180+ view.key_press_event.connect (on_view_key_press_event);
4181+ }
4182+
4183+ public new void grab_focus () {
4184+ if (view.get_realized ())
4185+ view.grab_focus ();
4186+ else { /* wait until realized */
4187+ GLib.Timeout.add (100, () => {
4188+ view.grab_focus ();
4189+ return !view.get_realized ();
4190+ });
4191+ }
4192+ }
4193+
4194+ public unowned GLib.List<unowned GOF.File> get_selected_files () {
4195+ return selected_files;
4196+ }
4197+
4198+ public bool is_frozen () {
4199+ return updates_frozen;
4200+ }
4201+
4202+/*** Protected Methods */
4203+ protected void set_active_slot (bool scroll = true) {
4204+ slot.active (scroll);
4205+ }
4206+
4207+ protected void load_location (GLib.File location) {
4208+ path_change_request (location, Marlin.OpenFlag.DEFAULT, false);
4209+ }
4210+
4211+ protected void load_root_location (GLib.File location) {
4212+ path_change_request (location, Marlin.OpenFlag.DEFAULT, true);
4213+ }
4214+
4215+ /** Operations on selections */
4216+ protected void activate_selected_items (Marlin.OpenFlag flag = Marlin.OpenFlag.DEFAULT,
4217+ GLib.List<unowned GOF.File> selection = get_selected_files ()) {
4218+ if (updates_frozen || in_trash)
4219+ return;
4220+
4221+ uint nb_elem = selection.length ();
4222+
4223+ if (nb_elem < 1)
4224+ return;
4225+
4226+ unowned Gdk.Screen screen = Eel.gtk_widget_get_screen (this);
4227+ bool only_folders = selection_only_contains_folders (selection);
4228+
4229+ if (nb_elem < 10 && (default_app == null || only_folders)) {
4230+ /* launch each selected file individually ignoring selections greater than 10 */
4231+ bool only_one_file = (nb_elem == 1);
4232+
4233+ foreach (unowned GOF.File file in selection) {
4234+ /* Prevent too rapid activation of files - causes New Tab to crash for example */
4235+ GLib.Timeout.add (50, () => {
4236+ activate_file (file, screen, flag, only_one_file);
4237+ return false;
4238+ });
4239+ }
4240+ } else if (default_app != null)
4241+ open_files_with (default_app, selection);
4242+ }
4243+
4244+ protected void preview_selected_items () {
4245+ if (previewer == null) /* At present this is the case! */
4246+ activate_selected_items (Marlin.OpenFlag.DEFAULT);
4247+ else {
4248+ unowned GLib.List<unowned GOF.File>? selection = get_selected_files ();
4249+
4250+ if (selection == null)
4251+ return;
4252+
4253+ Gdk.Screen screen = Eel.gtk_widget_get_screen (this);
4254+ GLib.List<GLib.File> location_list = null;
4255+ GOF.File file = selection.data;
4256+ location_list.prepend (file.location);
4257+ Gdk.AppLaunchContext context = screen.get_display ().get_app_launch_context ();
4258+ try {
4259+ GLib.AppInfo previewer_app = GLib.AppInfo.create_from_commandline (previewer, null, 0);
4260+ previewer_app.launch (location_list, context as GLib.AppLaunchContext);
4261+ } catch (GLib.Error error) {
4262+ Eel.show_error_dialog (_("Failed to preview"), error.message, null);
4263+ }
4264+ }
4265+ }
4266+
4267+ protected void select_gof_file (GOF.File file) {
4268+ var iter = Gtk.TreeIter ();
4269+
4270+ if (!model.get_first_iter_for_file (file, out iter))
4271+ return; /* file not in model */
4272+
4273+ var path = model.get_path (iter);
4274+ set_cursor (path, false, true, false);
4275+ }
4276+
4277+ protected void add_gof_file_to_selection (GOF.File file) {
4278+ var iter = Gtk.TreeIter ();
4279+
4280+ if (!model.get_first_iter_for_file (file, out iter))
4281+ return; /* file not in model */
4282+
4283+ var path = model.get_path (iter);
4284+ select_path (path);
4285+ }
4286+
4287+ protected void after_restore_selection (Gtk.TreePath path) {
4288+ set_cursor (selection_before_delete, false, true, false);
4289+ selection_before_delete = null;
4290+ }
4291+
4292+ /** Directory signal handlers. */
4293+ /* Signal could be from subdirectory as well as slot directory */
4294+ protected void connect_directory_handlers (GOF.Directory.Async dir) {
4295+ assert (dir != null);
4296+ dir.file_loaded.connect (on_directory_file_loaded);
4297+ dir.file_added.connect (on_directory_file_added);
4298+ dir.file_changed.connect (on_directory_file_changed);
4299+ dir.file_deleted.connect (on_directory_file_deleted);
4300+ dir.icon_changed.connect (on_directory_file_icon_changed);
4301+ dir.done_loading.connect (on_directory_done_loading);
4302+ dir.thumbs_loaded.connect (on_directory_thumbs_loaded);
4303+ }
4304+
4305+ protected void disconnect_directory_handlers (GOF.Directory.Async dir) {
4306+ /* If the directory is still loading the file_loaded signal handler
4307+ /* will not have been disconnected */
4308+ if (dir.is_loading ())
4309+ dir.file_loaded.disconnect (on_directory_file_loaded);
4310+
4311+ dir.file_added.disconnect (on_directory_file_added);
4312+ dir.file_changed.disconnect (on_directory_file_changed);
4313+ dir.file_deleted.disconnect (on_directory_file_deleted);
4314+ dir.icon_changed.disconnect (on_directory_file_icon_changed);
4315+ dir.done_loading.disconnect (on_directory_done_loading);
4316+ dir.thumbs_loaded.disconnect (on_directory_thumbs_loaded);
4317+ }
4318+
4319+ public void change_directory (GOF.Directory.Async old_dir, GOF.Directory.Async new_dir) {
4320+ cancel_thumbnailing ();
4321+ freeze_tree ();
4322+ old_dir.cancel ();
4323+ disconnect_directory_handlers (old_dir);
4324+ block_model ();
4325+
4326+ loaded_subdirectories.@foreach ((dir) => {
4327+ remove_subdirectory (dir);
4328+ });
4329+
4330+ loaded_subdirectories = null;
4331+ model.clear ();
4332+ unblock_model ();
4333+
4334+ connect_directory_handlers (new_dir);
4335+ update_menu_actions ();
4336+ model.set_sort_column_id (slot.directory.file.sort_column_id, slot.directory.file.sort_order);
4337+ }
4338+
4339+ public void reload () {
4340+ change_directory (slot.directory, slot.directory);
4341+ }
4342+
4343+ protected void connect_drag_drop_signals (Gtk.Widget widget) {
4344+ /* Set up as drop site */
4345+ Gtk.drag_dest_set (widget, Gtk.DestDefaults.MOTION, drop_targets, Gdk.DragAction.ASK | file_drag_actions);
4346+ widget.drag_drop.connect (on_drag_drop);
4347+ widget.drag_data_received.connect (on_drag_data_received);
4348+ widget.drag_leave.connect (on_drag_leave);
4349+ widget.drag_motion.connect (on_drag_motion);
4350+
4351+ /* Set up as drag source */
4352+ Gtk.drag_source_set (widget, Gdk.ModifierType.BUTTON1_MASK, drag_targets, file_drag_actions);
4353+ widget.drag_begin.connect (on_drag_begin);
4354+ widget.drag_data_get.connect (on_drag_data_get);
4355+ widget.drag_data_delete.connect (on_drag_data_delete);
4356+ widget.drag_end.connect (on_drag_end);
4357+ }
4358+
4359+ protected void cancel_drag_timer () {
4360+ disconnect_drag_timeout_motion_and_release_events ();
4361+ cancel_timeout (ref drag_timer_id);
4362+ }
4363+
4364+ protected void cancel_thumbnailing () {
4365+ slot.directory.cancel ();
4366+ cancel_timeout (ref thumbnail_source_id);
4367+
4368+ if (thumbnail_request > 0) {
4369+ thumbnailer.dequeue (thumbnail_request);
4370+ thumbnail_request = 0;
4371+ }
4372+ }
4373+
4374+ protected bool is_drag_pending () {
4375+ return drag_has_begun;
4376+ }
4377+
4378+ protected bool selection_only_contains_folders (GLib.List<unowned GOF.File> list) {
4379+ bool only_folders = true;
4380+
4381+ list.@foreach ((file) => {
4382+ if (!file.is_folder ())
4383+ only_folders = false;
4384+ });
4385+
4386+ return only_folders;
4387+ }
4388+
4389+ /** Handle scroll events */
4390+ protected bool handle_scroll_event (Gdk.EventScroll event) {
4391+ if (updates_frozen)
4392+ return true;
4393+
4394+ if ((event.state & Gdk.ModifierType.CONTROL_MASK) > 0) {
4395+ switch (event.direction) {
4396+ case Gdk.ScrollDirection.UP:
4397+ zoom_in ();
4398+ return true;
4399+
4400+ case Gdk.ScrollDirection.DOWN:
4401+ zoom_out ();
4402+ return true;
4403+
4404+ case Gdk.ScrollDirection.SMOOTH:
4405+ double delta_x, delta_y;
4406+ event.get_scroll_deltas (out delta_x, out delta_y);
4407+ /* try to emulate a normal scrolling event by summing deltas.
4408+ * step size of 0.5 chosen to match sensitivity */
4409+ total_delta_y += delta_y;
4410+
4411+ if (total_delta_y >= 0.5) {
4412+ total_delta_y = 0;
4413+ zoom_out ();
4414+ } else if (total_delta_y <= -0.5) {
4415+ total_delta_y = 0;
4416+ zoom_in ();
4417+ }
4418+ return true;
4419+
4420+ default:
4421+ break;
4422+ }
4423+ }
4424+ return false;
4425+ }
4426+
4427+ protected void show_or_queue_context_menu (Gdk.Event event) {
4428+ if (selected_files != null)
4429+ queue_context_menu (event);
4430+ else
4431+ show_context_menu (event);
4432+ }
4433+
4434+ protected unowned GLib.List<unowned GOF.File> get_selected_files_for_transfer (GLib.List<unowned GOF.File> selection = get_selected_files ()) {
4435+ unowned GLib.List<unowned GOF.File> list = null;
4436+
4437+ selection.@foreach ((file) => {
4438+ list.prepend (file);
4439+ });
4440+
4441+ return list;
4442+ }
4443+
4444+/*** Private methods */
4445+ /** File operations */
4446+
4447+ private void activate_file (GOF.File file, Gdk.Screen? screen, Marlin.OpenFlag flag, bool only_one_file) {
4448+ if (updates_frozen || in_trash)
4449+ return;
4450+
4451+ debug ("activate file %s only one file %s", file.uri, only_one_file.to_string ());
4452+ GLib.File location = file.location.dup ();
4453+
4454+ if (screen == null)
4455+ screen = Eel.gtk_widget_get_screen (this);
4456+
4457+ if (file.is_folder ()) {
4458+ switch (flag) {
4459+ case Marlin.OpenFlag.NEW_TAB:
4460+ window.add_tab (location, Marlin.ViewMode.CURRENT);
4461+ break;
4462+
4463+ case Marlin.OpenFlag.NEW_WINDOW:
4464+ window.add_window(location, Marlin.ViewMode.CURRENT);
4465+ break;
4466+
4467+ default:
4468+ if (only_one_file)
4469+ load_location (location);
4470+
4471+ break;
4472+ }
4473+ } else if (only_one_file && file.is_root_network_folder ())
4474+ load_location (location);
4475+ else if (only_one_file && file.is_executable ())
4476+ file.execute (screen, null, null);
4477+ else if (only_one_file && default_app != null)
4478+ file.open_single (screen, default_app);
4479+ else
4480+ warning ("Unable to activate this file. Default app is %s", default_app != null ? default_app.get_name () : "null");
4481+ }
4482+
4483+ private void trash_or_delete_files (GLib.List<unowned GOF.File> file_list, bool delete_if_already_in_trash) {
4484+ GLib.List<GLib.File> locations = null;
4485+
4486+ file_list.@foreach ((file) => {
4487+ locations.prepend (file.location);
4488+ });
4489+
4490+ if (locations != null) {
4491+ locations.reverse ();
4492+ Marlin.FileOperations.trash_or_delete (locations,
4493+ window as Gtk.Window,
4494+ (void*) after_trash_or_delete,
4495+ null);
4496+ }
4497+ }
4498+
4499+ private void add_file (GOF.File file, GOF.Directory.Async dir) {
4500+ model.add_file (file, dir);
4501+
4502+ if (select_added_files)
4503+ add_gof_file_to_selection (file);
4504+ }
4505+
4506+ private void new_empty_file (string? parent_uri = null) {
4507+ if (parent_uri == null)
4508+ parent_uri = slot.directory.file.uri;
4509+
4510+ Marlin.FileOperations.new_file (this as Gtk.Widget,
4511+ null,
4512+ parent_uri,
4513+ null,
4514+ null,
4515+ 0,
4516+ (Marlin.CreateCallback?) create_file_done,
4517+ this);
4518+ }
4519+
4520+ private void new_empty_folder () {
4521+ Marlin.FileOperations.new_folder (null, null, slot.location, (Marlin.CreateCallback?) create_file_done, this);
4522+ }
4523+
4524+ protected void rename_file (GOF.File file_to_rename) {
4525+ unselect_all ();
4526+ select_gof_file (file_to_rename);
4527+
4528+ if (file_to_rename.is_writable ())
4529+ start_renaming_file (file_to_rename, false);
4530+ else
4531+ warning ("You do not have permission to rename this file");
4532+ }
4533+
4534+
4535+/** File operation callbacks */
4536+ static void create_file_done (GLib.File? new_file, void* data) {
4537+ if (new_file == null)
4538+ return;
4539+
4540+ var view = (FM.AbstractDirectoryView)data;
4541+ var file_to_rename = GOF.File.@get (new_file);
4542+ /* Allow time for the file to appear in the tree model before renaming */
4543+ GLib.Timeout.add (50, () => {
4544+ view.rename_file (file_to_rename);
4545+ return false;
4546+ });
4547+ }
4548+
4549+ private void after_trash_or_delete (GLib.HashTable? debuting_files, bool user_cancel, void* data) {
4550+ if (user_cancel)
4551+ selection_was_removed = false;
4552+ }
4553+
4554+ private void trash_or_delete_selected_files () {
4555+ /* This might be rapidly called multiple times for the same selection
4556+ * when using keybindings. So we remember if the current selection
4557+ * was already removed (but the view doesn't know about it yet).
4558+ */
4559+ if (!selection_was_removed) {
4560+ unowned GLib.List<unowned GOF.File> selection = get_selected_files_for_transfer ();
4561+ if (selection != null) {
4562+ selection_was_removed = true;
4563+ trash_or_delete_files (selection, true);
4564+ }
4565+ }
4566+ }
4567+
4568+
4569+ private void delete_selected_files () {
4570+ unowned GLib.List<unowned GOF.File> selection = get_selected_files_for_transfer ();
4571+ if (selection == null)
4572+ return;
4573+
4574+ GLib.List<GLib.File> locations = null;
4575+
4576+ selection.@foreach ((file) => {
4577+ locations.prepend (file.location);
4578+ });
4579+
4580+ locations.reverse ();
4581+ Marlin.FileOperations.@delete (locations, window as Gtk.Window, null, null);
4582+ }
4583+
4584+/** Signal Handlers */
4585+
4586+ /** Menu actions */
4587+ /** Selection actions */
4588+
4589+ private void on_selection_action_rename (GLib.SimpleAction action, GLib.Variant? param) {
4590+ rename_selected_file ();
4591+ }
4592+
4593+ private void rename_selected_file () {
4594+ if (selected_files == null)
4595+ return;
4596+
4597+ if (selected_files.next != null)
4598+ /* TODO invoke batch renamer see bug #1014122*/
4599+ warning ("Cannot rename multiple files (yet) - renaming first only");
4600+
4601+ var file = selected_files.first ().data;
4602+ bool preselect_whole_name = file.is_folder ();
4603+
4604+ start_renaming_file (file, preselect_whole_name);
4605+ }
4606+
4607+ private void on_selection_action_cut (GLib.SimpleAction action, GLib.Variant? param) {
4608+ unowned GLib.List<unowned GOF.File> selection = get_selected_files_for_transfer ();
4609+ clipboard.cut_files (selection);
4610+ }
4611+
4612+ private void on_selection_action_trash (GLib.SimpleAction action, GLib.Variant? param) {
4613+ trash_or_delete_selected_files ();
4614+ }
4615+
4616+ private void on_selection_action_delete (GLib.SimpleAction action, GLib.Variant? param) {
4617+ delete_selected_files ();
4618+ }
4619+
4620+ private void on_selection_action_restore (GLib.SimpleAction action, GLib.Variant? param) {
4621+ unowned GLib.List<unowned GOF.File> selection = get_selected_files_for_transfer ();
4622+ Marlin.restore_files_from_trash (selection, window);
4623+ }
4624+
4625+ private void on_selection_action_open_executable (GLib.SimpleAction action, GLib.Variant? param) {
4626+ unowned GLib.List<unowned GOF.File> selection = get_files_for_action ();
4627+ GOF.File file = selection.data as GOF.File;
4628+ unowned Gdk.Screen screen = Eel.gtk_widget_get_screen (this);
4629+ file.execute (screen, null, null);
4630+ }
4631+
4632+ private void on_selection_action_open_with_default (GLib.SimpleAction action, GLib.Variant? param) {
4633+ activate_selected_items (Marlin.OpenFlag.DEFAULT);
4634+ }
4635+
4636+ private void on_selection_action_open_with_app (GLib.SimpleAction action, GLib.Variant? param) {
4637+ var index = int.parse (param.get_string ());
4638+ open_files_with (open_with_apps.nth_data ((uint)index), get_files_for_action ());
4639+ }
4640+
4641+ private void on_selection_action_open_with_other_app () {
4642+ unowned GLib.List<unowned GOF.File> selection = get_files_for_action ();
4643+ GOF.File file = selection.data as GOF.File;
4644+
4645+ Gtk.DialogFlags flags = Gtk.DialogFlags.MODAL |
4646+ Gtk.DialogFlags.DESTROY_WITH_PARENT |
4647+ Gtk.DialogFlags.USE_HEADER_BAR;
4648+
4649+ var dialog = new Gtk.AppChooserDialog (window, flags, file.location);
4650+ dialog.set_heading (_("Select an application"));
4651+
4652+ var check_default = new Gtk.CheckButton.with_label (_("Set as default"));
4653+ dialog.get_content_area ().pack_start (check_default, false, false, 0);
4654+ dialog.show_all ();
4655+
4656+ int response = dialog.run ();
4657+
4658+ if (response == Gtk.ResponseType.OK) {
4659+ var app =dialog.get_app_info ();
4660+ if (check_default.get_active ()) {
4661+ try {
4662+ app.set_as_default_for_type (file.get_ftype ());
4663+ }
4664+ catch (GLib.Error error) {
4665+ critical ("Could not set as default: %s", error.message);
4666+ }
4667+ }
4668+ open_files_with (app, selection);
4669+ }
4670+
4671+ dialog.destroy ();
4672+ }
4673+
4674+ private void on_common_action_bookmark (GLib.SimpleAction action, GLib.Variant? param) {
4675+ if (selected_files != null)
4676+ window.sidebar.add_uri (selected_files.data.uri);
4677+ else
4678+ window.sidebar.add_uri (slot.directory.file.uri);
4679+ }
4680+
4681+ /** Background actions */
4682+
4683+ private void change_state_show_hidden (GLib.SimpleAction action) {
4684+ window.change_state_show_hidden (action);
4685+ }
4686+
4687+ private void on_background_action_new (GLib.SimpleAction action, GLib.Variant? param) {
4688+ switch (param.get_string ()) {
4689+ case "FOLDER":
4690+ new_empty_folder ();
4691+ break;
4692+
4693+ case "FILE":
4694+ new_empty_file ();
4695+ break;
4696+
4697+ default:
4698+ break;
4699+ }
4700+ }
4701+
4702+ private void on_background_action_create_from (GLib.SimpleAction action, GLib.Variant? param) {
4703+ int index = int.parse (param.get_string ());
4704+ create_from_template (templates.nth_data ((uint)index));
4705+ }
4706+
4707+ private void on_background_action_sort_by_changed (GLib.SimpleAction action, GLib.Variant? val) {
4708+ string sort = val.get_string ();
4709+ set_sort (sort, false);
4710+ }
4711+ private void on_background_action_reverse_changed (GLib.SimpleAction action, GLib.Variant? val) {
4712+ set_sort (null, true);
4713+ }
4714+
4715+ private void set_sort (string? col_name, bool reverse) {
4716+ int sort_column_id;
4717+ Gtk.SortType sort_order;
4718+
4719+ if (model.get_sort_column_id (out sort_column_id, out sort_order)) {
4720+ if (col_name != null)
4721+ sort_column_id = get_column_id_from_string (col_name);
4722+
4723+ if (reverse) {
4724+ if (sort_order == Gtk.SortType.ASCENDING)
4725+ sort_order = Gtk.SortType.DESCENDING;
4726+ else
4727+ sort_order = Gtk.SortType.ASCENDING;
4728+ }
4729+
4730+ model.set_sort_column_id (sort_column_id, sort_order);
4731+ }
4732+ }
4733+
4734+ /** Common actions */
4735+
4736+ private void on_common_action_open_in (GLib.SimpleAction action, GLib.Variant? param) {
4737+ default_app = null;
4738+
4739+ switch (param.get_string ()) {
4740+ case "TAB":
4741+ activate_selected_items (Marlin.OpenFlag.NEW_TAB, get_files_for_action ());
4742+ break;
4743+
4744+ case "WINDOW":
4745+ activate_selected_items (Marlin.OpenFlag.NEW_WINDOW, get_files_for_action ());
4746+ break;
4747+
4748+ case "TERMINAL":
4749+ open_selected_in_terminal (get_files_for_action ());
4750+ break;
4751+
4752+ default:
4753+ break;
4754+ }
4755+ }
4756+
4757+ private void open_selected_in_terminal (GLib.List<unowned GOF.File> selection = get_selected_files ()) {
4758+ var terminal = new GLib.DesktopAppInfo (Marlin.OPEN_IN_TERMINAL_DESKTOP_ID);
4759+
4760+ if (terminal != null)
4761+ open_files_with (terminal, selection);
4762+ }
4763+
4764+ private void on_common_action_properties (GLib.SimpleAction action, GLib.Variant? param) {
4765+ new Marlin.View.PropertiesWindow (get_files_for_action (), this, window);
4766+ }
4767+
4768+ private void on_common_action_copy (GLib.SimpleAction action, GLib.Variant? param) {
4769+ clipboard.copy_files (get_selected_files_for_transfer (get_files_for_action ()));
4770+ }
4771+
4772+ private void on_common_action_paste_into (GLib.SimpleAction action, GLib.Variant? param) {
4773+ var file = get_files_for_action ().nth_data (0);
4774+
4775+ if (file != null && clipboard.get_can_paste ()) {
4776+ prepare_to_select_added_files ();
4777+ if (file.is_folder () && !clipboard.has_file (file))
4778+ clipboard.paste_files (file.get_target_location (), this as Gtk.Widget, null);
4779+ else
4780+ clipboard.paste_files (slot.directory.location, this as Gtk.Widget, null);
4781+ }
4782+ }
4783+
4784+
4785+ private void on_directory_file_added (GOF.Directory.Async dir, GOF.File file) {
4786+ add_file (file, dir);
4787+ }
4788+
4789+ private void on_directory_file_loaded (GOF.Directory.Async dir, GOF.File file) {
4790+ select_added_files = false;
4791+ add_file (file, dir);
4792+ }
4793+
4794+ private void on_directory_file_changed (GOF.Directory.Async dir, GOF.File file) {
4795+ remove_marlin_icon_info_cache (file);
4796+ model.file_changed (file, dir);
4797+ /* 2nd parameter is for returned request id if required - we do not use it? */
4798+ /* This is required if we need to dequeue the request */
4799+ thumbnailer.queue_file (file, null, false);
4800+ }
4801+
4802+ private void on_directory_file_icon_changed (GOF.Directory.Async dir, GOF.File file) {
4803+ model.file_changed (file, dir);
4804+ }
4805+
4806+ private void on_directory_file_deleted (GOF.Directory.Async dir, GOF.File file) {
4807+ remove_marlin_icon_info_cache (file);
4808+ model.remove_file (file, dir);
4809+ if (file.is_folder ()) {
4810+ var file_dir = GOF.Directory.Async.cache_lookup (file.location);
4811+ if (file_dir != null) {
4812+ file_dir.purge_dir_from_cache ();
4813+ slot.folder_deleted (file, file_dir);
4814+ }
4815+ }
4816+ }
4817+
4818+ private void on_directory_done_loading (GOF.Directory.Async dir) {
4819+ debug ("DV directory done loading %s", dir.file.uri);
4820+ dir.file_loaded.disconnect (on_directory_file_loaded);
4821+ in_trash = (dir.file.uri == Marlin.TRASH_URI); /* trash cannot be subdirectory */
4822+ thaw_tree ();
4823+ queue_draw ();
4824+ }
4825+
4826+ private void on_directory_thumbs_loaded (GOF.Directory.Async dir) {
4827+ Marlin.IconInfo.infos_caches ();
4828+ }
4829+
4830+ /** Handle zoom level change */
4831+ private void on_zoom_level_changed (Marlin.ZoomLevel zoom) {
4832+ model.set_property ("size", icon_size);
4833+ change_zoom_level ();
4834+
4835+ if (get_realized ())
4836+ load_thumbnails (slot.directory, zoom);
4837+ }
4838+
4839+ /** Handle Preference changes */
4840+ private void on_show_hidden_files_changed (GLib.Object prefs, GLib.ParamSpec pspec) {
4841+ bool show = (prefs as GOF.Preferences).show_hidden_files;
4842+ if (!show) {
4843+ block_model ();
4844+ model.clear ();
4845+ }
4846+
4847+ directory_hidden_changed (slot.directory, show);
4848+ if (loaded_subdirectories != null)
4849+ loaded_subdirectories.@foreach ((dir) => {
4850+ directory_hidden_changed (dir, show);
4851+ });
4852+
4853+ if (!show)
4854+ unblock_model ();
4855+
4856+ action_set_state (background_actions, "show_hidden", show);
4857+ }
4858+
4859+ private void directory_hidden_changed (GOF.Directory.Async dir, bool show) {
4860+ dir.file_loaded.connect (on_directory_file_loaded); /* disconnected by on_done_loading callback.*/
4861+
4862+ if (show)
4863+ dir.load_hiddens ();
4864+ else
4865+ dir.load ();
4866+ }
4867+
4868+ private void on_interpret_desktop_files_changed () {
4869+ slot.directory.update_desktop_files ();
4870+ }
4871+
4872+ /** Handle popup menu events */
4873+ private bool on_popup_menu () {
4874+ Gdk.Event event = Gtk.get_current_event ();
4875+ show_or_queue_context_menu (event);
4876+ return true;
4877+ }
4878+
4879+ /** Handle Button events */
4880+ private bool on_drag_timeout_button_release (Gdk.EventButton event) {
4881+ /* Only active during drag timeout */
4882+ cancel_drag_timer ();
4883+
4884+ if (drag_button == Gdk.BUTTON_SECONDARY)
4885+ show_context_menu (event);
4886+
4887+ return true;
4888+ }
4889+
4890+ private bool on_button_press_event (Gdk.EventButton event) {
4891+ /* Extra mouse button action: button8 = "Back" button9 = "Forward" */
4892+ GLib.Action? action = null;
4893+ GLib.SimpleActionGroup main_actions = window.get_action_group ();
4894+ if (event.type == Gdk.EventType.BUTTON_PRESS) {
4895+ if (event.button == 8)
4896+ action = main_actions.lookup_action ("Back");
4897+ else if (event.button == 9)
4898+ action = main_actions.lookup_action ("Forward");
4899+
4900+ if (action != null) {
4901+ action.activate (null);
4902+ return true;
4903+ }
4904+ }
4905+ return false;
4906+ }
4907+
4908+/** Handle Motion events */
4909+ private bool on_drag_timeout_motion_notify (Gdk.EventMotion event) {
4910+ /* Only active during drag timeout */
4911+ Gdk.DragContext context;
4912+ var widget = get_real_view ();
4913+ int x = (int)event.x;
4914+ int y = (int)event.y;
4915+
4916+ if (Gtk.drag_check_threshold (widget, drag_x, drag_y, x, y)) {
4917+ cancel_drag_timer ();
4918+ should_activate = false;
4919+ var target_list = new Gtk.TargetList (drag_targets);
4920+ var actions = file_drag_actions;
4921+
4922+ if (drag_button == 3)
4923+ actions |= Gdk.DragAction.ASK;
4924+
4925+ context = Gtk.drag_begin_with_coordinates (widget,
4926+ target_list,
4927+ actions,
4928+ drag_button,
4929+ (Gdk.Event) event,
4930+ x, y);
4931+ return true;
4932+ } else
4933+ return false;
4934+ }
4935+
4936+/** Handle TreeModel events */
4937+ protected virtual void on_row_deleted (Gtk.TreePath path) {
4938+ GLib.List<Gtk.TreePath>? selected_paths = get_selected_paths ();
4939+ selection_before_delete = null;
4940+
4941+ /* Do nothing if the deleted row is not selected or there is more than one file selected */
4942+ if (selected_paths == null ||
4943+ selected_paths.length () != 1 ||
4944+ selected_paths.find_custom (path, Gtk.TreePath.compare) == null)
4945+
4946+ return;
4947+
4948+ /* Create a copy the path (we're not allowed to modify it in this handler) */
4949+ Gtk.TreePath path_copy = path.copy ();
4950+
4951+ /* Remember the selected path so that it can be restored after the row has
4952+ * been removed. If the first row is removed, select the first row after the
4953+ * removal, if any other row is removed, select the row before that one */
4954+ path_copy.prev ();
4955+ selection_before_delete = path_copy.copy ();
4956+ }
4957+
4958+/** Handle clipboard signal */
4959+ private void on_clipboard_changed () {
4960+ update_menu_actions ();
4961+ /* show possible change in appearance of cut items */
4962+ queue_draw ();
4963+ }
4964+
4965+/** Handle Selection changes */
4966+ public void notify_selection_changed () {
4967+ selection_was_removed = false;
4968+
4969+ if (!get_realized ())
4970+ return;
4971+
4972+ if (updates_frozen)
4973+ return;
4974+
4975+ update_menu_actions ();
4976+ window.selection_changed (get_selected_files ());
4977+ }
4978+
4979+ /** Handle size allocation event */
4980+ private void on_size_allocate (Gtk.Allocation allocation) {
4981+ schedule_thumbnail_timeout ();
4982+ }
4983+
4984+/** DRAG AND DROP */
4985+
4986+ /** Handle Drag source signals*/
4987+
4988+ private void on_drag_begin (Gdk.DragContext context) {
4989+ drag_has_begun = true;
4990+ should_activate = false;
4991+ drag_file_list = get_selected_files_for_transfer ();
4992+
4993+ if (drag_file_list == null)
4994+ return;
4995+
4996+ GOF.File file = drag_file_list.first ().data;
4997+
4998+ if (file != null && file.pix != null)
4999+ Gtk.drag_set_icon_pixbuf (context, file.pix, 0, 0);
5000+ else
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: