Merge lp:~matzipan/scratch/folder-manager-improvement into lp:~elementary-apps/scratch/scratch

Proposed by Zisu Andrei
Status: Needs review
Proposed branch: lp:~matzipan/scratch/folder-manager-improvement
Merge into: lp:~elementary-apps/scratch/scratch
Diff against target: 2056 lines (+647/-1169)
17 files modified
plugins/CMakeLists.txt (+1/-2)
plugins/filemanager/CMakeLists.txt (+0/-29)
plugins/filemanager/File.vala (+0/-207)
plugins/filemanager/FileManagerPlugin.vala (+0/-113)
plugins/filemanager/FileView.vala (+0/-297)
plugins/filemanager/Settings.vala (+0/-36)
plugins/filemanager/filemanager.plugin (+0/-10)
plugins/folder-manager/CMakeLists.txt (+3/-0)
plugins/folder-manager/File.vala (+112/-112)
plugins/folder-manager/FileItem.vala (+39/-0)
plugins/folder-manager/FileView.vala (+125/-292)
plugins/folder-manager/FolderItem.vala (+245/-0)
plugins/folder-manager/FolderManagerPlugin.vala (+49/-56)
plugins/folder-manager/Item.vala (+69/-0)
plugins/folder-manager/folder-manager.plugin (+4/-4)
schemas/CMakeLists.txt (+0/-1)
schemas/org.pantheon.scratch.plugins.file-manager.gschema.xml (+0/-10)
To merge this branch: bzr merge lp:~matzipan/scratch/folder-manager-improvement
Reviewer Review Type Date Requested Status
Jeremy Wootten code, function Approve
Zisu Andrei (community) Needs Resubmitting
Danielle Foré Pending
Review via email: mp+312195@code.launchpad.net

This proposal supersedes a proposal from 2016-11-18.

Commit message

Merge File Manager into Folder Manager and improve usability.

Description of the change

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

This LGTM. My only slight concern is that folder manager is currently a default plugin. So perhaps we should have this replace that instead of the file manager plugin. That way we don't have to monkey around with the defaults and people can upgrade to the new plugin seamlessly.

Revision history for this message
Zisu Andrei (matzipan) wrote : Posted in a previous version of this proposal

I just realized there's no "add folder" button either. I'll see what I can do about your comments too

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

Couple code style comments. If you're checking the same variable more than twice, make sure to use a switch/case statement :) https://elementary.io/docs/code/reference#indentation

1779. By Zisu Andrei

Simplify file/folder validity check. Move common construct/rename/trash code into superclass. GObject constructor for File

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

On compiling there is a warning:
"warning: method `Scratch.Plugins.FolderManager.File.reset_cache' never used".

See also inline comments.

Function review to follow.

review: Needs Fixing (code)
Revision history for this message
Jeremy Wootten (jeremywootten) wrote :

There are some issues in function - All but the first two may be considered outside the scope of this branch:

This branch:
1) rename a file causes another document to load because cursor does not follow (and, if open, file in document view not updated with new name)
2) Second opened folder saved in settings but not restored (removed on restarting)

Others?:
3) "New Folder" different from Files "untitled folder"
4) "New File" different from Files "new file".
5) Expanded status not saved in settings.

review: Needs Fixing (function)
Revision history for this message
Zisu Andrei (matzipan) wrote :

For 1 I have a nother branch [1] which fixes the issue, however, there is a limitation in Granite.SourceList which prevents elements from being sorted when they are being renamed in the list. I don't want to code a workaround in this branch since it will be fixed anyway.

Investigating 2.

5 would be outside of the scope of this branch. I believe trunk didn't do that either.

[1] https://code.launchpad.net/~matzipan/scratch/use-rename-event/+merge/312213

Revision history for this message
Zisu Andrei (matzipan) wrote :

One response to your comment inline. I basically just copied the code over from the original implementation. I will address it.

1780. By Zisu Andrei

Address raised issues and some slight refactoring on the open folder mechanism

Revision history for this message
Zisu Andrei (matzipan) wrote :

Issues were addressed.

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

Some further comments on the code (not necessarily blocking):

1) The class "Scratch.Plugins.FolderManager.Item" is described as abstract but not defined as such and it would be better if a public class had its own file.

2) The reference to "view" is not really required in "Item", only in "FolderItem" (and is it necessary to have a back reference to the Item's parent?).

3) The function "on_child_removed" is not currently used

Revision history for this message
Zisu Andrei (matzipan) wrote :

Also discovered an issue with deleting an opened folder (it doesn't remove the sidebar entry, it only trashes the file).

And there seems to be a problem with opening empty folders.

1781. By Zisu Andrei

Move Item to its own file

1782. By Zisu Andrei

Fix unused method warning

1783. By Zisu Andrei

Write settings on folder removal

Revision history for this message
Zisu Andrei (matzipan) wrote :

Addressed 1 and 3.

2 isn't true. FileItem uses view for view.start_editing_item (this).

I know it's bad practice, but I don't want to make it this branches' scope to refactor everything. I just wanted to get the basic usecases working.

I also fixed an issue where it wouldn't save to settings when the folder is closed.

Current problems:
* when a new folder or file is created, it only shows the editable entry for a short time.
* there seems to be a problem with opening empty folders.

1784. By Zisu Andrei

Remove folder on trash

1785. By Zisu Andrei

Stop selecting other items after deleting an item

1786. By Zisu Andrei

Files doesn't use brackets

1787. By Zisu Andrei

Add warning about files with no valid files

1788. By Zisu Andrei

Add break to make it more efficient

1789. By Zisu Andrei

Add new keyword to overriding function

1790. By Zisu Andrei

Fix FolderItems not editable after creation

Revision history for this message
Zisu Andrei (matzipan) wrote :

Just realized that SourceList headers are not visible when they have no children. I don't think there's anything I can change about that.

I will add a check against this.

I've spent 3 hours on the issue "when a new folder or file is created, it only shows the editable entry for a short time.". I could not tell why this is happening. I added a Idle.add() workaround and it worked like this.

Dan thinks this merge request is too large. Unfortunately, I don't think I can break this down into smaller chunks - I don't wanna be committing something I know wouldn't work. And I cannot get it smaller.

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

I agree that it is not easy to split this kind of branch, and you are saving 500 lines of code so it is worth the extra effort of checking for regressions.

The comment about back references is not critical and can be disregarded at least in the branch.

One of the revisions added the "new" keyword. Is there a reason you did this rather than mark the corresponding function in the abstract class "Item" as virtual?

There are still some issues with folder items that have just been created (using the folder manager plugin):

A
 1) Create a folder item in the root folder
 2) Immediately create another folder within that new folder

 Two subfolder items appear even though only one subfolder is created on disk. Deleting one of them causes both to disappear.

B
 1) Create a folder item in the root folder
 2) Immediately create a new file within that new folder - two file entries appear
 3) Delete one file entry - both disappear
 4) Create a new folder - one folder entry and one file entry appear
 5) Delete the file entry - only the file entry disappears.

review: Needs Fixing (function)
Revision history for this message
Jeremy Wootten (jeremywootten) wrote :

The double creation effect is due to the FileMonitor detecting the creation and adding another item. You need to block the monitor while creating an item internally.

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

Ignore last comment - it seems that is not the problem

Revision history for this message
Zisu Andrei (matzipan) wrote :

> One of the revisions added the "new" keyword. Is there a reason you did this rather than mark the corresponding function in the abstract class "Item" as virtual?

Initially I used "override", however, it seems "override" in Vala is not the same as in Java, so I removed it, and then the compiler noticed my declaration is shadowing a function in the parent class, and suggested I add "new" if this is intentional.

1791. By Zisu Andrei

Fix double creation on empty folder loading

Revision history for this message
Zisu Andrei (matzipan) wrote :

It's ready for review again.

I jotted down 2 future improvements here: https://bugs.launchpad.net/scratch/+bug/1646924

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

The last revisions fixes the duplicate creation problem. There is a small bug - the "newly_created_path" variable should be nulled after it has served its purpose. If not, if you create a new file/folder with the folder-manager and then delete it and recreate it with Files, both Files and the folder-manager go into edit mode. Better to check whether it is null before using it in an equality.

I have confirmed the future improvement bug.

review: Needs Fixing (code)
1792. By Zisu Andrei

Null the newly_created_path after it has been processed

Revision history for this message
Zisu Andrei (matzipan) wrote :

Thanks for catching that one. That's how I was intending to do it, but between my brain and my hands something got lost.

I believe the null check is not necessary. It seems to be running alright on my end.

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

I agree Vala does not mind nulls in equalities - it just felt wrong - and it may be a little more efficient to avoid invoking source.get_path () but it is not critical.

I'll give it another test tomorrow but it is looking good now.

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

