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
=== modified file 'src/PantheonTerminalWindow.vala'
--- src/PantheonTerminalWindow.vala 2013-12-26 21:48:07 +0000
+++ src/PantheonTerminalWindow.vala 2014-01-21 20:49:25 +0000
@@ -29,6 +29,8 @@
29 private Gtk.Clipboard clipboard;29 private Gtk.Clipboard clipboard;
3030
31 private GLib.List <TerminalWidget> terminals = new GLib.List <TerminalWidget> ();31 private GLib.List <TerminalWidget> terminals = new GLib.List <TerminalWidget> ();
32 private HashTable<string, TerminalWidget> restorable_terminals;
33 private int restorable_counter = 0;
3234
33 public TerminalWidget current_terminal = null;35 public TerminalWidget current_terminal = null;
34 private bool is_fullscreen = false;36 private bool is_fullscreen = false;
@@ -139,6 +141,9 @@
139 open_tabs ();141 open_tabs ();
140142
141 set_size_request (app.minimum_width, app.minimum_height);143 set_size_request (app.minimum_width, app.minimum_height);
144
145 destroy.connect (on_destroy);
146 restorable_terminals = new HashTable<string, TerminalWidget> (str_hash, str_equal);
142 }147 }
143148
144 private void setup_ui () {149 private void setup_ui () {
@@ -161,11 +166,14 @@
161 notebook.tab_switched.connect (on_switch_page);166 notebook.tab_switched.connect (on_switch_page);
162 notebook.tab_moved.connect (on_tab_moved);167 notebook.tab_moved.connect (on_tab_moved);
163 notebook.tab_reordered.connect (on_tab_reordered);168 notebook.tab_reordered.connect (on_tab_reordered);
169 notebook.tab_restored.connect (on_tab_restored);
164 notebook.tab_duplicated.connect (on_tab_duplicated);170 notebook.tab_duplicated.connect (on_tab_duplicated);
165 notebook.close_tab_requested.connect (on_close_tab_requested);171 notebook.close_tab_requested.connect (on_close_tab_requested);
166 notebook.new_tab_requested.connect (on_new_tab_requested);172 notebook.new_tab_requested.connect (on_new_tab_requested);
167 notebook.allow_new_window = true;173 notebook.allow_new_window = true;
168 notebook.allow_duplication = true;174 notebook.allow_duplication = true;
175 notebook.allow_restoring = true;
176 notebook.max_restorable_tabs = 5;
169 notebook.group_name = "pantheon-terminal";177 notebook.group_name = "pantheon-terminal";
170 notebook.can_focus = false;178 notebook.can_focus = false;
171 add (notebook);179 add (notebook);
@@ -218,6 +226,17 @@
218 }226 }
219227
220 break;228 break;
229 case Gdk.Key.@D:
230 case Gdk.Key.@d:
231 if ((e.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
232 if (!current_terminal.has_foreground_process ()) {
233 current_terminal.tab.close ();
234 current_terminal.grab_focus ();
235 return true;
236 }
237 }
238
239 break;
221 }240 }
222241
223 return false;242 return false;
@@ -291,7 +310,14 @@
291 }310 }
292 }311 }
293312
294 t.kill_ps ();313 if (!t.child_has_exited) {
314 if (notebook.n_tabs >= 2) {
315 make_restorable (tab);
316 } else {
317 t.kill_ps ();
318 }
319 }
320
295 if (notebook.n_tabs - 1 == 0) {321 if (notebook.n_tabs - 1 == 0) {
296 update_saved_window_state ();322 update_saved_window_state ();
297 reset_saved_tabs ();323 reset_saved_tabs ();
@@ -304,6 +330,16 @@
304 current_terminal.grab_focus ();330 current_terminal.grab_focus ();
305 }331 }
306332
333 private void on_tab_restored (string label, string restore_key, GLib.Icon? icon) {
334 TerminalWidget term = restorable_terminals.get (restore_key);
335 var tab = create_tab (label, icon, term);
336
337 restorable_terminals.remove (restore_key);
338 notebook.insert_tab (tab, -1);
339 notebook.current = tab;
340 term.grab_focus ();
341 }
342
307 private void on_tab_moved (Granite.Widgets.Tab tab, int x, int y) {343 private void on_tab_moved (Granite.Widgets.Tab tab, int x, int y) {
308 var new_window = app.new_window_with_coords (x, y, false);344 var new_window = app.new_window_with_coords (x, y, false);
309 var terminal = (tab.page as Gtk.Grid).get_child_at (0, 0) as TerminalWidget;345 var terminal = (tab.page as Gtk.Grid).get_child_at (0, 0) as TerminalWidget;
@@ -407,10 +443,6 @@
407 /* Set up terminal */443 /* Set up terminal */
408 var t = new TerminalWidget (this);444 var t = new TerminalWidget (this);
409 t.scrollback_lines = settings.scrollback_lines;445 t.scrollback_lines = settings.scrollback_lines;
410 var g = new Gtk.Grid ();
411 var sb = new Gtk.Scrollbar (Gtk.Orientation.VERTICAL, t.vadjustment);
412 g.attach (t, 0, 0, 1, 1);
413 g.attach (sb, 1, 0, 1, 1);
414446
415 /* Make the terminal occupy the whole GUI */447 /* Make the terminal occupy the whole GUI */
416 t.vexpand = true;448 t.vexpand = true;
@@ -426,14 +458,11 @@
426 t.run_program (program);458 t.run_program (program);
427 }459 }
428460
429 var tab = new Granite.Widgets.Tab (_("Terminal"), null, g);461 var tab = create_tab (_("Terminal"), null, t);
430
431 t.tab = tab;
432 tab.ellipsize_mode = Pango.EllipsizeMode.START;
433462
434 t.child_exited.connect (() => {463 t.child_exited.connect (() => {
435 if (!t.killed) {464 if (!t.killed) {
436 tab.close ();465 t.tab.close ();
437 }466 }
438 });467 });
439468
@@ -450,6 +479,34 @@
450 t.grab_focus ();479 t.grab_focus ();
451 }480 }
452481
482 private Granite.Widgets.Tab create_tab (string label, GLib.Icon? icon, TerminalWidget term) {
483 var g = new Gtk.Grid ();
484 var sb = new Gtk.Scrollbar (Gtk.Orientation.VERTICAL, term.vadjustment);
485 g.attach (term, 0, 0, 1, 1);
486 g.attach (sb, 1, 0, 1, 1);
487 var tab = new Granite.Widgets.Tab (label, icon, g);
488 term.tab = tab;
489 tab.ellipsize_mode = Pango.EllipsizeMode.START;
490
491 return tab;
492 }
493
494 private void make_restorable (Granite.Widgets.Tab tab) {
495 var restore_key = "%d".printf (restorable_counter++);
496 var page = tab.page as Gtk.Grid;
497 var term = page.get_child_at (0, 0) as TerminalWidget;
498
499 terminals.remove (term);
500 page.remove (term);
501 restorable_terminals.insert (restore_key, term);
502 tab.restore_data = restore_key;
503 tab.dropped_callback = (() => {
504 unowned TerminalWidget t = restorable_terminals.get (tab.restore_data);
505 t.kill_ps ();
506 restorable_terminals.remove (tab.restore_data);
507 });
508 }
509
453 public void run_program_term (string program) {510 public void run_program_term (string program) {
454 new_tab ("", program);511 new_tab ("", program);
455 }512 }
@@ -494,6 +551,12 @@
494 return false;551 return false;
495 }552 }
496553
554 private void on_destroy () {
555 foreach (unowned TerminalWidget t in restorable_terminals.get_values ()) {
556 t.kill_ps ();
557 }
558 }
559
497 void action_quit () {560 void action_quit () {
498561
499 }562 }
@@ -516,6 +579,7 @@
516579
517 void action_close_tab () {580 void action_close_tab () {
518 current_terminal.tab.close ();581 current_terminal.tab.close ();
582 current_terminal.grab_focus ();
519 }583 }
520584
521 void action_new_window () {585 void action_new_window () {

Subscribers

People subscribed via source and target branches