Merge lp:~niclasl/pantheon-terminal/fix-1218681 into lp:~elementary-apps/pantheon-terminal/trunk

Proposed by Niclas Lockner
Status: Merged
Approved by: David Gomes
Approved revision: 545
Merged at revision: 544
Proposed branch: lp:~niclasl/pantheon-terminal/fix-1218681
Merge into: lp:~elementary-apps/pantheon-terminal/trunk
Diff against target: 172 lines (+74/-10)
1 file modified
src/PantheonTerminalWindow.vala (+74/-10)
To merge this branch: bzr merge lp:~niclasl/pantheon-terminal/fix-1218681
Reviewer Review Type Date Requested Status
David Gomes (community) Needs Fixing
Niclas Lockner (community) Approve
Julián Unrrein (community) Needs Fixing
Review via email: mp+184483@code.launchpad.net

Commit message

Implemented support for restoring tabs.
Fixes bug #1218681.

Description of the change

DynamicNotebook: Fixed support for restoring closed tabs #1218681.

To post a comment you must log in.
Revision history for this message
Julián Unrrein (junrrein) wrote :

Code style:

Newline after diff line 29.

Testing:

1) Have at least two tabs opened in a Terminal window.
2) Drag one of them to form a new window.
3) Open at least one other tab in this new window (so that the dragged tab isn't the only one).
4) Close the dragged tab.

The closed dragged tab goes to the trash bin in the original window, instead of the new one as it should be.

Note that the new window may close itself when closing the dragged tab (this is known to happen in trunk).

review: Needs Fixing
Revision history for this message
David Gomes (davidgomes) wrote :

>tab.restore_data = t.get_shell_location ();

If you have two tabs in the same location won't that mess it up? Shouldn't restore_data be an unique ID? I'm never sure what restore_data is for.

In any case, remove the trailing whitespace you added.

review: Needs Fixing
Revision history for this message
Niclas Lockner (niclasl) wrote :

I'm not by my computer at the moment, but if I remember it correctly, the tab is only added to the ClosedTabs object in the DynamicNotebook object if it has a unique restore_data. At the moment, two closed tabs with the same location leads to only one of the tabs being added to the list of restorable tabs. This is the intended behavior, right?

And it seems that restore_data is intended to be whatever helps restoring the tab. I looked at how it was done in Files, and I think it also used the tab's current location.

Have you had time to speak to voldyman about the signals?

Revision history for this message
Julien Spautz (julien-spautz) wrote :

As Niclas said, the restore_data has to be unique.

There is no real limitation as to what you put into restore_data. In most cases a file path or URL/URI is the easiest thing to do, but it could also be some sort of ID.

See the original blueprint: https://blueprints.launchpad.net/granite/+spec/restoring-closed-tabs

I haven't tested this branch, but I assume restoring a tab will only restore the location without the content, i.e. previously entered commands. I think it would be better to restore the content too. For this, you could store the terminal widgets in a hashmap, mapped to some ID, which you'll assign to restore_data.

I hope that clears things up a little.

Revision history for this message
Niclas Lockner (niclasl) wrote :

I agree that saving the actual terminal and not only the directory would be more useful, but if I'm going to save the TerminalWidget object when a tab is closed, shouldn't there be a maximum number of stored objects, e.g. 10 last closed? If not, then hidden terminal widgets and bash processes would keep accumulating while the window is kept open. If it is limited, the same limit would need to be added to DynamicNotebook so that it doesn't store unlimited number of tabs.

The problem with the tab that gets added to the first window's trash bin even though it was closed in the second window seems to be related to DynamicNotebook as well. When the tab is moved from window A to window B, the signal handlers that the notebook in window A added to the tab are kept alongside the new handlers added by the notebook in window B. When the tab is closed, the connected signals from both window A and B are activated and therefore the tab ends up in window A's trash bin.

So how should I proceed with this? It looks like this cannot be fixed (in a nice way) without also touching DynamicNotebook.

Revision history for this message
Julien Spautz (julien-spautz) wrote :

The tab ending up in the wrong window is a granite bug that you cannot fix here.

I'm not sure if the maximum amount of closed tabs should be hardcoded or be allowed to vary across apps, that needs some discussion, but there should definitely be a limit.

I don't know Terminal's code very well, but is there a way to store the content of a terminal widget more efficiently? Assuming we don't allow closing tabs with background processes, it would be sufficient to save the text only, which might be a more viable solution, if storing the terminal widgets themselves takes up too much memory.

Revision history for this message
Niclas Lockner (niclasl) wrote :

I'll file a separate bug report for granite then.

I cannot find anything in the API that suggests that it's possible to insert a text to a Vte.Terminal object, which makes sense I guess. And even though it would be possible, the terminal widget is not completely restored anyway since the command history probably wont be restored (it is the bash process that holds that information, right?)

Revision history for this message
Niclas Lockner (niclasl) wrote :

Okay, so my plan to solve this is as follows.

