<br><br><div class="gmail_quote">On Sun, Jun 3, 2012 at 4:09 PM, S. Daniel Francis <span dir="ltr"><<a href="mailto:francis@sugarlabs.org" target="_blank">francis@sugarlabs.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

Signed-off-by: Daniel Francis <<a href="mailto:francis@sugarlabs.org">francis@sugarlabs.org</a>><br>
Signed-off-by: Agustin Zubiaga <<a href="mailto:aguz@sugarlabs.org">aguz@sugarlabs.org</a>><br>
---<br>
 icons/close-tab.svg |   27 ++++++++++<br>
 terminal.py         |   42 ++++++++++-----<br>
 widgets.py          |  150 +++++++++++++++++++++++++++++++++++++++++++++++++++<br>
 3 files changed, 206 insertions(+), 13 deletions(-)<br>
 create mode 100644 icons/close-tab.svg<br>
 create mode 100644 widgets.py<br>
<br>
diff --git a/icons/close-tab.svg b/icons/close-tab.svg<br>
new file mode 100644<br>
index 0000000..782ad24<br>
--- /dev/null<br>
+++ b/icons/close-tab.svg<br>
@@ -0,0 +1,27 @@<br>
+<?xml version="1.0" encoding="UTF-8"?><br>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"<br>
"<a href="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" target="_blank">http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd</a>" [<br>
+  <!ENTITY fill_color "#FFFFFF"><br>
+  <!ENTITY stroke_color "#010101"><br>
+]><br>
+<svg<br>
+   xmlns="<a href="http://www.w3.org/2000/svg" target="_blank">http://www.w3.org/2000/svg</a>"<br>
+   version="1.1"<br>
+   width="22.16"<br>
+   height="22.16"<br>
+   viewBox="0 <a href="tel:0%2022.16%2022.16" value="+5722162216">0 22.16 22.16</a>"<br>
+   id="browse-close-tab"<br>
+   xml:space="preserve"><br>
+  <g<br>
+   transform="matrix(1.3,0,0,1.3,-3.2682282,-3.3351543)"<br>
+   id="browse-dialog-cancel"<br>
+   style="stroke:&fill_color;;stroke-width:2.69230771;stroke-miterlimit:4;stroke-dasharray:none"><br>
+    <path<br>
+   d="M <a href="tel:14.798121" value="+5714798121">14.798121</a>,<a href="tel:7.2131543" value="+5772131543">7.2131543</a> <a href="tel:6.9900671" value="+5769900671">6.9900671</a>,<a href="tel:15.021208" value="+5715021208">15.021208</a>"<br>


+   id="path2986"<br>
+   style="fill:none;stroke:&fill_color;;stroke-width:2.69230771;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"<br>
/><br>
+    <path<br>
+   d="M <a href="tel:6.9900671" value="+5769900671">6.9900671</a>,<a href="tel:7.2131543" value="+5772131543">7.2131543</a> <a href="tel:14.798121" value="+5714798121">14.798121</a>,<a href="tel:15.021208" value="+5715021208">15.021208</a>"<br>


+   id="path3756"<br>
+   style="fill:none;stroke:&fill_color;;stroke-width:2.69230771;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"<br>
/><br>
+  </g><br>
+</svg><br>
diff --git a/terminal.py b/terminal.py<br>
index 0ba2871..6918e3f 100644<br>
--- a/terminal.py<br>
+++ b/terminal.py<br>
@@ -32,7 +32,6 @@ from gi.repository import Pango<br>
 from sugar3.graphics.toolbutton import ToolButton<br>
 from sugar3.graphics.toolbarbox import ToolbarBox<br>
 from sugar3.graphics.toolbarbox import ToolbarButton<br>
-from sugar3.graphics.notebook import Notebook<br>
<br>
 from sugar3.activity.widgets import EditToolbar<br>
 from sugar3.activity.widgets import ActivityToolbarButton<br>