If the "execute" permission is removed from a folder then the "add file" and "add folder" options are still active. If these are chosen then scratch hangs while maxing out a core. (Under these circumstances, Files gives a "permission denied" error dialog. However, if the folder is set to "read only" but has "execute" permission, attempting to add a file fails silently (with a error message in the terminal). Under these circumstances Files does not offer to create a file in the context menu.

I also notices that "Move to trash" is offered for all folders including the user home folder. Hopefully this would fail but I did not feel like trying it (!).

Revision history for this message
Zisu Andrei (matzipan) wrote :

I discovered one more issue, if I compile a C++ file with g++ bla.c, a.out shows up (unexpected). If I close Scratch and open it back up, the file doesn't show up anymore (expected).

Also if I click the a.out entry, it freezes my editor.

It's not clear to me why this happens.

review: Needs Fixing
1793. By Zisu Andrei

Check if folder is executable to avoid infinite loop

Revision history for this message
Zisu Andrei (matzipan) wrote :

Ok, so the above commit checks if the folder is executable before checking for file existence to avoid infinite looping.

However, there is much more involved work necessary to allow all files to track their attributes, like File does. For this, I have created the bug report [1].

For the "move to trash issue" I added a note in [2].

[1] https://bugs.launchpad.net/scratch/+bug/1650902
[2] https://bugs.launchpad.net/scratch/+bug/1646924

Revision history for this message
Zisu Andrei (matzipan) wrote :

Ok, I notice the a.out issue I reported above is a regression in trunk too, so I'll file a bug for this, since it's outside the scope of this branch.

review: Needs Resubmitting
Revision history for this message
Zisu Andrei (matzipan) wrote :

Found an issue: when you create a folder in Files, it gets created in Scratch just fine. If you create a file in that folder in Files, the folder should be marked as expandable in Scratch, but that does not happen.

review: Needs Fixing
1794. By Zisu Andrei

Monitor files and update unloaded folders

Revision history for this message
Zisu Andrei (matzipan) wrote :

Fixed.

review: Needs Resubmitting
1795. By Zisu Andrei

Move menu items case

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

I get a crash under the following conditions:

1. Open Scratch with the folder manager showing a folder.

2. Using Files, copy another folder in the folder that is open in Scratch.

3. Scratch crashes.

review: Needs Fixing (function)
Revision history for this message
Jeremy Wootten (jeremywootten) wrote :

Note: The crash mention above does not occur if the folder is empty or contains an empty folder.

1796. By Zisu Andrei

Fix segmentation fault on unloaded folder monitor event

1797. By Zisu Andrei

Only add valid files and directories to children and update unloaded directories better

Revision history for this message
Zisu Andrei (matzipan) wrote :

I have fixed the raised issue. It was introduced in my last commit, I didn't check everything - sorry for that.

I also added some improvements for unloaded folders and folder children validity.

review: Needs Resubmitting
Revision history for this message
Ralph Plawetzki (purejava) wrote :

Yeah, I also tested it. The issue described in https://code.launchpad.net/~matzipan/scratch/folder-manager-improvement/+merge/312195/comments/814590 is fixed with r 1797.

Another question: is it intended that one has to select both plugins (file manager and folder manager) in the plugins menu to activate the side bar?

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

The lastest revisions fix the crash previously noted, so I think this now good to go :)

The "File Manager" extension will be no longer needed once this is merged but will still be installed on some users systems and therefore will still appear in the extension list. I am not sure whether an update can uninstall this extension or whether it will need to be "blacklisted" so that it does not show. That can be done in another branch.

review: Approve (code, function)
Revision history for this message
Zisu Andrei (matzipan) wrote :

@Ralph: this is a caused because the old plugin that was merged into this one was never deleted upon install. I'm not sure how to deal with this. Maybe Dan or Cody can suggest.

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

@Ralph: You should be able to safely delete (or move elsewhere if you want to be able to reverse it) the folder "filemanager" in /usr/lib/x86_64-linux-gnu/scratch/plugins/ once it is no longer needed. You will need to be root.

This will remove the plugin from the list of available plugins in Scratch.

Unmerged revisions

1797. By Zisu Andrei

Only add valid files and directories to children and update unloaded directories better

1796. By Zisu Andrei

Fix segmentation fault on unloaded folder monitor event

1795. By Zisu Andrei

Move menu items case

1794. By Zisu Andrei

Monitor files and update unloaded folders

1793. By Zisu Andrei

Check if folder is executable to avoid infinite loop

1792. By Zisu Andrei

Null the newly_created_path after it has been processed

1791. By Zisu Andrei

Fix double creation on empty folder loading

1790. By Zisu Andrei

Fix FolderItems not editable after creation

1789. By Zisu Andrei

Add new keyword to overriding function

1788. By Zisu Andrei