I create my own branch of Granite, where I
* solve the signal issue
* implement an upper limit (changeable with a setter) of the number of restorable tabs
* implement a new signal on the tab that gets activated when DynamicNotebook removes the tab from the set of restorable tabs (due to the limit being exceeded, or "Clear All" being pressed)

In this pantheon-terminal branch, I will
* store closed terminal widgets in a hashmap, as Julien suggests.
* let restored tabs reuse the existing terminal widget
* connect a callback to the "tab no longer restorable" signal that removes the terminal widget from the hashmap.

The Granite branch is just so that I have something to work with in pantheon-terminal, but if you like the changes you can use them as you like.

Does this sound good?

Revision history for this message
Julián Unrrein (junrrein) wrote :

Hi Niclas,

Your plan sounds perfectly reasonable, you can go with it.

However, the Granite branch will need to get merged for your Terminal branch to get merged too.

Revision history for this message
David Gomes (davidgomes) wrote :

Remove the extra trailing whitepsace you added.

review: Needs Fixing
Revision history for this message
David Gomes (davidgomes) :
review: Approve
521. By Launchpad Translations on behalf of elementary-apps

Launchpad automatic translations update.

522. By Cody Garver

Silence C compilation warnings.

523. By David Gomes

Got rid of all the using statements.

524. By Rui Gomes

Minor fixes to the INSTALL file.

525. By Jeremy Wootten

Fixed bug #1208140, moved -d to -w, steps necessary taken to fix #1033456 in Files.

526. By Cody Garver

Release 0.2.4

527. By Sergey "Shnatsel" Davidoff

Use proper NoDisplay value to stop "Open Terminal Here" from showing up in Slingshot and other application launchers.

528. By Cody Garver

Release 0.2.4.1

529. By Launchpad Translations on behalf of elementary-apps

Launchpad automatic translations update.

530. By Launchpad Translations on behalf of elementary-apps

Launchpad automatic translations update.

531. By Niclas Lockner

Removed unused code

532. By Akshay Shekher

License fixes

533. By Launchpad Translations on behalf of elementary-apps

Launchpad automatic translations update.

Revision history for this message
David Gomes (davidgomes) :
review: Abstain
534. By Niclas Lockner

Made compatible with DynamicNotebook's new API.

535. By David Gomes

Granite version bump.

536. By David Gomes

Fixed typo.

537. By Niclas Lockner

Small-scale refactorization.

Simplified tab closing and window state saving, as well as the visibility of the tab bar code.
Silenced runtime errors and compiler warnings.
Fixed shell processes not being killed as well as follow-last-tab behavior not working correctly.

538. By Jeremy Wootten

Changed "Open Terminal Here" to simply "Terminal".

539. By Niclas Lockner

Implemented support for restoring tabs

Revision history for this message
Niclas Lockner (niclasl) wrote :

This is now ready to be reviewed again

540. By Niclas Lockner

Remove removed tab from the terminals list

Revision history for this message
Niclas Lockner (niclasl) wrote :

I just noticed the missing space after create_tab. I'll fix that later today.

review: Needs Fixing
541. By Niclas Lockner

Code-style fix

542. By Niclas Lockner

Code-style fix

Revision history for this message
Niclas Lockner (niclasl) wrote :

Code-style fixed

review: Approve
543. By Niclas Lockner

Catch Ctrl+D

544. By Niclas Lockner

Catch both Ctrl + d and D

Revision history for this message
David Gomes (davidgomes) wrote :

There seems to be a bug. Say I have two tabs open and I close the second one with Ctrl+Shift+W or Ctrl+D, the first tab isn't focused. I think it's a problem with your branch because it doesn't happen with trunk.

review: Needs Fixing
545. By Niclas Lockner

Let the new terminal grab the focus after the old one is closed

Revision history for this message
David Gomes (davidgomes) wrote :