@@ -40,6 +39,9 @@ from sugar3.activity.widgets import StopButton<br>
 from sugar3.activity import activity<br>
 from sugar3 import env<br>
<br>
+from widgets import BrowserNotebook<br>
+from widgets import TabLabel<br>
+<br>
 MASKED_ENVIRONMENT = [<br>
     'DBUS_SESSION_BUS_ADDRESS',<br>
     'PPID']<br>
@@ -118,7 +120,8 @@ class TerminalActivity(activity.Activity):<br>
         toolbar_box.show()<br>
         self._update_accelerators(toolbar_box)<br>
<br>
-        self._notebook = Notebook()<br>
+        self._notebook = BrowserNotebook()<br>
+        self._notebook.connect("tab-added", self.__open_tab_cb)<br>
         self._notebook.set_property("tab-pos", Gtk.PositionType.TOP)<br>
         self._notebook.set_scrollable(True)<br>
         self._notebook.show()<br>
@@ -248,17 +251,24 @@ class TerminalActivity(activity.Activity):<br>
         index = self._create_tab(None)<br>
         self._notebook.page = index<br>
         if self._notebook.get_n_pages() == 2:<br>
+            self._notebook.get_tab_label(self._notebook.get_nth_page(0<br>
+                                                    )).show_close_button()<br>
             self._delete_tab_button.props.sensitive = True<br>
             self._previous_tab_button.props.sensitive = True<br>
             self._next_tab_button.props.sensitive = True<br>
<br>
-    def __close_tab_cb(self, btn):<br>
-        self._close_tab(self._notebook.props.page)<br>
+    def __close_tab_cb(self, btn, child):<br>
+        index = self._notebook.page_num(child)<br>
+        self._close_tab(index)<br>
         if self._notebook.get_n_pages() == 1:<br>
+            self._notebook.get_tab_label(self._notebook.get_nth_page(0<br>
+                                                    )).hide_close_button()<br>
             self._delete_tab_button.props.sensitive = False<br>
             self._previous_tab_button.props.sensitive = False<br>
             self._next_tab_button.props.sensitive = False<br>
<br>
+        self._notebook.update_tab_sizes()<br>
+<br>
     def __prev_tab_cb(self, btn):<br>
         if self._notebook.props.page == 0:<br>
             self._notebook.props.page = self._notebook.get_n_pages() - 1<br>
@@ -305,9 +315,8 @@ class TerminalActivity(activity.Activity):<br>
         vt.connect("window-title-changed", self.__tab_title_changed_cb)<br>
<br>
         # FIXME have to resend motion events to parent, see #1402<br>
-        vt.connect('motion-notify-event', self.__motion_notify_cb)<br>
+        #vt.connect('motion-notify-event', self.__motion_notify_cb)<br>
<br>
-        #FIXME Drag and drop not working SL#3655<br>
         #vt.drag_dest_set(Gtk.DestDefaults.MOTION | Gtk.DestDefaults.DROP,<br>
         #       [('text/plain', 0, 0), ('STRING', 0, 1)],<br>
         #       Gdk.DragAction.DEFAULT |<br>
@@ -318,25 +327,32 @@ class TerminalActivity(activity.Activity):<br>
<br>
         vt.show()<br>
<br>
-        label = Gtk.Label()<br>
-<br>
         scrollbar = Gtk.VScrollbar.new(vt.get_vadjustment())<br>
-        scrollbar.show()<br>
<br>
         box = Gtk.HBox()<br>
         box.pack_start(vt, True, True, 0)<br>
         box.pack_start(scrollbar, False, True, 0)<br>
<br>
         box.vt = vt<br>
-        box.label = label<br>
+        box.show()<br>
+<br>
+        tablabel = TabLabel(box)<br>
+        tablabel.connect('tab-close', self.__close_tab_cb)<br>
+        tablabel.update_size(200)<br>
+        box.label = tablabel<br>
+<br>
+        index = self._notebook.append_page(box, tablabel)<br>
+        tablabel.show_all()<br>
<br>
-        index = self._notebook.append_page(box, label)<br>
+        self._notebook.update_tab_sizes()<br>
         self._notebook.show_all()<br>
