Merge lp:~kjtehprogrammer/pantheon-files/editable-pathbar into lp:~elementary-apps/pantheon-files/trunk
- editable-pathbar
- Merge into trunk
Status: | Merged | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Danielle Foré | ||||||||||||||||||||||||||||
Approved revision: | 1501 | ||||||||||||||||||||||||||||
Merged at revision: | 1513 | ||||||||||||||||||||||||||||
Proposed branch: | lp:~kjtehprogrammer/pantheon-files/editable-pathbar | ||||||||||||||||||||||||||||
Merge into: | lp:~elementary-apps/pantheon-files/trunk | ||||||||||||||||||||||||||||
Diff against target: |
2198 lines (+448/-1335) 10 files modified
libwidgets/BreadcrumbsElements.vala (+2/-1) libwidgets/BreadcrumbsEntry.vala (+0/-494) libwidgets/CMakeLists.txt (+1/-3) libwidgets/LocationBar.vala (+354/-502) libwidgets/tests/CMakeLists.txt (+0/-27) libwidgets/tests/tests-pathbar.vala (+0/-213) src/View/Chrome/TopMenu.vala (+5/-8) src/View/LocationBar.vala (+75/-81) src/View/ViewContainer.vala (+4/-5) src/View/Window.vala (+7/-1) |
||||||||||||||||||||||||||||
To merge this branch: | bzr merge lp:~kjtehprogrammer/pantheon-files/editable-pathbar | ||||||||||||||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Avi Romanoff (community) | Approve | ||
Jeremy Wootten | Approve | ||
Danielle Foré | Approve | ||
Review via email: mp+216531@code.launchpad.net |
Commit message
* Makes the pathbar editable text when it receives focus. The entire path can be modified. It's also possible to put "ssh://user@domain" (or ftp, sftp, smb, etc) and get prompted for a password to connect.
* Switches Gtk.EventBox and custom Entry widget out with Gtk.Entry
Description of the change
Makes the pathbar editable text when it receives focus. The entire path can be modified. It's also possible to put "ssh://user@domain" (or ftp, sftp, smb, etc) and get prompted for a password to connect.
This is a great jumping off point for searching in files. The breadcrumb entry class already attempts to auto-complete what you're typing for the directory path (which still works in this change), we would just need to modify how it displays results and tie it into Zeitgeist.
KJ Lawrence (kjtehprogrammer) wrote : | # |
I'm not sure I follow on #1. When you click an empty area the entire path -should- appear and be completely selected. Typing without unselecting the path (clicking off or pressing an arrow key) should wipe out the entire path, similar to the way Windows Explorer works. Here's how it appears on mine when I clicked an empty area to edit the path: http://
#2, good catch I'll have to look into that.
Danielle Foré (danrabbit) wrote : | # |
I really don't like that it selects the entire entry when you click into it. I would expect it to place the cursor at the end without any selection. I'm not sure Windows anything is the best example of good UX :p
I also noticed a bug where auto-completing with tab navigates to the next directory (which of itself feels a little pre-emptive but not necessarily wrong) but the entry doesn't update to reflect the change in path.
I do like the overall concept of just switching to a plain text entry though. I'm sure that would make the code simpler and eliminate bugs caused by the custom widget.
I also noticed a regression where the "go" icon shows in the entry even when you haven't typed a path that is different from your current path.
Jeremy Wootten (jeremywootten) wrote : | # |
Looks like it is a theme issue then - I am running it on Ubuntu 14.04
rather than Isis and the path appears in bold rather than highlighted for
some reason but other text selections (e.g. file renaming) appear
highlghted. It looks like Daniel agrees with me that the path should not
be selected initially (i.e. typing just appends).
On 4 May 2014 03:32, KJ Lawrence <email address hidden> wrote:
> I'm not sure I follow on #1. When you click an empty area the entire path
> -should- appear and be completely selected. Typing without unselecting the
> path (clicking off or pressing an arrow key) should wipe out the entire
> path, similar to the way Windows Explorer works. Here's how it appears on
> mine when I clicked an empty area to edit the path:
> http://
>
> #2, good catch I'll have to look into that.
> --
>
> https:/
> You are reviewing the proposed merge of
> lp:~kjtehprogrammer/pantheon-files/editable-pathbar into lp:pantheon-files.
>
- 1487. By KJ Lawrence
-
- Fixes some code styling and removes some debugging code
- Fixes regression where the "go" icon shows up without changing anything
- Removes auto-highlighting all text
- Fixed path issues with file://, trash:// and network:// - 1488. By KJ Lawrence
-
Merging with trunk
KJ Lawrence (kjtehprogrammer) wrote : | # |
Sorry for the long update, a lot going on lately. Give the latest commit a shot. The commit message gives a rundown of everything that was changed.
Daniel, if you make me remove the pre-emptive directory switching with tab you'll have made these last two hours for (almost) nothing! However, it would not be difficult to do so (just one line), your call on how you want it to flow. :) I can say that it is sort of nice to have the pre-emptive directory switching because it gives you a view of all of the files in a folder so you have any idea of what to continue typing going forward. I'm sure it's a draw back on extremely large folders though.
Jeremy Wootten (jeremywootten) wrote : | # |
Good work, but there are still a few issues with this:
1) Line 358 - unused variable "files"
2) If you type the name of a non-existent folder with a space in the name and then choose "Create folder", the folder is created with "%20" instead of "space" in the name.
3) Keep pressing "Backspace" - you can delete the last "/". Then pressing "Enter" does not load root directory view, although the location bar displays it (because the generated path is invalid - "file://" instead of "file:///").
4) Pressing "Ctrl-Backspace" then "Enter" when the path has spaces gives a "directory not found" error - this is related to point 2).
I like the pre-emptive directory switching.
- 1489. By KJ Lawrence
-
- Fixes escaping issues with the paths (would affect paths with spaces)
- Fixed issue with auto-completion with certain special characters (#)
- Clearing out the entire path takes you to the root folder
KJ Lawrence (kjtehprogrammer) wrote : | # |
Good catches. It's a pain to track down these encoding issues with spaces and special characters... try this commit and see if everything is working for you. Try testing special characters in the path and everything.
Avi Romanoff (aroman) wrote : | # |
A few things from an end-user's perspective (haven't even looked at the code)
1. Unlike other entry fields, I can't use <ctrl>+left/right to jump between words. In this case, words = directories. This is really important, I think.
2. Similar to #1, <ctrl>+a doesn't select-all. It should. (Shouldn't we get #1 and #2 for free?)
3. Typing ~/some_dir does not resolve to /home/$
4. I shouldn't be able to bread-crumb-ize non-existent paths. See: http://
KJ Lawrence (kjtehprogrammer) wrote : | # |
You give me too much credit, Haha. I used the existing text entry field, which pretty much implements all input text field functions , obviously missing those two and connect menus. I can certainly try to switch it out though.
How did you manage #4?
Avi Romanoff (aroman) wrote : | # |
I didn't have to do anything special for #4 :p
I just clicked in the pathbar, typed "some_directory", and hit enter. The pathbar indicates that I'm viewing a non-existent directory, when in reality the file view never changes.
Danielle Foré (danrabbit) wrote : | # |
Hm something funny is going on. I can also make the pathbar display stuff that isn't the current directory :p http://
- 1490. By KJ Lawrence
-
Switching Gtk.EventBox and custom Entry widget out with Gtk.Entry
KJ Lawrence (kjtehprogrammer) wrote : | # |
Not done yet, just wanted to get this committed. Would love for you guys to test it and get opinions!
Switched out Gtk.EventBox and the custom Entry widget we had with an actual Gtk.Entry widget. It plays nicely with elementary theming, but I have no idea beyond that! There are a couple of things I coded around (positioning completion text during the draw for one) that I don't think I did quite right (might be prone to breaking if smaller/larger texts are set for the location bar).
Since it's switched out with a Gtk.Entry widget there are a couple of nice advantages! There's the standard context menu (Copy/Cut/Paste, etc). You can also use CTRL + L/R to move between directories. There's a lot less code (got to completely eliminate a file) and what there is has been simplified. Also, since it's a Gtk.Entry widget you can do a progress bar similar to Midori now - I'm sure that's useful for something.
I'm sure there are plenty of code styling issues and debug statements that have not been cleaned up, so don't worry too much on those right now. I still have to fix some pathing and completion issues and need to get the "Goto" icon back.
Corentin Noël (tintou) wrote : | # |
I like what you did there, with a lightweight code, Files will be even easier to maintain! There are still some warnings but as you said it's a WIP.
- 1491. By KJ Lawrence
-
- Added "Go" arrow back to Location bar
- Fixed file pathing issues with special characters (#, " ", etc)
- Reorganized some of the LocationBar code to make it easier to follow
- Implemented 'up' functionality
KJ Lawrence (kjtehprogrammer) wrote : | # |
One step closer. Go arrow is back (highlights immediately now). I think I've finally got this pathing issue resolved for everything, but please do try to break it. I also implemented the "Up" functionality since we never had it before (but had the groundwork in place for it).
Besides cleaning the code up and fixing styling, the only thing that's left is to fix Ctrl + A functionality. It looks like the entire Window is handling that instead of just the view, so pressing Ctrl + A highlights everything in the files view - might be a little tricky to track that one down.
Please test! Code should be clean enough for review now too if people want to start looking at that.
- 1492. By KJ Lawrence
-
- Code styling fixes and some comments
- Fix for auto-completion where multiple results are turned - 1493. By KJ Lawrence
-
Merging with trunk
- 1494. By KJ Lawrence
-
Fixed auto-complete issue with ~
KJ Lawrence (kjtehprogrammer) wrote : | # |
Okay, just some final touches. I cleaned up the code a little bit more and fixed an issue with auto-completion where if you found multiple results (I had a folder with "ctags-trunk" and "ctags-better-php") it would auto-complete up until they diverged and then take you to a "Folder 'ctags-' does not exist" page. Now it will auto-complete until they diverge but keep you where you are at.
I'm honestly not sure how to change things to fix selecting with Ctrl+A with the pathbar - accelerators confuse me!
- 1495. By KJ Lawrence
-
Fixed issue where multiple directories with the same starting letter (but with different capitalizations) would crash Files when auto-completing
- 1496. By KJ Lawrence
-
Fixes auto-completion with spaces
Unescapes paths that show up in Back/Forward button
Jeremy Wootten (jeremywootten) wrote : | # |
The following issues are now fixed:
1) Now handles paths with spaces correctly.
2) Ctrl left and right work as expected
3) ~ resolves to use home directory
4) I cannot reproduce Daniel's problem with this revision, although the steps involved were not stated.
The following issues remain:
1) It is still possible to delete the initial "/"; pressing "Enter" then produces a "Folder '.' can't be found" message. If you select Create folder '.' then an error message is produced. See inline diff comment at line 927.
2) Ctrl-A selects all folders in the current view instead of the whole path. This is because the event goes to the top window default handler first. A solution to this is to intercept the event at the window level and pass directly to the location bar if it has focus. e.g. In Window.vala -
if (top_menu.
});
3) Rather than use hex values to compare with event.keyval it would be better to use Gdk.Key.<keyname>. e.g. Gdk.Key.Tab instead of 0xff09: A list of the correct names can be found in /usr/share/
4) The breadcrumbs still pre-emptively display non-existent paths while the view displays the 'Folder not found' dialog, although personally I do not find this objectionable as you have the option to create the path at that point.
5) The entry is in insert mode to begin with. Design team should confirm whether insert or overwrite mode is better.
- 1497. By KJ Lawrence
-
Use Gdk.Key name instead of Hex value
Fixes Ctrl+A in Location Bar
Fixes issue where missing root '/' would try to create a new directory
KJ Lawrence (kjtehprogrammer) wrote : | # |
1. I could have sworn I got that one... Well, fixed now (hopefully!)
2. That works quite well, thanks!
3. Never did like the hex values - took them from the original entry class, but didn't really think about how to switch them out.
For 4/5 that's really a design team call. I too like the pre-emptive stuff. If the directory I navigate to doesn't exist it's pretty convenient for it to ask me to make it. Otherwise I can just press Ctrl+L or click the pathbar and go back to navigating around. It's nicer than Nautilus' and Windows Explorer's error popup saying it doesn't exist - at least I can do something with it this way.
Danielle Foré (danrabbit) wrote : | # |
KJ, do you have any idea why the go icon looks so light? Is there perhaps some leftover code where the color is being overridden or the opacity is being changed? It's definitely not the same color as (for example) the clear icon in search entries.
I'm still not sure I'm a massive fan of switching directories before pressing "enter", but I think we can go with it and see if it gets reported as a bug ;)
Anyways, this branch looks awesome. It fixes some minor visual issues with box-shadow in gtk 3.14 as well :)
Jeremy Wootten (jeremywootten) wrote : | # |
Good work!
Sorry, theres one more issue that I've just noticed - the substitutions for "~", "ssh:" etc happen even when they are just part of the filename and not only at the start of the path. For example, try to create a directory called "a_folder_
KJ Lawrence (kjtehprogrammer) wrote : | # |
Daniel, it's 20% transparent right now. I could make it 10% or even 5% to make it a little more obvious. There wouldn't be as much of an effect when hovering over it, though there might still be enough of one. I'm not sure if there's a way to make it darker. I think I noticed the difference between how dark it looked in Midori and in the current Files, but not sure if the transparency change will get them closer to each other or not.
Jeremy, so I should just swap out the ~ on the first character? Should I still swap it if there's a slash in front of it, or only if it's the first character?
Danielle Foré (danrabbit) wrote : | # |
KJ, if we're using a regular gtk.entry now the color should be completely handled in the theme :)
KJ Lawrence (kjtehprogrammer) wrote : | # |
Interesting... I didn't know until you mentioned it, but I guess Gtk.Entry can automatically handle the go icon it looks like. I'll have to update some of the code to use that rather than manually handling it - might make things even more cleaner. I'm still learning Gtk and am honestly amazed by how much the Gtk.Entry widget can do, haha.
Right now the Go icon is being drawn independently so I doubt you'll be able to control it through the theme. After I change it to be drawn by the entry directly you should be good then.
Danielle Foré (danrabbit) wrote : | # |
Oh wow xD
KJ Lawrence (kjtehprogrammer) wrote : | # |
Yeah, a lot of my work on this was based off of the original "BreadcrumbsEntry" class that existed before, which basically re-created the entire Gtk.Entry widget. I didn't realize how much so until I actually switched to using an Entry widget instead and half of the code vanished (the big block of red below)...
Jeremy Wootten (jeremywootten) wrote : | # |
I would take as a guide the behaviour of the 'cd' command when in a terminal in the root directory when followed by the path in the location bar. If you type "cd ~" it takes you to the home directory. If you type "cd /~" it gives "No such file or directory" (unless you happen to have a folder named "~" in your root directory). So I would say only substitute it if it is the first character in the path.
- 1498. By KJ Lawrence
-
Use the secondary icon for 'go' instead of manually drawing it
KJ Lawrence (kjtehprogrammer) wrote : | # |
Okay, ~ is only accepted as the first character and the Go icon is set as the secondary icon for Gtk.Entry, so it should be themable. I didn't notice any difference when hovering, but I may be missing something from my setup. There's no more code to manually drawing the arrow anymore, so I think any hover effect is now out of my control.
Changing the cursor for a Gtk.Entry is a pain in the ass, just an FYI. Not sure if I did it right (line 974 in the diff shows what I had to do), so that should be reviewed, but it works the way you would expect.
- 1499. By KJ Lawrence
-
Merging in trunk!
Cody Garver (codygarver) wrote : | # |
libwidgets/
int width = get_allocated_width ();
- 1500. By KJ Lawrence
-
Fixes a unused variable warning
KJ Lawrence (kjtehprogrammer) wrote : | # |
Fixed
Jeremy Wootten (jeremywootten) : | # |
Avi Romanoff (aroman) wrote : | # |
Looking really good, but I have three minor issues:
1. Network URI support (which is apparently being targeted by this bug as well) is still buggy. Specifically, typing a URI (ex: `afp://Wan.local/`) does in fact cause Files to display a prompt for username/password, but it also causes the tab bar to show "This folder does not exist", and this title does not go away even when the credentials are entered and the share is loaded.
See: http://
I dunno if this is actually supposed to be fixed by this branch, but https:/
2. When changing directories, the whole pathbar animates, even if most the breadcrumb hierarchy stayed constant. For example, clicking on a directory "foo" inside ~/Developer/
3. Clicking on the right-most breadcrumb causes the pathbar to re-animate. Why?
Cody Garver (codygarver) wrote : | # |
1 is bug #1198334
KJ Lawrence (kjtehprogrammer) wrote : | # |
1. The network URI support was actually what I initially started all of this for, the rest was just kind of fill in place. Though, it was just for being able to type in the path, versus using the network dialog box. I can dig into the tab issue and see what I can find.
2. I didn't see that with normal files usage. Just clicking a folder in the normal view was correct, it just appended the extra breadcrumb entry without re-rendering. Though, I did notice that if you click any of the breadcrumbs that it re-renders. Apparently that's the way the existing Files setup is, so I'd have to dig into that to make it a little more smooth. It does it somewhere though, so this shouldn't be too difficult.
3. It does this for the current Files code too - not really sure why, I'd have to dig into it. It re-renders for any of the breadcrumbs that are clicked (but doesn't redraw all of the breadcrumbs when you're just navigating around, which is kind of strange).
- 1501. By KJ Lawrence
-
- Fixes issue where breadcrumbs would always fully redraw when clicked
- Removes unused code
KJ Lawrence (kjtehprogrammer) wrote : | # |
Okay, that commit should fix the breadcrumb fully redrawing issue.
Thanks Cody, I thought I saw something on that before. Should I look at it as part of my changes or leave it alone then, since it has its own bug report? If I leave it alone, do we need to make these changes dependent on it, or just commit anyways?
Avi Romanoff (aroman) wrote : | # |
I can confirm that the breadcrumb full-redrawing bug is fixed -- awesome!
I think this should be merged to trunk and those other bugs (which were not introduced by this branch) should be fixed separately. I say, approve.
Preview Diff
1 | === modified file 'libwidgets/BreadcrumbsElements.vala' |
2 | --- libwidgets/BreadcrumbsElements.vala 2014-05-13 21:10:32 +0000 |
3 | +++ libwidgets/BreadcrumbsElements.vala 2014-06-14 19:18:12 +0000 |
4 | @@ -41,6 +41,7 @@ |
5 | } |
6 | } |
7 | Gdk.Pixbuf icon; |
8 | + public bool hidden = false; |
9 | public bool display = true; |
10 | public bool display_text = true; |
11 | public string? text_displayed = null; |
12 | @@ -187,4 +188,4 @@ |
13 | |
14 | return x; |
15 | } |
16 | -} |
17 | +} |
18 | \ No newline at end of file |
19 | |
20 | === removed file 'libwidgets/BreadcrumbsEntry.vala' |
21 | --- libwidgets/BreadcrumbsEntry.vala 2013-08-10 20:32:58 +0000 |
22 | +++ libwidgets/BreadcrumbsEntry.vala 1970-01-01 00:00:00 +0000 |
23 | @@ -1,494 +0,0 @@ |
24 | -/* |
25 | - * Copyright (c) 2011 Lucas Baudin <xapantu@gmail.com> |
26 | - * |
27 | - * Marlin is free software; you can redistribute it and/or |
28 | - * modify it under the terms of the GNU General Public License as |
29 | - * published by the Free Software Foundation; either version 2 of the |
30 | - * License, or (at your option) any later version. |
31 | - * |
32 | - * Marlin is distributed in the hope that it will be useful, |
33 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
34 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
35 | - * General Public License for more details. |
36 | - * |
37 | - * You should have received a copy of the GNU General Public |
38 | - * License along with this program; see the file COPYING. If not, |
39 | - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
40 | - * Boston, MA 02111-1307, USA. |
41 | - * |
42 | - */ |
43 | - |
44 | -public class Marlin.View.Chrome.BreadcrumbsEntry : GLib.Object { |
45 | - Gtk.IMContext im_context; |
46 | - public string text = ""; |
47 | - public int cursor = 0; |
48 | - public string completion = ""; |
49 | - uint timeout; |
50 | - bool blink = true; |
51 | - public Gdk.Pixbuf arrow_img; |
52 | - |
53 | - double selection_mouse_start = -1; |
54 | - double selection_mouse_end = -1; |
55 | - double selection_start = 0; |
56 | - double selection_end = 0; |
57 | - int selected_start = -1; |
58 | - int selected_end = -1; |
59 | - internal bool hover = false; |
60 | - bool focus = false; |
61 | - |
62 | - bool is_selecting = false; |
63 | - bool need_selection_update = false; |
64 | - |
65 | - double text_width; |
66 | - double text_height; |
67 | - |
68 | - public signal void enter (); |
69 | - public signal void backspace (); |
70 | - public signal void left (); |
71 | - public signal void up (); |
72 | - public signal void down (); |
73 | - public signal void left_full (); |
74 | - public signal void need_draw (); |
75 | - public signal void paste (); |
76 | - public signal void need_completion (); |
77 | - public signal void completed (); |
78 | - public signal void escape (); |
79 | - |
80 | - /** |
81 | - * Create a new BreadcrumbsEntry object. It is used to display the entry |
82 | - * which is a the left of the pathbar. It is *not* a Gtk.Widget, it is |
83 | - * only a class which holds some data and draws an entry to a given |
84 | - * Cairo.Context. |
85 | - * Events must be sent to the appropriate function (key_press_event, |
86 | - * key_release_event, mouse_motion_event, etc...). These events must be |
87 | - * relative to the widget, so, you need to do some things like |
88 | - * event.x -= entry_x before sending the events. |
89 | - * It can be drawn using the draw() function. |
90 | - **/ |
91 | - public BreadcrumbsEntry () { |
92 | - im_context = new Gtk.IMMulticontext (); |
93 | - im_context.commit.connect (commit); |
94 | - |
95 | - /* Load arrow image */ |
96 | - try { |
97 | - arrow_img = Gtk.IconTheme.get_default ().load_icon ("go-jump-symbolic", 16, Gtk.IconLookupFlags.GENERIC_FALLBACK); |
98 | - } catch (Error err) { |
99 | - stderr.printf ("Unable to load home icon: %s", err.message); |
100 | - } |
101 | - } |
102 | - |
103 | - /** |
104 | - * Call this function if the parent widgets has the focus, it will start |
105 | - * computing the blink cursor, will enable cursor and selection drawing. |
106 | - **/ |
107 | - public void show () { |
108 | - focus = true; |
109 | - |
110 | - if (timeout > 0) |
111 | - Source.remove (timeout); |
112 | - |
113 | - timeout = Timeout.add (700, () => { |
114 | - blink = !blink; |
115 | - need_draw (); |
116 | - |
117 | - return true; |
118 | - }); |
119 | - } |
120 | - |
121 | - /** |
122 | - * Delete the text selected. |
123 | - **/ |
124 | - public void delete_selection () { |
125 | - if (selected_start > 0 && selected_end > 0) { |
126 | - int first = selected_start > selected_end ? selected_end : selected_start; |
127 | - int second = selected_start > selected_end ? selected_start : selected_end; |
128 | - |
129 | - text = text.slice (0, first) + text.slice (second, text.length); |
130 | - reset_selection (); |
131 | - cursor = first; |
132 | - } |
133 | - } |
134 | - |
135 | - /** |
136 | - * Insert some text at the cursor position. |
137 | - * |
138 | - * @param to_insert The text you want to insert. |
139 | - **/ |
140 | - public void insert (string to_insert) { |
141 | - if (to_insert != null && to_insert.length > 0) { |
142 | - int first = selected_start > selected_end ? selected_end : selected_start; |
143 | - int second = selected_start > selected_end ? selected_start : selected_end; |
144 | - |
145 | - if (first != second && second > 0) { |
146 | - text = text.slice (0, first) + to_insert + text.slice (second, text.length); |
147 | - selected_start = -1; |
148 | - selected_end = -1; |
149 | - selection_start = 0; |
150 | - selection_end = 0; |
151 | - cursor = first + to_insert.length; |
152 | - } else { |
153 | - text = text.slice (0,cursor) + to_insert + text.slice (cursor, text.length); |
154 | - cursor += to_insert.length; |
155 | - } |
156 | - } |
157 | - |
158 | - need_completion (); |
159 | - } |
160 | - |
161 | - /** |
162 | - * A callback from our im_context. |
163 | - **/ |
164 | - private void commit (string character) { |
165 | - insert (character); |
166 | - } |
167 | - |
168 | - public void key_press_event (Gdk.EventKey event) { |
169 | - /* FIXME: I can't find the vapi to not use hardcoded key value. */ |
170 | - /* FIXME: we should use Gtk.BindingSet, but the vapi file seems buggy */ |
171 | - |
172 | - bool control_pressed = (event.state & Gdk.ModifierType.CONTROL_MASK) == 4; |
173 | - bool shift_pressed = ! ((event.state & Gdk.ModifierType.SHIFT_MASK) == 0); |
174 | - |
175 | - switch (event.keyval) { |
176 | - case 0xff51: /* left */ |
177 | - if (cursor > 0 && !control_pressed && !shift_pressed) { |
178 | - cursor --; /* No control pressed, the cursor is not at the begin */ |
179 | - reset_selection (); |
180 | - } else if (cursor == 0 && control_pressed) { |
181 | - left_full (); /* Control pressed, the cursor is at the begin */ |
182 | - } else if (control_pressed) { |
183 | - cursor = 0; |
184 | - } else if (cursor > 0 && shift_pressed) { |
185 | - if (selected_start < 0) { |
186 | - selected_start = cursor; |
187 | - } |
188 | - |
189 | - if (selected_end < 0) { |
190 | - selected_end = cursor; |
191 | - } |
192 | - |
193 | - cursor--; |
194 | - selected_start = cursor; |
195 | - need_selection_update = true; |
196 | - } else { |
197 | - left (); |
198 | - } |
199 | - |
200 | - break; |
201 | - |
202 | - case 0xff53: /* right */ |
203 | - if (cursor < text.length && !shift_pressed) { |
204 | - cursor++; |
205 | - reset_selection (); |
206 | - } else if (cursor < text.length && shift_pressed) { |
207 | - if (selected_start < 0) { |
208 | - selected_start = cursor; |
209 | - } |
210 | - |
211 | - if (selected_end < 0) { |
212 | - selected_end = cursor; |
213 | - } |
214 | - |
215 | - cursor++; |
216 | - selected_start = cursor; |
217 | - need_selection_update = true; |
218 | - } else if (!shift_pressed) { |
219 | - complete (); |
220 | - } |
221 | - |
222 | - break; |
223 | - |
224 | - case 0xff0d: /* enter */ |
225 | - reset_selection (); |
226 | - enter (); |
227 | - break; |
228 | - |
229 | - case 0xff08: /* backspace */ |
230 | - if (get_selection () != null) { |
231 | - delete_selection (); |
232 | - need_completion (); |
233 | - } else if (cursor > 0) { |
234 | - text = text.slice (0, cursor - 1) + text.slice (cursor, text.length); |
235 | - cursor--; |
236 | - need_completion (); |
237 | - } else { |
238 | - backspace (); |
239 | - } |
240 | - |
241 | - break; |
242 | - |
243 | - case 0xffff: /* delete */ |
244 | - if (get_selection () == null && cursor < text.length && control_pressed) { |
245 | - text = text.slice (0, cursor); |
246 | - } else if (get_selection () == null && cursor < text.length) { |
247 | - text = text.slice (0, cursor) + text.slice (cursor + 1, text.length); |
248 | - } else if (get_selection () != null) { |
249 | - delete_selection (); |
250 | - } |
251 | - |
252 | - need_completion (); |
253 | - break; |
254 | - |
255 | - case 0xff09: /* tab */ |
256 | - complete (); |
257 | - break; |
258 | - |
259 | - case 0xff54: /* down */ |
260 | - down (); |
261 | - break; |
262 | - |
263 | - case 0xff52: /* up */ |
264 | - up (); |
265 | - break; |
266 | - |
267 | - case 0xff1b: /* escape */ |
268 | - escape (); |
269 | - break; |
270 | - |
271 | - case 0xff50: /* Home */ |
272 | - cursor = 0; |
273 | - break; |
274 | - |
275 | - case 0xff57: /* End */ |
276 | - cursor = text.length; |
277 | - break; |
278 | - |
279 | - default: |
280 | - im_context.filter_keypress (event); |
281 | - break; |
282 | - } |
283 | - |
284 | - blink = true; |
285 | - print ("%x\n", event.keyval); |
286 | - } |
287 | - |
288 | - public void complete () { |
289 | - reset_selection (); |
290 | - |
291 | - if (completion != "") { |
292 | - text += completion + "/"; |
293 | - cursor += completion.length + 1; |
294 | - completion = ""; |
295 | - completed (); |
296 | - } |
297 | - } |
298 | - |
299 | - public string? get_selection () { |
300 | - int first = selected_start > selected_end ? selected_end : selected_start; |
301 | - int second = selected_start > selected_end ? selected_start : selected_end; |
302 | - |
303 | - if (!(first < 0 || second < 0)) |
304 | - return text.slice (first,second); |
305 | - |
306 | - return null; |
307 | - } |
308 | - |
309 | - public void key_release_event (Gdk.EventKey event) { |
310 | - im_context.filter_keypress (event); |
311 | - } |
312 | - |
313 | - public void mouse_motion_event (Gdk.EventMotion event, double width) { |
314 | - hover = false; |
315 | - |
316 | - if (event.x < width && event.x > width - arrow_img.get_width ()) |
317 | - hover = true; |
318 | - |
319 | - if (is_selecting) |
320 | - selection_mouse_end = event.x > 0 ? event.x : 1; |
321 | - } |
322 | - |
323 | - public void mouse_press_event(Gdk.EventButton event, double width) { |
324 | - reset_selection (); |
325 | - blink = true; |
326 | - |
327 | - if (event.x < width && event.x > width - arrow_img.get_width ()) { |
328 | - enter (); |
329 | - } else if (event.x >= 0) { |
330 | - is_selecting = true; |
331 | - selection_mouse_start = event.x; |
332 | - selection_mouse_end = event.x; |
333 | - } else if (event.x >= -20) { |
334 | - is_selecting = true; |
335 | - selection_mouse_start = -1; |
336 | - selection_mouse_end = -1; |
337 | - } |
338 | - need_draw (); |
339 | - } |
340 | - |
341 | - public void mouse_release_event (Gdk.EventButton event) { |
342 | - selection_mouse_end = event.x; |
343 | - is_selecting = false; |
344 | - } |
345 | - |
346 | - /** |
347 | - * Reset the current selection. This function won't ask for re-drawing, |
348 | - * so, you will need to re-draw your entry by hand. It can be used after |
349 | - * a #text set, to avoid weird things. |
350 | - **/ |
351 | - public void reset_selection () { |
352 | - selected_start = -1; |
353 | - selected_end = -1; |
354 | - selection_start = 0; |
355 | - selection_end = 0; |
356 | - } |
357 | - |
358 | - private void update_selection (Cairo.Context cr, Gtk.Widget widget) { |
359 | - double last_diff = double.MAX; |
360 | - Pango.Layout layout = widget.create_pango_layout (text); |
361 | - |
362 | - if (selection_mouse_start > 0) { |
363 | - selected_start = -1; |
364 | - selection_start = 0; |
365 | - cursor = text.length; |
366 | - |
367 | - for (int i = 0; i <= text.length; i++) { |
368 | - layout.set_text (text.slice(0, i), -1); |
369 | - |
370 | - if (Math.fabs (selection_mouse_start - get_width (layout)) < last_diff) { |
371 | - last_diff = Math.fabs (selection_mouse_start - get_width (layout)); |
372 | - selection_start = get_width (layout); |
373 | - selected_start = i; |
374 | - } |
375 | - } |
376 | - |
377 | - selection_mouse_start = -1; |
378 | - } |
379 | - |
380 | - if (selection_mouse_end > 0) { |
381 | - last_diff = double.MAX; |
382 | - selected_end = -1; |
383 | - selection_end = 0; |
384 | - cursor = text.length; |
385 | - |
386 | - for (int i = 0; i <= text.length; i++) { |
387 | - layout.set_text (text.slice (0, i), -1); |
388 | - |
389 | - if (Math.fabs (selection_mouse_end - get_width (layout)) < last_diff) { |
390 | - last_diff = Math.fabs (selection_mouse_end - get_width (layout)); |
391 | - selection_end = get_width (layout); |
392 | - selected_end = i; |
393 | - cursor = i; |
394 | - } |
395 | - } |
396 | - |
397 | - selection_mouse_end = -1; |
398 | - } |
399 | - } |
400 | - |
401 | - private void computetext_width (Pango.Layout pango) { |
402 | - int text_width, text_height; |
403 | - pango.get_size (out text_width, out text_height); |
404 | - this.text_width = Pango.units_to_double (text_width); |
405 | - this.text_height = Pango.units_to_double (text_height); |
406 | - } |
407 | - |
408 | - /** |
409 | - * A utility function to get the width of a Pango.Layout. Maybe it could |
410 | - * be moved to a less specific file/lib. |
411 | - * |
412 | - * @param pango a pango layout |
413 | - * @return the width of the layout |
414 | - **/ |
415 | - private double get_width (Pango.Layout pango) { |
416 | - int text_width, text_height; |
417 | - pango.get_size (out text_width, out text_height); |
418 | - return Pango.units_to_double (text_width); |
419 | - } |
420 | - |
421 | - private void update_selection_key (Cairo.Context cr, Gtk.Widget widget) { |
422 | - Pango.Layout layout = widget.create_pango_layout (text); |
423 | - layout.set_text (text.slice (0, selected_end), -1); |
424 | - selection_end = get_width (layout); |
425 | - layout.set_text (text.slice (0, selected_start), -1); |
426 | - selection_start = get_width (layout); |
427 | - need_selection_update = false; |
428 | - } |
429 | - |
430 | - public void draw(Cairo.Context cr, |
431 | - double x, double height, double width, |
432 | - Gtk.Widget widget, Gtk.StyleContext button_context) { |
433 | - |
434 | - update_selection (cr, widget); |
435 | - |
436 | - if (need_selection_update) |
437 | - update_selection_key (cr, widget); |
438 | - |
439 | - cr.set_source_rgba (0, 0, 0, 0.8); |
440 | - |
441 | - Pango.Layout layout = widget.create_pango_layout (text); |
442 | - computetext_width (layout); |
443 | - button_context.render_layout (cr, x, height / 2 - text_height / 2, layout); |
444 | - |
445 | - layout.set_text (text.slice (0, cursor), -1); |
446 | - |
447 | - if (blink && focus) { |
448 | - cr.rectangle (x + get_width (layout), height / 6, 1, 4 * height / 6); |
449 | - cr.fill (); |
450 | - } |
451 | - |
452 | - if (text != "") { |
453 | - Gdk.cairo_set_source_pixbuf (cr,arrow_img, |
454 | - x + width - arrow_img.get_width() - 10, |
455 | - height/2 - arrow_img.get_height() / 2); |
456 | - |
457 | - if (hover) |
458 | - cr.paint (); |
459 | - else |
460 | - cr.paint_with_alpha (0.8); |
461 | - } |
462 | - |
463 | - /* draw completion */ |
464 | - cr.move_to (x + text_width, height / 2 - text_height / 2); |
465 | - layout.set_text (completion, -1); |
466 | - |
467 | -#if VALA_0_14 |
468 | - Gdk.RGBA color = button_context.get_color (Gtk.StateFlags.NORMAL); |
469 | -#else |
470 | - Gdk.RGBA color = Gdk.RGBA (); |
471 | - button_context.get_color (Gtk.StateFlags.NORMAL, color); |
472 | -#endif |
473 | - cr.set_source_rgba (color.red, color.green, color.blue, color.alpha - 0.3); |
474 | - Pango.cairo_show_layout (cr, layout); |
475 | - |
476 | - /* draw selection */ |
477 | - if (focus && selected_start >= 0 && selected_end >= 0) { |
478 | - cr.rectangle (x + selection_start, height / 6, selection_end - selection_start, 4 * height / 6); |
479 | -#if VALA_0_14 |
480 | - color = button_context.get_background_color (Gtk.StateFlags.SELECTED); |
481 | -#else |
482 | - button_context.get_background_color (Gtk.StateFlags.SELECTED, color); |
483 | -#endif |
484 | - Gdk.cairo_set_source_rgba (cr, color); |
485 | - cr.fill (); |
486 | - |
487 | - layout.set_text (get_selection (), -1); |
488 | - |
489 | -#if VALA_0_14 |
490 | - color = button_context.get_color (Gtk.StateFlags.SELECTED); |
491 | -#else |
492 | - button_context.get_color (Gtk.StateFlags.SELECTED, color); |
493 | -#endif |
494 | - Gdk.cairo_set_source_rgba (cr, color); |
495 | - cr.move_to (x + Math.fmin (selection_start, selection_end), |
496 | - height / 2 - text_height / 2); |
497 | - |
498 | - Pango.cairo_show_layout (cr, layout); |
499 | - } |
500 | - } |
501 | - |
502 | - public void reset () { |
503 | - text = ""; |
504 | - cursor = 0; |
505 | - completion = ""; |
506 | - } |
507 | - |
508 | - public void hide () { |
509 | - focus = false; |
510 | - if (timeout > 0) |
511 | - Source.remove (timeout); |
512 | - } |
513 | - |
514 | - ~BreadcrumbsEntry () { |
515 | - hide (); |
516 | - } |
517 | -} |
518 | |
519 | === modified file 'libwidgets/CMakeLists.txt' |
520 | --- libwidgets/CMakeLists.txt 2014-03-27 10:22:25 +0000 |
521 | +++ libwidgets/CMakeLists.txt 2014-06-14 19:18:12 +0000 |
522 | @@ -41,7 +41,6 @@ |
523 | LocationBar.vala |
524 | PoofWindow.vala |
525 | BreadcrumbsElements.vala |
526 | - BreadcrumbsEntry.vala |
527 | PACKAGES |
528 | gtk+-3.0 |
529 | granite |
530 | @@ -66,5 +65,4 @@ |
531 | SOVERSION ${MARLINWIDGETS_SOVERSION}) |
532 | |
533 | install (TARGETS ${PKGNAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/) |
534 | -target_link_libraries(${PKGNAME} ${DEPS_LIBRARIES}) |
535 | -add_subdirectory(tests) |
536 | +target_link_libraries(${PKGNAME} ${DEPS_LIBRARIES}) |
537 | \ No newline at end of file |
538 | |
539 | === modified file 'libwidgets/LocationBar.vala' |
540 | --- libwidgets/LocationBar.vala 2014-05-13 21:10:32 +0000 |
541 | +++ libwidgets/LocationBar.vala 2014-06-14 19:18:12 +0000 |
542 | @@ -34,27 +34,24 @@ |
543 | string? text_displayed; |
544 | } |
545 | |
546 | -public abstract class Marlin.View.Chrome.BasePathBar : Gtk.EventBox { |
547 | +public abstract class Marlin.View.Chrome.BasePathBar : Gtk.Entry { |
548 | |
549 | public string current_right_click_path; |
550 | public string current_right_click_root; |
551 | - //double right_click_root; |
552 | + |
553 | + protected string text_completion = ""; |
554 | + protected bool multiple_completions = false; |
555 | + protected bool text_changed = false; |
556 | + protected bool arrow_hovered = false; |
557 | + protected bool ignore_focus_in = false; |
558 | + protected bool ignore_change = false; |
559 | + private Gdk.Pixbuf arrow_img; |
560 | |
561 | /* if we must display the BreadcrumbsElement which are in newbreads. */ |
562 | bool view_old = false; |
563 | |
564 | - /* Used to decide if this button press event must be send to the |
565 | - * integrated entry or not. */ |
566 | - double x_render_saved = 0; |
567 | - |
568 | - /* if we have the focus or not |
569 | - * FIXME: this should be replaced with some nice Gtk.Widget method. */ |
570 | - public new bool focus = false; |
571 | - |
572 | - public Gtk.ActionGroup clipboard_actions; |
573 | - |
574 | /* This list will contain all BreadcrumbsElement */ |
575 | - Gee.ArrayList<BreadcrumbsElement> elements; |
576 | + protected Gee.ArrayList<BreadcrumbsElement> elements; |
577 | |
578 | /* This list will contain the BreadcrumbsElement which are animated */ |
579 | Gee.List<BreadcrumbsElement> newbreads; |
580 | @@ -67,19 +64,18 @@ |
581 | |
582 | Gtk.StyleContext button_context; |
583 | Gtk.StyleContext button_context_active; |
584 | - public BreadcrumbsEntry entry; |
585 | |
586 | /** |
587 | * When the user click on a breadcrumb, or when he enters a path by hand |
588 | * in the integrated entry |
589 | **/ |
590 | - public signal void activate_alternate (string path); |
591 | - public signal void changed (string changed); |
592 | + public signal void activate_alternate (File file); |
593 | + public signal void path_changed (File file); |
594 | public signal void need_completion (); |
595 | |
596 | List<IconDirectory?> icons; |
597 | |
598 | - string text = ""; |
599 | + string current_path = ""; |
600 | |
601 | int selected = -1; |
602 | int space_breads = 12; |
603 | @@ -87,7 +83,10 @@ |
604 | int y; |
605 | string protocol; |
606 | |
607 | + public signal void completed (); |
608 | public signal void escape (); |
609 | + public signal void up (); |
610 | + public signal void down (); |
611 | |
612 | private int timeout = -1; |
613 | |
614 | @@ -96,17 +95,16 @@ |
615 | |
616 | private Granite.Services.IconFactory icon_factory; |
617 | |
618 | - |
619 | construct { |
620 | - add_events (Gdk.EventMask.BUTTON_PRESS_MASK |
621 | - | Gdk.EventMask.BUTTON_RELEASE_MASK |
622 | - | Gdk.EventMask.KEY_PRESS_MASK |
623 | - | Gdk.EventMask.KEY_RELEASE_MASK |
624 | - | Gdk.EventMask.POINTER_MOTION_MASK |
625 | - | Gdk.EventMask.LEAVE_NOTIFY_MASK); |
626 | - init_clipboard (); |
627 | icon_factory = Granite.Services.IconFactory.get_default (); |
628 | icons = new List<IconDirectory?> (); |
629 | + |
630 | + /* Load arrow image */ |
631 | + try { |
632 | + arrow_img = Gtk.IconTheme.get_default ().load_icon ("go-jump-symbolic", 16, Gtk.IconLookupFlags.GENERIC_FALLBACK); |
633 | + } catch (Error err) { |
634 | + stderr.printf ("Unable to load home icon: %s", err.message); |
635 | + } |
636 | |
637 | button_context = get_style_context (); |
638 | button_context.add_class ("button"); |
639 | @@ -118,92 +116,26 @@ |
640 | Granite.Widgets.Utils.set_theming (this, ".noradius-button{border-radius:0px;}", null, |
641 | Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); |
642 | |
643 | - left_padding = 5;//border.left; |
644 | + left_padding = border.left; |
645 | right_padding = border.right; |
646 | |
647 | - set_can_focus (true); |
648 | - set_visible_window (false); |
649 | - |
650 | /* x padding */ |
651 | x = 0; |
652 | /* y padding */ |
653 | y = 0; |
654 | |
655 | elements = new Gee.ArrayList<BreadcrumbsElement> (); |
656 | - |
657 | - entry = new BreadcrumbsEntry (); |
658 | - |
659 | - entry.enter.connect (on_entry_enter); |
660 | - |
661 | - /* Let's connect the signals ;) |
662 | - * FIXME: there could be a separate function for each signal */ |
663 | - entry.need_draw.connect (queue_draw); |
664 | - |
665 | - entry.left.connect (() => { |
666 | - if (elements.size > 0) { |
667 | - var element = elements[elements.size - 1]; |
668 | - elements.remove (element); |
669 | - |
670 | - if (element.display) { |
671 | - if (entry.text[0] != '/') { |
672 | - entry.text = element.text + "/" + entry.text; |
673 | - entry.cursor = element.text.length + 1; |
674 | - } else { |
675 | - entry.text = element.text + entry.text; |
676 | - entry.cursor = element.text.length; |
677 | - } |
678 | - entry.reset_selection (); |
679 | - } |
680 | - } |
681 | - }); |
682 | - |
683 | - entry.left_full.connect (() => { |
684 | - string tmp = entry.text; |
685 | - string tmp_entry = ""; |
686 | - |
687 | - foreach (BreadcrumbsElement element in elements) { |
688 | - if (element.display) { |
689 | - if (tmp_entry[0] != '/') |
690 | - tmp_entry += element.text + "/"; |
691 | - else |
692 | - tmp_entry += element.text; |
693 | - } |
694 | - } |
695 | - |
696 | - entry.text = tmp_entry + tmp; |
697 | - elements.clear (); |
698 | - }); |
699 | - |
700 | - entry.backspace.connect (() => { |
701 | - if (elements.size > 0) { |
702 | - string strloc = get_elements_path (); |
703 | - File location = File.new_for_commandline_arg (strloc); |
704 | - location = location.get_parent (); |
705 | - |
706 | - if (location == null) |
707 | - location = File.new_for_commandline_arg (protocol); |
708 | - |
709 | - changed (location.get_uri () + "/"); |
710 | - grab_focus (); |
711 | - } |
712 | - }); |
713 | - |
714 | - entry.escape.connect (() => { |
715 | - escape (); |
716 | - }); |
717 | - |
718 | - entry.need_completion.connect (() => { |
719 | - need_completion (); |
720 | - }); |
721 | - |
722 | - entry.paste.connect (() => { |
723 | - var display = get_display (); |
724 | - Gdk.Atom atom = Gdk.SELECTION_CLIPBOARD; |
725 | - Gtk.Clipboard clipboard = Gtk.Clipboard.get_for_display (display, atom); |
726 | - clipboard.request_text (request_text); |
727 | - }); |
728 | - |
729 | - entry.hide (); |
730 | + |
731 | + secondary_icon_activatable = true; |
732 | + secondary_icon_sensitive = true; |
733 | + truncate_multiline = true; |
734 | + activate.connect (on_activate); |
735 | + icon_press.connect (on_activate); |
736 | + motion_notify_event.connect (on_motion_notify); |
737 | + focus_in_event.connect (on_focus_in); |
738 | + focus_out_event.connect (on_focus_out); |
739 | + grab_focus.connect_after (on_grab_focus); |
740 | + changed.connect (on_change); |
741 | |
742 | /* Drag and drop */ |
743 | Gtk.TargetEntry target_uri_list = {"text/uri-list", 0, 0}; |
744 | @@ -213,6 +145,170 @@ |
745 | drag_motion.connect (on_drag_motion); |
746 | drag_data_received.connect (on_drag_data_received); |
747 | } |
748 | + |
749 | + public override bool key_press_event (Gdk.EventKey event) { |
750 | + switch (event.keyval) { |
751 | + case Gdk.Key.KP_Tab: |
752 | + case Gdk.Key.Tab: |
753 | + complete (); |
754 | + return true; |
755 | + |
756 | + case Gdk.Key.KP_Down: |
757 | + case Gdk.Key.Down: |
758 | + down (); |
759 | + return true; |
760 | + |
761 | + case Gdk.Key.KP_Up: |
762 | + case Gdk.Key.Up: |
763 | + up (); |
764 | + return true; |
765 | + |
766 | + case Gdk.Key.Escape: |
767 | + escape (); |
768 | + return true; |
769 | + } |
770 | + |
771 | + return base.key_press_event (event); |
772 | + } |
773 | + |
774 | + public override bool button_press_event (Gdk.EventButton event) { |
775 | + if (is_focus) |
776 | + return base.button_press_event (event); |
777 | + |
778 | + foreach (BreadcrumbsElement element in elements) |
779 | + element.pressed = false; |
780 | + |
781 | + var el = get_element_from_coordinates ((int) event.x, (int) event.y); |
782 | + |
783 | + if (el != null) |
784 | + el.pressed = true; |
785 | + |
786 | + queue_draw (); |
787 | + |
788 | + if (timeout == -1 && event.button == 1) { |
789 | + timeout = (int) Timeout.add (150, () => { |
790 | + select_bread_from_coord (event); |
791 | + timeout = -1; |
792 | + return false; |
793 | + }); |
794 | + } |
795 | + |
796 | + if (event.button == 2) { |
797 | + if (el != null) { |
798 | + selected = elements.index_of (el); |
799 | + var newpath = get_path_from_element (el); |
800 | + activate_alternate (get_file_for_path (newpath)); |
801 | + } |
802 | + |
803 | + return true; |
804 | + } |
805 | + |
806 | + if (event.button == 3) |
807 | + return select_bread_from_coord (event); |
808 | + |
809 | + return true; |
810 | + } |
811 | + |
812 | + public override bool button_release_event (Gdk.EventButton event) { |
813 | + reset_elements_states (); |
814 | + |
815 | + if (timeout != -1) { |
816 | + Source.remove ((uint) timeout); |
817 | + timeout = -1; |
818 | + } |
819 | + |
820 | + if (is_focus) |
821 | + return base.button_release_event (event); |
822 | + |
823 | + if (event.button == 1) { |
824 | + var el = get_element_from_coordinates ((int) event.x, (int) event.y); |
825 | + if (el != null) { |
826 | + selected = elements.index_of (el); |
827 | + var newpath = get_path_from_element (el); |
828 | + path_changed (get_file_for_path (newpath)); |
829 | + } else |
830 | + grab_focus (); |
831 | + } |
832 | + |
833 | + return base.button_release_event (event); |
834 | + } |
835 | + |
836 | + void on_change () { |
837 | + if (ignore_change) { |
838 | + ignore_change = false; |
839 | + return; |
840 | + } |
841 | + |
842 | + set_entry_icon (true, (text.length > 0) ? "Navigate to: " + text : ""); |
843 | + text_completion = ""; |
844 | + need_completion (); |
845 | + } |
846 | + |
847 | + bool on_motion_notify (Gdk.EventMotion event) { |
848 | + int x = (int) event.x; |
849 | + double x_render = 0; |
850 | + double x_previous = -10; |
851 | + set_tooltip_text (""); |
852 | + |
853 | + if (is_focus) |
854 | + return base.motion_notify_event (event); |
855 | + |
856 | + foreach (BreadcrumbsElement element in elements) { |
857 | + if (element.display) { |
858 | + x_render += element.real_width; |
859 | + if (x <= x_render + 5 && x > x_previous + 5) { |
860 | + selected = elements.index_of (element); |
861 | + set_tooltip_text (_("Go to %s").printf (element.text)); |
862 | + break; |
863 | + } |
864 | + |
865 | + x_previous = x_render; |
866 | + } |
867 | + } |
868 | + |
869 | + if (event.x > 0 && event.x < x_render + 5) |
870 | + set_entry_cursor (new Gdk.Cursor (Gdk.CursorType.ARROW)); |
871 | + else |
872 | + set_entry_cursor (null); |
873 | + |
874 | + return base.motion_notify_event (event); |
875 | + } |
876 | + |
877 | + bool on_focus_out (Gdk.EventFocus event) { |
878 | + if (is_focus) { |
879 | + ignore_focus_in = true; |
880 | + return base.focus_out_event (event); |
881 | + } |
882 | + |
883 | + ignore_focus_in = false; |
884 | + set_entry_icon (false); |
885 | + set_entry_text (""); |
886 | + |
887 | + return base.focus_out_event (event); |
888 | + } |
889 | + |
890 | + bool on_focus_in (Gdk.EventFocus event) { |
891 | + if (ignore_focus_in) |
892 | + return base.focus_in_event (event); |
893 | + |
894 | + set_entry_text (GLib.Uri.unescape_string (get_elements_path () |
895 | + .replace ("file:////", "/") |
896 | + .replace ("file:///", "/") |
897 | + .replace ("trash:///", "") |
898 | + .replace ("network:///", ""))); |
899 | + |
900 | + return base.focus_in_event (event); |
901 | + } |
902 | + |
903 | + void on_grab_focus () { |
904 | + select_region (0, 0); |
905 | + set_position (-1); |
906 | + } |
907 | + |
908 | + void on_activate () { |
909 | + path_changed (get_file_for_path (text + text_completion)); |
910 | + text_completion = ""; |
911 | + } |
912 | |
913 | void on_drag_begin (Gdk.DragContext drag_context) { |
914 | critical ("Start drag..."); |
915 | @@ -242,11 +338,11 @@ |
916 | var newpath = get_path_from_element (el); |
917 | print ("Move to: %s\n", newpath); |
918 | var target_file = GLib.File.new_for_uri (newpath); |
919 | - on_file_droped (uris, target_file, real_action); |
920 | + on_file_dropped (uris, target_file, real_action); |
921 | } |
922 | } |
923 | |
924 | - protected abstract void on_file_droped (List<GLib.File> uris, GLib.File target_file, Gdk.DragAction real_action); |
925 | + protected abstract void on_file_dropped (List<GLib.File> uris, GLib.File target_file, Gdk.DragAction real_action); |
926 | |
927 | bool on_drag_motion (Gdk.DragContext context, int x, int y, uint time) { |
928 | Gtk.drag_unhighlight (this); |
929 | @@ -265,6 +361,70 @@ |
930 | |
931 | return false; |
932 | } |
933 | + |
934 | + protected void add_icon (IconDirectory icon) { |
935 | + if (icon.gicon != null) |
936 | + icon.icon = icon_factory.load_symbolic_icon_from_gicon (button_context, icon.gicon, 16); |
937 | + else |
938 | + icon.icon = icon_factory.load_symbolic_icon (button_context, icon.icon_name, 16); |
939 | + |
940 | + icons.append (icon); |
941 | + } |
942 | + |
943 | + public void complete () { |
944 | + if (text_completion.length == 0) |
945 | + return; |
946 | + |
947 | + string path = text + text_completion; |
948 | + |
949 | + /* If there are multiple results, tab as far as we can, otherwise do the entire result */ |
950 | + if (!multiple_completions) { |
951 | + set_entry_text (path + "/"); |
952 | + completed (); |
953 | + } else |
954 | + set_entry_text (path); |
955 | + } |
956 | + |
957 | + public void reset_elements_states () { |
958 | + foreach (BreadcrumbsElement element in elements) |
959 | + element.pressed = false; |
960 | + |
961 | + queue_draw (); |
962 | + } |
963 | + |
964 | + public void set_entry_text (string text) { |
965 | + ignore_change = true; |
966 | + text_completion = ""; |
967 | + this.text = text; |
968 | + set_position (-1); |
969 | + } |
970 | + |
971 | + public void set_entry_cursor (Gdk.Cursor? cursor) { |
972 | + /* Only child 13 needs to be modified for the cursor - there may be a better way to do this */ |
973 | + get_window ().get_children ().nth_data (13).set_cursor (cursor ?? new Gdk.Cursor (Gdk.CursorType.XTERM)); |
974 | + } |
975 | + |
976 | + public void set_entry_icon (bool active, string? tooltip = null) { |
977 | + if (!active) |
978 | + secondary_icon_pixbuf = null; |
979 | + else { |
980 | + secondary_icon_pixbuf = arrow_img; |
981 | + secondary_icon_tooltip_text = tooltip; |
982 | + } |
983 | + } |
984 | + |
985 | + public double get_all_breadcrumbs_width (out int breadcrumbs_count) { |
986 | + double total_width = 0.0; |
987 | + breadcrumbs_count = 0; |
988 | + foreach (BreadcrumbsElement element in elements) { |
989 | + if (element.display) { |
990 | + total_width += element.width; |
991 | + element.max_width = -1; |
992 | + breadcrumbs_count++; |
993 | + } |
994 | + } |
995 | + return total_width; |
996 | + } |
997 | |
998 | private BreadcrumbsElement? get_element_from_coordinates (int x, int y) { |
999 | double x_render = 0; |
1000 | @@ -295,39 +455,6 @@ |
1001 | return newpath; |
1002 | } |
1003 | |
1004 | - protected void add_icon (IconDirectory icon) { |
1005 | - if (icon.gicon != null) |
1006 | - icon.icon = icon_factory.load_symbolic_icon_from_gicon (button_context, icon.gicon, 16); |
1007 | - else |
1008 | - icon.icon = icon_factory.load_symbolic_icon (button_context, icon.icon_name, 16); |
1009 | - |
1010 | - icons.append (icon); |
1011 | - } |
1012 | - |
1013 | - protected void init_clipboard () { |
1014 | - clipboard_actions = new Gtk.ActionGroup ("ClipboardActions"); |
1015 | - clipboard_actions.add_actions (action_entries, this); |
1016 | - } |
1017 | - |
1018 | - static const Gtk.ActionEntry[] action_entries = { |
1019 | - /* name, stock id */ { "Cut", Gtk.Stock.CUT, |
1020 | - /* label, accelerator */ null, null, |
1021 | - /* tooltip */ N_("Cut the selected text to the clipboard"), |
1022 | - action_cut }, |
1023 | - /* name, stock id */ { "Copy", Gtk.Stock.COPY, |
1024 | - /* label, accelerator */ null, null, |
1025 | - /* tooltip */ N_("Copy the selected text to the clipboard"), |
1026 | - action_copy }, |
1027 | - /* name, stock id */ { "Paste", Gtk.Stock.PASTE, |
1028 | - /* label, accelerator */ null, null, |
1029 | - /* tooltip */ N_("Paste the text stored on the clipboard"), |
1030 | - action_paste }, |
1031 | - /* name, stock id */ { "Paste Into Folder", Gtk.Stock.PASTE, |
1032 | - /* label, accelerator */ null, null, |
1033 | - /* tooltip */ N_("Paste the text stored on the clipboard"), |
1034 | - action_paste } |
1035 | - }; |
1036 | - |
1037 | /** |
1038 | * Get the current path of the PathBar, based on the elements that it contains |
1039 | **/ |
1040 | @@ -337,37 +464,36 @@ |
1041 | |
1042 | foreach (BreadcrumbsElement element in elements) { |
1043 | if (element.display) |
1044 | - strpath += element.text + "/"; /* sometimes, + "/" is useless |
1045 | - * but we are never careful enough */ |
1046 | - /* FIXME make sure the comment never happen |
1047 | - * -> it seems to be necessary in all cases */ |
1048 | + strpath += element.text + "/"; |
1049 | } |
1050 | |
1051 | return strpath; |
1052 | } |
1053 | - |
1054 | - private void action_paste (Gtk.Action action) { |
1055 | - var display = get_display (); |
1056 | - Gdk.Atom atom = Gdk.SELECTION_CLIPBOARD; |
1057 | - Gtk.Clipboard clipboard = Gtk.Clipboard.get_for_display (display, atom); |
1058 | - clipboard.request_text (request_text); |
1059 | - } |
1060 | - |
1061 | - private void action_copy (Gtk.Action action) { |
1062 | - string? selection = entry.get_selection (); |
1063 | - if (selection != null) { /* else, it means that there is no selection */ |
1064 | - var display = get_display (); |
1065 | - Gdk.Atom atom = Gdk.SELECTION_CLIPBOARD; |
1066 | - Gtk.Clipboard clipboard = Gtk.Clipboard.get_for_display (display, atom); |
1067 | - clipboard.set_text (entry.get_selection (), entry.get_selection ().length); |
1068 | - } |
1069 | - } |
1070 | - |
1071 | - private void action_cut (Gtk.Action action) { |
1072 | - action_copy (action); |
1073 | - entry.delete_selection (); |
1074 | - } |
1075 | - |
1076 | + |
1077 | + /** |
1078 | + * Gets a properly escaped GLib.File for the given path |
1079 | + **/ |
1080 | + public File get_file_for_path (string path) { |
1081 | + string reserved_chars = (GLib.Uri.RESERVED_CHARS_GENERIC_DELIMITERS + GLib.Uri.RESERVED_CHARS_SUBCOMPONENT_DELIMITERS + " ").replace("#", ""); |
1082 | + string newpath = GLib.Uri.unescape_string (path ?? ""); |
1083 | + |
1084 | + /* Format our path so its valid */ |
1085 | + if (newpath == "") |
1086 | + newpath = "/"; |
1087 | + |
1088 | + if (newpath[0] == '~') |
1089 | + newpath = newpath.replace("~", Environment.get_home_dir ()); |
1090 | + |
1091 | + if (!newpath.contains("://")) |
1092 | + newpath = Marlin.ROOT_FS_URI + newpath; |
1093 | + |
1094 | + newpath = newpath.replace("ssh:", "sftp:"); |
1095 | + newpath = GLib.Uri.escape_string (newpath, reserved_chars, true); |
1096 | + |
1097 | + File file = File.new_for_commandline_arg (newpath); |
1098 | + return file; |
1099 | + } |
1100 | + |
1101 | /** |
1102 | * Select the breadcrumb to make a right click. This function check |
1103 | * where the user click, then, it loads a context menu with the others |
1104 | @@ -377,7 +503,7 @@ |
1105 | * @param event a button event to compute the coords of the new menu. |
1106 | * |
1107 | **/ |
1108 | - private bool select_bread_from_coord (Gdk.EventButton event) { |
1109 | + private bool select_bread_from_coord (Gdk.EventButton event) { |
1110 | var el = get_element_from_coordinates ((int) event.x, (int) event.y); |
1111 | |
1112 | if (el != null) { |
1113 | @@ -397,104 +523,12 @@ |
1114 | } |
1115 | |
1116 | return false; |
1117 | - } |
1118 | - |
1119 | - public override bool button_press_event (Gdk.EventButton event) { |
1120 | - foreach (BreadcrumbsElement element in elements) |
1121 | - element.pressed = false; |
1122 | - |
1123 | - var el = get_element_from_coordinates ((int) event.x, (int) event.y); |
1124 | - |
1125 | - if (el != null) |
1126 | - el.pressed = true; |
1127 | - |
1128 | - queue_draw (); |
1129 | - |
1130 | - if (timeout == -1 && event.button == 1) { |
1131 | - timeout = (int) Timeout.add (800, () => { |
1132 | - select_bread_from_coord (event); |
1133 | - timeout = -1; |
1134 | - return false; |
1135 | - }); |
1136 | - } |
1137 | - |
1138 | - if (event.button == 2) { |
1139 | - if (el != null) { |
1140 | - selected = elements.index_of (el); |
1141 | - var newpath = get_path_from_element (el); |
1142 | - activate_alternate (newpath); |
1143 | - } |
1144 | - } |
1145 | - |
1146 | - if (event.button == 3) |
1147 | - return select_bread_from_coord(event); |
1148 | - |
1149 | - if (focus) { |
1150 | - event.x -= x_render_saved; |
1151 | - entry.mouse_press_event(event, get_allocated_width() - x_render_saved); |
1152 | - } |
1153 | - |
1154 | - return true; |
1155 | - } |
1156 | - |
1157 | - public override bool button_release_event (Gdk.EventButton event) { |
1158 | - reset_elements_states (); |
1159 | - |
1160 | - if (timeout != -1) { |
1161 | - Source.remove ((uint) timeout); |
1162 | - timeout = -1; |
1163 | - } |
1164 | - |
1165 | - if (event.button == 1) { |
1166 | - var el = get_element_from_coordinates ((int) event.x, (int) event.y); |
1167 | - if (el != null) { |
1168 | - selected = elements.index_of (el); |
1169 | - var newpath = get_path_from_element (el); |
1170 | - print ("%s\n\n\n", newpath); |
1171 | - changed (newpath); |
1172 | - } else { |
1173 | - grab_focus (); |
1174 | - } |
1175 | - } |
1176 | - |
1177 | - if (focus) { |
1178 | - event.x -= x_render_saved; |
1179 | - entry.mouse_release_event(event); |
1180 | - } |
1181 | - |
1182 | - return true; |
1183 | - } |
1184 | - |
1185 | - private void on_entry_enter () { |
1186 | - text = get_elements_path (); |
1187 | - |
1188 | - if (text != "") |
1189 | - changed (text + "/" + entry.text + entry.completion); |
1190 | - else |
1191 | - changed (entry.text + entry.completion); |
1192 | - |
1193 | - //entry.reset(); |
1194 | - } |
1195 | - |
1196 | - public override bool key_press_event (Gdk.EventKey event) { |
1197 | - entry.key_press_event (event); |
1198 | - queue_draw (); |
1199 | - return true; |
1200 | - } |
1201 | - |
1202 | - public override bool key_release_event (Gdk.EventKey event) { |
1203 | - entry.key_release_event (event); |
1204 | - queue_draw (); |
1205 | - return true; |
1206 | - } |
1207 | + } |
1208 | |
1209 | public virtual string? update_breadcrumbs (string newpath, string breadpath) { |
1210 | string strloc; |
1211 | |
1212 | - debug ("Update breadcrumb text %s", newpath); |
1213 | - |
1214 | if (Posix.strncmp (newpath, "./", 2) == 0) { |
1215 | - entry.reset (); |
1216 | return null; |
1217 | } |
1218 | |
1219 | @@ -507,7 +541,6 @@ |
1220 | } |
1221 | |
1222 | return strloc; |
1223 | - |
1224 | } |
1225 | |
1226 | /** |
1227 | @@ -517,19 +550,18 @@ |
1228 | * be animated. |
1229 | **/ |
1230 | public void change_breadcrumbs (string newpath) { |
1231 | - debug ("Change breadcrumbs to %s", newpath); |
1232 | - var explode_protocol = newpath.split ("://"); |
1233 | + var explode_protocol = Uri.unescape_string (newpath).split ("://"); |
1234 | |
1235 | if (explode_protocol.length > 1) { |
1236 | protocol = explode_protocol[0] + "://"; |
1237 | - text = explode_protocol[1]; |
1238 | + current_path = explode_protocol[1]; |
1239 | } else { |
1240 | - text = newpath; |
1241 | + current_path = newpath; |
1242 | protocol = Marlin.ROOT_FS_URI; |
1243 | } |
1244 | |
1245 | selected = -1; |
1246 | - var breads = text.split ("/"); |
1247 | + var breads = current_path.split ("/"); |
1248 | var newelements = new Gee.ArrayList<BreadcrumbsElement> (); |
1249 | if (breads.length == 0 || breads[0] == "") { |
1250 | var element = new BreadcrumbsElement (protocol, left_padding, right_padding); |
1251 | @@ -567,15 +599,6 @@ |
1252 | |
1253 | int max_path = int.min (elements.size, newelements.size); |
1254 | |
1255 | - bool same = true; |
1256 | - |
1257 | - for (int i = 0; i < max_path; i++) { |
1258 | - if (newelements[i].text != elements[i].text) { |
1259 | - same = false; |
1260 | - break; |
1261 | - } |
1262 | - } |
1263 | - |
1264 | foreach (IconDirectory icon in icons) { |
1265 | if (icon.protocol && icon.path == protocol) { |
1266 | newelements[0].set_icon(icon.icon); |
1267 | @@ -637,7 +660,6 @@ |
1268 | |
1269 | elements.clear (); |
1270 | elements = newelements; |
1271 | - entry.reset (); |
1272 | } |
1273 | |
1274 | uint anim = 0; |
1275 | @@ -720,246 +742,76 @@ |
1276 | }); |
1277 | } |
1278 | |
1279 | -/* disabled, waiting for deletion or fix the hardcoded stuff |
1280 | - * or just draw elements by elements with widgets state flags */ |
1281 | -#if 0 |
1282 | - private void draw_selection (Cairo.Context cr) { |
1283 | - /* If a dir is selected (= mouse hover)*/ |
1284 | - if (selected != -1) { |
1285 | - int height = get_allocated_height(); |
1286 | - /* FIXME: this block could be cleaned up, +7 and +5 are |
1287 | - * hardcoded. */ |
1288 | - double x_hl = y + right_padding + left_padding; |
1289 | - |
1290 | - if (selected > 0) { |
1291 | - foreach(BreadcrumbsElement element in elements) { |
1292 | - if (element.display) { |
1293 | - x_hl += element.real_width; |
1294 | - } |
1295 | - |
1296 | - if (element == elements[selected - 1]) { |
1297 | - break; |
1298 | - } |
1299 | - } |
1300 | - } |
1301 | - |
1302 | - x_hl += 7; |
1303 | - double first_stop = x_hl - 7 * (height / 2 - y)/(height / 2 - height / 3) + 5; |
1304 | - double text_width = (elements[selected].max_width > 0 ? elements[selected].max_width : elements[selected].text_width); |
1305 | - |
1306 | - cr.move_to(first_stop, |
1307 | - y + 1); |
1308 | - cr.line_to(x_hl + 3, |
1309 | - height / 2); |
1310 | - cr.line_to(first_stop, |
1311 | - height - y - 1); |
1312 | - |
1313 | - x_hl += text_width; |
1314 | - |
1315 | - double second_stop = x_hl - 7 * (height / 2 - y)/(height / 2 - height / 3) + 5; |
1316 | - cr.line_to(second_stop, |
1317 | - height - y - 1); |
1318 | - cr.line_to(x_hl + 3, |
1319 | - height / 2); |
1320 | - cr.line_to(second_stop, |
1321 | - y + 1); |
1322 | - cr.close_path(); |
1323 | -#if VALA_0_14 |
1324 | - Gdk.RGBA color = button_context.get_background_color(Gtk.StateFlags.SELECTED); |
1325 | -#else |
1326 | - Gdk.RGBA color = Gdk.RGBA(); |
1327 | - button_context.get_background_color(Gtk.StateFlags.SELECTED, color); |
1328 | -#endif |
1329 | - |
1330 | - Cairo.Pattern pat = new Cairo.Pattern.linear(first_stop, y, second_stop, y); |
1331 | - pat.add_color_stop_rgba(0.7, color.red, color.green, color.blue, 0); |
1332 | - pat.add_color_stop_rgba(1, color.red, color.green, color.blue, 0.6); |
1333 | - |
1334 | - cr.set_source(pat); |
1335 | - cr.fill(); |
1336 | - } |
1337 | - } |
1338 | -#endif |
1339 | - |
1340 | - public override bool motion_notify_event (Gdk.EventMotion event) { |
1341 | - int x = (int) event.x; |
1342 | - double x_render = 0; |
1343 | - double x_previous = -10; |
1344 | - selected = -1; |
1345 | - set_tooltip_text (""); |
1346 | - |
1347 | - foreach (BreadcrumbsElement element in elements) { |
1348 | - if (element.display) { |
1349 | - x_render += element.real_width; |
1350 | - if (x <= x_render + 5 && x > x_previous + 5) { |
1351 | - selected = elements.index_of (element); |
1352 | - //TODO doesn't work |
1353 | - set_tooltip_text (_("Go to %s").printf (element.text)); |
1354 | - break; |
1355 | - } |
1356 | - |
1357 | - x_previous = x_render; |
1358 | - } |
1359 | - } |
1360 | - |
1361 | - event.x -= x_render_saved; |
1362 | - entry.mouse_motion_event (event, get_allocated_width () - x_render_saved); |
1363 | - |
1364 | - if (event.x > 0 && event.x + x_render_saved < get_allocated_width () - entry.arrow_img.get_width ()) |
1365 | - get_window ().set_cursor (new Gdk.Cursor (Gdk.CursorType.XTERM)); |
1366 | - else |
1367 | - get_window ().set_cursor (null); |
1368 | - |
1369 | - return true; |
1370 | - } |
1371 | - |
1372 | - public override bool leave_notify_event (Gdk.EventCrossing event) { |
1373 | - selected = -1; |
1374 | - entry.hover = false; |
1375 | - queue_draw (); |
1376 | - get_window ().set_cursor (null); |
1377 | - return false; |
1378 | - } |
1379 | - |
1380 | - public override bool focus_out_event (Gdk.EventFocus event) { |
1381 | - focus = false; |
1382 | - //button_context = button_widget_context; |
1383 | - button_context.set_state (Gtk.StateFlags.NORMAL); |
1384 | - entry.hide (); |
1385 | - return true; |
1386 | - } |
1387 | - |
1388 | - public override bool focus_in_event (Gdk.EventFocus event) { |
1389 | - entry.show (); |
1390 | - //button_context = entry_context; |
1391 | - //button_context.set_state (StateFlags.ACTIVE); |
1392 | - focus = true; |
1393 | - return true; |
1394 | - } |
1395 | - |
1396 | - private void request_text (Gtk.Clipboard clip, string? text) { |
1397 | - if (text != null) |
1398 | - entry.insert (text.replace ("\n", "")); |
1399 | - } |
1400 | - |
1401 | - public double get_all_breadcrumbs_width (out int breadcrumbs_count) { |
1402 | - double total_width = 0.0; |
1403 | - breadcrumbs_count = 0; |
1404 | - foreach (BreadcrumbsElement element in elements) { |
1405 | - if (element.display) { |
1406 | - total_width += element.width; |
1407 | - element.max_width = -1; |
1408 | - breadcrumbs_count++; |
1409 | - } |
1410 | - } |
1411 | - return total_width; |
1412 | - } |
1413 | - |
1414 | - public void reset_elements_states () { |
1415 | - foreach (BreadcrumbsElement element in elements) |
1416 | - element.pressed = false; |
1417 | - |
1418 | - queue_draw (); |
1419 | - } |
1420 | - |
1421 | public override bool draw (Cairo.Context cr) { |
1422 | if (button_context_active == null) { |
1423 | button_context_active = new Gtk.StyleContext (); |
1424 | button_context_active.set_path(button_context.get_path ()); |
1425 | button_context_active.set_state (Gtk.StateFlags.ACTIVE); |
1426 | } |
1427 | - |
1428 | - //button_context.set_state (StateFlags.NORMAL); |
1429 | - //propagate_draw (get_child (), cr); |
1430 | + |
1431 | + base.draw (cr); |
1432 | double height = get_allocated_height (); |
1433 | double width = get_allocated_width (); |
1434 | - double margin = y; |
1435 | - |
1436 | - /* Ensure there is an editable area to the right of the breadcrumbs */ |
1437 | - double width_marged = width - 2*margin - MINIMUM_LOCATION_BAR_ENTRY_WIDTH; |
1438 | - double height_marged = height - 2*margin; |
1439 | - double x_render = margin; |
1440 | - |
1441 | - /* Draw toolbar background */ |
1442 | - button_context.render_background (cr, 0, margin, width, height_marged); |
1443 | - |
1444 | - int breadcrumbs_displayed = 0; |
1445 | - double max_width = get_all_breadcrumbs_width (out breadcrumbs_displayed); |
1446 | - |
1447 | - if (max_width > width_marged) { /* let's check if the breadcrumbs are bigger than the widget */ |
1448 | - /* each element must not be bigger than the width/breadcrumbs count */ |
1449 | - double max_element_width = width_marged/breadcrumbs_displayed; |
1450 | - |
1451 | + |
1452 | + if (!is_focus) { |
1453 | + double margin = y; |
1454 | + |
1455 | + /* Ensure there is an editable area to the right of the breadcrumbs */ |
1456 | + double width_marged = width - 2*margin - MINIMUM_LOCATION_BAR_ENTRY_WIDTH; |
1457 | + double height_marged = height - 2*margin; |
1458 | + double x_render = margin; |
1459 | + int breadcrumbs_displayed = 0; |
1460 | + double max_width = get_all_breadcrumbs_width (out breadcrumbs_displayed); |
1461 | + |
1462 | + if (max_width > width_marged) { /* let's check if the breadcrumbs are bigger than the widget */ |
1463 | + /* each element must not be bigger than the width/breadcrumbs count */ |
1464 | + double max_element_width = width_marged/breadcrumbs_displayed; |
1465 | + |
1466 | + foreach (BreadcrumbsElement element in elements) { |
1467 | + if (element.display && element.width < max_element_width) { |
1468 | + breadcrumbs_displayed --; |
1469 | + max_element_width += (max_element_width - element.width)/breadcrumbs_displayed; |
1470 | + } |
1471 | + } |
1472 | + |
1473 | + foreach (BreadcrumbsElement element in elements) |
1474 | + if (element.display && element.width > max_element_width) |
1475 | + element.max_width = max_element_width - element.left_padding - element.right_padding - element.last_height/2; |
1476 | + } |
1477 | + |
1478 | + cr.save (); |
1479 | + /* Really draw the elements */ |
1480 | foreach (BreadcrumbsElement element in elements) { |
1481 | - if (element.display && element.width < max_element_width) { |
1482 | - breadcrumbs_displayed --; |
1483 | - max_element_width += (max_element_width - element.width)/breadcrumbs_displayed; |
1484 | + if (element.display) { |
1485 | + x_render = element.draw (cr, x_render, margin, height_marged, button_context, this); |
1486 | + /* save element x axis position */ |
1487 | + element.x = x_render - element.real_width; |
1488 | } |
1489 | } |
1490 | |
1491 | - foreach (BreadcrumbsElement element in elements) |
1492 | - if (element.display && element.width > max_element_width) |
1493 | - element.max_width = max_element_width - element.left_padding - element.right_padding - element.last_height/2; |
1494 | - } |
1495 | - |
1496 | - cr.save (); |
1497 | - /* Really draw the elements */ |
1498 | - foreach (BreadcrumbsElement element in elements) { |
1499 | - if (element.display) { |
1500 | - x_render = element.draw (cr, x_render, margin, height_marged, button_context, this); |
1501 | - /* save element x axis position */ |
1502 | - element.x = x_render - element.real_width; |
1503 | - } |
1504 | - } |
1505 | - |
1506 | - /* Draw the old breadcrumbs, only for the animations */ |
1507 | - if (view_old) |
1508 | - foreach (BreadcrumbsElement element in newbreads) |
1509 | - if (element.display) |
1510 | - x_render = element.draw(cr, x_render, margin, height_marged, button_context, this); |
1511 | - |
1512 | - //draw_selection(cr); |
1513 | - |
1514 | - x_render_saved = x_render + space_breads/2; |
1515 | - |
1516 | - cr.restore (); |
1517 | - /* Draw frame: it must be done at the end to be on the background drawn by pressed breadcrumbs */ |
1518 | - |
1519 | - if (focus) { |
1520 | - cr.save (); |
1521 | - x_render -= height_marged/2 + 3; |
1522 | - |
1523 | - cr.move_to (0, 0); |
1524 | - cr.line_to (0, y + height_marged + 3); |
1525 | - cr.line_to (x_render, y + height_marged + 3); |
1526 | - cr.line_to (x_render, y + height_marged); |
1527 | - cr.line_to (x_render + height_marged / 2, y + height_marged / 2); |
1528 | - cr.line_to (x_render, y); |
1529 | - cr.line_to (x_render, 0); |
1530 | - cr.close_path (); |
1531 | - cr.clip (); |
1532 | - button_context.render_frame (cr, 0, margin, width, height_marged); |
1533 | - cr.restore (); |
1534 | - cr.save (); |
1535 | - |
1536 | - cr.move_to (x_render, get_allocated_height()); |
1537 | - cr.line_to (x_render, y + height_marged); |
1538 | - cr.line_to (x_render + height_marged / 2, y + height_marged / 2); |
1539 | - cr.line_to (x_render, y); |
1540 | - cr.line_to (x_render, 0); |
1541 | - cr.line_to (get_allocated_width(), 0); |
1542 | - cr.line_to (get_allocated_width(), y + height_marged); |
1543 | - cr.close_path (); |
1544 | - cr.clip (); |
1545 | - button_context_active.render_background (cr, 0, margin, width, height_marged); |
1546 | - button_context_active.render_frame (cr, 0, margin, width, height_marged); |
1547 | - cr.restore (); |
1548 | - x_render += height_marged / 2 + 3; |
1549 | + /* Draw the old breadcrumbs, only for the animations */ |
1550 | + if (view_old) |
1551 | + foreach (BreadcrumbsElement element in newbreads) |
1552 | + if (element.display) |
1553 | + x_render = element.draw(cr, x_render, margin, height_marged, button_context, this); |
1554 | + |
1555 | + cr.restore (); |
1556 | } else { |
1557 | - button_context.render_frame (cr, 0, margin, width, height_marged); |
1558 | + if (text_completion != "") { |
1559 | + int layout_width, layout_height; |
1560 | + double text_width, text_height; |
1561 | + |
1562 | + cr.set_source_rgba (0, 0, 0, 0.4); |
1563 | + Pango.Layout layout = create_pango_layout (text); |
1564 | + layout.get_size (out layout_width, out layout_height); |
1565 | + text_width = Pango.units_to_double (layout_width); |
1566 | + text_height = Pango.units_to_double (layout_height); |
1567 | + cr.move_to (text_width + 4, text_height / 4); |
1568 | + layout.set_text (text_completion, -1); |
1569 | + Pango.cairo_show_layout (cr, layout); |
1570 | + } |
1571 | } |
1572 | - |
1573 | - entry.draw (cr, x_render + space_breads / 2, height, width - x_render, this, button_context); |
1574 | + |
1575 | return true; |
1576 | } |
1577 | |
1578 | @@ -977,4 +829,4 @@ |
1579 | var file = File.new_for_commandline_arg (newpath); |
1580 | return file.get_parent () != null; |
1581 | } |
1582 | -} |
1583 | +} |
1584 | \ No newline at end of file |
1585 | |
1586 | === removed directory 'libwidgets/tests' |
1587 | === removed file 'libwidgets/tests/CMakeLists.txt' |
1588 | --- libwidgets/tests/CMakeLists.txt 2014-01-24 18:36:41 +0000 |
1589 | +++ libwidgets/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1590 | @@ -1,27 +0,0 @@ |
1591 | -include_directories(../../libcore/) |
1592 | -find_package(Vala REQUIRED) |
1593 | -include(ValaPrecompile) |
1594 | -vala_precompile(VALA_C widgets-test |
1595 | - tests-pathbar.vala |
1596 | -PACKAGES |
1597 | - gtk+-3.0 |
1598 | - gee-0.8 |
1599 | - posix |
1600 | - marlinwidgets |
1601 | -OPTIONS |
1602 | - --thread |
1603 | - --vapidir=${CMAKE_BINARY_DIR}/libwidgets/ |
1604 | - --vapidir=${CMAKE_BINARY_DIR}/libcore/ |
1605 | -) |
1606 | - |
1607 | -find_package(PkgConfig) |
1608 | -pkg_check_modules(DEPS REQUIRED gtk+-3.0 gee-0.8) |
1609 | - |
1610 | -add_definitions(${DEPS_CFLAGS} ${DEPS_CFLAGS_OTHER}) |
1611 | - |
1612 | -add_executable(widgets-test ${VALA_C}) |
1613 | - |
1614 | -link_directories(${DEPS_LIBRARY_DIRS}) |
1615 | -target_link_libraries(widgets-test marlinwidgets ${DEPS_LIBRARIES}) |
1616 | -add_dependencies(widgets-test marlinwidgets) |
1617 | -add_test_executable(widgets-test) |
1618 | \ No newline at end of file |
1619 | |
1620 | === removed file 'libwidgets/tests/tests-pathbar.vala' |
1621 | --- libwidgets/tests/tests-pathbar.vala 2014-03-27 10:39:12 +0000 |
1622 | +++ libwidgets/tests/tests-pathbar.vala 1970-01-01 00:00:00 +0000 |
1623 | @@ -1,213 +0,0 @@ |
1624 | -/* |
1625 | - * Copyright (c) 2011 Lucas Baudin <xapantu@gmail.com> |
1626 | - * |
1627 | - * Marlin is free software; you can redistribute it and/or |
1628 | - * modify it under the terms of the GNU General Public License as |
1629 | - * published by the Free Software Foundation; either version 2 of the |
1630 | - * License, or (at your option) any later version. |
1631 | - * |
1632 | - * Marlin is distributed in the hope that it will be useful, |
1633 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1634 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1635 | - * General Public License for more details. |
1636 | - * |
1637 | - * You should have received a copy of the GNU General Public |
1638 | - * License along with this program; see the file COPYING. If not, |
1639 | - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
1640 | - * Boston, MA 02111-1307, USA. |
1641 | - * |
1642 | - */ |
1643 | - |
1644 | -public class Breadcrumbs : Marlin.View.Chrome.BasePathBar { |
1645 | - public Breadcrumbs (Gtk.UIManager ui) {} |
1646 | - public override void load_right_click_menu (double x, double y) {} |
1647 | - protected override void on_file_droped (List<GLib.File> uris, GLib.File target_file, Gdk.DragAction real_action) {} |
1648 | -} |
1649 | - |
1650 | -void add_pathbar_tests () { |
1651 | -/* TODO: they are broken currently */ |
1652 | - Test.add_func ("/marlin/pathbar/general", () => { |
1653 | - Test.log_set_fatal_handler (() => { return false; }); |
1654 | - var breads = new Breadcrumbs (new Gtk.UIManager ()); |
1655 | - var bread_entry = new Marlin.View.Chrome.BreadcrumbsEntry (); |
1656 | - assert (bread_entry is Marlin.View.Chrome.BreadcrumbsEntry); |
1657 | - assert (bread_entry.text == ""); |
1658 | -#if VALA_0_24 |
1659 | - Gdk.Event e = new Gdk.Event (Gdk.EventType.KEY_PRESS); |
1660 | - Gdk.EventKey event = e.key; |
1661 | -#else |
1662 | - Gdk.EventKey event = Gdk.EventKey (); |
1663 | -#endif |
1664 | - event.window = breads.get_window (); |
1665 | - event.keyval = 0x061; /* a */ |
1666 | - bread_entry.key_press_event (event); |
1667 | - assert (bread_entry.text == "a"); |
1668 | - bread_entry.key_press_event (event); |
1669 | - assert (bread_entry.text == "aa"); |
1670 | - assert (bread_entry.cursor == 2); |
1671 | - |
1672 | - event.keyval = 0xff51; /* left */ |
1673 | - bread_entry.key_press_event (event); |
1674 | - assert (bread_entry.cursor == 1); |
1675 | - bread_entry.key_press_event (event); |
1676 | - assert (bread_entry.cursor == 0); |
1677 | - bread_entry.key_press_event (event); |
1678 | - assert (bread_entry.cursor == 0); |
1679 | - event.keyval = 0xff53; /* right */ |
1680 | - bread_entry.key_press_event (event); |
1681 | - assert (bread_entry.cursor == 1); |
1682 | - |
1683 | - /* insertion in the middle */ |
1684 | - event.keyval = 0x062; /* b */ |
1685 | - bread_entry.key_press_event (event); |
1686 | - assert (bread_entry.text == "aba"); |
1687 | - event.keyval = 0x063; /* c */ |
1688 | - bread_entry.key_press_event (event); |
1689 | - assert (bread_entry.text == "abca"); |
1690 | - |
1691 | - /* insertion at the end */ |
1692 | - assert (bread_entry.cursor == 3); |
1693 | - event.keyval = 0xff53; /* right */ |
1694 | - bread_entry.key_press_event (event); |
1695 | - assert (bread_entry.cursor == 4); |
1696 | - event.keyval = 0x064; /* d */ |
1697 | - bread_entry.key_press_event (event); |
1698 | - assert (bread_entry.text == "abcad"); |
1699 | - |
1700 | - /* backspace */ |
1701 | - event.keyval = 0xff08; /* backspace */ |
1702 | - bread_entry.key_press_event (event); |
1703 | - assert (bread_entry.text == "abca"); |
1704 | - event.keyval = 0xff51; /* left */ |
1705 | - bread_entry.key_press_event (event); |
1706 | - event.keyval = 0xff08; /* backspace */ |
1707 | - bread_entry.key_press_event (event); |
1708 | - assert (bread_entry.text == "aba"); |
1709 | - event.keyval = 0xff53; /* right */ |
1710 | - bread_entry.key_press_event (event); |
1711 | - event.keyval = 0x063; /* c */ |
1712 | - bread_entry.key_press_event (event); |
1713 | - assert (bread_entry.text == "abac"); |
1714 | - event.keyval = 0xff51; /* left */ |
1715 | - bread_entry.key_press_event (event); |
1716 | - |
1717 | - /* selection */ |
1718 | - event.state = Gdk.ModifierType.SHIFT_MASK; |
1719 | - event.keyval = 0xff51; /* left */ |
1720 | - bread_entry.key_press_event (event); |
1721 | - assert (bread_entry.get_selection () == "a"); |
1722 | - bread_entry.key_press_event (event); |
1723 | - assert (bread_entry.get_selection () == "ba"); |
1724 | - bread_entry.key_press_event (event); |
1725 | - assert (bread_entry.get_selection () == "aba"); |
1726 | - bread_entry.key_press_event (event); |
1727 | - assert (bread_entry.get_selection () == "aba"); |
1728 | - event.keyval = 0xff53; /* right */ |
1729 | - bread_entry.key_press_event (event); |
1730 | - assert (bread_entry.get_selection () == "ba"); |
1731 | - bread_entry.key_press_event (event); |
1732 | - assert (bread_entry.get_selection () == "a"); |
1733 | - bread_entry.key_press_event (event); |
1734 | - assert (bread_entry.get_selection () == ""); |
1735 | - bread_entry.key_press_event (event); |
1736 | - assert (bread_entry.get_selection () == "c"); |
1737 | - event.keyval = 0xff51; /* left */ |
1738 | - bread_entry.key_press_event (event); |
1739 | - assert (bread_entry.get_selection () == ""); |
1740 | - }); |
1741 | - |
1742 | - Test.add_func ("/marlin/pathbar/start-selection", () => { |
1743 | - Test.log_set_fatal_handler (() => { return false; }); |
1744 | - var breads = new Breadcrumbs (new Gtk.UIManager ()); |
1745 | - var bread_entry = new Marlin.View.Chrome.BreadcrumbsEntry (); |
1746 | - assert (bread_entry is Marlin.View.Chrome.BreadcrumbsEntry); |
1747 | - assert (bread_entry.text == ""); |
1748 | - bread_entry.text = "abdcefghij/"; |
1749 | - bread_entry.cursor = ("abdcefghij/").length; |
1750 | -#if VALA_0_24 |
1751 | - Gdk.Event e = new Gdk.Event (Gdk.EventType.KEY_PRESS); |
1752 | - Gdk.EventKey event = e.key; |
1753 | -#else |
1754 | - Gdk.EventKey event = Gdk.EventKey (); |
1755 | -#endif |
1756 | - event.state = Gdk.ModifierType.SHIFT_MASK; |
1757 | - event.window = breads.get_window (); |
1758 | - event.keyval = 0xff51; /* left */ |
1759 | - bread_entry.key_press_event (event); |
1760 | - assert (bread_entry.get_selection () == "/"); |
1761 | - }); |
1762 | - |
1763 | - Test.add_func ("/marlin/pathbar/backspace-without-text", () => { |
1764 | - Test.log_set_fatal_handler (() => { return false; }); |
1765 | - var breads = new Breadcrumbs (new Gtk.UIManager ()); |
1766 | - var bread_entry = new Marlin.View.Chrome.BreadcrumbsEntry (); |
1767 | - assert (bread_entry is Marlin.View.Chrome.BreadcrumbsEntry); |
1768 | - assert (bread_entry.text == ""); |
1769 | -#if VALA_0_24 |
1770 | - Gdk.Event e = new Gdk.Event (Gdk.EventType.KEY_PRESS); |
1771 | - Gdk.EventKey event = e.key; |
1772 | -#else |
1773 | - Gdk.EventKey event = Gdk.EventKey (); |
1774 | -#endif |
1775 | - event.window = breads.get_window (); |
1776 | - event.keyval = 0xff08; /* backspace */ |
1777 | - bread_entry.key_press_event (event); |
1778 | - assert (bread_entry.get_selection () == null); |
1779 | - }); |
1780 | - |
1781 | - Test.add_func ("/marlin/pathbar/backspace-keypress", () => { |
1782 | - Test.log_set_fatal_handler (() => { return false; }); |
1783 | - var breads = new Breadcrumbs (new Gtk.UIManager()); |
1784 | - Marlin.View.Chrome.BreadcrumbsEntry bread_entry = breads.entry; |
1785 | - assert (bread_entry is Marlin.View.Chrome.BreadcrumbsEntry); |
1786 | - assert (bread_entry.text == ""); |
1787 | -#if VALA_0_24 |
1788 | - Gdk.Event e = new Gdk.Event (Gdk.EventType.KEY_PRESS); |
1789 | - Gdk.EventKey event = e.key; |
1790 | -#else |
1791 | - Gdk.EventKey event = Gdk.EventKey (); |
1792 | -#endif |
1793 | - event.window = breads.get_window (); |
1794 | - event.keyval = 0xff08; /* backspace */ |
1795 | - breads.key_press_event (event); |
1796 | - assert (bread_entry.get_selection () == null); |
1797 | - |
1798 | - event.keyval = 0x061; /* a */ |
1799 | - breads.change_breadcrumbs (""); |
1800 | - breads.change_breadcrumbs ("/"); |
1801 | - }); |
1802 | - |
1803 | - Test.add_func ("/marlin/pathbar/go-to-trash", () => { |
1804 | - Test.log_set_fatal_handler (() => { return false; }); |
1805 | - var breads = new Breadcrumbs (new Gtk.UIManager ()); |
1806 | - breads.change_breadcrumbs ("trash:///"); |
1807 | - breads.change_breadcrumbs ("/home/there"); |
1808 | - }); |
1809 | - |
1810 | - Test.add_func ("/marlin/pathbar/non-file-system-location", () => { |
1811 | - Test.log_set_fatal_handler (() => { return false; }); |
1812 | - var breads = new Breadcrumbs (new Gtk.UIManager ()); |
1813 | - breads.change_breadcrumbs ("trash://"); |
1814 | - breads.change_breadcrumbs ("trash://dir/folder/"); |
1815 | - assert (breads.get_elements_path () == "trash://dir/folder/"); |
1816 | - breads.change_breadcrumbs ("ftp://test@test.com/dir/folder/"); |
1817 | - assert (breads.get_elements_path () == "ftp://test@test.com/dir/folder/"); |
1818 | - }); |
1819 | - |
1820 | -} |
1821 | - |
1822 | -void main (string[] args) { |
1823 | - Gtk.init (ref args); |
1824 | - Test.init (ref args); |
1825 | - //Preferences.settings = new GLib.Settings("org.gnome.marlin.preferences"); |
1826 | - |
1827 | - add_pathbar_tests (); |
1828 | - |
1829 | - Idle.add (() => { |
1830 | - Test.run (); |
1831 | - Gtk.main_quit (); |
1832 | - return false; |
1833 | - } |
1834 | - ); |
1835 | - Gtk.main (); |
1836 | -} |
1837 | |
1838 | === modified file 'src/View/Chrome/TopMenu.vala' |
1839 | --- src/View/Chrome/TopMenu.vala 2014-04-25 00:31:37 +0000 |
1840 | +++ src/View/Chrome/TopMenu.vala 2014-06-14 19:18:12 +0000 |
1841 | @@ -62,18 +62,15 @@ |
1842 | win.current_tab.slot.view_box.grab_focus (); |
1843 | }); |
1844 | |
1845 | - location_bar.activate.connect (() => { |
1846 | - File file = File.new_for_commandline_arg (GLib.Uri.escape_string ( |
1847 | - location_bar.path, |
1848 | - (GLib.Uri.RESERVED_CHARS_GENERIC_DELIMITERS + GLib.Uri.RESERVED_CHARS_SUBCOMPONENT_DELIMITERS).replace("#", ""), |
1849 | - false)); |
1850 | + location_bar.activate.connect ((file) => { |
1851 | win.current_tab.path_changed (file); |
1852 | }); |
1853 | |
1854 | - location_bar.activate_alternate.connect ((a) => { |
1855 | - win.add_tab (File.new_for_commandline_arg (a)); |
1856 | + location_bar.activate_alternate.connect ((file) => { |
1857 | + win.add_tab (file); |
1858 | }); |
1859 | |
1860 | + |
1861 | location_bar.show_all (); |
1862 | view_switcher.margin_right = 20; |
1863 | pack_start (location_bar); |
1864 | @@ -81,4 +78,4 @@ |
1865 | show (); |
1866 | } |
1867 | } |
1868 | -} |
1869 | +} |
1870 | \ No newline at end of file |
1871 | |
1872 | === modified file 'src/View/LocationBar.vala' |
1873 | --- src/View/LocationBar.vala 2014-05-13 21:10:32 +0000 |
1874 | +++ src/View/LocationBar.vala 2014-06-14 19:18:12 +0000 |
1875 | @@ -27,10 +27,10 @@ |
1876 | private string _path; |
1877 | public new string path { |
1878 | set { |
1879 | - var new_path = value; |
1880 | + var new_path = GLib.Uri.unescape_string (value); |
1881 | _path = new_path; |
1882 | - if (!bread.focus && !win.freeze_view_changes) { |
1883 | - bread.entry.reset (); |
1884 | + if (!bread.is_focus && !win.freeze_view_changes) { |
1885 | + bread.text = ""; |
1886 | bread.change_breadcrumbs (new_path); |
1887 | } |
1888 | } |
1889 | @@ -41,8 +41,8 @@ |
1890 | |
1891 | Marlin.View.Window win; |
1892 | |
1893 | - public new signal void activate (); |
1894 | - public signal void activate_alternate (string path); |
1895 | + public new signal void activate (GLib.File file); |
1896 | + public signal void activate_alternate (GLib.File file); |
1897 | public signal void escape (); |
1898 | |
1899 | public override void get_preferred_width (out int minimum_width, out int natural_width) { |
1900 | @@ -55,8 +55,8 @@ |
1901 | bread = new Breadcrumbs (ui, win); |
1902 | bread.escape.connect (() => { escape(); }); |
1903 | |
1904 | - bread.changed.connect (on_bread_changed); |
1905 | - bread.activate_alternate.connect ((a) => { activate_alternate(a); }); |
1906 | + bread.path_changed.connect (on_path_changed); |
1907 | + bread.activate_alternate.connect ((file) => { activate_alternate(file); }); |
1908 | |
1909 | margin_top = 4; |
1910 | margin_bottom = 4; |
1911 | @@ -65,7 +65,7 @@ |
1912 | pack_start (bread, true, true, 0); |
1913 | } |
1914 | |
1915 | - private void on_bread_changed (string changed) { |
1916 | + private void on_path_changed (File file) { |
1917 | if (win.freeze_view_changes) |
1918 | return; |
1919 | |
1920 | @@ -75,14 +75,7 @@ |
1921 | else |
1922 | win.current_tab.slot.view_box.grab_focus (); |
1923 | |
1924 | - //_path = changed; |
1925 | - path = changed; |
1926 | - activate(); |
1927 | - |
1928 | - /* This prevents that the location bar is left in a weird state |
1929 | - * when going from a non-existent folder to another one underneath. */ |
1930 | - bread.entry.reset (); |
1931 | - bread.change_breadcrumbs (changed); |
1932 | + activate(file); |
1933 | } |
1934 | } |
1935 | |
1936 | @@ -98,7 +91,7 @@ |
1937 | |
1938 | /* Used for the context menu we show when there is a right click */ |
1939 | GOF.Directory.Async files_menu = null; |
1940 | - |
1941 | + |
1942 | bool autocompleted = false; |
1943 | |
1944 | Marlin.View.Window win; |
1945 | @@ -190,35 +183,45 @@ |
1946 | IconDirectory icon = {"/", Marlin.ICON_FILESYSTEM_SYMBOLIC, false, null, null, null, false, null}; |
1947 | icon.exploded = {"/"}; |
1948 | add_icon (icon); |
1949 | + |
1950 | + up.connect (() => { |
1951 | + File file = get_file_for_path (text); |
1952 | + File parent = file.get_parent (); |
1953 | + |
1954 | + if (parent != null && file.get_uri () != parent.get_uri ()) |
1955 | + change_breadcrumbs (parent.get_uri ()); |
1956 | + |
1957 | + win.current_tab.up (); |
1958 | + grab_focus (); |
1959 | + }); |
1960 | |
1961 | - entry.down.connect (() => { |
1962 | - /* focus back the view */ |
1963 | + down.connect (() => { |
1964 | + // focus back the view |
1965 | if (win.current_tab.content_shown) |
1966 | win.current_tab.content.grab_focus (); |
1967 | else |
1968 | win.current_tab.slot.view_box.grab_focus (); |
1969 | }); |
1970 | |
1971 | - entry.completed.connect (() => { |
1972 | - string path = get_elements_path (); |
1973 | - update_breadcrumbs (entry.text, path); |
1974 | + completed.connect (() => { |
1975 | + string path = ""; |
1976 | + string newpath = update_breadcrumbs (get_file_for_path (text).get_uri (), path); |
1977 | + |
1978 | + foreach (BreadcrumbsElement element in elements) { |
1979 | + if (!element.hidden) |
1980 | + path += element.text + "/"; |
1981 | + } |
1982 | + |
1983 | + if (path != newpath) |
1984 | + change_breadcrumbs (newpath); |
1985 | + |
1986 | grab_focus (); |
1987 | }); |
1988 | - |
1989 | - menu = new Gtk.Menu (); |
1990 | - menu.show_all (); |
1991 | - |
1992 | + |
1993 | need_completion.connect (on_need_completion); |
1994 | - } |
1995 | - |
1996 | - protected void merge_in_clipboard_actions () { |
1997 | - ui.insert_action_group (clipboard_actions, 0); |
1998 | - ui.ensure_update (); |
1999 | - } |
2000 | - |
2001 | - protected void merge_out_clipboard_actions () { |
2002 | - ui.remove_action_group (clipboard_actions); |
2003 | - ui.ensure_update (); |
2004 | + |
2005 | + menu = new Gtk.Menu (); |
2006 | + menu.show_all (); |
2007 | } |
2008 | |
2009 | /** |
2010 | @@ -230,56 +233,59 @@ |
2011 | * |
2012 | **/ |
2013 | private void on_file_loaded(GOF.File file) { |
2014 | - if(file.is_folder () && file.get_display_name ().length > to_search.length) { |
2015 | - if (file.get_display_name ().ascii_ncasecmp (to_search, to_search.length) == 0) { |
2016 | + string file_display_name = GLib.Uri.unescape_string (file.get_display_name ()); |
2017 | + if(file.is_folder () && file_display_name.length > to_search.length) { |
2018 | + if (file_display_name.ascii_ncasecmp (to_search, to_search.length) == 0) { |
2019 | if (!autocompleted) { |
2020 | - entry.completion = file.get_display_name ().slice (to_search.length, file.get_display_name ().length); |
2021 | + text_completion = file_display_name.slice (to_search.length, file_display_name.length); |
2022 | autocompleted = true; |
2023 | } else { |
2024 | - string file_complet = file.get_display_name ().slice (to_search.length, file.get_display_name ().length); |
2025 | + string file_complet = file_display_name.slice (to_search.length, file_display_name.length); |
2026 | string to_add = ""; |
2027 | - for (int i = 0; i < (entry.completion.length > file_complet.length ? file_complet.length : entry.completion.length); i++) { |
2028 | - if (entry.completion[i] == file_complet[i]) |
2029 | - to_add += entry.completion[i].to_string (); |
2030 | + for (int i = 0; i < (text_completion.length > file_complet.length ? file_complet.length : text_completion.length); i++) { |
2031 | + if (text_completion[i] == file_complet[i]) |
2032 | + to_add += text_completion[i].to_string (); |
2033 | else |
2034 | break; |
2035 | } |
2036 | - entry.completion = to_add; |
2037 | + text_completion = to_add; |
2038 | + multiple_completions = true; |
2039 | } |
2040 | + |
2041 | /* autocompletion is case insensitive so we have to change the first completed |
2042 | * parts: the entry.text. |
2043 | */ |
2044 | - string str = entry.text.slice (0, entry.text.length - to_search.length); |
2045 | - if (str == null) |
2046 | - str = ""; |
2047 | - entry.text = str + file.get_display_name ().slice (0, to_search.length); |
2048 | + string str = text.slice (0, text.length - to_search.length); |
2049 | + if (str != null && !multiple_completions) { |
2050 | + text = str + file.get_display_name ().slice (0, to_search.length); |
2051 | + set_position (-1); |
2052 | + } |
2053 | } |
2054 | } |
2055 | } |
2056 | |
2057 | public void on_need_completion () { |
2058 | - to_search = ""; |
2059 | - string path = get_elements_path (); |
2060 | - string[] stext = entry.text.split ("/"); |
2061 | - int stext_len = stext.length; |
2062 | - if (stext_len > 0) |
2063 | - to_search = stext[stext.length -1]; |
2064 | + File file = get_file_for_path (text); |
2065 | + to_search = file.get_basename (); |
2066 | |
2067 | - entry.completion = ""; |
2068 | autocompleted = false; |
2069 | - |
2070 | - path += entry.text; |
2071 | - message ("path %s to_search %s", path, to_search); |
2072 | - if (to_search != "") |
2073 | - path = Marlin.Utils.get_parent (path); |
2074 | - |
2075 | - if (path != null && path.length > 0) { |
2076 | - var directory = File.new_for_uri (path); |
2077 | - files = GOF.Directory.Async.from_gfile (directory); |
2078 | - if (files.file.exists) { |
2079 | + multiple_completions = false; |
2080 | + |
2081 | + if (to_search != "" && file.has_parent (null)) |
2082 | + file = file.get_parent (); |
2083 | + else |
2084 | + return; |
2085 | + |
2086 | + var directory = file; |
2087 | + var files_cache = files; |
2088 | + |
2089 | + files = GOF.Directory.Async.from_gfile (directory); |
2090 | + if (files.file.exists) { |
2091 | + /* Verify that we got a new instance of files so we do not double up events */ |
2092 | + if (files_cache != files) |
2093 | files.file_loaded.connect (on_file_loaded); |
2094 | - files.load (); |
2095 | - } |
2096 | + |
2097 | + files.load (); |
2098 | } |
2099 | } |
2100 | |
2101 | @@ -363,7 +369,7 @@ |
2102 | file.launch (win.get_screen (), app); |
2103 | } |
2104 | |
2105 | - protected override void on_file_droped (List<GLib.File> uris, GLib.File target_file, Gdk.DragAction real_action) { |
2106 | + protected override void on_file_dropped (List<GLib.File> uris, GLib.File target_file, Gdk.DragAction real_action) { |
2107 | Marlin.FileOperations.copy_move(uris, null, target_file, real_action); |
2108 | } |
2109 | |
2110 | @@ -376,18 +382,6 @@ |
2111 | return strloc; |
2112 | } |
2113 | |
2114 | - public override bool focus_out_event (Gdk.EventFocus event) { |
2115 | - base.focus_out_event (event); |
2116 | - merge_out_clipboard_actions (); |
2117 | - return true; |
2118 | - } |
2119 | - |
2120 | - public override bool focus_in_event (Gdk.EventFocus event) { |
2121 | - base.focus_in_event (event); |
2122 | - merge_in_clipboard_actions (); |
2123 | - return true; |
2124 | - } |
2125 | - |
2126 | private void get_menu_position (Gtk.Menu menu, out int x, out int y, out bool push_in) { |
2127 | x = (int) menu_x_root; |
2128 | y = (int) menu_y_root; |
2129 | @@ -414,4 +408,4 @@ |
2130 | Gtk.get_current_event_time ()); |
2131 | } |
2132 | } |
2133 | -} |
2134 | +} |
2135 | \ No newline at end of file |
2136 | |
2137 | === modified file 'src/View/ViewContainer.vala' |
2138 | --- src/View/ViewContainer.vala 2014-04-30 09:13:59 +0000 |
2139 | +++ src/View/ViewContainer.vala 2014-06-14 19:18:12 +0000 |
2140 | @@ -75,8 +75,7 @@ |
2141 | |
2142 | path_changed.connect ((myfile) => { |
2143 | /* location didn't change, do nothing */ |
2144 | - if (slot != null && myfile != null && slot.directory.file.exists |
2145 | - && slot.location.equal (myfile)) |
2146 | + if (slot != null && myfile != null && slot.directory.file.exists && slot.location.equal (myfile)) |
2147 | return; |
2148 | |
2149 | change_view(view_mode, myfile); |
2150 | @@ -322,7 +321,7 @@ |
2151 | // You see if I would just use back(n) the reference to n would be passed |
2152 | // in the clusure, restulting in a value of n which would always be n=1. So |
2153 | // by introducting a new variable I can bypass this anoyance. |
2154 | - var item = new Gtk.MenuItem.with_label (path); |
2155 | + var item = new Gtk.MenuItem.with_label (GLib.Uri.unescape_string (path)); |
2156 | item.activate.connect (() => { back(cn); }); |
2157 | back_menu.insert (item, -1); |
2158 | } |
2159 | @@ -338,7 +337,7 @@ |
2160 | var n = 1; |
2161 | foreach (var path in list) { |
2162 | int cn = n++; // For explenation look up |
2163 | - var item = new Gtk.MenuItem.with_label (path); |
2164 | + var item = new Gtk.MenuItem.with_label (GLib.Uri.unescape_string (path)); |
2165 | item.activate.connect (() => forward (cn)); |
2166 | forward_menu.insert (item, -1); |
2167 | } |
2168 | @@ -353,4 +352,4 @@ |
2169 | } |
2170 | |
2171 | } |
2172 | -} |
2173 | +} |
2174 | \ No newline at end of file |
2175 | |
2176 | === modified file 'src/View/Window.vala' |
2177 | --- src/View/Window.vala 2014-06-06 18:51:14 +0000 |
2178 | +++ src/View/Window.vala 2014-06-14 19:18:12 +0000 |
2179 | @@ -243,6 +243,12 @@ |
2180 | /*/ |
2181 | /* Connect and abstract signals to local ones |
2182 | /*/ |
2183 | + key_press_event.connect ((event) => { |
2184 | + if (top_menu.location_bar.bread.is_focus) |
2185 | + return top_menu.location_bar.bread.key_press_event (event); |
2186 | + |
2187 | + return false; |
2188 | + }); |
2189 | |
2190 | window_state_event.connect ((event) => { |
2191 | if ((bool) event.changed_mask & Gdk.WindowState.MAXIMIZED) { |
2192 | @@ -861,4 +867,4 @@ |
2193 | |
2194 | }; |
2195 | } |
2196 | -} |
2197 | +} |
2198 | \ No newline at end of file |
This branch basically works but there a few problems:
1) Upon entering edit mode, the cursor appears at the end of the existing path and the path does not appear selected. If you start typing, the whole of the path disappears. If what is typed next does not begin with "/" then it is appended to the path that disappeared, otherwise a new path is created. This could be confusing to an inexperienced user. I suggest that the current path continue to be displayed and what is typed is appended to it, unless the first character typed is "Delete". This is what you would expect to happen based on the position of the cursor. Need UI team input on this.
2) After entering edit mode, press BackSpace to delete the existing path, then Enter. The path bar shows the root directory but a completely different folder is loaded.
3) Line 50: Remove or change to "debug ( .. )" debugging message that does not indicate an error.
Line 78: Remove commented out code.
Line 248, 261, 274: Recommended comment style is /* .... */