Diff lines 46-47. Maybe you should just call action_close_tab () there?

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/PantheonTerminalWindow.vala'
2--- src/PantheonTerminalWindow.vala 2013-12-26 21:48:07 +0000
3+++ src/PantheonTerminalWindow.vala 2014-01-21 20:49:25 +0000
4@@ -29,6 +29,8 @@
5 private Gtk.Clipboard clipboard;
6
7 private GLib.List <TerminalWidget> terminals = new GLib.List <TerminalWidget> ();
8+ private HashTable<string, TerminalWidget> restorable_terminals;
9+ private int restorable_counter = 0;
10
11 public TerminalWidget current_terminal = null;
12 private bool is_fullscreen = false;
13@@ -139,6 +141,9 @@
14 open_tabs ();
15
16 set_size_request (app.minimum_width, app.minimum_height);
17+
18+ destroy.connect (on_destroy);
19+ restorable_terminals = new HashTable<string, TerminalWidget> (str_hash, str_equal);
20 }
21
22 private void setup_ui () {
23@@ -161,11 +166,14 @@
24 notebook.tab_switched.connect (on_switch_page);
25 notebook.tab_moved.connect (on_tab_moved);
26 notebook.tab_reordered.connect (on_tab_reordered);
27+ notebook.tab_restored.connect (on_tab_restored);
28 notebook.tab_duplicated.connect (on_tab_duplicated);
29 notebook.close_tab_requested.connect (on_close_tab_requested);
30 notebook.new_tab_requested.connect (on_new_tab_requested);
31 notebook.allow_new_window = true;
32 notebook.allow_duplication = true;
33+ notebook.allow_restoring = true;
34+ notebook.max_restorable_tabs = 5;
35 notebook.group_name = "pantheon-terminal";
36 notebook.can_focus = false;
37 add (notebook);
38@@ -218,6 +226,17 @@
39 }
40
41 break;
42+ case Gdk.Key.@D:
43+ case Gdk.Key.@d:
44+ if ((e.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
45+ if (!current_terminal.has_foreground_process ()) {
46+ current_terminal.tab.close ();
47+ current_terminal.grab_focus ();
48+ return true;
49+ }
50+ }
51+
52+ break;
53 }
54
55 return false;
56@@ -291,7 +310,14 @@
57 }
58 }
59
60- t.kill_ps ();
61+ if (!t.child_has_exited) {
62+ if (notebook.n_tabs >= 2) {
63+ make_restorable (tab);
64+ } else {
65+ t.kill_ps ();
66+ }
67+ }
68+
69 if (notebook.n_tabs - 1 == 0) {
70 update_saved_window_state ();
71 reset_saved_tabs ();
72@@ -304,6 +330,16 @@
73 current_terminal.grab_focus ();
74 }
75
76+ private void on_tab_restored (string label, string restore_key, GLib.Icon? icon) {
77+ TerminalWidget term = restorable_terminals.get (restore_key);
78+ var tab = create_tab (label, icon, term);
79+
80+ restorable_terminals.remove (restore_key);
81+ notebook.insert_tab (tab, -1);
82+ notebook.current = tab;
83+ term.grab_focus ();
84+ }
85+
86 private void on_tab_moved (Granite.Widgets.Tab tab, int x, int y) {
87 var new_window = app.new_window_with_coords (x, y, false);
88 var terminal = (tab.page as Gtk.Grid).get_child_at (0, 0) as TerminalWidget;
89@@ -407,10 +443,6 @@
90 /* Set up terminal */
91 var t = new TerminalWidget (this);
92 t.scrollback_lines = settings.scrollback_lines;
93- var g = new Gtk.Grid ();
94- var sb = new Gtk.Scrollbar (Gtk.Orientation.VERTICAL, t.vadjustment);
95- g.attach (t, 0, 0, 1, 1);
96- g.attach (sb, 1, 0, 1, 1);
97
98 /* Make the terminal occupy the whole GUI */
99 t.vexpand = true;
100@@ -426,14 +458,11 @@
101 t.run_program (program);
102 }
103
104- var tab = new Granite.Widgets.Tab (_("Terminal"), null, g);
105-
106- t.tab = tab;
107- tab.ellipsize_mode = Pango.EllipsizeMode.START;
108+ var tab = create_tab (_("Terminal"), null, t);
109
110 t.child_exited.connect (() => {
111 if (!t.killed) {
112- tab.close ();
113+ t.tab.close ();
114 }
115 });
116
117@@ -450,6 +479,34 @@
118 t.grab_focus ();
119 }
120
121+ private Granite.Widgets.Tab create_tab (string label, GLib.Icon? icon, TerminalWidget term) {
122+ var g = new Gtk.Grid ();
123+ var sb = new Gtk.Scrollbar (Gtk.Orientation.VERTICAL, term.vadjustment);
124+ g.attach (term, 0, 0, 1, 1);
125+ g.attach (sb, 1, 0, 1, 1);
126+ var tab = new Granite.Widgets.Tab (label, icon, g);
127+ term.tab = tab;
128+ tab.ellipsize_mode = Pango.EllipsizeMode.START;
129+
130+ return tab;
131+ }
132+
133+ private void make_restorable (Granite.Widgets.Tab tab) {
134+ var restore_key = "%d".printf (restorable_counter++);
135+ var page = tab.page as Gtk.Grid;
136+ var term = page.get_child_at (0, 0) as TerminalWidget;
137+
138+ terminals.remove (term);
139+ page.remove (term);
140+ restorable_terminals.insert (restore_key, term);
141+ tab.restore_data = restore_key;
142+ tab.dropped_callback = (() => {
143+ unowned TerminalWidget t = restorable_terminals.get (tab.restore_data);
144+ t.kill_ps ();
145+ restorable_terminals.remove (tab.restore_data);
146+ });
147+ }
148+
149 public void run_program_term (string program) {
150 new_tab ("", program);
151 }
152@@ -494,6 +551,12 @@
153 return false;
154 }
155
156+ private void on_destroy () {
157+ foreach (unowned TerminalWidget t in restorable_terminals.get_values ()) {
158+ t.kill_ps ();
159+ }
160+ }
161+
162 void action_quit () {
163
164 }
165@@ -516,6 +579,7 @@
166
167 void action_close_tab () {
168 current_terminal.tab.close ();
169+ current_terminal.grab_focus ();
170 }
171
172 void action_new_window () {

Subscribers

People subscribed via source and target branches