<br>
         # Uncomment this to only show the tab bar when there is at least<br>
         # one tab. I think it's useful to always see it, since it displays<br>
         # the 'window title'.<br>
         # self._notebook.props.show_tabs = self._notebook.get_n_pages() > 1<br>
+        tablabel.hide_close_button() if self._notebook.get_n_pages() == 1\<br>
+                                     else None<br>
<br>
         # Launch the default shell in the HOME directory.<br>
         os.chdir(os.environ["HOME"])<br>
@@ -380,8 +396,8 @@ class TerminalActivity(activity.Activity):<br>
<br>
         return index<br>
<br>
-    def __motion_notify_cb(self, widget, event):<br>
-        self.emit('motion-notify-event', event)<br>
+#    def __motion_notify_cb(self, widget, event):<br>
+#        self.emit('motion-notify-event', event)<br>
<br>
     def __become_root_cb(self, button):<br>
         vt = self._notebook.get_nth_page(self._notebook.get_current_page()).vt<br>
diff --git a/widgets.py b/widgets.py<br>
new file mode 100644<br>
index 0000000..94eff64<br>
--- /dev/null<br>
+++ b/widgets.py<br>
@@ -0,0 +1,150 @@<br>
+#!/usr/bin/env python<br>
+# -*- coding: utf-8 -*-<br>
+# Copyright (C) 2006, Red Hat, Inc.<br>
+# Copyright (C) 2011, One Laptop Per Child<br>
+# Copyright (C) 2009, Tomeu Vizoso, Simon Schampijer<br>
+#<br>
+# This program is free software; you can redistribute it and/or modify<br>
+# it under the terms of the GNU General Public License as published by<br>
+# the Free Software Foundation; either version 2 of the License, or<br>
+# (at your option) any later version.<br>
+#,<br>
+# This program is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
+# GNU General Public License for more details.<br>
+#<br>
+# You should have received a copy of the GNU General Public License<br>
+# along with this program; if not, write to the Free Software<br>
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA<br>
+<br>
+from gi.repository import GObject<br>
+from gi.repository import Gtk<br>
+from gi.repository import Pango<br>
+<br>
+from sugar3.graphics.icon import Icon<br>
+<br>
+<br>
+class TabAdd(Gtk.Button):<br>
+    __gtype_name__ = 'BrowseTabAdd'<br>
+<br>
+    __gsignals__ = {<br>
+        'tab-added': (GObject.SignalFlags.RUN_FIRST,<br>
+                      None,<br>
+                      ([])),<br>
+    }<br>
+<br>
+    def __init__(self):<br>
+        GObject.GObject.__init__(self)<br>
+<br>
+        add_tab_icon = Icon(icon_name='add')<br>
+        self.props.relief = Gtk.ReliefStyle.NONE<br>
+        self.props.focus_on_click = False<br>
+        icon_box = Gtk.HBox()<br>
+        icon_box.pack_start(add_tab_icon, True, False, 0)<br>
+        self.add(icon_box)<br>
+        self.connect('clicked', self.__button_clicked_cb)<br>
+        self.set_name('browse-tab-add')<br>
+        add_tab_icon.show()<br>
+        icon_box.show()<br>
+        self.show()<br>
+<br>
+    def __button_clicked_cb(self, button):<br>
+        self.emit('tab-added')<br>
+<br>
+<br>
+class BrowserNotebook(Gtk.Notebook):<br>
+    __gtype_name__ = 'BrowseNotebook'<br>
+<br>
+    __gsignals__ = {<br>
+        'tab-added': (GObject.SignalFlags.RUN_FIRST,<br>
+                      None,<br>
+                      ([])),<br>
+    }<br>
+<br>
+    """Handle an extra tab at the end with an Add Tab button."""<br>
+<br>
+    def __init__(self):<br>
+        GObject.GObject.__init__(self)<br>
+<br>
+        self.first_expose = True<br>
+        self.connect("draw", self._draw_cb)<br>
+        self._tab_add = TabAdd()<br>
+        self._tab_add.connect('tab-added', self.on_add_tab)<br>
+        self.set_action_widget(self._tab_add, Gtk.PackType.END)<br>
+        self._tab_add.show()<br>
+<br>
+    def _draw_cb(self, widget, event):<br>
+        if self.first_expose:<br>
+            self.update_tab_sizes()<br>
+            self.first_expose = False<br>
+<br>
+    def on_add_tab(self, obj):<br>
+        self.emit('tab-added')<br>
+<br>
+    def update_tab_sizes(self):<br>
+        n_pages = self.get_n_pages()<br>
+        canvas_size = self.get_allocation()<br>
+<br>
+        # FIXME<br>
+        # overlap_size = self.style_get_property('tab-overlap') * n_pages - 1<br>
+        overlap_size = 0<br>
+        allowed_size = canvas_size.width - overlap_size<br>
+<br>
+        tab_new_size = int(float(allowed_size) / (n_pages) -\<br>
+                           self._tab_add.get_allocation().width - 5)<br>
+<br>
+        for page_idx in range(n_pages):<br>
+            page = self.get_nth_page(page_idx)<br>
+            label = self.get_tab_label(page)<br>
+            label.update_size(tab_new_size)<br>
+<br>
+<br>
+class TabLabel(Gtk.HBox):<br>
+    __gtype_name__ = 'BrowseTabLabel'<br>
+<br>
+    __gsignals__ = {<br>
+        'tab-close': (GObject.SignalFlags.RUN_FIRST,<br>
+                      None,<br>
+                      ([GObject.TYPE_PYOBJECT])),<br>
+    }<br>
+<br>
+    def __init__(self, child):<br>
+        GObject.GObject.__init__(self)<br>
+<br>
+        self.child = child<br>
+        self._label = Gtk.Label(label="")<br>
+        self._label.set_ellipsize(Pango.EllipsizeMode.END)<br>
+        self._label.set_alignment(0, 0.5)<br>
+        self.pack_start(self._label, True, True, 0)<br>
+        self._label.show()<br>
+<br>
+        close_tab_icon = Icon(icon_name='close-tab')<br>
+        button = Gtk.Button()<br>
+        button.props.relief = Gtk.ReliefStyle.NONE<br>
+        button.props.focus_on_click = False<br>
+        icon_box = Gtk.HBox()<br>
+        icon_box.pack_start(close_tab_icon, True, False, 0)<br>
+        button.add(icon_box)<br>
+        button.connect('clicked', self.__button_clicked_cb)<br>
+        button.set_name('browse-tab-close')<br>
+        self.pack_start(button, False, True, 0)<br>
+        close_tab_icon.show()<br>
+        icon_box.show()<br>
+        button.show()<br>
+        self._close_button = button<br>
+<br>
+    def set_text(self, title):<br>
+        self._label.set_text(title)<br>
+<br>
+    def update_size(self, size):<br>
+        self.set_size_request(size, -1)<br>
+<br>
+    def hide_close_button(self):<br>
+        self._close_button.hide()<br>
+<br>
+    def show_close_button(self):<br>
+        self._close_button.show()<br>
+<br>
+    def __button_clicked_cb(self, button):<br>
+        self.emit('tab-close', self.child)<br>
--<br>
1.7.10.2<br>
</blockquote></div><br><div>Daniel committed it as:</div><div><br></div><div><a href="http://git.sugarlabs.org/terminal/mainline/commit/2a3addb355c60e98dd714e8216a77f07a6f38d9a">http://git.sugarlabs.org/terminal/mainline/commit/2a3addb355c60e98dd714e8216a77f07a6f38d9a</a></div>

<div><br></div><div><br></div><div>Cheers.</div><div><br></div>