Merge lp:~matzipan/scratch/folder-manager-improvement into lp:~elementary-apps/scratch/scratch
- folder-manager-improvement
- Merge into scratch
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 |
Related bugs: |
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
Wrapping up the final changes for: https:/
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal | # |
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
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:/
- 1779. By Zisu Andrei
-
Simplify file/folder validity check. Move common construct/
rename/ trash code into superclass. GObject constructor for File
Jeremy Wootten (jeremywootten) wrote : | # |
On compiling there is a warning:
"warning: method `Scratch.
See also inline comments.
Function review to follow.
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.
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:/
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
Zisu Andrei (matzipan) wrote : | # |
Issues were addressed.
Jeremy Wootten (jeremywootten) wrote : | # |
Some further comments on the code (not necessarily blocking):
1) The class "Scratch.
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
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
Zisu Andrei (matzipan) wrote : | # |
Addressed 1 and 3.
2 isn't true. FileItem uses view for view.start_
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
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.
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.
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.
Jeremy Wootten (jeremywootten) wrote : | # |
Ignore last comment - it seems that is not the problem
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
Zisu Andrei (matzipan) wrote : | # |
It's ready for review again.
I jotted down 2 future improvements here: https:/
Jeremy Wootten (jeremywootten) wrote : | # |
The last revisions fixes the duplicate creation problem. There is a small bug - the "newly_
I have confirmed the future improvement bug.
- 1792. By Zisu Andrei
-
Null the newly_created_path after it has been processed
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.
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.
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 (!).
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.
- 1793. By Zisu Andrei
-
Check if folder is executable to avoid infinite loop
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:/
[2] https:/
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.
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.
- 1794. By Zisu Andrei
-
Monitor files and update unloaded folders
Zisu Andrei (matzipan) wrote : | # |
Fixed.
- 1795. By Zisu Andrei
-
Move menu items case
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.
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
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.
Ralph Plawetzki (purejava) wrote : | # |
Yeah, I also tested it. The issue described in https:/
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?
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.
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.
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/
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
1 | === modified file 'plugins/CMakeLists.txt' |
2 | --- plugins/CMakeLists.txt 2016-09-03 10:30:53 +0000 |
3 | +++ plugins/CMakeLists.txt 2016-12-21 19:02:16 +0000 |
4 | @@ -6,8 +6,7 @@ |
5 | ) |
6 | |
7 | add_subdirectory (contractor) |
8 | -add_subdirectory (pastebin) |
9 | -add_subdirectory (filemanager) |
10 | +add_subdirectory (pastebin) |
11 | add_subdirectory (folder-manager) |
12 | add_subdirectory (terminal) |
13 | add_subdirectory (browser-preview) |
14 | |
15 | === removed directory 'plugins/filemanager' |
16 | === removed file 'plugins/filemanager/CMakeLists.txt' |
17 | --- plugins/filemanager/CMakeLists.txt 2016-09-03 10:30:53 +0000 |
18 | +++ plugins/filemanager/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
19 | @@ -1,29 +0,0 @@ |
20 | -add_definitions(${NORMAL_CFLAGS}) |
21 | -link_directories(${NORMAL_LINK_DIRS}) |
22 | - |
23 | -set (PLUGIN_NAME "filemanager") |
24 | - |
25 | -vala_precompile(VALA_C ${PLUGIN_NAME} |
26 | - FileManagerPlugin.vala |
27 | - File.vala |
28 | - Settings.vala |
29 | - FileView.vala |
30 | -PACKAGES |
31 | - gtk+-3.0 |
32 | - gee-0.8 |
33 | - granite |
34 | - scratchcore |
35 | - libpeas-1.0 |
36 | - gtksourceview-3.0 |
37 | - ${ZEITGEIST_DEPS} |
38 | -OPTIONS |
39 | - ${DEFAULT_PLUGIN_OPTIONS} |
40 | -) |
41 | - |
42 | -add_library(${PLUGIN_NAME} MODULE ${VALA_C}) |
43 | -add_dependencies(${PLUGIN_NAME} ${LIBNAME}) |
44 | - |
45 | -install(TARGETS ${PLUGIN_NAME} DESTINATION ${PLUGINDIR}/${PLUGIN_NAME}) |
46 | -install(FILES ${PLUGIN_NAME}.plugin DESTINATION ${PLUGINDIR}/${PLUGIN_NAME}) |
47 | - |
48 | -message("-- File Manager plugin will be compiled") |
49 | |
50 | === removed file 'plugins/filemanager/File.vala' |
51 | --- plugins/filemanager/File.vala 2013-07-16 15:56:38 +0000 |
52 | +++ plugins/filemanager/File.vala 1970-01-01 00:00:00 +0000 |
53 | @@ -1,207 +0,0 @@ |
54 | -// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
55 | -/*** |
56 | - BEGIN LICENSE |
57 | - |
58 | - Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com> |
59 | - This program is free software: you can redistribute it and/or modify it |
60 | - under the terms of the GNU Lesser General Public License version 3, as published |
61 | - by the Free Software Foundation. |
62 | - |
63 | - This program is distributed in the hope that it will be useful, but |
64 | - WITHOUT ANY WARRANTY; without even the implied warranties of |
65 | - MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
66 | - PURPOSE. See the GNU General Public License for more details. |
67 | - |
68 | - You should have received a copy of the GNU General Public License along |
69 | - with this program. If not, see <http://www.gnu.org/licenses/> |
70 | - |
71 | - END LICENSE |
72 | -***/ |
73 | - |
74 | -namespace Scratch.Plugins.FileManager { |
75 | - |
76 | - /** |
77 | - * Class for easily dealing with files. |
78 | - */ |
79 | - internal class File : GLib.Object { |
80 | - |
81 | - public GLib.File file; |
82 | - private GLib.FileInfo info; |
83 | - |
84 | - private enum Type { |
85 | - VALID_FILE, |
86 | - VALID_FOLDER, |
87 | - UNKNOWN, |
88 | - INVALID |
89 | - } |
90 | - |
91 | - public File (string path) { |
92 | - file = GLib.File.new_for_path (path); |
93 | - |
94 | - info = new FileInfo (); |
95 | - try { |
96 | - info = file.query_info ( |
97 | - GLib.FileAttribute.STANDARD_CONTENT_TYPE + "," + |
98 | - GLib.FileAttribute.STANDARD_IS_BACKUP + "," + |
99 | - GLib.FileAttribute.STANDARD_IS_HIDDEN + "," + |
100 | - GLib.FileAttribute.STANDARD_DISPLAY_NAME + "," + |
101 | - GLib.FileAttribute.STANDARD_TYPE, |
102 | - FileQueryInfoFlags.NONE); |
103 | - } catch (GLib.Error error) { |
104 | - info = null; |
105 | - warning (error.message); |
106 | - } |
107 | - } |
108 | - |
109 | - // returns the path the file |
110 | - string _path = null; |
111 | - public string path { |
112 | - get { return _path != null ? _path : _path = file.get_path (); } |
113 | - } |
114 | - |
115 | - // returns the basename of the file |
116 | - string _name = null; |
117 | - public string name { |
118 | - get { return _name != null ? _name : _name = info.get_display_name (); } |
119 | - } |
120 | - |
121 | - // returns the icon of the file's content type |
122 | - GLib.Icon _icon = null; |
123 | - public GLib.Icon icon { |
124 | - get { |
125 | - if (_icon != null) |
126 | - return _icon; |
127 | - //var content_type = info.get_attribute_string (FileAttribute.STANDARD_FAST_CONTENT_TYPE); |
128 | - var content_type = info.get_content_type (); |
129 | - return _icon = GLib.ContentType.get_icon (content_type); |
130 | - } |
131 | - } |
132 | - |
133 | - // checks if file exists |
134 | - public bool exists { |
135 | - get { return file.query_exists (); } |
136 | - } |
137 | - |
138 | - Type _type = Type.UNKNOWN; |
139 | - // checks if we're dealing with a non-hidden, non-backup directory |
140 | - public bool is_valid_directory { |
141 | - get { |
142 | - if (_type == Type.VALID_FILE) |
143 | - return false; |
144 | - if (_type == Type.VALID_FOLDER) |
145 | - return true; |
146 | - if (_type == Type.INVALID) |
147 | - return false; |
148 | - |
149 | - if (info.get_file_type () != FileType.DIRECTORY || |
150 | - info.get_is_hidden () || info.get_is_backup ()) { |
151 | - return false; |
152 | - } |
153 | - |
154 | - bool has_valid_children = false; |
155 | - |
156 | - foreach (var child in children) { |
157 | - if (child.is_valid_textfile) { |
158 | - _type = Type.VALID_FOLDER; |
159 | - return has_valid_children = true; |
160 | - } |
161 | - } |
162 | - |
163 | - foreach (var child in children) { |
164 | - if (child.is_valid_directory) { |
165 | - has_valid_children = true; |
166 | - _type = Type.VALID_FOLDER; |
167 | - return has_valid_children = true; |
168 | - } |
169 | - } |
170 | - |
171 | - return false; |
172 | - } |
173 | - } |
174 | - |
175 | - // checks if we're dealing with a textfile |
176 | - public bool is_valid_textfile { |
177 | - get { |
178 | - if (_type == Type.VALID_FILE) |
179 | - return true; |
180 | - if (_type == Type.VALID_FOLDER) |
181 | - return false; |
182 | - if (_type == Type.INVALID) |
183 | - return false; |
184 | - |
185 | - if (info.get_file_type () == FileType.REGULAR) { |
186 | - //var content_type = info.get_attribute_string (FileAttribute.STANDARD_FAST_CONTENT_TYPE); |
187 | - var content_type = info.get_content_type (); |
188 | - if (ContentType.is_a (content_type, "text/*") && |
189 | - !info.get_is_backup () && |
190 | - !info.get_is_hidden ()) { |
191 | - _type = Type.VALID_FILE; |
192 | - return true; |
193 | - } |
194 | - } |
195 | - |
196 | - return false; |
197 | - } |
198 | - } |
199 | - |
200 | - // returns a list of all children of a directory |
201 | - GLib.List <File> _children = null; |
202 | - public GLib.List <File> children { |
203 | - get { |
204 | - if (_children != null) |
205 | - return _children; |
206 | - |
207 | - var parent = GLib.File.new_for_path (file.get_path ()); |
208 | - try { |
209 | - var enumerator = parent.enumerate_children ( |
210 | - GLib.FileAttribute.STANDARD_NAME, |
211 | - FileQueryInfoFlags.NONE |
212 | - ); |
213 | - |
214 | - var file_info = new FileInfo (); |
215 | - while ((file_info = enumerator.next_file ()) != null) { |
216 | - var child = parent.get_child (file_info.get_name ()); |
217 | - _children.append (new File (child.get_path ())); |
218 | - } |
219 | - } catch (GLib.Error error) { |
220 | - warning (error.message); |
221 | - } |
222 | - |
223 | - return _children; |
224 | - } |
225 | - } |
226 | - |
227 | - public void rename (string name) { |
228 | - try { |
229 | - file.set_display_name (name); |
230 | - } catch (GLib.Error error) { |
231 | - warning (error.message); |
232 | - } |
233 | - } |
234 | - |
235 | - /*public void trash () { |
236 | - try { |
237 | - file.trash (); |
238 | - } catch (GLib.Error error) { |
239 | - warning (error.message); |
240 | - } |
241 | - }*/ |
242 | - |
243 | - public void reset_cache () { |
244 | - _name = null; |
245 | - _path = null; |
246 | - _icon = null; |
247 | - _children = null; |
248 | - _type = Type.UNKNOWN; |
249 | - } |
250 | - |
251 | - public static int compare (File a, File b) { |
252 | - if (a.is_valid_directory && b.is_valid_textfile) |
253 | - return -1; |
254 | - if (a.is_valid_textfile && b.is_valid_directory) |
255 | - return 1; |
256 | - return strcmp (a.path.collate_key_for_filename (), |
257 | - b.path.collate_key_for_filename ()); |
258 | - } |
259 | - } |
260 | -} |
261 | |
262 | === removed file 'plugins/filemanager/FileManagerPlugin.vala' |
263 | --- plugins/filemanager/FileManagerPlugin.vala 2016-09-03 11:50:58 +0000 |
264 | +++ plugins/filemanager/FileManagerPlugin.vala 1970-01-01 00:00:00 +0000 |
265 | @@ -1,113 +0,0 @@ |
266 | -// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
267 | -/*** |
268 | - BEGIN LICENSE |
269 | - |
270 | - Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com> |
271 | - This program is free software: you can redistribute it and/or modify it |
272 | - under the terms of the GNU Lesser General Public License version 3, as published |
273 | - by the Free Software Foundation. |
274 | - |
275 | - This program is distributed in the hope that it will be useful, but |
276 | - WITHOUT ANY WARRANTY; without even the implied warranties of |
277 | - MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
278 | - PURPOSE. See the GNU General Public License for more details. |
279 | - |
280 | - You should have received a copy of the GNU General Public License along |
281 | - with this program. If not, see <http://www.gnu.org/licenses/> |
282 | - |
283 | - END LICENSE |
284 | -***/ |
285 | - |
286 | -public const string NAME = _("Folder Manager"); |
287 | -public const string DESCRIPTION = _("Basic folder manager with file browsing"); |
288 | - |
289 | -namespace Scratch.Plugins { |
290 | - public class FileManagerPlugin : Peas.ExtensionBase, Peas.Activatable { |
291 | - public Scratch.Services.Interface plugins; |
292 | - |
293 | - Gtk.Box box; |
294 | - FileManager.FileView view; |
295 | - |
296 | - public Object object { owned get; construct; } |
297 | - |
298 | - public FileManagerPlugin () { |
299 | - message ("Starting File Manager Plugin"); |
300 | - } |
301 | - |
302 | - public void activate () { |
303 | - plugins = (Scratch.Services.Interface) object; |
304 | - plugins.hook_notebook_sidebar.connect (on_hook_sidebar); |
305 | - } |
306 | - |
307 | - public void deactivate () { |
308 | - if (box != null) |
309 | - box.destroy(); |
310 | - } |
311 | - |
312 | - public void update_state () { |
313 | - |
314 | - } |
315 | - |
316 | - void on_hook_sidebar (Gtk.Notebook notebook) { |
317 | - if (view != null) |
318 | - return; |
319 | - |
320 | - box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); |
321 | - |
322 | - // File View |
323 | - view = new FileManager.FileView (); |
324 | - |
325 | - view.select.connect ((a) => { |
326 | - var file = GLib.File.new_for_path (a); |
327 | - plugins.open_file (file); |
328 | - }); |
329 | - |
330 | - // Toolbar |
331 | - var toolbar = new Gtk.Toolbar (); |
332 | - toolbar.set_icon_size (Gtk.IconSize.SMALL_TOOLBAR); |
333 | - toolbar.get_style_context ().add_class ("inline-toolbar"); |
334 | - |
335 | - var parent = new Gtk.ToolButton (null, null); |
336 | - parent.tooltip_text = _("Go to parent"); |
337 | - parent.icon_name = "go-up-symbolic"; |
338 | - parent.clicked.connect (() => { |
339 | - view.open_parent (); |
340 | - parent.sensitive = !(view.folder.file.file.get_path () == "/"); |
341 | - }); |
342 | - |
343 | - var spacer = new Gtk.ToolItem (); |
344 | - spacer.set_expand (true); |
345 | - |
346 | - var add = new Gtk.ToolButton (null, null); |
347 | - add.tooltip_text = _("Add file"); |
348 | - add.icon_name = "list-add-symbolic"; |
349 | - add.clicked.connect (() => { |
350 | - view.add_file (); |
351 | - }); |
352 | - |
353 | - var remove = new Gtk.ToolButton (null, null); |
354 | - remove.tooltip_text = _("Remove file"); |
355 | - remove.icon_name = "edit-delete-symbolic"; |
356 | - remove.clicked.connect (() => { |
357 | - view.remove_file (); |
358 | - }); |
359 | - |
360 | - toolbar.insert (parent, -1); |
361 | - toolbar.insert (spacer, -1); |
362 | - toolbar.insert (add, -1); |
363 | - toolbar.insert (remove, -1); |
364 | - |
365 | - box.pack_start (view, true, true, 0); |
366 | - box.pack_start (toolbar, false, false, 0); |
367 | - box.show_all (); |
368 | - |
369 | - notebook.append_page (box, new Gtk.Label (_("File Manager"))); |
370 | - } |
371 | - } |
372 | -} |
373 | - |
374 | -[ModuleInit] |
375 | -public void peas_register_types (GLib.TypeModule module) { |
376 | - var objmodule = module as Peas.ObjectModule; |
377 | - objmodule.register_extension_type (typeof (Peas.Activatable), typeof (Scratch.Plugins.FileManagerPlugin)); |
378 | -} |
379 | |
380 | === removed file 'plugins/filemanager/FileView.vala' |
381 | --- plugins/filemanager/FileView.vala 2016-09-03 11:50:58 +0000 |
382 | +++ plugins/filemanager/FileView.vala 1970-01-01 00:00:00 +0000 |
383 | @@ -1,297 +0,0 @@ |
384 | -// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
385 | -/*** |
386 | - BEGIN LICENSE |
387 | - |
388 | - Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com> |
389 | - This program is free software: you can redistribute it and/or modify it |
390 | - under the terms of the GNU Lesser General Public License version 3, as published |
391 | - by the Free Software Foundation. |
392 | - |
393 | - This program is distributed in the hope that it will be useful, but |
394 | - WITHOUT ANY WARRANTY; without even the implied warranties of |
395 | - MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
396 | - PURPOSE. See the GNU General Public License for more details. |
397 | - |
398 | - You should have received a copy of the GNU General Public License along |
399 | - with this program. If not, see <http://www.gnu.org/licenses/> |
400 | - |
401 | - END LICENSE |
402 | -***/ |
403 | - |
404 | -namespace Scratch.Plugins.FileManager { |
405 | - Settings settings; |
406 | - |
407 | - /** |
408 | - * SourceList that displays folders and their contents. |
409 | - */ |
410 | - internal class FileView : Granite.Widgets.SourceList { |
411 | - public FolderItem? folder = null; |
412 | - public signal void select (string file); |
413 | - public FileView () { |
414 | - this.width_request = 180; |
415 | - |
416 | - this.item_selected.connect ((item) => { |
417 | - select ((item as FileItem).path); |
418 | - }); |
419 | - |
420 | - this.root.child_removed.connect (() => { |
421 | - this.selected = null; |
422 | - }); |
423 | - |
424 | - settings = new Settings (); |
425 | - restore_settings (); |
426 | - } |
427 | - |
428 | - public void open_parent () { |
429 | - GLib.File parent = this.folder.file.file.get_parent (); |
430 | - this.root.remove (this.folder); |
431 | - open_folder (new File (parent.get_path ())); |
432 | - } |
433 | - |
434 | - public void open_folder (File folder, bool expand = true) { |
435 | - if (is_open (folder)) { |
436 | - warning ("Folder '%s' is already open.", folder.path); |
437 | - return; |
438 | - } else if (!folder.is_valid_directory) { |
439 | - warning ("Cannot open invalid directory."); |
440 | - return; |
441 | - } |
442 | - |
443 | - // Clean the SourceList before start adding something |
444 | - if (this.folder != null) { |
445 | - this.root.remove (this.folder); |
446 | - } |
447 | - |
448 | - this.folder = new FolderItem (folder, this); |
449 | - this.root.add (this.folder); |
450 | - |
451 | - this.folder.expanded = expand; |
452 | - write_settings (); |
453 | - } |
454 | - |
455 | - public void add_file () { |
456 | - string path = folder.file.file.get_path () + _("/New File"); |
457 | - var file = GLib.File.new_for_path (path); |
458 | - int n = 1; |
459 | - while (file.query_exists ()) { |
460 | - file = GLib.File.new_for_path (path + n.to_string ()); |
461 | - n++; |
462 | - } |
463 | - |
464 | - try { |
465 | - file.create (FileCreateFlags.NONE); |
466 | - } catch (Error e) { |
467 | - warning (e.message); |
468 | - } |
469 | - |
470 | - var item = new FileItem (new File (file.get_path ())); |
471 | - this.folder.add (item); |
472 | - } |
473 | - |
474 | - public void remove_file () { |
475 | - if (this.selected is FileItem) { |
476 | - var file = GLib.File.new_for_path (((FileItem)selected).file.file.get_path ()); |
477 | - try { |
478 | - file.delete (); |
479 | - this.root.remove (selected); |
480 | - } catch (Error e) { |
481 | - warning (e.message); |
482 | - } |
483 | - } |
484 | - |
485 | - this.selected = null; |
486 | - } |
487 | - |
488 | - private bool is_open (File folder) { |
489 | - foreach (var child in root.children) { |
490 | - if (folder.path == (child as Item).path) { |
491 | - return true; |
492 | - } |
493 | - } |
494 | - |
495 | - return false; |
496 | - } |
497 | - |
498 | - private void write_settings () { |
499 | - settings.opened_folder = this.folder.file.file.get_path (); |
500 | - } |
501 | - |
502 | - private void restore_settings () { |
503 | - var folder = new File (settings.opened_folder); |
504 | - if (settings.opened_folder == "" || settings.opened_folder == null || !folder.is_valid_directory) { |
505 | - settings.opened_folder = GLib.Environment.get_home_dir (); |
506 | - } |
507 | - |
508 | - open_folder (new File (settings.opened_folder)); |
509 | - } |
510 | - } |
511 | - |
512 | - /** |
513 | - * Common abstract class for normal and expandable items. |
514 | - */ |
515 | - internal class Item : Granite.Widgets.SourceList.ExpandableItem, Granite.Widgets.SourceListSortable { |
516 | - public File file { get; construct; } |
517 | - public string path { get { return file.path; } } |
518 | - |
519 | - public int compare (Granite.Widgets.SourceList.Item a, Granite.Widgets.SourceList.Item b) { |
520 | - if (a is FolderItem && b is FileItem) { |
521 | - return -1; |
522 | - } else if (a is FileItem && b is FolderItem) { |
523 | - return 1; |
524 | - } |
525 | - |
526 | - return File.compare ((a as Item).file, (b as Item).file); |
527 | - } |
528 | - |
529 | - public bool allow_dnd_sorting () { |
530 | - return false; |
531 | - } |
532 | - } |
533 | - |
534 | - /** |
535 | - * Normal item in the source list, represents a textfile. |
536 | - * TODO Remove, Rename |
537 | - */ |
538 | - internal class FileItem : Item { |
539 | - //Gtk.Menu menu; |
540 | - //Gtk.MenuItem item_trash; |
541 | - public FileItem (File file) requires (file.is_valid_textfile) { |
542 | - Object (file: file); |
543 | - |
544 | - this.selectable = true; |
545 | - this.editable = true; |
546 | - this.name = file.name; |
547 | - this.icon = file.icon; |
548 | - } |
549 | - |
550 | - public void rename (string new_name) { |
551 | - string new_uri = file.file.get_parent ().get_uri () + "/" + new_name; |
552 | - debug (new_uri); |
553 | - file.rename (new_name); |
554 | - } |
555 | - |
556 | - /*public override Gtk.Menu? get_context_menu () { |
557 | - menu = new Gtk.Menu (); |
558 | - item_trash = new Gtk.MenuItem.with_label (_("Move to Trash")); |
559 | - menu.append (item_trash); |
560 | - item_trash.activate.connect (() => { file.trash (); }); |
561 | - menu.show_all (); |
562 | - return menu; |
563 | - }*/ |
564 | - } |
565 | - |
566 | - /** |
567 | - * Expandable item in the source list, represents a folder. |
568 | - * Monitored for changes inside the directory. |
569 | - * TODO remove, rename, create new file |
570 | - */ |
571 | - internal class FolderItem : Item { |
572 | - public signal void folder_open (GLib.File folder); |
573 | - public FileView view { get; construct; } |
574 | - |
575 | - private GLib.FileMonitor monitor; |
576 | - private bool children_loaded = false; |
577 | - |
578 | - //Gtk.Menu menu; |
579 | - //Gtk.MenuItem item_trash; |
580 | - //Gtk.MenuItem item_create; |
581 | - |
582 | - public FolderItem (File file, FileView view) requires (file.is_valid_directory) { |
583 | - Object (file: file, view: view); |
584 | - |
585 | - this.editable = false; |
586 | - this.selectable = false; |
587 | - this.name = file.name; |
588 | - this.icon = file.icon; |
589 | - |
590 | - this.add (new Granite.Widgets.SourceList.Item ("")); // dummy |
591 | - this.toggled.connect (() => { |
592 | - if (this.expanded && this.n_children <= 1) { |
593 | - this.clear (); |
594 | - this.add_children (); |
595 | - children_loaded = true; |
596 | - } |
597 | - }); |
598 | - |
599 | - try { |
600 | - monitor = file.file.monitor_directory (GLib.FileMonitorFlags.NONE); |
601 | - monitor.changed.connect ((s,d,e) => { on_changed (s,d,e); }); |
602 | - } catch (GLib.Error e) { |
603 | - warning (e.message); |
604 | - } |
605 | - } |
606 | - |
607 | - public override Gtk.Menu? get_context_menu () { |
608 | - if (this == this.view.root.children.to_array ()[0]) { |
609 | - return null; |
610 | - } |
611 | - |
612 | - var menu = new Gtk.Menu (); |
613 | - var item = new Gtk.MenuItem.with_label (_("Open")); |
614 | - item.activate.connect (() => { this.folder_open (this.file.file); }); |
615 | - menu.append (item); |
616 | - menu.show_all (); |
617 | - return menu; |
618 | - } |
619 | - |
620 | - internal void add_children () { |
621 | - foreach (var child in file.children) { |
622 | - if (child.is_valid_directory) { |
623 | - var item = new FolderItem (child, view); |
624 | - item.folder_open.connect (() => { |
625 | - this.view.open_folder (child); |
626 | - }); |
627 | - |
628 | - add (item); |
629 | - } else if (child.is_valid_textfile) { |
630 | - var item = new FileItem (child); |
631 | - add (item); |
632 | - item.edited.connect (item.rename); |
633 | - } |
634 | - } |
635 | - } |
636 | - |
637 | - private void on_changed (GLib.File source, GLib.File? dest, GLib.FileMonitorEvent event) { |
638 | - if (!children_loaded) { |
639 | - this.file.reset_cache (); |
640 | - return; |
641 | - } |
642 | - |
643 | - switch (event) { |
644 | - case GLib.FileMonitorEvent.DELETED: |
645 | - var children_tmp = new Gee.ArrayList<Granite.Widgets.SourceList.Item> (); |
646 | - children_tmp.add_all (children); |
647 | - foreach (var item in children_tmp) { |
648 | - if ((item as Item).path == source.get_path ()) { |
649 | - remove (item); |
650 | - } |
651 | - } |
652 | - |
653 | - break; |
654 | - case GLib.FileMonitorEvent.CREATED: |
655 | - if (source.query_exists () == false) { |
656 | - return; |
657 | - } |
658 | - |
659 | - var file = new File (source.get_path ()); |
660 | - var exists = false; |
661 | - foreach (var item in children) { |
662 | - if ((item as Item).path == file.path) { |
663 | - exists = true; |
664 | - } |
665 | - } |
666 | - |
667 | - if (!exists) { |
668 | - if (file.is_valid_textfile) { |
669 | - this.add (new FileItem (file)); |
670 | - } else if (file.is_valid_directory) { |
671 | - this.add (new FolderItem (file, view)); |
672 | - } |
673 | - } |
674 | - |
675 | - break; |
676 | - } |
677 | - } |
678 | - } |
679 | - |
680 | -} |
681 | |
682 | === removed file 'plugins/filemanager/Settings.vala' |
683 | --- plugins/filemanager/Settings.vala 2013-07-16 15:56:38 +0000 |
684 | +++ plugins/filemanager/Settings.vala 1970-01-01 00:00:00 +0000 |
685 | @@ -1,36 +0,0 @@ |
686 | -// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
687 | -/*** |
688 | - BEGIN LICENSE |
689 | - |
690 | - Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com> |
691 | - This program is free software: you can redistribute it and/or modify it |
692 | - under the terms of the GNU Lesser General Public License version 3, as published |
693 | - by the Free Software Foundation. |
694 | - |
695 | - This program is distributed in the hope that it will be useful, but |
696 | - WITHOUT ANY WARRANTY; without even the implied warranties of |
697 | - MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
698 | - PURPOSE. See the GNU General Public License for more details. |
699 | - |
700 | - You should have received a copy of the GNU General Public License along |
701 | - with this program. If not, see <http://www.gnu.org/licenses/> |
702 | - |
703 | - END LICENSE |
704 | -***/ |
705 | - |
706 | -namespace Scratch.Plugins.FileManager { |
707 | - |
708 | - /** |
709 | - * Class for interacting with gsettings. |
710 | - */ |
711 | - internal class Settings : Granite.Services.Settings { |
712 | - |
713 | - private const string SCHEMA = "org.pantheon.scratch.plugins.file-manager"; |
714 | - |
715 | - public string opened_folder { get; set; } |
716 | - |
717 | - public Settings () { |
718 | - base (SCHEMA); |
719 | - } |
720 | - } |
721 | -} |
722 | |
723 | === removed file 'plugins/filemanager/filemanager.plugin' |
724 | --- plugins/filemanager/filemanager.plugin 2013-07-16 15:56:38 +0000 |
725 | +++ plugins/filemanager/filemanager.plugin 1970-01-01 00:00:00 +0000 |
726 | @@ -1,10 +0,0 @@ |
727 | -[Plugin] |
728 | -Module=filemanager |
729 | -Loader=C |
730 | -IAge=2 |
731 | -Name=File Manager |
732 | -Description=Browse files and directories |
733 | -Icon=system-file-manager |
734 | -Authors=Mario Guerriero |
735 | -Copyright=Copyright © 2013 Scratch Developers |
736 | -Website=http://launchpad.net/scratch |
737 | |
738 | === modified file 'plugins/folder-manager/CMakeLists.txt' |
739 | --- plugins/folder-manager/CMakeLists.txt 2016-09-03 10:30:53 +0000 |
740 | +++ plugins/folder-manager/CMakeLists.txt 2016-12-21 19:02:16 +0000 |
741 | @@ -8,6 +8,9 @@ |
742 | File.vala |
743 | Settings.vala |
744 | FileView.vala |
745 | + Item.vala |
746 | + FileItem.vala |
747 | + FolderItem.vala |
748 | PACKAGES |
749 | gtk+-3.0 |
750 | gee-0.8 |
751 | |
752 | === modified file 'plugins/folder-manager/File.vala' |
753 | --- plugins/folder-manager/File.vala 2013-06-02 13:07:08 +0000 |
754 | +++ plugins/folder-manager/File.vala 2016-12-21 19:02:16 +0000 |
755 | @@ -1,79 +1,70 @@ |
756 | // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
757 | -/*** |
758 | - BEGIN LICENSE |
759 | - |
760 | - Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com> |
761 | - This program is free software: you can redistribute it and/or modify it |
762 | - under the terms of the GNU Lesser General Public License version 3, as published |
763 | - by the Free Software Foundation. |
764 | - |
765 | - This program is distributed in the hope that it will be useful, but |
766 | - WITHOUT ANY WARRANTY; without even the implied warranties of |
767 | - MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
768 | - PURPOSE. See the GNU General Public License for more details. |
769 | - |
770 | - You should have received a copy of the GNU General Public License along |
771 | - with this program. If not, see <http://www.gnu.org/licenses/> |
772 | - |
773 | - END LICENSE |
774 | -***/ |
775 | +/*- |
776 | + * Copyright (c) 2016 elementary LLC. (https://elementary.io) |
777 | + * |
778 | + * This program is free software: you can redistribute it and/or modify |
779 | + * it under the terms of the GNU General Public License as published by |
780 | + * the Free Software Foundation, either version 3 of the License, or |
781 | + * (at your option) any later version. |
782 | + * |
783 | + * This program is distributed in the hope that it will be useful, |
784 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
785 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
786 | + * GNU General Public License for more details. |
787 | + * |
788 | + * You should have received a copy of the GNU General Public License |
789 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
790 | + * |
791 | + * Authored by: Julien Spautz <spautz.julien@gmail.com>, Andrei-Costin Zisu <matzipan@gmail.com> |
792 | + */ |
793 | |
794 | namespace Scratch.Plugins.FolderManager { |
795 | |
796 | /** |
797 | * Class for easily dealing with files. |
798 | */ |
799 | - internal class File : GLib.Object { |
800 | - |
801 | - public GLib.File file; |
802 | - private GLib.FileInfo info; |
803 | - |
804 | - private enum Type { |
805 | - VALID_FILE, |
806 | - VALID_FOLDER, |
807 | - UNKNOWN, |
808 | - INVALID |
809 | - } |
810 | + public class File : GLib.Object { |
811 | + |
812 | + public GLib.File file { get; private set; } |
813 | + private GLib.FileInfo? info; |
814 | |
815 | public File (string path) { |
816 | - file = GLib.File.new_for_path (path); |
817 | - |
818 | - info = new FileInfo (); |
819 | - try { |
820 | - info = file.query_info ( |
821 | - GLib.FileAttribute.STANDARD_CONTENT_TYPE + "," + |
822 | - GLib.FileAttribute.STANDARD_IS_BACKUP + "," + |
823 | - GLib.FileAttribute.STANDARD_IS_HIDDEN + "," + |
824 | - GLib.FileAttribute.STANDARD_DISPLAY_NAME + "," + |
825 | - GLib.FileAttribute.STANDARD_TYPE, |
826 | - FileQueryInfoFlags.NONE); |
827 | - } catch (GLib.Error error) { |
828 | - info = null; |
829 | - warning (error.message); |
830 | - } |
831 | + Object (path: path); |
832 | } |
833 | |
834 | // returns the path the file |
835 | - string _path = null; |
836 | public string path { |
837 | - get { return _path != null ? _path : _path = file.get_path (); } |
838 | + owned get { |
839 | + return file.get_path (); |
840 | + } |
841 | + set construct { |
842 | + load_file_for_path (value); |
843 | + } |
844 | } |
845 | |
846 | // returns the basename of the file |
847 | - string _name = null; |
848 | + private string _name; |
849 | public string name { |
850 | - get { return _name != null ? _name : _name = info.get_display_name (); } |
851 | + get { |
852 | + if (info == null) { |
853 | + return ""; |
854 | + } |
855 | + |
856 | + _name = info.get_display_name (); |
857 | + return _name; |
858 | + } |
859 | } |
860 | |
861 | // returns the icon of the file's content type |
862 | - GLib.Icon _icon = null; |
863 | + private GLib.Icon? _icon = null; |
864 | public GLib.Icon icon { |
865 | get { |
866 | - if (_icon != null) |
867 | + if (_icon != null) { |
868 | return _icon; |
869 | - //var content_type = info.get_attribute_string (FileAttribute.STANDARD_FAST_CONTENT_TYPE); |
870 | - var content_type = info.get_content_type (); |
871 | - return _icon = GLib.ContentType.get_icon (content_type); |
872 | + } |
873 | + |
874 | + _icon = GLib.ContentType.get_icon (info.get_content_type ()); |
875 | + return _icon; |
876 | } |
877 | } |
878 | |
879 | @@ -82,37 +73,15 @@ |
880 | get { return file.query_exists (); } |
881 | } |
882 | |
883 | - Type _type = Type.UNKNOWN; |
884 | // checks if we're dealing with a non-hidden, non-backup directory |
885 | - public bool is_valid_directory { |
886 | - get { |
887 | - if (_type == Type.VALID_FILE) |
888 | + public bool is_valid_directory { |
889 | + get { |
890 | + if (info.get_is_hidden () || info.get_is_backup ()) { |
891 | return false; |
892 | - if (_type == Type.VALID_FOLDER) |
893 | + } |
894 | + |
895 | + if (info.get_file_type () == FileType.DIRECTORY) { |
896 | return true; |
897 | - if (_type == Type.INVALID) |
898 | - return false; |
899 | - |
900 | - if (info.get_file_type () != FileType.DIRECTORY || |
901 | - info.get_is_hidden () || info.get_is_backup ()) { |
902 | - return false; |
903 | - } |
904 | - |
905 | - bool has_valid_children = false; |
906 | - |
907 | - foreach (var child in children) { |
908 | - if (child.is_valid_textfile) { |
909 | - _type = Type.VALID_FOLDER; |
910 | - return has_valid_children = true; |
911 | - } |
912 | - } |
913 | - |
914 | - foreach (var child in children) { |
915 | - if (child.is_valid_directory) { |
916 | - has_valid_children = true; |
917 | - _type = Type.VALID_FOLDER; |
918 | - return has_valid_children = true; |
919 | - } |
920 | } |
921 | |
922 | return false; |
923 | @@ -122,34 +91,37 @@ |
924 | // checks if we're dealing with a textfile |
925 | public bool is_valid_textfile { |
926 | get { |
927 | - if (_type == Type.VALID_FILE) |
928 | + if (info.get_is_hidden () || info.get_is_backup ()) { |
929 | + return false; |
930 | + } |
931 | + |
932 | + if (info.get_file_type () == FileType.REGULAR && |
933 | + ContentType.is_a (info.get_content_type (), "text/*")) { |
934 | return true; |
935 | - if (_type == Type.VALID_FOLDER) |
936 | - return false; |
937 | - if (_type == Type.INVALID) |
938 | - return false; |
939 | - |
940 | - if (info.get_file_type () == FileType.REGULAR) { |
941 | - //var content_type = info.get_attribute_string (FileAttribute.STANDARD_FAST_CONTENT_TYPE); |
942 | - var content_type = info.get_content_type (); |
943 | - if (ContentType.is_a (content_type, "text/*") && |
944 | - !info.get_is_backup () && |
945 | - !info.get_is_hidden ()) { |
946 | - _type = Type.VALID_FILE; |
947 | - return true; |
948 | - } |
949 | } |
950 | |
951 | return false; |
952 | } |
953 | } |
954 | |
955 | + // Files can be executed and folders can be cd'd into |
956 | + public bool is_executable { |
957 | + get { |
958 | + try { |
959 | + return get_boolean_file_attribute(GLib.FileAttribute.ACCESS_CAN_EXECUTE); |
960 | + } catch (GLib.Error error) { |
961 | + return false; |
962 | + } |
963 | + } |
964 | + } |
965 | + |
966 | // returns a list of all children of a directory |
967 | - GLib.List <File> _children = null; |
968 | + GLib.List <File>? _children = null; |
969 | public GLib.List <File> children { |
970 | get { |
971 | - if (_children != null) |
972 | + if (_children != null) { |
973 | return _children; |
974 | + } |
975 | |
976 | var parent = GLib.File.new_for_path (file.get_path ()); |
977 | try { |
978 | @@ -161,7 +133,11 @@ |
979 | var file_info = new FileInfo (); |
980 | while ((file_info = enumerator.next_file ()) != null) { |
981 | var child = parent.get_child (file_info.get_name ()); |
982 | - _children.append (new File (child.get_path ())); |
983 | + var file = new File (child.get_path ()); |
984 | + |
985 | + if (file.is_valid_directory || file.is_valid_textfile) { |
986 | + _children.append (file); |
987 | + } |
988 | } |
989 | } catch (GLib.Error error) { |
990 | warning (error.message); |
991 | @@ -171,9 +147,33 @@ |
992 | } |
993 | } |
994 | |
995 | - /*public void rename (string name) { |
996 | - try { |
997 | - file.set_display_name (name); |
998 | + private bool get_boolean_file_attribute(string attribute) throws GLib.Error { |
999 | + var info = file.query_info(attribute, GLib.FileQueryInfoFlags.NONE); |
1000 | + |
1001 | + return info.get_attribute_boolean(attribute); |
1002 | + } |
1003 | + |
1004 | + private void load_file_for_path (string path) { |
1005 | + file = GLib.File.new_for_path (path); |
1006 | + |
1007 | + info = new FileInfo (); |
1008 | + try { |
1009 | + var query_string = GLib.FileAttribute.STANDARD_CONTENT_TYPE + "," + |
1010 | + GLib.FileAttribute.STANDARD_IS_BACKUP + "," + |
1011 | + GLib.FileAttribute.STANDARD_IS_HIDDEN + "," + |
1012 | + GLib.FileAttribute.STANDARD_DISPLAY_NAME + "," + |
1013 | + GLib.FileAttribute.STANDARD_TYPE; |
1014 | + |
1015 | + info = file.query_info (query_string, FileQueryInfoFlags.NONE); |
1016 | + } catch (GLib.Error error) { |
1017 | + info = null; |
1018 | + warning (error.message); |
1019 | + } |
1020 | + } |
1021 | + |
1022 | + public void rename (string name) { |
1023 | + try { |
1024 | + file.set_display_name (name); |
1025 | } catch (GLib.Error error) { |
1026 | warning (error.message); |
1027 | } |
1028 | @@ -185,23 +185,23 @@ |
1029 | } catch (GLib.Error error) { |
1030 | warning (error.message); |
1031 | } |
1032 | - }*/ |
1033 | - |
1034 | - public void reset_cache () { |
1035 | - _name = null; |
1036 | - _path = null; |
1037 | - _icon = null; |
1038 | - _children = null; |
1039 | - _type = Type.UNKNOWN; |
1040 | } |
1041 | |
1042 | public static int compare (File a, File b) { |
1043 | - if (a.is_valid_directory && b.is_valid_textfile) |
1044 | + if (a.is_valid_directory && b.is_valid_textfile) { |
1045 | return -1; |
1046 | - if (a.is_valid_textfile && b.is_valid_directory) |
1047 | + } |
1048 | + |
1049 | + if (a.is_valid_textfile && b.is_valid_directory) { |
1050 | return 1; |
1051 | + } |
1052 | + |
1053 | return strcmp (a.path.collate_key_for_filename (), |
1054 | b.path.collate_key_for_filename ()); |
1055 | } |
1056 | + |
1057 | + public void invalidate_cache () { |
1058 | + _children = null; |
1059 | + } |
1060 | } |
1061 | } |
1062 | |
1063 | === added file 'plugins/folder-manager/FileItem.vala' |
1064 | --- plugins/folder-manager/FileItem.vala 1970-01-01 00:00:00 +0000 |
1065 | +++ plugins/folder-manager/FileItem.vala 2016-12-21 19:02:16 +0000 |
1066 | @@ -0,0 +1,39 @@ |
1067 | +// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
1068 | +/*- |
1069 | + * Copyright (c) 2016 elementary LLC. (https://elementary.io) |
1070 | + * |
1071 | + * This program is free software: you can redistribute it and/or modify |
1072 | + * it under the terms of the GNU General Public License as published by |
1073 | + * the Free Software Foundation, either version 3 of the License, or |
1074 | + * (at your option) any later version. |
1075 | + * |
1076 | + * This program is distributed in the hope that it will be useful, |
1077 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1078 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1079 | + * GNU General Public License for more details. |
1080 | + * |
1081 | + * You should have received a copy of the GNU General Public License |
1082 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1083 | + * |
1084 | + * Authored by: Corentin Noël <corentin@elementary.io>, Andrei-Costin Zisu <matzipan@gmail.com> |
1085 | + */ |
1086 | + |
1087 | +public class Scratch.Plugins.FolderManager.FileItem : Item { |
1088 | + public FileItem (Scratch.Plugins.FolderManager.File file, Scratch.Plugins.FolderManager.FileView view) requires (file.is_valid_textfile) { |
1089 | + Object (file: file, view: view); |
1090 | + } |
1091 | + |
1092 | + public override Gtk.Menu? get_context_menu () { |
1093 | + var menu = new Gtk.Menu (); |
1094 | + var rename_item = new Gtk.MenuItem.with_label (_("Rename")); |
1095 | + rename_item.activate.connect (() => view.start_editing_item (this)); |
1096 | + menu.append (rename_item); |
1097 | + |
1098 | + var delete_item = new Gtk.MenuItem.with_label (_("Move to Trash")); |
1099 | + delete_item.activate.connect (() => do_remove ()); |
1100 | + menu.append (delete_item); |
1101 | + |
1102 | + menu.show_all (); |
1103 | + return menu; |
1104 | + } |
1105 | +} |
1106 | |
1107 | === modified file 'plugins/folder-manager/FileView.vala' |
1108 | --- plugins/folder-manager/FileView.vala 2015-09-16 01:11:55 +0000 |
1109 | +++ plugins/folder-manager/FileView.vala 2016-12-21 19:02:16 +0000 |
1110 | @@ -1,22 +1,22 @@ |
1111 | // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
1112 | -/*** |
1113 | - BEGIN LICENSE |
1114 | - |
1115 | - Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com> |
1116 | - This program is free software: you can redistribute it and/or modify it |
1117 | - under the terms of the GNU Lesser General Public License version 3, as published |
1118 | - by the Free Software Foundation. |
1119 | - |
1120 | - This program is distributed in the hope that it will be useful, but |
1121 | - WITHOUT ANY WARRANTY; without even the implied warranties of |
1122 | - MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1123 | - PURPOSE. See the GNU General Public License for more details. |
1124 | - |
1125 | - You should have received a copy of the GNU General Public License along |
1126 | - with this program. If not, see <http://www.gnu.org/licenses/> |
1127 | - |
1128 | - END LICENSE |
1129 | -***/ |
1130 | +/*- |
1131 | + * Copyright (c) 2016 elementary LLC. (https://elementary.io) |
1132 | + * |
1133 | + * This program is free software: you can redistribute it and/or modify |
1134 | + * it under the terms of the GNU General Public License as published by |
1135 | + * the Free Software Foundation, either version 3 of the License, or |
1136 | + * (at your option) any later version. |
1137 | + * |
1138 | + * This program is distributed in the hope that it will be useful, |
1139 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1140 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1141 | + * GNU General Public License for more details. |
1142 | + * |
1143 | + * You should have received a copy of the GNU General Public License |
1144 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1145 | + * |
1146 | + * Authored by: Julien Spautz <spautz.julien@gmail.com>, Andrei-Costin Zisu <matzipan@gmail.com> |
1147 | + */ |
1148 | |
1149 | namespace Scratch.Plugins.FolderManager { |
1150 | Settings settings; |
1151 | @@ -24,289 +24,122 @@ |
1152 | /** |
1153 | * SourceList that displays folders and their contents. |
1154 | */ |
1155 | - internal class FileView : Granite.Widgets.SourceList { |
1156 | - |
1157 | - public signal void select (string file); |
1158 | - |
1159 | - public FileView () { |
1160 | - this.width_request = 180; |
1161 | - this.item_selected.connect ((item) => { |
1162 | - select ((item as FileItem).path); |
1163 | - }); |
1164 | + public class FileView : Granite.Widgets.SourceList { |
1165 | + public signal void select (GLib.File file); |
1166 | + public signal void welcome_visible (bool visible); |
1167 | + |
1168 | + // This is a workaround for SourceList silliness: you cannot remove an item |
1169 | + // without it automatically selecting another one. |
1170 | + public bool ignore_next_select = false; |
1171 | + |
1172 | + construct { |
1173 | + width_request = 180; |
1174 | + |
1175 | + item_selected.connect (on_item_selected); |
1176 | |
1177 | settings = new Settings (); |
1178 | } |
1179 | |
1180 | - public void restore_saved_state () { |
1181 | - foreach (var path in settings.opened_folders) |
1182 | - add_folder (new File (path), false); |
1183 | - } |
1184 | - |
1185 | - public void open_folder (File folder) { |
1186 | - if (is_open (folder)) { |
1187 | - warning ("Folder '%s' is already open.", folder.path); |
1188 | - return; |
1189 | - } else if (!folder.is_valid_directory) { |
1190 | - warning ("Cannot open invalid directory."); |
1191 | - return; |
1192 | - } |
1193 | - |
1194 | - add_folder (folder, true); |
1195 | - write_settings (); |
1196 | - } |
1197 | - |
1198 | - private void add_folder (File folder, bool expand) { |
1199 | - if (is_open (folder)) { |
1200 | - warning ("Folder '%s' is already open.", folder.path); |
1201 | - return; |
1202 | - } else if (!folder.is_valid_directory) { |
1203 | - warning ("Cannot open invalid directory."); |
1204 | - return; |
1205 | - } |
1206 | - |
1207 | - var folder_root = new MainFolderItem (folder); |
1208 | - this.root.add (folder_root); |
1209 | - |
1210 | - folder_root.expanded = expand; |
1211 | - folder_root.closed.connect (() => { |
1212 | - root.remove (folder_root); |
1213 | - write_settings (); |
1214 | - }); |
1215 | - } |
1216 | - |
1217 | - private bool is_open (File folder) { |
1218 | - foreach (var child in root.children) |
1219 | - if (folder.path == (child as Item).path) |
1220 | + private void on_item_selected (Granite.Widgets.SourceList.Item? item) { |
1221 | + // This is a workaround for SourceList silliness: you cannot remove an item |
1222 | + // without it automatically selecting another one. |
1223 | + if (ignore_next_select) { |
1224 | + ignore_next_select = false; |
1225 | + return; |
1226 | + } |
1227 | + |
1228 | + if (item is FileItem) { |
1229 | + select ((item as FileItem).file.file); |
1230 | + } |
1231 | + } |
1232 | + |
1233 | + public void open_folder (string folder_path) { |
1234 | + add_folder (folder_path); |
1235 | + write_settings (); |
1236 | + } |
1237 | + |
1238 | + public void close_folder (string folder_path) { |
1239 | + foreach (var child in root.children) { |
1240 | + var folder = child as FolderItem; |
1241 | + |
1242 | + if (folder != null && folder.path == folder_path) { |
1243 | + // This is a workaround for SourceList silliness: you cannot remove an item |
1244 | + // without it automatically selecting another one. |
1245 | + ignore_next_select = true; |
1246 | + root.remove(folder); |
1247 | + selected = null; |
1248 | + } |
1249 | + } |
1250 | + |
1251 | + if (root.n_children == 0) { |
1252 | + welcome_visible (true); |
1253 | + } |
1254 | + |
1255 | + write_settings (); |
1256 | + } |
1257 | + |
1258 | + // Adds the folder to the view without saving to settings |
1259 | + private void add_folder (string folder_path, bool expand = true) { |
1260 | + if (folder_path == null) { |
1261 | + warning ("Was given null folder path to add"); |
1262 | + } else if (is_open (folder_path)) { |
1263 | + warning ("Folder '%s' is already open.", folder_path); |
1264 | + return; |
1265 | + } |
1266 | + |
1267 | + var folder = new File(folder_path); |
1268 | + |
1269 | + if (!folder.is_valid_directory) { |
1270 | + warning ("Cannot open invalid directory."); |
1271 | + return; |
1272 | + } |
1273 | + |
1274 | + bool has_valid_children = false; |
1275 | + |
1276 | + foreach (var child in folder.children) { |
1277 | + if (child.is_valid_textfile || child.is_valid_directory) { |
1278 | + has_valid_children = true; |
1279 | + break; |
1280 | + } |
1281 | + } |
1282 | + |
1283 | + if (!has_valid_children) { |
1284 | + warning ("Cannot open empty directory due to limitations with Granite.SourceList."); |
1285 | + return; |
1286 | + } |
1287 | + |
1288 | + var folder_item = new FolderItem (folder, this); |
1289 | + folder_item.expanded = expand; |
1290 | + root.add (folder_item); |
1291 | + |
1292 | + welcome_visible (false); |
1293 | + } |
1294 | + |
1295 | + public void restore_settings () { |
1296 | + foreach (var folder_path in settings.opened_folders) { |
1297 | + add_folder (folder_path); |
1298 | + } |
1299 | + } |
1300 | + |
1301 | + private bool is_open (string folder_path) { |
1302 | + foreach (var child in root.children) { |
1303 | + if (folder_path == (child as Item).path) { |
1304 | return true; |
1305 | + } |
1306 | + } |
1307 | + |
1308 | return false; |
1309 | } |
1310 | |
1311 | private void write_settings () { |
1312 | - string[] to_save = {}; |
1313 | - |
1314 | - foreach (var main_folder in root.children) { |
1315 | - var saved = false; |
1316 | - |
1317 | - foreach (var saved_folder in to_save) { |
1318 | - if ((main_folder as Item).path == saved_folder) { |
1319 | - saved = true; |
1320 | - break; |
1321 | - } |
1322 | - } |
1323 | - |
1324 | - if (!saved) { |
1325 | - to_save += (main_folder as Item).path; |
1326 | - } |
1327 | - } |
1328 | - |
1329 | - settings.opened_folders = to_save; |
1330 | - } |
1331 | - } |
1332 | - |
1333 | - /** |
1334 | - * Common abstract class for file and filder items. |
1335 | - */ |
1336 | - internal class Item: Granite.Widgets.SourceList.ExpandableItem, Granite.Widgets.SourceListSortable { |
1337 | - public File file { get; construct; } |
1338 | - public string path { get { return file.path; } } |
1339 | - |
1340 | - public int compare (Granite.Widgets.SourceList.Item a, Granite.Widgets.SourceList.Item b) { |
1341 | - if (a is FolderItem && b is FileItem) { |
1342 | - return -1; |
1343 | - } else if (a is FileItem && b is FolderItem) { |
1344 | - return 1; |
1345 | - } |
1346 | - |
1347 | - return File.compare ((a as Item).file, (b as Item).file); |
1348 | - } |
1349 | - |
1350 | - public bool allow_dnd_sorting () { |
1351 | - return false; |
1352 | - } |
1353 | - } |
1354 | - |
1355 | - /** |
1356 | - * Normal item in the source list, represents a textfile. |
1357 | - * TODO Remove, Rename |
1358 | - */ |
1359 | - internal class FileItem : Item { |
1360 | - |
1361 | - //Gtk.Menu menu; |
1362 | - //Gtk.MenuItem item_trash; |
1363 | - |
1364 | - public FileItem (File file) requires (file.is_valid_textfile) { |
1365 | - Object (file: file); |
1366 | - |
1367 | - this.selectable = true; |
1368 | - //this.editable = true; |
1369 | - this.name = file.name; |
1370 | - this.icon = file.icon; |
1371 | - } |
1372 | - |
1373 | - /*public void rename (string new_name) { |
1374 | - file.rename (new_name); |
1375 | - }*/ |
1376 | - |
1377 | - /*public override Gtk.Menu? get_context_menu () { |
1378 | - menu = new Gtk.Menu (); |
1379 | - item_trash = new Gtk.MenuItem.with_label (_("Move to Trash")); |
1380 | - menu.append (item_trash); |
1381 | - item_trash.activate.connect (() => { file.trash (); }); |
1382 | - menu.show_all (); |
1383 | - return menu; |
1384 | - }*/ |
1385 | - } |
1386 | - |
1387 | - /** |
1388 | - * Expandable item in the source list, represents a folder. |
1389 | - * Monitored for changes inside the directory. |
1390 | - * TODO remove, rename, create new file |
1391 | - */ |
1392 | - internal class FolderItem : Item { |
1393 | - |
1394 | - //Gtk.Menu menu; |
1395 | - //Gtk.MenuItem item_trash; |
1396 | - //Gtk.MenuItem item_create; |
1397 | - |
1398 | - private GLib.FileMonitor monitor; |
1399 | - private bool children_loaded = false; |
1400 | - |
1401 | - public FolderItem (File file) requires (file.is_valid_directory) { |
1402 | - Object (file: file); |
1403 | - |
1404 | - this.editable = false; |
1405 | - this.selectable = false; |
1406 | - this.name = file.name; |
1407 | - this.icon = file.icon; |
1408 | - |
1409 | - this.add (new Granite.Widgets.SourceList.Item ("")); // dummy |
1410 | - this.toggled.connect (() => { |
1411 | - if (this.expanded && this.n_children <= 1) { |
1412 | - this.clear (); |
1413 | - this.add_children (); |
1414 | - children_loaded = true; |
1415 | - } |
1416 | - }); |
1417 | - |
1418 | - try { |
1419 | - monitor = file.file.monitor_directory (GLib.FileMonitorFlags.NONE); |
1420 | - monitor.changed.connect ((s,d,e) => { on_changed (s,d,e); }); |
1421 | - } catch (GLib.Error e) { |
1422 | - warning (e.message); |
1423 | - } |
1424 | - } |
1425 | - |
1426 | - /*public override Gtk.Menu? get_context_menu () { |
1427 | - menu = new Gtk.Menu (); |
1428 | - item_trash = new Gtk.MenuItem.with_label (_("Move to Trash")); |
1429 | - item_create = new Gtk.MenuItem.with_label (_("Create new File")); |
1430 | - menu.append (item_trash); |
1431 | - menu.append (item_create); |
1432 | - item_trash.activate.connect (() => { file.trash (); }); |
1433 | - item_create.activate.connect (() => { |
1434 | - var new_file = GLib.File.new_for_path (file.path + "/new File"); |
1435 | - |
1436 | - try { |
1437 | - FileOutputStream os = new_file.create (FileCreateFlags.NONE); |
1438 | - } catch (Error e) { |
1439 | - warning ("Error: %s\n", e.message); |
1440 | - } |
1441 | - }); |
1442 | - menu.show_all (); |
1443 | - return menu; |
1444 | - }*/ |
1445 | - |
1446 | - internal void add_children () { |
1447 | - foreach (var child in file.children) { |
1448 | - if (child.is_valid_directory) { |
1449 | - var item = new FolderItem (child); |
1450 | - add (item); |
1451 | - } else if (child.is_valid_textfile) { |
1452 | - var item = new FileItem (child); |
1453 | - add (item); |
1454 | - //item.edited.connect (item.rename); |
1455 | - } |
1456 | - } |
1457 | - } |
1458 | - |
1459 | - private void on_changed (GLib.File source, GLib.File? dest, GLib.FileMonitorEvent event) { |
1460 | - |
1461 | - if (!children_loaded) { |
1462 | - this.file.reset_cache (); |
1463 | - return; |
1464 | - } |
1465 | - |
1466 | - switch (event) { |
1467 | - case GLib.FileMonitorEvent.DELETED: |
1468 | - var children_tmp = new Gee.ArrayList<Granite.Widgets.SourceList.Item> (); |
1469 | - children_tmp.add_all (children); |
1470 | - foreach (var item in children_tmp) { |
1471 | - if ((item as Item).path == source.get_path ()) { |
1472 | - remove (item); |
1473 | - } |
1474 | - } |
1475 | - |
1476 | - break; |
1477 | - case GLib.FileMonitorEvent.CREATED: |
1478 | - if (source.query_exists () == false) { |
1479 | - return; |
1480 | - } |
1481 | - |
1482 | - var file = new File (source.get_path ()); |
1483 | - var exists = false; |
1484 | - foreach (var item in children) { |
1485 | - if ((item as Item).path == file.path) { |
1486 | - exists = true; |
1487 | - break; |
1488 | - } |
1489 | - } |
1490 | - |
1491 | - if (!exists) { |
1492 | - if (file.is_valid_textfile) { |
1493 | - this.add (new FileItem (file)); |
1494 | - } else if (file.is_valid_directory) { |
1495 | - this.add (new FolderItem (file)); |
1496 | - } |
1497 | - } |
1498 | - |
1499 | - break; |
1500 | - } |
1501 | - } |
1502 | - } |
1503 | - |
1504 | - /** |
1505 | - * Special root folder. |
1506 | - * TODO rename, create new file |
1507 | - */ |
1508 | - internal class MainFolderItem : FolderItem { |
1509 | - public signal void closed (); |
1510 | - |
1511 | - Gtk.Menu menu; |
1512 | - Gtk.MenuItem item_close; |
1513 | - //Gtk.MenuItem item_create; |
1514 | - |
1515 | - public MainFolderItem (File file) requires (file.is_valid_directory) { |
1516 | - base (file); |
1517 | - } |
1518 | - |
1519 | - public override Gtk.Menu? get_context_menu () { |
1520 | - menu = new Gtk.Menu (); |
1521 | - item_close = new Gtk.MenuItem.with_label (_("Close Folder")); |
1522 | - //item_create = new Gtk.MenuItem.with_label (_("Create new File")); |
1523 | - menu.append (item_close); |
1524 | - //menu.append (item_create); |
1525 | - item_close.activate.connect (() => { closed (); }); |
1526 | - /*item_create.activate.connect (() => { |
1527 | - var new_file = GLib.File.new_for_path (file.path + "/new File"); |
1528 | - |
1529 | - try { |
1530 | - FileOutputStream os = new_file.create (FileCreateFlags.NONE); |
1531 | - } catch (Error e) { |
1532 | - warning ("Error: %s\n", e.message); |
1533 | - } |
1534 | - });*/ |
1535 | - menu.show_all (); |
1536 | - return menu; |
1537 | + string[] paths = {}; |
1538 | + foreach (var child in root.children) { |
1539 | + if (child is FolderItem) { |
1540 | + paths += ((FolderItem) child).path; |
1541 | + } |
1542 | + } |
1543 | + |
1544 | + settings.opened_folders = paths; |
1545 | } |
1546 | } |
1547 | } |
1548 | |
1549 | === added file 'plugins/folder-manager/FolderItem.vala' |
1550 | --- plugins/folder-manager/FolderItem.vala 1970-01-01 00:00:00 +0000 |
1551 | +++ plugins/folder-manager/FolderItem.vala 2016-12-21 19:02:16 +0000 |
1552 | @@ -0,0 +1,245 @@ |
1553 | +// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
1554 | +/*- |
1555 | + * Copyright (c) 2016 elementary LLC. (https://elementary.io) |
1556 | + * |
1557 | + * This program is free software: you can redistribute it and/or modify |
1558 | + * it under the terms of the GNU General Public License as published by |
1559 | + * the Free Software Foundation, either version 3 of the License, or |
1560 | + * (at your option) any later version. |
1561 | + * |
1562 | + * This program is distributed in the hope that it will be useful, |
1563 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1564 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1565 | + * GNU General Public License for more details. |
1566 | + * |
1567 | + * You should have received a copy of the GNU General Public License |
1568 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1569 | + * |
1570 | + * Authored by: Corentin Noël <corentin@elementary.io>, Andrei-Costin Zisu <matzipan@gmail.com> |
1571 | + */ |
1572 | + |
1573 | +public class Scratch.Plugins.FolderManager.FolderItem : Item { |
1574 | + private GLib.FileMonitor monitor; |
1575 | + private bool children_loaded = false; |
1576 | + private string? newly_created_path = null; |
1577 | + |
1578 | + public FolderItem (Scratch.Plugins.FolderManager.File file, Scratch.Plugins.FolderManager.FileView view) requires (file.is_valid_directory) { |
1579 | + Object (file: file, view: view); |
1580 | + } |
1581 | + |
1582 | + construct { |
1583 | + if (file.children.length () > 0) { |
1584 | + add (new Granite.Widgets.SourceList.Item ("")); // dummy |
1585 | + } |
1586 | + |
1587 | + toggled.connect (() => { |
1588 | + if (expanded && n_children <= 1) { |
1589 | + clear (); |
1590 | + add_children (); |
1591 | + children_loaded = true; |
1592 | + } |
1593 | + }); |
1594 | + |
1595 | + try { |
1596 | + monitor = file.file.monitor_directory (GLib.FileMonitorFlags.NONE); |
1597 | + monitor.changed.connect (on_changed); |
1598 | + } catch (GLib.Error e) { |
1599 | + warning (e.message); |
1600 | + } |
1601 | + } |
1602 | + |
1603 | + public override Gtk.Menu? get_context_menu () { |
1604 | + var menu = new Gtk.Menu (); |
1605 | + if (parent == view.root) { |
1606 | + var item = new Gtk.MenuItem.with_label (_("Close Folder")); |
1607 | + item.activate.connect (do_close); |
1608 | + menu.append (item); |
1609 | + } else { |
1610 | + var item = new Gtk.MenuItem.with_label (_("Open")); |
1611 | + item.activate.connect (() => { view.open_folder (file.path); }); |
1612 | + menu.append (item); |
1613 | + } |
1614 | + |
1615 | + var rename_item = new Gtk.MenuItem.with_label (_("Rename")); |
1616 | + rename_item.activate.connect (() => view.start_editing_item (this)); |
1617 | + menu.append (rename_item); |
1618 | + |
1619 | + var new_file_item = new Gtk.MenuItem.with_label (_("Add File")); |
1620 | + new_file_item.activate.connect (() => add_file ()); |
1621 | + menu.append (new_file_item); |
1622 | + |
1623 | + var new_folder_item = new Gtk.MenuItem.with_label (_("Add Folder")); |
1624 | + new_folder_item.activate.connect(() => add_folder ()); |
1625 | + menu.append (new_folder_item); |
1626 | + |
1627 | + var delete_item = new Gtk.MenuItem.with_label (_("Move to Trash")); |
1628 | + delete_item.activate.connect (() => do_remove ()); |
1629 | + menu.append (delete_item); |
1630 | + |
1631 | + menu.show_all (); |
1632 | + return menu; |
1633 | + } |
1634 | + |
1635 | + internal void add_children () { |
1636 | + foreach (var child in file.children) { |
1637 | + if (child.is_valid_directory) { |
1638 | + var item = new FolderItem (child, view); |
1639 | + add (item); |
1640 | + } else if (child.is_valid_textfile) { |
1641 | + var item = new FileItem (child, view); |
1642 | + add (item); |
1643 | + } |
1644 | + } |
1645 | + } |
1646 | + |
1647 | + private void do_close () { |
1648 | + monitor.cancel (); |
1649 | + view.close_folder (path); |
1650 | + } |
1651 | + |
1652 | + protected new void do_remove () { |
1653 | + if (parent == view.root) { |
1654 | + do_close (); |
1655 | + } |
1656 | + |
1657 | + base.do_remove (); |
1658 | + } |
1659 | + |
1660 | + private void on_changed (GLib.File source, GLib.File? dest, GLib.FileMonitorEvent event) { |
1661 | + if (!children_loaded) { |
1662 | + switch (event) { |
1663 | + case GLib.FileMonitorEvent.DELETED: |
1664 | + // This is a pretty intensive operation. For each file deleted, the cache will be |
1665 | + // invalidated and recreated again, from disk. If it turns out users are seeing |
1666 | + // slugishness or slowness when deleting a lot of files, then it might be worth |
1667 | + // doing some sort of timer deferred action. |
1668 | + file.invalidate_cache (); |
1669 | + if (file.children.length () == 0) { |
1670 | + clear (); |
1671 | + } |
1672 | + break; |
1673 | + case GLib.FileMonitorEvent.CREATED: |
1674 | + if (source.query_exists () == false) { |
1675 | + return; |
1676 | + } |
1677 | + |
1678 | + if (n_children == 0) { |
1679 | + add (new Granite.Widgets.SourceList.Item ("")); // dummy |
1680 | + } |
1681 | + break; |
1682 | + } |
1683 | + } else { |
1684 | + switch (event) { |
1685 | + case GLib.FileMonitorEvent.DELETED: |
1686 | + var children_tmp = new Gee.ArrayList<Granite.Widgets.SourceList.Item> (); |
1687 | + children_tmp.add_all (children); |
1688 | + foreach (var item in children_tmp) { |
1689 | + if ((item as Item).path == source.get_path ()) { |
1690 | + // This is a workaround for SourceList silliness: you cannot remove an item |
1691 | + // without it automatically selecting another one. |
1692 | + view.ignore_next_select = true; |
1693 | + remove (item); |
1694 | + view.selected = null; |
1695 | + } |
1696 | + } |
1697 | + |
1698 | + break; |
1699 | + case GLib.FileMonitorEvent.CREATED: |
1700 | + if (source.query_exists () == false) { |
1701 | + return; |
1702 | + } |
1703 | + |
1704 | + var file = new File (source.get_path ()); |
1705 | + var exists = false; |
1706 | + foreach (var item in children) { |
1707 | + if ((item as Item).path == file.path) { |
1708 | + exists = true; |
1709 | + } |
1710 | + } |
1711 | + |
1712 | + Item? item = null; |
1713 | + |
1714 | + if (!exists) { |
1715 | + if (file.is_valid_textfile) { |
1716 | + item = new FileItem (file, view); |
1717 | + } else if (file.is_valid_directory) { |
1718 | + item = new FolderItem (file, view); |
1719 | + } |
1720 | + } |
1721 | + |
1722 | + if (item != null) { |
1723 | + add (item); |
1724 | + |
1725 | + if (source.get_path () == newly_created_path) { |
1726 | + newly_created_path = null; |
1727 | + |
1728 | + /* |
1729 | + * Avoid race condition between adding and editing folder item |
1730 | + * (not required for file items). |
1731 | + */ |
1732 | + GLib.Idle.add(() => { |
1733 | + view.start_editing_item (item); |
1734 | + return false; |
1735 | + }); |
1736 | + } |
1737 | + } |
1738 | + |
1739 | + break; |
1740 | + } |
1741 | + } |
1742 | + |
1743 | + |
1744 | + } |
1745 | + |
1746 | + private void add_folder () { |
1747 | + if (!file.is_executable) { |
1748 | + // This is necessary to avoid infinite loop below |
1749 | + warning("Unable to open parent folder"); |
1750 | + return; |
1751 | + } |
1752 | + |
1753 | + var new_folder = file.file.get_child (_("untitled folder")); |
1754 | + |
1755 | + var n = 1; |
1756 | + while (new_folder.query_exists ()) { |
1757 | + new_folder = file.file.get_child (_("untitled folder %d").printf (n)); |
1758 | + n++; |
1759 | + } |
1760 | + |
1761 | + try { |
1762 | + expanded = true; |
1763 | + |
1764 | + new_folder.make_directory (); |
1765 | + |
1766 | + newly_created_path = new_folder.get_path (); |
1767 | + } catch (Error e) { |
1768 | + warning (e.message); |
1769 | + } |
1770 | + } |
1771 | + |
1772 | + private void add_file () { |
1773 | + if (!file.is_executable) { |
1774 | + // This is necessary to avoid infinite loop below |
1775 | + warning("Unable to open parent folder"); |
1776 | + return; |
1777 | + } |
1778 | + |
1779 | + var new_file = file.file.get_child (_("new file")); |
1780 | + |
1781 | + var n = 1; |
1782 | + while (new_file.query_exists ()) { |
1783 | + new_file = file.file.get_child (_("new file %d").printf (n)); |
1784 | + n++; |
1785 | + } |
1786 | + |
1787 | + try { |
1788 | + expanded = true; |
1789 | + |
1790 | + new_file.create (FileCreateFlags.NONE); |
1791 | + |
1792 | + newly_created_path = new_file.get_path (); |
1793 | + } catch (Error e) { |
1794 | + warning (e.message); |
1795 | + } |
1796 | + } |
1797 | +} |
1798 | |
1799 | === modified file 'plugins/folder-manager/FolderManagerPlugin.vala' |
1800 | --- plugins/folder-manager/FolderManagerPlugin.vala 2015-12-01 11:37:53 +0000 |
1801 | +++ plugins/folder-manager/FolderManagerPlugin.vala 2016-12-21 19:02:16 +0000 |
1802 | @@ -23,13 +23,10 @@ |
1803 | |
1804 | namespace Scratch.Plugins { |
1805 | public class FolderManagerPlugin : Peas.ExtensionBase, Peas.Activatable { |
1806 | - |
1807 | + public Scratch.Services.Interface plugins; |
1808 | + Gtk.Button button; |
1809 | FolderManager.FileView view; |
1810 | - Gtk.ToolButton tool_button; |
1811 | - |
1812 | - int index = 0; |
1813 | - |
1814 | - Scratch.Services.Interface plugins; |
1815 | + |
1816 | public Object object { owned get; construct; } |
1817 | |
1818 | public FolderManagerPlugin () { |
1819 | @@ -43,74 +40,70 @@ |
1820 | } |
1821 | |
1822 | public void deactivate () { |
1823 | - if (view != null) |
1824 | + if (view != null) { |
1825 | view.destroy(); |
1826 | - if (tool_button != null) { |
1827 | - //(tool_button.parent as Scratch.Widgets.Toolbar).open_button.visible = true; |
1828 | - tool_button.destroy (); |
1829 | + view = null; |
1830 | + } |
1831 | + |
1832 | + if (button != null) { |
1833 | + button.destroy(); |
1834 | + button = null; |
1835 | } |
1836 | } |
1837 | |
1838 | public void update_state () { |
1839 | + |
1840 | } |
1841 | |
1842 | void on_hook_sidebar (Gtk.Notebook notebook) { |
1843 | - if (view != null) |
1844 | + if (view != null) { |
1845 | return; |
1846 | + } |
1847 | |
1848 | + // File View |
1849 | view = new FolderManager.FileView (); |
1850 | - |
1851 | - view.select.connect ((a) => { |
1852 | - var file = GLib.File.new_for_path (a); |
1853 | - plugins.open_file (file); |
1854 | - }); |
1855 | - |
1856 | - view.root.child_added.connect (() => { |
1857 | - if (view.get_n_visible_children (view.root) == 0) { |
1858 | - index = notebook.append_page (view, new Gtk.Label (_("Folders"))); |
1859 | + view.select.connect ((file) => plugins.open_file (file)); |
1860 | + view.welcome_visible.connect ((visible) => { |
1861 | + if (visible) { |
1862 | + view.parent.remove (view); |
1863 | + } else if (view.parent == null) { |
1864 | + view.show_all (); |
1865 | + var icon = new Gtk.Image.from_icon_name ("folder-symbolic", Gtk.IconSize.MENU); |
1866 | + icon.tooltip_text = _("Folder Manager"); |
1867 | + notebook.append_page (view, icon); |
1868 | } |
1869 | }); |
1870 | |
1871 | - view.root.child_removed.connect (() => { |
1872 | - if (view.get_n_visible_children (view.root) == 1) |
1873 | - notebook.remove_page (index); |
1874 | - }); |
1875 | - |
1876 | - view.restore_saved_state (); |
1877 | + view.restore_settings (); |
1878 | } |
1879 | |
1880 | void on_hook_toolbar (Gtk.HeaderBar toolbar) { |
1881 | - if (tool_button != null) |
1882 | + if (button != null) { |
1883 | return; |
1884 | - |
1885 | - //(toolbar as Scratch.Widgets.Toolbar).open_button.visible = false; |
1886 | - var icon = new Gtk.Image.from_icon_name ("folder-saved-search", Gtk.IconSize.LARGE_TOOLBAR); |
1887 | - tool_button = new Gtk.ToolButton (icon, _("Open a folder")); |
1888 | - tool_button.tooltip_text = _("Open a folder"); |
1889 | - tool_button.clicked.connect (() => { |
1890 | - Gtk.Window window = plugins.manager.window; |
1891 | - Gtk.FileChooserDialog chooser = new Gtk.FileChooserDialog ( |
1892 | - "Select a folder.", window, Gtk.FileChooserAction.SELECT_FOLDER, |
1893 | - _("_Cancel"), Gtk.ResponseType.CANCEL, |
1894 | - _("_Open"), Gtk.ResponseType.ACCEPT); |
1895 | - chooser.select_multiple = true; |
1896 | - |
1897 | - if (chooser.run () == Gtk.ResponseType.ACCEPT) { |
1898 | - SList<string> uris = chooser.get_uris (); |
1899 | - foreach (unowned string uri in uris) { |
1900 | - var folder = new FolderManager.File (uri.replace ("file:///", "/")); |
1901 | - view.open_folder (folder); // emit signal |
1902 | - } |
1903 | - } |
1904 | - |
1905 | - chooser.close (); |
1906 | - }); |
1907 | - |
1908 | - icon.show (); |
1909 | - tool_button.show (); |
1910 | - |
1911 | - toolbar.pack_start (tool_button); |
1912 | - //toolbar.insert (tool_button, 1); |
1913 | + } |
1914 | + |
1915 | + button = new Gtk.Button.from_icon_name ("folder-saved-search", Gtk.IconSize.LARGE_TOOLBAR); |
1916 | + button.tooltip_text = _("Open a folder"); |
1917 | + button.clicked.connect (() => open_dialog ()); |
1918 | + button.show_all (); |
1919 | + toolbar.pack_start (button); |
1920 | + } |
1921 | + |
1922 | + private void open_dialog () { |
1923 | + Gtk.Window window = plugins.manager.window; |
1924 | + Gtk.FileChooserDialog chooser = new Gtk.FileChooserDialog ( |
1925 | + "Select a folder.", window, Gtk.FileChooserAction.SELECT_FOLDER, |
1926 | + _("_Cancel"), Gtk.ResponseType.CANCEL, |
1927 | + _("_Open"), Gtk.ResponseType.ACCEPT); |
1928 | + chooser.select_multiple = true; |
1929 | + |
1930 | + if (chooser.run () == Gtk.ResponseType.ACCEPT) { |
1931 | + chooser.get_files ().foreach ((file) => { |
1932 | + view.open_folder (file.get_path ()); |
1933 | + }); |
1934 | + } |
1935 | + |
1936 | + chooser.close (); |
1937 | } |
1938 | } |
1939 | } |
1940 | |
1941 | === added file 'plugins/folder-manager/Item.vala' |
1942 | --- plugins/folder-manager/Item.vala 1970-01-01 00:00:00 +0000 |
1943 | +++ plugins/folder-manager/Item.vala 2016-12-21 19:02:16 +0000 |
1944 | @@ -0,0 +1,69 @@ |
1945 | +// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- |
1946 | +/*- |
1947 | + * Copyright (c) 2016 elementary LLC. (https://elementary.io) |
1948 | + * |
1949 | + * This program is free software: you can redistribute it and/or modify |
1950 | + * it under the terms of the GNU General Public License as published by |
1951 | + * the Free Software Foundation, either version 3 of the License, or |
1952 | + * (at your option) any later version. |
1953 | + * |
1954 | + * This program is distributed in the hope that it will be useful, |
1955 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1956 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1957 | + * GNU General Public License for more details. |
1958 | + * |
1959 | + * You should have received a copy of the GNU General Public License |
1960 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1961 | + * |
1962 | + * Authored by: Julien Spautz <spautz.julien@gmail.com>, Andrei-Costin Zisu <matzipan@gmail.com> |
1963 | + */ |
1964 | + |
1965 | +namespace Scratch.Plugins.FolderManager { |
1966 | + /** |
1967 | + * Common abstract class for normal and expandable items. |
1968 | + */ |
1969 | + |
1970 | + public abstract class Item : Granite.Widgets.SourceList.ExpandableItem, Granite.Widgets.SourceListSortable { |
1971 | + public File file { get; construct; } |
1972 | + public Scratch.Plugins.FolderManager.FileView view { get; construct; } |
1973 | + public string path { |
1974 | + owned get { return file.path; } |
1975 | + set { file.path = value; } |
1976 | + } |
1977 | + |
1978 | + construct { |
1979 | + selectable = true; |
1980 | + editable = true; |
1981 | + name = file.name; |
1982 | + icon = file.icon; |
1983 | + |
1984 | + edited.connect (rename); |
1985 | + } |
1986 | + |
1987 | + protected void rename (string new_name) { |
1988 | + file.rename (new_name); |
1989 | + } |
1990 | + |
1991 | + protected void do_remove () { |
1992 | + file.trash (); |
1993 | + } |
1994 | + |
1995 | + public int compare (Granite.Widgets.SourceList.Item a, Granite.Widgets.SourceList.Item b) { |
1996 | + if (a is FolderItem && b is FileItem) { |
1997 | + return -1; |
1998 | + } else if (a is FileItem && b is FolderItem) { |
1999 | + return 1; |
2000 | + } |
2001 | + |
2002 | + if (a is Item && b is Item) { |
2003 | + return File.compare ((a as Item).file, (b as Item).file); |
2004 | + } |
2005 | + |
2006 | + return 0; |
2007 | + } |
2008 | + |
2009 | + public bool allow_dnd_sorting () { |
2010 | + return false; |
2011 | + } |
2012 | + } |
2013 | +} |
2014 | |
2015 | === modified file 'plugins/folder-manager/folder-manager.plugin' |
2016 | --- plugins/folder-manager/folder-manager.plugin 2013-06-08 15:17:42 +0000 |
2017 | +++ plugins/folder-manager/folder-manager.plugin 2016-12-21 19:02:16 +0000 |
2018 | @@ -3,8 +3,8 @@ |
2019 | Loader=C |
2020 | IAge=2 |
2021 | Name=Folder Manager |
2022 | -Description=Basic folder manager with file browsing |
2023 | -Icon=folder-saved-search |
2024 | -Authors=Julien Spautz <spautz.julien@gmail.com> |
2025 | -Copyright=Copyright © 2013 Scratch Developers |
2026 | +Description=Browse files and directories |
2027 | +Icon=system-file-manager |
2028 | +Authors=Mario Guerriero, Julien Spautz, Corentin Noël, Andrei Zisu |
2029 | +Copyright=Copyright © 2016 elementary LLC |
2030 | Website=http://launchpad.net/scratch |
2031 | |
2032 | === modified file 'schemas/CMakeLists.txt' |
2033 | --- schemas/CMakeLists.txt 2015-09-10 00:54:45 +0000 |
2034 | +++ schemas/CMakeLists.txt 2016-12-21 19:02:16 +0000 |
2035 | @@ -1,6 +1,5 @@ |
2036 | include(GSettings) |
2037 | add_schema("org.pantheon.scratch.gschema.xml") |
2038 | add_schema("org.pantheon.scratch.plugins.folder-manager.gschema.xml") |
2039 | -add_schema("org.pantheon.scratch.plugins.file-manager.gschema.xml") |
2040 | add_schema("org.pantheon.scratch.plugins.terminal.gschema.xml") |
2041 | add_schema("org.pantheon.scratch.plugins.spell.gschema.xml") |
2042 | |
2043 | === removed file 'schemas/org.pantheon.scratch.plugins.file-manager.gschema.xml' |
2044 | --- schemas/org.pantheon.scratch.plugins.file-manager.gschema.xml 2013-07-16 15:56:38 +0000 |
2045 | +++ schemas/org.pantheon.scratch.plugins.file-manager.gschema.xml 1970-01-01 00:00:00 +0000 |
2046 | @@ -1,10 +0,0 @@ |
2047 | -<?xml version="1.0" encoding="UTF-8"?> |
2048 | -<schemalist> |
2049 | - <schema path="/org/pantheon/scratch/plugins/file-manager/" id="org.pantheon.scratch.plugins.file-manager" gettext-domain="scratch"> |
2050 | - <key name="opened-folder" type="s"> |
2051 | - <default>''</default> |
2052 | - <summary>Opened folder.</summary> |
2053 | - <description>Opened folder that should be restored in startup.</description> |
2054 | - </key> |
2055 | - </schema> |
2056 | -</schemalist> |
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.