Add break to make it more efficient

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'plugins/CMakeLists.txt'
--- plugins/CMakeLists.txt 2016-09-03 10:30:53 +0000
+++ plugins/CMakeLists.txt 2016-12-21 19:02:16 +0000
@@ -6,8 +6,7 @@
6)6)
77
8add_subdirectory (contractor)8add_subdirectory (contractor)
9add_subdirectory (pastebin)9add_subdirectory (pastebin)
10add_subdirectory (filemanager)
11add_subdirectory (folder-manager)10add_subdirectory (folder-manager)
12add_subdirectory (terminal)11add_subdirectory (terminal)
13add_subdirectory (browser-preview)12add_subdirectory (browser-preview)
1413
=== removed directory 'plugins/filemanager'
=== removed file 'plugins/filemanager/CMakeLists.txt'
--- plugins/filemanager/CMakeLists.txt 2016-09-03 10:30:53 +0000
+++ plugins/filemanager/CMakeLists.txt 1970-01-01 00:00:00 +0000
@@ -1,29 +0,0 @@
1add_definitions(${NORMAL_CFLAGS})
2link_directories(${NORMAL_LINK_DIRS})
3
4set (PLUGIN_NAME "filemanager")
5
6vala_precompile(VALA_C ${PLUGIN_NAME}
7 FileManagerPlugin.vala
8 File.vala
9 Settings.vala
10 FileView.vala
11PACKAGES
12 gtk+-3.0
13 gee-0.8
14 granite
15 scratchcore
16 libpeas-1.0
17 gtksourceview-3.0
18 ${ZEITGEIST_DEPS}
19OPTIONS
20 ${DEFAULT_PLUGIN_OPTIONS}
21)
22
23add_library(${PLUGIN_NAME} MODULE ${VALA_C})
24add_dependencies(${PLUGIN_NAME} ${LIBNAME})
25
26install(TARGETS ${PLUGIN_NAME} DESTINATION ${PLUGINDIR}/${PLUGIN_NAME})
27install(FILES ${PLUGIN_NAME}.plugin DESTINATION ${PLUGINDIR}/${PLUGIN_NAME})
28
29message("-- File Manager plugin will be compiled")
300
=== removed file 'plugins/filemanager/File.vala'
--- plugins/filemanager/File.vala 2013-07-16 15:56:38 +0000
+++ plugins/filemanager/File.vala 1970-01-01 00:00:00 +0000
@@ -1,207 +0,0 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/***
3 BEGIN LICENSE
4
5 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>
6 This program is free software: you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License version 3, as published
8 by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranties of
12 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13 PURPOSE. See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program. If not, see <http://www.gnu.org/licenses/>
17
18 END LICENSE
19***/
20
21namespace Scratch.Plugins.FileManager {
22
23 /**
24 * Class for easily dealing with files.
25 */
26 internal class File : GLib.Object {
27
28 public GLib.File file;
29 private GLib.FileInfo info;
30
31 private enum Type {
32 VALID_FILE,
33 VALID_FOLDER,
34 UNKNOWN,
35 INVALID
36 }
37
38 public File (string path) {
39 file = GLib.File.new_for_path (path);
40
41 info = new FileInfo ();
42 try {
43 info = file.query_info (
44 GLib.FileAttribute.STANDARD_CONTENT_TYPE + "," +
45 GLib.FileAttribute.STANDARD_IS_BACKUP + "," +
46 GLib.FileAttribute.STANDARD_IS_HIDDEN + "," +
47 GLib.FileAttribute.STANDARD_DISPLAY_NAME + "," +
48 GLib.FileAttribute.STANDARD_TYPE,
49 FileQueryInfoFlags.NONE);
50 } catch (GLib.Error error) {
51 info = null;
52 warning (error.message);
53 }
54 }
55
56 // returns the path the file
57 string _path = null;
58 public string path {
59 get { return _path != null ? _path : _path = file.get_path (); }
60 }
61
62 // returns the basename of the file
63 string _name = null;
64 public string name {
65 get { return _name != null ? _name : _name = info.get_display_name (); }
66 }
67
68 // returns the icon of the file's content type
69 GLib.Icon _icon = null;
70 public GLib.Icon icon {
71 get {
72 if (_icon != null)
73 return _icon;
74 //var content_type = info.get_attribute_string (FileAttribute.STANDARD_FAST_CONTENT_TYPE);
75 var content_type = info.get_content_type ();
76 return _icon = GLib.ContentType.get_icon (content_type);
77 }
78 }
79
80 // checks if file exists
81 public bool exists {
82 get { return file.query_exists (); }
83 }
84
85 Type _type = Type.UNKNOWN;
86 // checks if we're dealing with a non-hidden, non-backup directory
87 public bool is_valid_directory {
88 get {
89 if (_type == Type.VALID_FILE)
90 return false;
91 if (_type == Type.VALID_FOLDER)
92 return true;
93 if (_type == Type.INVALID)
94 return false;
95
96 if (info.get_file_type () != FileType.DIRECTORY ||
97 info.get_is_hidden () || info.get_is_backup ()) {
98 return false;
99 }
100
101 bool has_valid_children = false;
102
103 foreach (var child in children) {
104 if (child.is_valid_textfile) {
105 _type = Type.VALID_FOLDER;
106 return has_valid_children = true;
107 }
108 }
109
110 foreach (var child in children) {
111 if (child.is_valid_directory) {
112 has_valid_children = true;
113 _type = Type.VALID_FOLDER;
114 return has_valid_children = true;
115 }
116 }
117
118 return false;
119 }
120 }
121
122 // checks if we're dealing with a textfile
123 public bool is_valid_textfile {
124 get {
125 if (_type == Type.VALID_FILE)
126 return true;
127 if (_type == Type.VALID_FOLDER)
128 return false;
129 if (_type == Type.INVALID)
130 return false;
131
132 if (info.get_file_type () == FileType.REGULAR) {
133 //var content_type = info.get_attribute_string (FileAttribute.STANDARD_FAST_CONTENT_TYPE);
134 var content_type = info.get_content_type ();
135 if (ContentType.is_a (content_type, "text/*") &&
136 !info.get_is_backup () &&
137 !info.get_is_hidden ()) {
138 _type = Type.VALID_FILE;
139 return true;
140 }
141 }
142
143 return false;
144 }
145 }
146
147 // returns a list of all children of a directory
148 GLib.List <File> _children = null;
149 public GLib.List <File> children {
150 get {
151 if (_children != null)
152 return _children;
153
154 var parent = GLib.File.new_for_path (file.get_path ());
155 try {
156 var enumerator = parent.enumerate_children (
157 GLib.FileAttribute.STANDARD_NAME,
158 FileQueryInfoFlags.NONE
159 );
160
161 var file_info = new FileInfo ();
162 while ((file_info = enumerator.next_file ()) != null) {
163 var child = parent.get_child (file_info.get_name ());
164 _children.append (new File (child.get_path ()));
165 }
166 } catch (GLib.Error error) {
167 warning (error.message);
168 }
169
170 return _children;
171 }
172 }
173
174 public void rename (string name) {
175 try {
176 file.set_display_name (name);
177 } catch (GLib.Error error) {
178 warning (error.message);
179 }
180 }
181
182 /*public void trash () {
183 try {
184 file.trash ();
185 } catch (GLib.Error error) {
186 warning (error.message);
187 }
188 }*/
189
190 public void reset_cache () {
191 _name = null;
192 _path = null;
193 _icon = null;
194 _children = null;
195 _type = Type.UNKNOWN;
196 }
197
198 public static int compare (File a, File b) {
199 if (a.is_valid_directory && b.is_valid_textfile)
200 return -1;
201 if (a.is_valid_textfile && b.is_valid_directory)
202 return 1;
203 return strcmp (a.path.collate_key_for_filename (),
204 b.path.collate_key_for_filename ());
205 }
206 }
207}
2080
=== removed file 'plugins/filemanager/FileManagerPlugin.vala'
--- plugins/filemanager/FileManagerPlugin.vala 2016-09-03 11:50:58 +0000
+++ plugins/filemanager/FileManagerPlugin.vala 1970-01-01 00:00:00 +0000
@@ -1,113 +0,0 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/***
3 BEGIN LICENSE
4
5 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>
6 This program is free software: you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License version 3, as published
8 by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranties of
12 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13 PURPOSE. See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program. If not, see <http://www.gnu.org/licenses/>
17
18 END LICENSE
19***/
20
21public const string NAME = _("Folder Manager");
22public const string DESCRIPTION = _("Basic folder manager with file browsing");
23
24namespace Scratch.Plugins {
25 public class FileManagerPlugin : Peas.ExtensionBase, Peas.Activatable {
26 public Scratch.Services.Interface plugins;
27
28 Gtk.Box box;
29 FileManager.FileView view;
30
31 public Object object { owned get; construct; }
32
33 public FileManagerPlugin () {
34 message ("Starting File Manager Plugin");
35 }
36
37 public void activate () {
38 plugins = (Scratch.Services.Interface) object;
39 plugins.hook_notebook_sidebar.connect (on_hook_sidebar);
40 }
41
42 public void deactivate () {
43 if (box != null)
44 box.destroy();
45 }
46
47 public void update_state () {
48
49 }
50
51 void on_hook_sidebar (Gtk.Notebook notebook) {
52 if (view != null)
53 return;
54
55 box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
56
57 // File View
58 view = new FileManager.FileView ();
59
60 view.select.connect ((a) => {
61 var file = GLib.File.new_for_path (a);
62 plugins.open_file (file);
63 });
64
65 // Toolbar
66 var toolbar = new Gtk.Toolbar ();
67 toolbar.set_icon_size (Gtk.IconSize.SMALL_TOOLBAR);
68 toolbar.get_style_context ().add_class ("inline-toolbar");
69
70 var parent = new Gtk.ToolButton (null, null);
71 parent.tooltip_text = _("Go to parent");
72 parent.icon_name = "go-up-symbolic";
73 parent.clicked.connect (() => {
74 view.open_parent ();
75 parent.sensitive = !(view.folder.file.file.get_path () == "/");
76 });
77
78 var spacer = new Gtk.ToolItem ();
79 spacer.set_expand (true);
80
81 var add = new Gtk.ToolButton (null, null);
82 add.tooltip_text = _("Add file");
83 add.icon_name = "list-add-symbolic";
84 add.clicked.connect (() => {
85 view.add_file ();
86 });
87
88 var remove = new Gtk.ToolButton (null, null);
89 remove.tooltip_text = _("Remove file");
90 remove.icon_name = "edit-delete-symbolic";
91 remove.clicked.connect (() => {
92 view.remove_file ();
93 });
94
95 toolbar.insert (parent, -1);
96 toolbar.insert (spacer, -1);
97 toolbar.insert (add, -1);
98 toolbar.insert (remove, -1);
99
100 box.pack_start (view, true, true, 0);
101 box.pack_start (toolbar, false, false, 0);
102 box.show_all ();
103
104 notebook.append_page (box, new Gtk.Label (_("File Manager")));
105 }
106 }
107}
108
109[ModuleInit]
110public void peas_register_types (GLib.TypeModule module) {
111 var objmodule = module as Peas.ObjectModule;
112 objmodule.register_extension_type (typeof (Peas.Activatable), typeof (Scratch.Plugins.FileManagerPlugin));
113}
1140
=== removed file 'plugins/filemanager/FileView.vala'
--- plugins/filemanager/FileView.vala 2016-09-03 11:50:58 +0000
+++ plugins/filemanager/FileView.vala 1970-01-01 00:00:00 +0000
@@ -1,297 +0,0 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/***
3 BEGIN LICENSE
4
5 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>
6 This program is free software: you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License version 3, as published
8 by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranties of
12 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13 PURPOSE. See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program. If not, see <http://www.gnu.org/licenses/>
17
18 END LICENSE
19***/
20
21namespace Scratch.Plugins.FileManager {
22 Settings settings;
23
24 /**
25 * SourceList that displays folders and their contents.
26 */
27 internal class FileView : Granite.Widgets.SourceList {
28 public FolderItem? folder = null;
29 public signal void select (string file);
30 public FileView () {
31 this.width_request = 180;
32
33 this.item_selected.connect ((item) => {
34 select ((item as FileItem).path);
35 });
36
37 this.root.child_removed.connect (() => {
38 this.selected = null;
39 });
40
41 settings = new Settings ();
42 restore_settings ();
43 }
44
45 public void open_parent () {
46 GLib.File parent = this.folder.file.file.get_parent ();
47 this.root.remove (this.folder);
48 open_folder (new File (parent.get_path ()));
49 }
50
51 public void open_folder (File folder, bool expand = true) {
52 if (is_open (folder)) {
53 warning ("Folder '%s' is already open.", folder.path);
54 return;
55 } else if (!folder.is_valid_directory) {
56 warning ("Cannot open invalid directory.");
57 return;
58 }
59
60 // Clean the SourceList before start adding something
61 if (this.folder != null) {
62 this.root.remove (this.folder);
63 }
64
65 this.folder = new FolderItem (folder, this);
66 this.root.add (this.folder);
67
68 this.folder.expanded = expand;
69 write_settings ();
70 }
71
72 public void add_file () {
73 string path = folder.file.file.get_path () + _("/New File");
74 var file = GLib.File.new_for_path (path);
75 int n = 1;
76 while (file.query_exists ()) {
77 file = GLib.File.new_for_path (path + n.to_string ());
78 n++;
79 }
80
81 try {
82 file.create (FileCreateFlags.NONE);
83 } catch (Error e) {
84 warning (e.message);
85 }
86
87 var item = new FileItem (new File (file.get_path ()));
88 this.folder.add (item);
89 }
90
91 public void remove_file () {
92 if (this.selected is FileItem) {
93 var file = GLib.File.new_for_path (((FileItem)selected).file.file.get_path ());
94 try {
95 file.delete ();
96 this.root.remove (selected);
97 } catch (Error e) {
98 warning (e.message);
99 }
100 }
101
102 this.selected = null;
103 }
104
105 private bool is_open (File folder) {
106 foreach (var child in root.children) {
107 if (folder.path == (child as Item).path) {
108 return true;
109 }
110 }
111
112 return false;
113 }
114
115 private void write_settings () {
116 settings.opened_folder = this.folder.file.file.get_path ();
117 }
118
119 private void restore_settings () {
120 var folder = new File (settings.opened_folder);
121 if (settings.opened_folder == "" || settings.opened_folder == null || !folder.is_valid_directory) {
122 settings.opened_folder = GLib.Environment.get_home_dir ();
123 }
124
125 open_folder (new File (settings.opened_folder));
126 }
127 }
128
129 /**
130 * Common abstract class for normal and expandable items.
131 */
132 internal class Item : Granite.Widgets.SourceList.ExpandableItem, Granite.Widgets.SourceListSortable {
133 public File file { get; construct; }
134 public string path { get { return file.path; } }
135
136 public int compare (Granite.Widgets.SourceList.Item a, Granite.Widgets.SourceList.Item b) {
137 if (a is FolderItem && b is FileItem) {
138 return -1;
139 } else if (a is FileItem && b is FolderItem) {
140 return 1;
141 }
142
143 return File.compare ((a as Item).file, (b as Item).file);
144 }
145
146 public bool allow_dnd_sorting () {
147 return false;
148 }
149 }
150
151 /**
152 * Normal item in the source list, represents a textfile.
153 * TODO Remove, Rename
154 */
155 internal class FileItem : Item {
156 //Gtk.Menu menu;
157 //Gtk.MenuItem item_trash;
158 public FileItem (File file) requires (file.is_valid_textfile) {
159 Object (file: file);
160
161 this.selectable = true;
162 this.editable = true;
163 this.name = file.name;
164 this.icon = file.icon;
165 }
166
167 public void rename (string new_name) {
168 string new_uri = file.file.get_parent ().get_uri () + "/" + new_name;
169 debug (new_uri);
170 file.rename (new_name);
171 }
172
173 /*public override Gtk.Menu? get_context_menu () {
174 menu = new Gtk.Menu ();
175 item_trash = new Gtk.MenuItem.with_label (_("Move to Trash"));
176 menu.append (item_trash);
177 item_trash.activate.connect (() => { file.trash (); });
178 menu.show_all ();
179 return menu;
180 }*/
181 }
182
183 /**
184 * Expandable item in the source list, represents a folder.
185 * Monitored for changes inside the directory.
186 * TODO remove, rename, create new file
187 */
188 internal class FolderItem : Item {
189 public signal void folder_open (GLib.File folder);
190 public FileView view { get; construct; }
191
192 private GLib.FileMonitor monitor;
193 private bool children_loaded = false;
194
195 //Gtk.Menu menu;
196 //Gtk.MenuItem item_trash;
197 //Gtk.MenuItem item_create;
198
199 public FolderItem (File file, FileView view) requires (file.is_valid_directory) {
200 Object (file: file, view: view);
201
202 this.editable = false;
203 this.selectable = false;
204 this.name = file.name;
205 this.icon = file.icon;
206
207 this.add (new Granite.Widgets.SourceList.Item ("")); // dummy
208 this.toggled.connect (() => {
209 if (this.expanded && this.n_children <= 1) {
210 this.clear ();
211 this.add_children ();
212 children_loaded = true;
213 }
214 });
215
216 try {
217 monitor = file.file.monitor_directory (GLib.FileMonitorFlags.NONE);
218 monitor.changed.connect ((s,d,e) => { on_changed (s,d,e); });
219 } catch (GLib.Error e) {
220 warning (e.message);
221 }
222 }
223
224 public override Gtk.Menu? get_context_menu () {
225 if (this == this.view.root.children.to_array ()[0]) {
226 return null;
227 }
228
229 var menu = new Gtk.Menu ();
230 var item = new Gtk.MenuItem.with_label (_("Open"));
231 item.activate.connect (() => { this.folder_open (this.file.file); });
232 menu.append (item);
233 menu.show_all ();
234 return menu;
235 }
236
237 internal void add_children () {
238 foreach (var child in file.children) {
239 if (child.is_valid_directory) {
240 var item = new FolderItem (child, view);
241 item.folder_open.connect (() => {
242 this.view.open_folder (child);
243 });
244
245 add (item);
246 } else if (child.is_valid_textfile) {
247 var item = new FileItem (child);
248 add (item);
249 item.edited.connect (item.rename);
250 }
251 }
252 }
253
254 private void on_changed (GLib.File source, GLib.File? dest, GLib.FileMonitorEvent event) {
255 if (!children_loaded) {
256 this.file.reset_cache ();
257 return;
258 }
259
260 switch (event) {
261 case GLib.FileMonitorEvent.DELETED:
262 var children_tmp = new Gee.ArrayList<Granite.Widgets.SourceList.Item> ();
263 children_tmp.add_all (children);
264 foreach (var item in children_tmp) {
265 if ((item as Item).path == source.get_path ()) {
266 remove (item);
267 }
268 }
269
270 break;
271 case GLib.FileMonitorEvent.CREATED:
272 if (source.query_exists () == false) {
273 return;
274 }
275
276 var file = new File (source.get_path ());
277 var exists = false;
278 foreach (var item in children) {
279 if ((item as Item).path == file.path) {
280 exists = true;
281 }
282 }
283
284 if (!exists) {
285 if (file.is_valid_textfile) {
286 this.add (new FileItem (file));
287 } else if (file.is_valid_directory) {
288 this.add (new FolderItem (file, view));
289 }
290 }
291
292 break;
293 }
294 }
295 }
296
297}
2980
=== removed file 'plugins/filemanager/Settings.vala'
--- plugins/filemanager/Settings.vala 2013-07-16 15:56:38 +0000
+++ plugins/filemanager/Settings.vala 1970-01-01 00:00:00 +0000
@@ -1,36 +0,0 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/***
3 BEGIN LICENSE
4
5 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>
6 This program is free software: you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License version 3, as published
8 by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranties of
12 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13 PURPOSE. See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program. If not, see <http://www.gnu.org/licenses/>
17
18 END LICENSE
19***/
20
21namespace Scratch.Plugins.FileManager {
22
23 /**
24 * Class for interacting with gsettings.
25 */
26 internal class Settings : Granite.Services.Settings {
27
28 private const string SCHEMA = "org.pantheon.scratch.plugins.file-manager";
29
30 public string opened_folder { get; set; }
31
32 public Settings () {
33 base (SCHEMA);
34 }
35 }
36}
370
=== removed file 'plugins/filemanager/filemanager.plugin'
--- plugins/filemanager/filemanager.plugin 2013-07-16 15:56:38 +0000
+++ plugins/filemanager/filemanager.plugin 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
1[Plugin]
2Module=filemanager
3Loader=C
4IAge=2
5Name=File Manager
6Description=Browse files and directories
7Icon=system-file-manager
8Authors=Mario Guerriero
9Copyright=Copyright © 2013 Scratch Developers
10Website=http://launchpad.net/scratch
110
=== modified file 'plugins/folder-manager/CMakeLists.txt'
--- plugins/folder-manager/CMakeLists.txt 2016-09-03 10:30:53 +0000
+++ plugins/folder-manager/CMakeLists.txt 2016-12-21 19:02:16 +0000
@@ -8,6 +8,9 @@
8 File.vala8 File.vala
9 Settings.vala9 Settings.vala
10 FileView.vala10 FileView.vala
11 Item.vala
12 FileItem.vala
13 FolderItem.vala
11PACKAGES14PACKAGES
12 gtk+-3.015 gtk+-3.0
13 gee-0.816 gee-0.8
1417
=== modified file 'plugins/folder-manager/File.vala'
--- plugins/folder-manager/File.vala 2013-06-02 13:07:08 +0000
+++ plugins/folder-manager/File.vala 2016-12-21 19:02:16 +0000
@@ -1,79 +1,70 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/***2/*-
3 BEGIN LICENSE3 * Copyright (c) 2016 elementary LLC. (https://elementary.io)
44 *
5 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>5 * This program is free software: you can redistribute it and/or modify
6 This program is free software: you can redistribute it and/or modify it6 * it under the terms of the GNU General Public License as published by
7 under the terms of the GNU Lesser General Public License version 3, as published7 * the Free Software Foundation, either version 3 of the License, or
8 by the Free Software Foundation.8 * (at your option) any later version.
99 *
10 This program is distributed in the hope that it will be useful, but10 * This program is distributed in the hope that it will be useful,
11 WITHOUT ANY WARRANTY; without even the implied warranties of11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 PURPOSE. See the GNU General Public License for more details.13 * GNU General Public License for more details.
1414 *
15 You should have received a copy of the GNU General Public License along15 * You should have received a copy of the GNU General Public License
16 with this program. If not, see <http://www.gnu.org/licenses/>16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1717 *
18 END LICENSE18 * Authored by: Julien Spautz <spautz.julien@gmail.com>, Andrei-Costin Zisu <matzipan@gmail.com>
19***/19 */
2020
21namespace Scratch.Plugins.FolderManager {21namespace Scratch.Plugins.FolderManager {
2222
23 /**23 /**
24 * Class for easily dealing with files.24 * Class for easily dealing with files.
25 */25 */
26 internal class File : GLib.Object {26 public class File : GLib.Object {
2727
28 public GLib.File file;28 public GLib.File file { get; private set; }
29 private GLib.FileInfo info;29 private GLib.FileInfo? info;
30
31 private enum Type {
32 VALID_FILE,
33 VALID_FOLDER,
34 UNKNOWN,
35 INVALID
36 }
3730
38 public File (string path) {31 public File (string path) {
39 file = GLib.File.new_for_path (path);32 Object (path: path);
40
41 info = new FileInfo ();
42 try {
43 info = file.query_info (
44 GLib.FileAttribute.STANDARD_CONTENT_TYPE + "," +
45 GLib.FileAttribute.STANDARD_IS_BACKUP + "," +
46 GLib.FileAttribute.STANDARD_IS_HIDDEN + "," +
47 GLib.FileAttribute.STANDARD_DISPLAY_NAME + "," +
48 GLib.FileAttribute.STANDARD_TYPE,
49 FileQueryInfoFlags.NONE);
50 } catch (GLib.Error error) {
51 info = null;
52 warning (error.message);
53 }
54 }33 }
5534
56 // returns the path the file35 // returns the path the file
57 string _path = null;
58 public string path {36 public string path {
59 get { return _path != null ? _path : _path = file.get_path (); }37 owned get {
38 return file.get_path ();
39 }
40 set construct {
41 load_file_for_path (value);
42 }
60 }43 }
6144
62 // returns the basename of the file45 // returns the basename of the file
63 string _name = null;46 private string _name;
64 public string name {47 public string name {
65 get { return _name != null ? _name : _name = info.get_display_name (); }48 get {
49 if (info == null) {
50 return "";
51 }
52
53 _name = info.get_display_name ();
54 return _name;
55 }
66 }56 }
6757
68 // returns the icon of the file's content type58 // returns the icon of the file's content type
69 GLib.Icon _icon = null;59 private GLib.Icon? _icon = null;
70 public GLib.Icon icon {60 public GLib.Icon icon {
71 get {61 get {
72 if (_icon != null)62 if (_icon != null) {
73 return _icon;63 return _icon;
74 //var content_type = info.get_attribute_string (FileAttribute.STANDARD_FAST_CONTENT_TYPE);64 }
75 var content_type = info.get_content_type ();65
76 return _icon = GLib.ContentType.get_icon (content_type);66 _icon = GLib.ContentType.get_icon (info.get_content_type ());
67 return _icon;
77 }68 }
78 }69 }
7970
@@ -82,37 +73,15 @@
82 get { return file.query_exists (); }73 get { return file.query_exists (); }
83 }74 }
8475
85 Type _type = Type.UNKNOWN;
86 // checks if we're dealing with a non-hidden, non-backup directory76 // checks if we're dealing with a non-hidden, non-backup directory
87 public bool is_valid_directory {77 public bool is_valid_directory {
88 get {78 get {
89 if (_type == Type.VALID_FILE)79 if (info.get_is_hidden () || info.get_is_backup ()) {
90 return false;80 return false;
91 if (_type == Type.VALID_FOLDER)81 }
82
83 if (info.get_file_type () == FileType.DIRECTORY) {
92 return true;84 return true;
93 if (_type == Type.INVALID)
94 return false;
95
96 if (info.get_file_type () != FileType.DIRECTORY ||
97 info.get_is_hidden () || info.get_is_backup ()) {
98 return false;
99 }
100
101 bool has_valid_children = false;
102
103 foreach (var child in children) {
104 if (child.is_valid_textfile) {
105 _type = Type.VALID_FOLDER;
106 return has_valid_children = true;
107 }
108 }
109
110 foreach (var child in children) {
111 if (child.is_valid_directory) {
112 has_valid_children = true;
113 _type = Type.VALID_FOLDER;
114 return has_valid_children = true;
115 }
116 }85 }
11786
118 return false;87 return false;
@@ -122,34 +91,37 @@
122 // checks if we're dealing with a textfile91 // checks if we're dealing with a textfile
123 public bool is_valid_textfile {92 public bool is_valid_textfile {
124 get {93 get {
125 if (_type == Type.VALID_FILE)94 if (info.get_is_hidden () || info.get_is_backup ()) {
95 return false;
96 }
97
98 if (info.get_file_type () == FileType.REGULAR &&
99 ContentType.is_a (info.get_content_type (), "text/*")) {
126 return true;100 return true;
127 if (_type == Type.VALID_FOLDER)
128 return false;
129 if (_type == Type.INVALID)
130 return false;
131
132 if (info.get_file_type () == FileType.REGULAR) {
133 //var content_type = info.get_attribute_string (FileAttribute.STANDARD_FAST_CONTENT_TYPE);
134 var content_type = info.get_content_type ();
135 if (ContentType.is_a (content_type, "text/*") &&
136 !info.get_is_backup () &&
137 !info.get_is_hidden ()) {
138 _type = Type.VALID_FILE;
139 return true;
140 }
141 }101 }
142102
143 return false;103 return false;
144 }104 }
145 }105 }
146106
107 // Files can be executed and folders can be cd'd into
108 public bool is_executable {
109 get {
110 try {
111 return get_boolean_file_attribute(GLib.FileAttribute.ACCESS_CAN_EXECUTE);
112 } catch (GLib.Error error) {
113 return false;
114 }
115 }
116 }
117
147 // returns a list of all children of a directory118 // returns a list of all children of a directory
148 GLib.List <File> _children = null;119 GLib.List <File>? _children = null;
149 public GLib.List <File> children {120 public GLib.List <File> children {
150 get {121 get {
151 if (_children != null)122 if (_children != null) {
152 return _children;123 return _children;
124 }
153125
154 var parent = GLib.File.new_for_path (file.get_path ());126 var parent = GLib.File.new_for_path (file.get_path ());
155 try {127 try {
@@ -161,7 +133,11 @@
161 var file_info = new FileInfo ();133 var file_info = new FileInfo ();
162 while ((file_info = enumerator.next_file ()) != null) {134 while ((file_info = enumerator.next_file ()) != null) {
163 var child = parent.get_child (file_info.get_name ());135 var child = parent.get_child (file_info.get_name ());
164 _children.append (new File (child.get_path ()));136 var file = new File (child.get_path ());
137
138 if (file.is_valid_directory || file.is_valid_textfile) {
139 _children.append (file);
140 }
165 }141 }
166 } catch (GLib.Error error) {142 } catch (GLib.Error error) {
167 warning (error.message);143 warning (error.message);
@@ -171,9 +147,33 @@
171 }147 }
172 }148 }
173149
174 /*public void rename (string name) {150 private bool get_boolean_file_attribute(string attribute) throws GLib.Error {
175 try {151 var info = file.query_info(attribute, GLib.FileQueryInfoFlags.NONE);
176 file.set_display_name (name);152
153 return info.get_attribute_boolean(attribute);
154 }
155
156 private void load_file_for_path (string path) {
157 file = GLib.File.new_for_path (path);
158
159 info = new FileInfo ();
160 try {
161 var query_string = GLib.FileAttribute.STANDARD_CONTENT_TYPE + "," +
162 GLib.FileAttribute.STANDARD_IS_BACKUP + "," +
163 GLib.FileAttribute.STANDARD_IS_HIDDEN + "," +
164 GLib.FileAttribute.STANDARD_DISPLAY_NAME + "," +
165 GLib.FileAttribute.STANDARD_TYPE;
166
167 info = file.query_info (query_string, FileQueryInfoFlags.NONE);
168 } catch (GLib.Error error) {
169 info = null;
170 warning (error.message);
171 }
172 }
173
174 public void rename (string name) {
175 try {
176 file.set_display_name (name);
177 } catch (GLib.Error error) {177 } catch (GLib.Error error) {
178 warning (error.message);178 warning (error.message);
179 }179 }
@@ -185,23 +185,23 @@
185 } catch (GLib.Error error) {185 } catch (GLib.Error error) {
186 warning (error.message);186 warning (error.message);
187 }187 }
188 }*/
189
190 public void reset_cache () {
191 _name = null;
192 _path = null;
193 _icon = null;
194 _children = null;
195 _type = Type.UNKNOWN;
196 }188 }
197189
198 public static int compare (File a, File b) {190 public static int compare (File a, File b) {
199 if (a.is_valid_directory && b.is_valid_textfile)191 if (a.is_valid_directory && b.is_valid_textfile) {
200 return -1;192 return -1;
201 if (a.is_valid_textfile && b.is_valid_directory)193 }
194
195 if (a.is_valid_textfile && b.is_valid_directory) {
202 return 1;196 return 1;
197 }
198
203 return strcmp (a.path.collate_key_for_filename (),199 return strcmp (a.path.collate_key_for_filename (),
204 b.path.collate_key_for_filename ());200 b.path.collate_key_for_filename ());
205 }201 }
202
203 public void invalidate_cache () {
204 _children = null;
205 }
206 }206 }
207}207}
208208
=== added file 'plugins/folder-manager/FileItem.vala'
--- plugins/folder-manager/FileItem.vala 1970-01-01 00:00:00 +0000
+++ plugins/folder-manager/FileItem.vala 2016-12-21 19:02:16 +0000
@@ -0,0 +1,39 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2016 elementary LLC. (https://elementary.io)
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authored by: Corentin Noël <corentin@elementary.io>, Andrei-Costin Zisu <matzipan@gmail.com>
19 */
20
21public class Scratch.Plugins.FolderManager.FileItem : Item {
22 public FileItem (Scratch.Plugins.FolderManager.File file, Scratch.Plugins.FolderManager.FileView view) requires (file.is_valid_textfile) {
23 Object (file: file, view: view);
24 }
25
26 public override Gtk.Menu? get_context_menu () {
27 var menu = new Gtk.Menu ();
28 var rename_item = new Gtk.MenuItem.with_label (_("Rename"));
29 rename_item.activate.connect (() => view.start_editing_item (this));
30 menu.append (rename_item);
31
32 var delete_item = new Gtk.MenuItem.with_label (_("Move to Trash"));
33 delete_item.activate.connect (() => do_remove ());
34 menu.append (delete_item);
35
36 menu.show_all ();
37 return menu;
38 }
39}
040
=== modified file 'plugins/folder-manager/FileView.vala'
--- plugins/folder-manager/FileView.vala 2015-09-16 01:11:55 +0000
+++ plugins/folder-manager/FileView.vala 2016-12-21 19:02:16 +0000
@@ -1,22 +1,22 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/***2/*-
3 BEGIN LICENSE3 * Copyright (c) 2016 elementary LLC. (https://elementary.io)
44 *
5 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>5 * This program is free software: you can redistribute it and/or modify
6 This program is free software: you can redistribute it and/or modify it6 * it under the terms of the GNU General Public License as published by
7 under the terms of the GNU Lesser General Public License version 3, as published7 * the Free Software Foundation, either version 3 of the License, or
8 by the Free Software Foundation.8 * (at your option) any later version.
99 *
10 This program is distributed in the hope that it will be useful, but10 * This program is distributed in the hope that it will be useful,
11 WITHOUT ANY WARRANTY; without even the implied warranties of11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 PURPOSE. See the GNU General Public License for more details.13 * GNU General Public License for more details.
1414 *
15 You should have received a copy of the GNU General Public License along15 * You should have received a copy of the GNU General Public License
16 with this program. If not, see <http://www.gnu.org/licenses/>16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1717 *
18 END LICENSE18 * Authored by: Julien Spautz <spautz.julien@gmail.com>, Andrei-Costin Zisu <matzipan@gmail.com>
19***/19 */
2020
21namespace Scratch.Plugins.FolderManager {21namespace Scratch.Plugins.FolderManager {
22 Settings settings;22 Settings settings;
@@ -24,289 +24,122 @@
24 /**24 /**
25 * SourceList that displays folders and their contents.25 * SourceList that displays folders and their contents.
26 */26 */
27 internal class FileView : Granite.Widgets.SourceList {27 public class FileView : Granite.Widgets.SourceList {
2828 public signal void select (GLib.File file);
29 public signal void select (string file);29 public signal void welcome_visible (bool visible);
3030
31 public FileView () {31 // This is a workaround for SourceList silliness: you cannot remove an item
32 this.width_request = 180;32 // without it automatically selecting another one.
33 this.item_selected.connect ((item) => {33 public bool ignore_next_select = false;
34 select ((item as FileItem).path);34
35 });35 construct {
36 width_request = 180;
37
38 item_selected.connect (on_item_selected);
3639
37 settings = new Settings ();40 settings = new Settings ();
38 }41 }
3942
40 public void restore_saved_state () {43 private void on_item_selected (Granite.Widgets.SourceList.Item? item) {
41 foreach (var path in settings.opened_folders)44 // This is a workaround for SourceList silliness: you cannot remove an item
42 add_folder (new File (path), false);45 // without it automatically selecting another one.
43 }46 if (ignore_next_select) {
4447 ignore_next_select = false;
45 public void open_folder (File folder) {48 return;
46 if (is_open (folder)) {49 }
47 warning ("Folder '%s' is already open.", folder.path);50
48 return;51 if (item is FileItem) {
49 } else if (!folder.is_valid_directory) {52 select ((item as FileItem).file.file);
50 warning ("Cannot open invalid directory.");53 }
51 return;54 }
52 }55
5356 public void open_folder (string folder_path) {
54 add_folder (folder, true);57 add_folder (folder_path);
55 write_settings ();58 write_settings ();
56 }59 }
5760
58 private void add_folder (File folder, bool expand) {61 public void close_folder (string folder_path) {
59 if (is_open (folder)) {62 foreach (var child in root.children) {
60 warning ("Folder '%s' is already open.", folder.path);63 var folder = child as FolderItem;
61 return;64
62 } else if (!folder.is_valid_directory) {65 if (folder != null && folder.path == folder_path) {
63 warning ("Cannot open invalid directory.");66 // This is a workaround for SourceList silliness: you cannot remove an item
64 return;67 // without it automatically selecting another one.
65 }68 ignore_next_select = true;
6669 root.remove(folder);
67 var folder_root = new MainFolderItem (folder);70 selected = null;
68 this.root.add (folder_root);71 }
6972 }
70 folder_root.expanded = expand;73
71 folder_root.closed.connect (() => {74 if (root.n_children == 0) {
72 root.remove (folder_root);75 welcome_visible (true);
73 write_settings ();76 }
74 });77
75 }78 write_settings ();
7679 }
77 private bool is_open (File folder) {80
78 foreach (var child in root.children)81 // Adds the folder to the view without saving to settings
79 if (folder.path == (child as Item).path)82 private void add_folder (string folder_path, bool expand = true) {
83 if (folder_path == null) {
84 warning ("Was given null folder path to add");
85 } else if (is_open (folder_path)) {
86 warning ("Folder '%s' is already open.", folder_path);
87 return;
88 }
89
90 var folder = new File(folder_path);
91
92 if (!folder.is_valid_directory) {
93 warning ("Cannot open invalid directory.");
94 return;
95 }
96
97 bool has_valid_children = false;
98
99 foreach (var child in folder.children) {
100 if (child.is_valid_textfile || child.is_valid_directory) {
101 has_valid_children = true;
102 break;
103 }
104 }
105
106 if (!has_valid_children) {
107 warning ("Cannot open empty directory due to limitations with Granite.SourceList.");
108 return;
109 }
110
111 var folder_item = new FolderItem (folder, this);
112 folder_item.expanded = expand;
113 root.add (folder_item);
114
115 welcome_visible (false);
116 }
117
118 public void restore_settings () {
119 foreach (var folder_path in settings.opened_folders) {
120 add_folder (folder_path);
121 }
122 }
123
124 private bool is_open (string folder_path) {
125 foreach (var child in root.children) {
126 if (folder_path == (child as Item).path) {
80 return true;127 return true;
128 }
129 }
130
81 return false;131 return false;
82 }132 }
83133
84 private void write_settings () {134 private void write_settings () {
85 string[] to_save = {};135 string[] paths = {};
86136 foreach (var child in root.children) {
87 foreach (var main_folder in root.children) {137 if (child is FolderItem) {
88 var saved = false;138 paths += ((FolderItem) child).path;
89139 }
90 foreach (var saved_folder in to_save) {140 }
91 if ((main_folder as Item).path == saved_folder) {141
92 saved = true;142 settings.opened_folders = paths;
93 break;
94 }
95 }
96
97 if (!saved) {
98 to_save += (main_folder as Item).path;
99 }
100 }
101
102 settings.opened_folders = to_save;
103 }
104 }
105
106 /**
107 * Common abstract class for file and filder items.
108 */
109 internal class Item: Granite.Widgets.SourceList.ExpandableItem, Granite.Widgets.SourceListSortable {
110 public File file { get; construct; }
111 public string path { get { return file.path; } }
112
113 public int compare (Granite.Widgets.SourceList.Item a, Granite.Widgets.SourceList.Item b) {
114 if (a is FolderItem && b is FileItem) {
115 return -1;
116 } else if (a is FileItem && b is FolderItem) {
117 return 1;
118 }
119
120 return File.compare ((a as Item).file, (b as Item).file);
121 }
122
123 public bool allow_dnd_sorting () {
124 return false;
125 }
126 }
127
128 /**
129 * Normal item in the source list, represents a textfile.
130 * TODO Remove, Rename
131 */
132 internal class FileItem : Item {
133
134 //Gtk.Menu menu;
135 //Gtk.MenuItem item_trash;
136
137 public FileItem (File file) requires (file.is_valid_textfile) {
138 Object (file: file);
139
140 this.selectable = true;
141 //this.editable = true;
142 this.name = file.name;
143 this.icon = file.icon;
144 }
145
146 /*public void rename (string new_name) {
147 file.rename (new_name);
148 }*/
149
150 /*public override Gtk.Menu? get_context_menu () {
151 menu = new Gtk.Menu ();
152 item_trash = new Gtk.MenuItem.with_label (_("Move to Trash"));
153 menu.append (item_trash);
154 item_trash.activate.connect (() => { file.trash (); });
155 menu.show_all ();
156 return menu;
157 }*/
158 }
159
160 /**
161 * Expandable item in the source list, represents a folder.
162 * Monitored for changes inside the directory.
163 * TODO remove, rename, create new file
164 */
165 internal class FolderItem : Item {
166
167 //Gtk.Menu menu;
168 //Gtk.MenuItem item_trash;
169 //Gtk.MenuItem item_create;
170
171 private GLib.FileMonitor monitor;
172 private bool children_loaded = false;
173
174 public FolderItem (File file) requires (file.is_valid_directory) {
175 Object (file: file);
176
177 this.editable = false;
178 this.selectable = false;
179 this.name = file.name;
180 this.icon = file.icon;
181
182 this.add (new Granite.Widgets.SourceList.Item ("")); // dummy
183 this.toggled.connect (() => {
184 if (this.expanded && this.n_children <= 1) {
185 this.clear ();
186 this.add_children ();
187 children_loaded = true;
188 }
189 });
190
191 try {
192 monitor = file.file.monitor_directory (GLib.FileMonitorFlags.NONE);
193 monitor.changed.connect ((s,d,e) => { on_changed (s,d,e); });
194 } catch (GLib.Error e) {
195 warning (e.message);
196 }
197 }
198
199 /*public override Gtk.Menu? get_context_menu () {
200 menu = new Gtk.Menu ();
201 item_trash = new Gtk.MenuItem.with_label (_("Move to Trash"));
202 item_create = new Gtk.MenuItem.with_label (_("Create new File"));
203 menu.append (item_trash);
204 menu.append (item_create);
205 item_trash.activate.connect (() => { file.trash (); });
206 item_create.activate.connect (() => {
207 var new_file = GLib.File.new_for_path (file.path + "/new File");
208
209 try {
210 FileOutputStream os = new_file.create (FileCreateFlags.NONE);
211 } catch (Error e) {
212 warning ("Error: %s\n", e.message);
213 }
214 });
215 menu.show_all ();
216 return menu;
217 }*/
218
219 internal void add_children () {
220 foreach (var child in file.children) {
221 if (child.is_valid_directory) {
222 var item = new FolderItem (child);
223 add (item);
224 } else if (child.is_valid_textfile) {
225 var item = new FileItem (child);
226 add (item);
227 //item.edited.connect (item.rename);
228 }
229 }
230 }
231
232 private void on_changed (GLib.File source, GLib.File? dest, GLib.FileMonitorEvent event) {
233
234 if (!children_loaded) {
235 this.file.reset_cache ();
236 return;
237 }
238
239 switch (event) {
240 case GLib.FileMonitorEvent.DELETED:
241 var children_tmp = new Gee.ArrayList<Granite.Widgets.SourceList.Item> ();
242 children_tmp.add_all (children);
243 foreach (var item in children_tmp) {
244 if ((item as Item).path == source.get_path ()) {
245 remove (item);
246 }
247 }
248
249 break;
250 case GLib.FileMonitorEvent.CREATED:
251 if (source.query_exists () == false) {
252 return;
253 }
254
255 var file = new File (source.get_path ());
256 var exists = false;
257 foreach (var item in children) {
258 if ((item as Item).path == file.path) {
259 exists = true;
260 break;
261 }
262 }
263
264 if (!exists) {
265 if (file.is_valid_textfile) {
266 this.add (new FileItem (file));
267 } else if (file.is_valid_directory) {
268 this.add (new FolderItem (file));
269 }
270 }
271
272 break;
273 }
274 }
275 }
276
277 /**
278 * Special root folder.
279 * TODO rename, create new file
280 */
281 internal class MainFolderItem : FolderItem {
282 public signal void closed ();
283
284 Gtk.Menu menu;
285 Gtk.MenuItem item_close;
286 //Gtk.MenuItem item_create;
287
288 public MainFolderItem (File file) requires (file.is_valid_directory) {
289 base (file);
290 }
291
292 public override Gtk.Menu? get_context_menu () {
293 menu = new Gtk.Menu ();
294 item_close = new Gtk.MenuItem.with_label (_("Close Folder"));
295 //item_create = new Gtk.MenuItem.with_label (_("Create new File"));
296 menu.append (item_close);
297 //menu.append (item_create);
298 item_close.activate.connect (() => { closed (); });
299 /*item_create.activate.connect (() => {
300 var new_file = GLib.File.new_for_path (file.path + "/new File");
301
302 try {
303 FileOutputStream os = new_file.create (FileCreateFlags.NONE);
304 } catch (Error e) {
305 warning ("Error: %s\n", e.message);
306 }
307 });*/
308 menu.show_all ();
309 return menu;
310 }143 }
311 }144 }
312}145}
313146
=== added file 'plugins/folder-manager/FolderItem.vala'
--- plugins/folder-manager/FolderItem.vala 1970-01-01 00:00:00 +0000
+++ plugins/folder-manager/FolderItem.vala 2016-12-21 19:02:16 +0000
@@ -0,0 +1,245 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2016 elementary LLC. (https://elementary.io)
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authored by: Corentin Noël <corentin@elementary.io>, Andrei-Costin Zisu <matzipan@gmail.com>
19 */
20
21public class Scratch.Plugins.FolderManager.FolderItem : Item {
22 private GLib.FileMonitor monitor;
23 private bool children_loaded = false;
24 private string? newly_created_path = null;
25
26 public FolderItem (Scratch.Plugins.FolderManager.File file, Scratch.Plugins.FolderManager.FileView view) requires (file.is_valid_directory) {
27 Object (file: file, view: view);
28 }
29
30 construct {
31 if (file.children.length () > 0) {
32 add (new Granite.Widgets.SourceList.Item ("")); // dummy
33 }
34
35 toggled.connect (() => {
36 if (expanded && n_children <= 1) {
37 clear ();
38 add_children ();
39 children_loaded = true;
40 }
41 });
42
43 try {
44 monitor = file.file.monitor_directory (GLib.FileMonitorFlags.NONE);
45 monitor.changed.connect (on_changed);
46 } catch (GLib.Error e) {
47 warning (e.message);
48 }
49 }
50
51 public override Gtk.Menu? get_context_menu () {
52 var menu = new Gtk.Menu ();
53 if (parent == view.root) {
54 var item = new Gtk.MenuItem.with_label (_("Close Folder"));
55 item.activate.connect (do_close);
56 menu.append (item);
57 } else {
58 var item = new Gtk.MenuItem.with_label (_("Open"));
59 item.activate.connect (() => { view.open_folder (file.path); });
60 menu.append (item);
61 }
62
63 var rename_item = new Gtk.MenuItem.with_label (_("Rename"));
64 rename_item.activate.connect (() => view.start_editing_item (this));
65 menu.append (rename_item);
66
67 var new_file_item = new Gtk.MenuItem.with_label (_("Add File"));
68 new_file_item.activate.connect (() => add_file ());
69 menu.append (new_file_item);
70
71 var new_folder_item = new Gtk.MenuItem.with_label (_("Add Folder"));
72 new_folder_item.activate.connect(() => add_folder ());
73 menu.append (new_folder_item);
74
75 var delete_item = new Gtk.MenuItem.with_label (_("Move to Trash"));
76 delete_item.activate.connect (() => do_remove ());
77 menu.append (delete_item);
78
79 menu.show_all ();
80 return menu;
81 }
82
83 internal void add_children () {
84 foreach (var child in file.children) {
85 if (child.is_valid_directory) {
86 var item = new FolderItem (child, view);
87 add (item);
88 } else if (child.is_valid_textfile) {
89 var item = new FileItem (child, view);
90 add (item);
91 }
92 }
93 }
94
95 private void do_close () {
96 monitor.cancel ();
97 view.close_folder (path);
98 }
99
100 protected new void do_remove () {
101 if (parent == view.root) {
102 do_close ();
103 }
104
105 base.do_remove ();
106 }
107
108 private void on_changed (GLib.File source, GLib.File? dest, GLib.FileMonitorEvent event) {
109 if (!children_loaded) {
110 switch (event) {
111 case GLib.FileMonitorEvent.DELETED:
112 // This is a pretty intensive operation. For each file deleted, the cache will be
113 // invalidated and recreated again, from disk. If it turns out users are seeing
114 // slugishness or slowness when deleting a lot of files, then it might be worth
115 // doing some sort of timer deferred action.
116 file.invalidate_cache ();
117 if (file.children.length () == 0) {
118 clear ();
119 }
120 break;
121 case GLib.FileMonitorEvent.CREATED:
122 if (source.query_exists () == false) {
123 return;
124 }
125
126 if (n_children == 0) {
127 add (new Granite.Widgets.SourceList.Item ("")); // dummy
128 }
129 break;
130 }
131 } else {
132 switch (event) {
133 case GLib.FileMonitorEvent.DELETED:
134 var children_tmp = new Gee.ArrayList<Granite.Widgets.SourceList.Item> ();
135 children_tmp.add_all (children);
136 foreach (var item in children_tmp) {
137 if ((item as Item).path == source.get_path ()) {
138 // This is a workaround for SourceList silliness: you cannot remove an item
139 // without it automatically selecting another one.
140 view.ignore_next_select = true;
141 remove (item);
142 view.selected = null;
143 }
144 }
145
146 break;
147 case GLib.FileMonitorEvent.CREATED:
148 if (source.query_exists () == false) {
149 return;
150 }
151
152 var file = new File (source.get_path ());
153 var exists = false;
154 foreach (var item in children) {
155 if ((item as Item).path == file.path) {
156 exists = true;
157 }
158 }
159
160 Item? item = null;
161
162 if (!exists) {
163 if (file.is_valid_textfile) {
164 item = new FileItem (file, view);
165 } else if (file.is_valid_directory) {
166 item = new FolderItem (file, view);
167 }
168 }
169
170 if (item != null) {
171 add (item);
172
173 if (source.get_path () == newly_created_path) {
174 newly_created_path = null;
175
176 /*
177 * Avoid race condition between adding and editing folder item
178 * (not required for file items).
179 */
180 GLib.Idle.add(() => {
181 view.start_editing_item (item);
182 return false;
183 });
184 }
185 }
186
187 break;
188 }
189 }
190
191
192 }
193
194 private void add_folder () {
195 if (!file.is_executable) {
196 // This is necessary to avoid infinite loop below
197 warning("Unable to open parent folder");
198 return;
199 }
200
201 var new_folder = file.file.get_child (_("untitled folder"));
202
203 var n = 1;
204 while (new_folder.query_exists ()) {
205 new_folder = file.file.get_child (_("untitled folder %d").printf (n));
206 n++;
207 }
208
209 try {
210 expanded = true;
211
212 new_folder.make_directory ();
213
214 newly_created_path = new_folder.get_path ();
215 } catch (Error e) {
216 warning (e.message);
217 }
218 }
219
220 private void add_file () {
221 if (!file.is_executable) {
222 // This is necessary to avoid infinite loop below
223 warning("Unable to open parent folder");
224 return;
225 }
226
227 var new_file = file.file.get_child (_("new file"));
228
229 var n = 1;
230 while (new_file.query_exists ()) {
231 new_file = file.file.get_child (_("new file %d").printf (n));
232 n++;
233 }
234
235 try {
236 expanded = true;
237
238 new_file.create (FileCreateFlags.NONE);
239
240 newly_created_path = new_file.get_path ();
241 } catch (Error e) {
242 warning (e.message);
243 }
244 }
245}
0246
=== modified file 'plugins/folder-manager/FolderManagerPlugin.vala'
--- plugins/folder-manager/FolderManagerPlugin.vala 2015-12-01 11:37:53 +0000
+++ plugins/folder-manager/FolderManagerPlugin.vala 2016-12-21 19:02:16 +0000
@@ -23,13 +23,10 @@
2323
24namespace Scratch.Plugins {24namespace Scratch.Plugins {
25 public class FolderManagerPlugin : Peas.ExtensionBase, Peas.Activatable {25 public class FolderManagerPlugin : Peas.ExtensionBase, Peas.Activatable {
2626 public Scratch.Services.Interface plugins;
27 Gtk.Button button;
27 FolderManager.FileView view;28 FolderManager.FileView view;
28 Gtk.ToolButton tool_button;29
29
30 int index = 0;
31
32 Scratch.Services.Interface plugins;
33 public Object object { owned get; construct; }30 public Object object { owned get; construct; }
3431
35 public FolderManagerPlugin () {32 public FolderManagerPlugin () {
@@ -43,74 +40,70 @@
43 }40 }
4441
45 public void deactivate () {42 public void deactivate () {
46 if (view != null)43 if (view != null) {
47 view.destroy();44 view.destroy();
48 if (tool_button != null) {45 view = null;
49 //(tool_button.parent as Scratch.Widgets.Toolbar).open_button.visible = true;46 }
50 tool_button.destroy ();47
48 if (button != null) {
49 button.destroy();
50 button = null;
51 }51 }
52 }52 }
5353
54 public void update_state () {54 public void update_state () {
55
55 }56 }
5657
57 void on_hook_sidebar (Gtk.Notebook notebook) {58 void on_hook_sidebar (Gtk.Notebook notebook) {
58 if (view != null)59 if (view != null) {
59 return;60 return;
61 }
6062
63 // File View
61 view = new FolderManager.FileView ();64 view = new FolderManager.FileView ();
6265 view.select.connect ((file) => plugins.open_file (file));
63 view.select.connect ((a) => {66 view.welcome_visible.connect ((visible) => {
64 var file = GLib.File.new_for_path (a);67 if (visible) {
65 plugins.open_file (file);68 view.parent.remove (view);
66 });69 } else if (view.parent == null) {
6770 view.show_all ();
68 view.root.child_added.connect (() => {71 var icon = new Gtk.Image.from_icon_name ("folder-symbolic", Gtk.IconSize.MENU);
69 if (view.get_n_visible_children (view.root) == 0) {72 icon.tooltip_text = _("Folder Manager");
70 index = notebook.append_page (view, new Gtk.Label (_("Folders")));73 notebook.append_page (view, icon);
71 }74 }
72 });75 });
7376
74 view.root.child_removed.connect (() => {77 view.restore_settings ();
75 if (view.get_n_visible_children (view.root) == 1)
76 notebook.remove_page (index);
77 });
78
79 view.restore_saved_state ();
80 }78 }
8179
82 void on_hook_toolbar (Gtk.HeaderBar toolbar) {80 void on_hook_toolbar (Gtk.HeaderBar toolbar) {
83 if (tool_button != null)81 if (button != null) {
84 return;82 return;
8583 }
86 //(toolbar as Scratch.Widgets.Toolbar).open_button.visible = false;84
87 var icon = new Gtk.Image.from_icon_name ("folder-saved-search", Gtk.IconSize.LARGE_TOOLBAR);85 button = new Gtk.Button.from_icon_name ("folder-saved-search", Gtk.IconSize.LARGE_TOOLBAR);
88 tool_button = new Gtk.ToolButton (icon, _("Open a folder"));86 button.tooltip_text = _("Open a folder");
89 tool_button.tooltip_text = _("Open a folder");87 button.clicked.connect (() => open_dialog ());
90 tool_button.clicked.connect (() => {88 button.show_all ();
91 Gtk.Window window = plugins.manager.window;89 toolbar.pack_start (button);
92 Gtk.FileChooserDialog chooser = new Gtk.FileChooserDialog (90 }
93 "Select a folder.", window, Gtk.FileChooserAction.SELECT_FOLDER,91
94 _("_Cancel"), Gtk.ResponseType.CANCEL,92 private void open_dialog () {
95 _("_Open"), Gtk.ResponseType.ACCEPT);93 Gtk.Window window = plugins.manager.window;
96 chooser.select_multiple = true;94 Gtk.FileChooserDialog chooser = new Gtk.FileChooserDialog (
9795 "Select a folder.", window, Gtk.FileChooserAction.SELECT_FOLDER,
98 if (chooser.run () == Gtk.ResponseType.ACCEPT) {96 _("_Cancel"), Gtk.ResponseType.CANCEL,
99 SList<string> uris = chooser.get_uris ();97 _("_Open"), Gtk.ResponseType.ACCEPT);
100 foreach (unowned string uri in uris) {98 chooser.select_multiple = true;
101 var folder = new FolderManager.File (uri.replace ("file:///", "/"));99
102 view.open_folder (folder); // emit signal100 if (chooser.run () == Gtk.ResponseType.ACCEPT) {
103 }101 chooser.get_files ().foreach ((file) => {
104 }102 view.open_folder (file.get_path ());
105103 });
106 chooser.close ();104 }
107 });105
108106 chooser.close ();
109 icon.show ();
110 tool_button.show ();
111
112 toolbar.pack_start (tool_button);
113 //toolbar.insert (tool_button, 1);
114 }107 }
115 }108 }
116}109}
117110
=== added file 'plugins/folder-manager/Item.vala'
--- plugins/folder-manager/Item.vala 1970-01-01 00:00:00 +0000
+++ plugins/folder-manager/Item.vala 2016-12-21 19:02:16 +0000
@@ -0,0 +1,69 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2016 elementary LLC. (https://elementary.io)
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authored by: Julien Spautz <spautz.julien@gmail.com>, Andrei-Costin Zisu <matzipan@gmail.com>
19 */
20
21namespace Scratch.Plugins.FolderManager {
22 /**
23 * Common abstract class for normal and expandable items.
24 */
25
26 public abstract class Item : Granite.Widgets.SourceList.ExpandableItem, Granite.Widgets.SourceListSortable {
27 public File file { get; construct; }
28 public Scratch.Plugins.FolderManager.FileView view { get; construct; }
29 public string path {
30 owned get { return file.path; }
31 set { file.path = value; }
32 }
33
34 construct {
35 selectable = true;
36 editable = true;
37 name = file.name;
38 icon = file.icon;
39
40 edited.connect (rename);
41 }
42
43 protected void rename (string new_name) {
44 file.rename (new_name);
45 }
46
47 protected void do_remove () {
48 file.trash ();
49 }
50
51 public int compare (Granite.Widgets.SourceList.Item a, Granite.Widgets.SourceList.Item b) {
52 if (a is FolderItem && b is FileItem) {
53 return -1;
54 } else if (a is FileItem && b is FolderItem) {
55 return 1;
56 }
57
58 if (a is Item && b is Item) {
59 return File.compare ((a as Item).file, (b as Item).file);
60 }
61
62 return 0;
63 }
64
65 public bool allow_dnd_sorting () {
66 return false;
67 }
68 }
69}
070
=== modified file 'plugins/folder-manager/folder-manager.plugin'
--- plugins/folder-manager/folder-manager.plugin 2013-06-08 15:17:42 +0000
+++ plugins/folder-manager/folder-manager.plugin 2016-12-21 19:02:16 +0000
@@ -3,8 +3,8 @@
3Loader=C3Loader=C
4IAge=24IAge=2
5Name=Folder Manager5Name=Folder Manager
6Description=Basic folder manager with file browsing6Description=Browse files and directories
7Icon=folder-saved-search7Icon=system-file-manager
8Authors=Julien Spautz <spautz.julien@gmail.com>8Authors=Mario Guerriero, Julien Spautz, Corentin Noël, Andrei Zisu
9Copyright=Copyright © 2013 Scratch Developers9Copyright=Copyright © 2016 elementary LLC
10Website=http://launchpad.net/scratch10Website=http://launchpad.net/scratch
1111
=== modified file 'schemas/CMakeLists.txt'
--- schemas/CMakeLists.txt 2015-09-10 00:54:45 +0000
+++ schemas/CMakeLists.txt 2016-12-21 19:02:16 +0000
@@ -1,6 +1,5 @@
1include(GSettings)1include(GSettings)
2add_schema("org.pantheon.scratch.gschema.xml")2add_schema("org.pantheon.scratch.gschema.xml")
3add_schema("org.pantheon.scratch.plugins.folder-manager.gschema.xml")3add_schema("org.pantheon.scratch.plugins.folder-manager.gschema.xml")
4add_schema("org.pantheon.scratch.plugins.file-manager.gschema.xml")
5add_schema("org.pantheon.scratch.plugins.terminal.gschema.xml")4add_schema("org.pantheon.scratch.plugins.terminal.gschema.xml")
6add_schema("org.pantheon.scratch.plugins.spell.gschema.xml")5add_schema("org.pantheon.scratch.plugins.spell.gschema.xml")
76
=== removed file 'schemas/org.pantheon.scratch.plugins.file-manager.gschema.xml'
--- schemas/org.pantheon.scratch.plugins.file-manager.gschema.xml 2013-07-16 15:56:38 +0000
+++ schemas/org.pantheon.scratch.plugins.file-manager.gschema.xml 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<schemalist>
3 <schema path="/org/pantheon/scratch/plugins/file-manager/" id="org.pantheon.scratch.plugins.file-manager" gettext-domain="scratch">
4 <key name="opened-folder" type="s">
5 <default>''</default>
6 <summary>Opened folder.</summary>
7 <description>Opened folder that should be restored in startup.</description>
8 </key>
9 </schema>
10</schemalist>

Subscribers

People subscribed via source and target branches