← Back to team overview

openerp-dev-web team mailing list archive

[Merge] lp:~openerp-dev/openobject-addons/6.0-bug-751556-tta into lp:openobject-addons/6.0

 

Tejaskumar Tank (OpenERP) has proposed merging lp:~openerp-dev/openobject-addons/6.0-bug-751556-tta into lp:openobject-addons/6.0.

Requested reviews:
  OpenERP Core Team (openerp)
Related bugs:
  Bug #751556 in OpenERP GTK Client: "Filters doesn't work except on text fields"
  https://bugs.launchpad.net/openobject-client/+bug/751556

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-addons/6.0-bug-751556-tta/+merge/58938
-- 
The attached diff has been truncated due to its size.
https://code.launchpad.net/~openerp-dev/openobject-addons/6.0-bug-751556-tta/+merge/58938
Your team OpenERP R&D Team is subscribed to branch lp:~openerp-dev/openobject-addons/6.0-bug-751556-tta.
=== added file 'MANIFEST.in'
--- MANIFEST.in	1970-01-01 00:00:00 +0000
+++ MANIFEST.in	2011-04-25 08:38:27 +0000
@@ -0,0 +1,14 @@
+include MANIFEST.in
+include setup.cfg
+include Makefile.translation
+include configure
+include configure.makefile
+include rpminstall_sh.txt
+include mydistutils.py
+include msgfmt.py
+include setup.nsi
+graft bin
+graft man
+graft doc
+graft debian
+global-exclude *pyc *~

=== added file 'Makefile.translation'
--- Makefile.translation	1970-01-01 00:00:00 +0000
+++ Makefile.translation	2011-04-25 08:38:27 +0000
@@ -0,0 +1,24 @@
+LANG=fr
+PYTHON_FILES=$(shell find -name "*py")
+PYTHONC_FILES=$(shell find -name "*pyc")
+APP=openerp-client
+LANGS=$(shell for i in `find bin/po -name "*.po"`; do basename $$i | cut -d'.' -f1; done;)
+
+all:
+
+clean:
+	rm -f bin/*bak $(PYTHONC_FILES)
+	rm -f bin/openerp.gladep
+
+translate_get:
+	xgettext -k_ -kN_ -o bin/po/$(APP).pot $(PYTHON_FILES) bin/openerp.glade bin/win_error.glade 
+
+translate_set:
+	for i in $(LANGS); do msgfmt bin/po/$$i.po -o bin/share/locale/$$i/LC_MESSAGES/$(APP).mo; done;
+
+merge:
+	for i in $(LANGS); do msgmerge bin/po/$$i.po bin/po/$(APP).pot -o bin/po/$$i.po --strict; done;
+
+test:
+	echo $(LANGS)
+

=== added directory 'bin'
=== added directory 'bin/SpiffGtkWidgets'
=== added directory 'bin/SpiffGtkWidgets/Calendar'
=== added file 'bin/SpiffGtkWidgets/Calendar/Calendar.py'
--- bin/SpiffGtkWidgets/Calendar/Calendar.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/Calendar.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,255 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import gtk
+import gobject
+import pango
+import calendar
+import datetime
+import time
+import util
+import hippo
+from SpiffGtkWidgets.color import to_int as c2i
+from CanvasDayRange        import CanvasDayRange
+
+class Calendar(hippo.Canvas):
+    RANGE_WEEK   = 1
+    RANGE_MONTH  = 2
+    RANGE_CUSTOM = 3
+
+    def __init__(self, model, mode='month'):
+        """
+        Constructor.
+        """
+        hippo.Canvas.__init__(self)
+        self.root = hippo.CanvasBox()
+        self.set_root(self.root)
+
+        # Init the model and view options.
+        self.realized      = False
+        self.model         = model
+        self.selected      = datetime.date(*time.localtime(time.time())[:3])
+        if mode == 'week':
+            self.range         = self.RANGE_WEEK
+            self.visible_range = model.get_week(self.selected)
+            self.active_range  = self.visible_range
+        else:
+            self.range         = self.RANGE_MONTH
+            self.visible_range = model.get_month_weeks(self.selected)
+            self.active_range  = model.get_month(self.selected)
+        # Widgets and canvas items.
+        self.range_item = None
+        self.colors     = None
+        self.font       = None
+
+        # Configure the canvas.
+        self.set_flags(gtk.CAN_FOCUS)
+        self.set_events(gtk.gdk.EXPOSURE_MASK
+                      | gtk.gdk.BUTTON_PRESS_MASK
+                      | gtk.gdk.BUTTON_RELEASE_MASK
+                      | gtk.gdk.POINTER_MOTION_MASK
+                      | gtk.gdk.POINTER_MOTION_HINT_MASK
+                      | gtk.gdk.KEY_PRESS_MASK
+                      | gtk.gdk.KEY_RELEASE_MASK
+                      | gtk.gdk.ENTER_NOTIFY_MASK
+                      | gtk.gdk.LEAVE_NOTIFY_MASK
+                      | gtk.gdk.FOCUS_CHANGE_MASK)
+        self.connect_after('realize',         self.on_realize)
+        self.connect      ('size-allocate',   self.on_size_allocate)
+        self.connect      ('key-press-event', self.on_key_press_event)
+
+
+    def set_range(self, range):
+        self.range = range
+        self.refresh()
+
+
+    def set_custom_range(self,
+                         start,
+                         end,
+                         active_start = None,
+                         active_end = None):
+        if active_start is None:
+            active_start = start
+        if active_end is None:
+            active_end = end
+        self.range         = self.RANGE_CUSTOM
+        self.visible_range = start, end
+        self.active_range  = active_start, active_end
+        self.refresh()
+
+
+    def select(self, date):
+        self.selected = date
+        self.refresh()
+
+
+    def get_selected(self):
+        return self.selected
+
+
+    def on_realize(self, *args):
+        self.realized = True
+        self.grab_focus()
+        self.on_size_allocate(*args)
+
+
+    def on_size_allocate(self, *args):
+        alloc = self.get_allocation()
+        if not self.realized: # or alloc.width < 10 or alloc.height < 10:
+            return
+        #self.set_bounds(0, 0, alloc.width, alloc.height)
+
+        # Initialize colors.
+        if self.colors is not None:
+            return
+
+        style       = self.get_style()
+        self.font   = style.font_desc
+        self.colors = dict(bg            = c2i(style.bg[gtk.STATE_PRELIGHT]),
+                           text          = c2i(style.fg[gtk.STATE_NORMAL]),
+                           text_inactive = c2i(style.fg[gtk.STATE_INSENSITIVE]),
+                           body          = c2i(style.light[gtk.STATE_ACTIVE]),
+                           body_today    = c2i('peach puff'),
+                           border        = c2i(style.mid[gtk.STATE_NORMAL]),
+                           selected      = c2i(style.mid[gtk.STATE_SELECTED]),
+                           inactive      = c2i(style.bg[gtk.STATE_PRELIGHT]))
+        self.refresh()
+
+    def refresh(self):
+        if not self.realized:
+            return
+        self.draw_background()
+        self.draw_days()
+
+    def draw_background(self):
+        self.root.color = self.colors['bg']
+
+    def draw_days(self):
+        """
+        Draws the currently selected range of days.
+        """
+        if self.range_item is None:
+            self.range_item = CanvasDayRange(self)
+            self.root.append(self.range_item, hippo.PACK_EXPAND)
+            self.range_item.connect('day-clicked',   self.on_day_clicked)
+            self.range_item.connect('time-clicked',  self.on_time_clicked)
+            self.range_item.connect('event-clicked', self.on_event_clicked)
+
+        if self.range == self.RANGE_WEEK:
+            self.visible_range = self.model.get_week(self.selected)
+            self.active_range  = self.visible_range
+        elif self.range == self.RANGE_MONTH:
+            self.visible_range = self.model.get_month_weeks(self.selected)
+            self.active_range  = self.model.get_month(self.selected)
+        elif self.range == self.RANGE_CUSTOM:
+            pass
+        else:
+            raise TypeError('Invalid range ' + self.range)
+
+        date                         = self.selected
+        self.range_item.range        = self.visible_range
+        self.range_item.active_range = self.active_range
+        self.range_item.selected     = self.selected
+        self.range_item.update()
+
+
+    def on_event_store_event_removed(self, store, event):
+        self.refresh()
+
+
+    def on_event_store_event_added(self, store, event):
+        self.refresh()
+
+
+    def on_key_press_event(self, widget, event):
+        date = self.get_selected()
+        if event.keyval == 65362:    # Up
+            self.select(util.previous_week(date))
+            self.emit('day-selected', date, event)
+        elif event.keyval == 65364:  # Down
+            self.select(util.next_week(date))
+            self.emit('day-selected', date, event)
+        elif event.keyval == 65361:  # Left
+            self.select(util.previous_day(date))
+            self.emit('day-selected', date, event)
+        elif event.keyval == 65363:  # Right
+            self.select(util.next_day(date))
+            self.emit('day-selected', date, event)
+        elif event.keyval == 65293:  # Enter
+            if not self.emit('day-activate', date, event):
+                self.set_range(self.RANGE_WEEK)
+        return True
+
+
+    def on_event_clicked(self, sender, event, ev):
+        self.emit('event-clicked', event, ev)
+
+
+    def on_day_clicked(self, sender, date, event):
+        if self.emit('date-clicked', date, event):
+            return True
+        if self.range == self.RANGE_MONTH \
+          and not self.range_item.is_active(date):
+            self.set_range(self.RANGE_MONTH)
+            if date < self.active_range[0]:
+                self.emit('do_month_back_forward', -1)
+            else:
+                self.emit('do_month_back_forward', 1)
+        self.select(date)
+        if self.emit('day-selected', date, event):
+            return True
+
+
+    def on_time_clicked(self, sender, date, event):
+        self.emit('time-clicked', date, event)
+
+
+gobject.signal_new('event-clicked',
+                   Calendar,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+gobject.signal_new('time-clicked',
+                   Calendar,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+gobject.signal_new('date-clicked',
+                   Calendar,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+gobject.signal_new('day-selected',
+                   Calendar,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+gobject.signal_new('day-activate',
+                   Calendar,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+
+gobject.signal_new('do_month_back_forward',
+                   Calendar,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT,))
+
+gobject.type_register(Calendar)

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasDay.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasDay.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasDay.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+import calendar
+import pango
+import util
+from SpiffGtkWidgets import color
+
+class CanvasDay(hippo.CanvasBox, hippo.CanvasItem):
+    """
+    A canvas item representing a day.
+    """
+    def __init__(self, cal, **kwargs):
+        """
+        Constructor.
+        """
+        hippo.CanvasBox.__init__(self, **kwargs)
+
+        self.cal         = cal
+        self.date        = kwargs.get('date')
+        self.active      = True
+        self.selected    = False
+        self.highlighted = False
+        self.show_rulers = False
+
+        # Create canvas items.
+        self.box  = hippo.CanvasGradient(padding = 2, padding_right = 5)
+        self.text = hippo.CanvasText(xalign    = hippo.ALIGNMENT_END,
+                                     size_mode = hippo.CANVAS_SIZE_ELLIPSIZE_END)
+        self.body = hippo.CanvasGradient()
+
+        self.box.append(self.text, hippo.PACK_EXPAND)
+        self.append(self.box)
+        self.append(self.body, hippo.PACK_EXPAND)
+        self.box.set_visible(True)
+
+
+    def set_date(self, date):
+        self.date = date
+
+
+    def set_active(self, active):
+        self.active = active
+
+
+    def set_selected(self, selected):
+        self.selected = selected
+
+
+    def set_highlighted(self, highlighted):
+        self.highlighted = highlighted
+
+
+    def set_show_title(self, show_title):
+        self.box.set_visible(show_title)
+
+
+    def set_show_rulers(self, show_rulers):
+        self.show_rulers = show_rulers
+
+
+    def _set_color(self, box, color):
+        box.set_property('start-color', color)
+        box.set_property('end-color',   color)
+
+
+    def get_body_position(self):
+        return self.get_position(self.body)
+
+
+    def get_body_allocation(self):
+        return self.body.get_allocation()
+
+
+    def update(self):
+        # Draw the title box.
+        if self.selected:
+            self._set_color(self.box, self.cal.colors['selected'])
+        elif not self.active:
+            self._set_color(self.box, self.cal.colors['inactive'])
+        else:
+            self._set_color(self.box, self.cal.colors['border'])
+
+        # Draw the title text.
+        if self.date is not None:
+            #day_name = self.cal.model.get_day_name(self.date)
+            #caption  = '%s %s' % (self.date.timetuple()[2], day_name)
+            caption  = '%d' % self.date.timetuple()[2]
+            self.text.set_property('font',  self.cal.font.to_string())
+            self.text.set_property('text',  caption)
+            self.text.set_property('color', self.cal.colors['text'])
+
+        # Draw the "body" of the day.
+        if self.highlighted:
+            self._set_color(self.body, self.cal.colors['body_today'])
+        else:
+            self._set_color(self.body, self.cal.colors['body'])
+        self.body.set_property('spacing', 0)
+
+
+    def do_paint_above_children(self, ctx, rect):
+        if not self.show_rulers:
+            return
+        ctx.set_source_rgba(*color.to_rgba(self.cal.colors['inactive']))
+        ctx.rectangle(rect.x, rect.y, rect.width, rect.height)
+        ctx.set_line_width(1)
+        ctx.set_dash((1, 1))
+        ctx.clip()
+        w, h = self.get_allocation()
+
+        for n in range(0, 24):
+            y = n * h / 24
+            ctx.move_to(0, y)
+            ctx.line_to(w, y)
+        ctx.stroke()
+
+gobject.type_register(CanvasDay)

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasDayRange.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasDayRange.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasDayRange.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,375 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+import math
+import time
+import datetime
+import util
+from SpiffGtkWidgets  import color
+from CanvasTimeline   import CanvasTimeline
+from CanvasGrid       import CanvasGrid
+from CanvasDay        import CanvasDay
+from CanvasTable      import CanvasTable
+from CanvasHEventView import CanvasHEventView
+from CanvasVEventView import CanvasVEventView
+
+class CanvasDayRange(CanvasTable, hippo.CanvasItem):
+    """
+    A canvas item that shows a range of days.
+    """
+    def __init__(self, cal, **kwargs):
+        """
+        Constructor.
+        """
+        CanvasTable.__init__(self, **kwargs)
+
+        self.cal          = cal
+        self.range        = kwargs.get('range')
+        self.active_range = self.range
+        self.selected     = None
+
+        # Create canvas items.
+        self.scroll        = hippo.CanvasScrollbars()
+        self.hbox_top      = hippo.CanvasBox(orientation = hippo.ORIENTATION_HORIZONTAL)
+        self.vbox_top      = hippo.CanvasBox()
+        self.day_captions  = CanvasTable()
+        self.timeline      = CanvasTimeline(self.cal)
+        self.padding_left  = hippo.CanvasBox()
+        self.padding_right = hippo.CanvasBox()
+        self.allday_view   = CanvasHEventView(self.cal, yalign = hippo.ALIGNMENT_FILL)
+        self.grid          = CanvasGrid(self._new_cell)
+        self.gridbox       = hippo.CanvasBox(orientation = hippo.ORIENTATION_HORIZONTAL)
+        self.vevent_views  = {}
+        self.hevent_views  = {}
+        self.allocs        = {}
+        self.allocs[self.padding_left]  = (0, 0, 0, 0)
+        self.allocs[self.padding_right] = (0, 0, 0, 0)
+
+        self.vbox_top.append(self.day_captions)
+        self.vbox_top.append(self.allday_view)
+        self.day_captions.set_homogeneus_columns(True)
+
+        self.hbox_top.append(self.padding_left)
+        self.hbox_top.append(self.vbox_top, hippo.PACK_EXPAND)
+        self.hbox_top.append(self.padding_right)
+
+        self.gridbox.append(self.timeline)
+        self.gridbox.append(self.grid, hippo.PACK_EXPAND)
+        self.scroll.set_root(self.gridbox)
+
+        self.add(self.hbox_top, 0, 1, 0, 1)
+        self.add(self.scroll,   0, 1, 1, 2)
+        self.set_row_expand(1, True)
+        self.set_column_expand(0, True)
+        self.allday_view.show_normal = False
+        self.allday_view.connect('event-clicked', self.on_view_event_clicked)
+        self.grid.connect('paint', self.on_grid_paint)
+        self.grid.set_homogeneus_columns(True)
+
+
+    def on_day_button_press_event(self, widget, event):
+        date = datetime.datetime(*widget.date.timetuple()[:3])
+        self.emit('time-clicked', date, event)
+        self.emit('day-clicked', widget.date, event)
+
+
+    def on_view_time_clicked(self, view, item, ev, time):
+        date = datetime.date(*time.timetuple()[:3])
+        self.emit('time-clicked', time, ev)
+        self.emit('day-clicked', date, ev)
+
+
+    def on_view_event_clicked(self, view, item, ev):
+        self.emit('event-clicked', item.event, ev)
+
+
+    def _new_cell(self):
+        cell = CanvasDay(self.cal, xalign = hippo.ALIGNMENT_FILL)
+        cell.connect('button-press-event', self.on_day_button_press_event)
+        return cell
+
+
+    def is_active(self, date):
+        return self.active_range[0] <= date <= self.active_range[1]
+
+
+    def _get_event_view(self, row, start, end, horizontal):
+        if horizontal:
+            if row in self.hevent_views:
+                view = self.hevent_views[row]
+                view.set_range(start, end)
+                return view
+            view = CanvasHEventView(self.cal, start, end)
+            self.hevent_views[row] = view
+        else:
+            if row in self.vevent_views:
+                view = self.vevent_views[row]
+                view.set_range(start, end)
+                return view
+            view = CanvasVEventView(self.cal, start, end)
+            self.vevent_views[row] = view
+        view.connect('time-clicked',  self.on_view_time_clicked)
+        view.connect('event-clicked', self.on_view_event_clicked)
+        self.allocs[view] = (0, 0, 0, 0)
+        self.gridbox.append(view, hippo.PACK_FIXED)
+        return view
+
+
+    def _remove_vevent_view(self, cell):
+        if not cell in self.vevent_views:
+            return
+        view = self.vevent_views[cell]
+        self.gridbox.remove(view)
+        del self.vevent_views[cell]
+        del self.allocs[view]
+
+
+    def _remove_hevent_view(self, cell):
+        if not cell in self.hevent_views:
+            return
+        view = self.hevent_views[cell]
+        self.gridbox.remove(view)
+        del self.hevent_views[cell]
+        del self.allocs[view]
+
+
+    def update_one_row(self):
+        self.scroll.set_policy(hippo.ORIENTATION_VERTICAL,
+                               hippo.SCROLLBAR_ALWAYS)
+        self.grid.set_properties(box_height = 800)
+        self.allday_view.set_range(*self.range)
+        self.allday_view.set_visible(True)
+        self.padding_left.set_visible(True)
+        self.padding_right.set_visible(True)
+
+        # Hide all event views.
+        for cell in [c for c in self.hevent_views]:
+            self._remove_hevent_view(cell)
+        current_children = self.grid.get_children()
+        for cell in [c for c in self.vevent_views]:
+            if cell not in current_children:
+                self._remove_vevent_view(cell)
+
+        # Create an event view on top of each cell.
+        for child in self.grid.get_children():
+            start = child.date
+            end   = util.end_of_day(child.date)
+            view  = self._get_event_view(child, start, end, False)
+            self.allocs[view] = (0, 0, 0, 0)
+
+
+    def update_multi_row(self):
+        self.scroll.set_policy(hippo.ORIENTATION_VERTICAL,
+                               hippo.SCROLLBAR_NEVER)
+        self.grid.set_properties(box_height = -1)
+        self.allday_view.set_visible(False)
+        self.padding_left.set_visible(False)
+        self.padding_right.set_visible(False)
+
+        # Hide all event views.
+        for cell in [c for c in self.vevent_views]:
+            self._remove_vevent_view(cell)
+        current_children = self.grid.get_children()
+        for cell in [c for c in self.hevent_views]:
+            if cell not in current_children:
+                self._remove_hevent_view(cell)
+
+        # Create an event view on top of each row.
+        for row in self.grid.get_rows():
+            start = row[0].date
+            end   = row[-1].date
+            view  = self._get_event_view(row[0], start, end, True)
+            self.allocs[view] = (0, 0, 0, 0)
+
+
+    def update(self):
+        date  = self.range[0]
+        days  = (self.range[1] - self.range[0]).days + 1
+        rows  = int(math.ceil(float(days) / 7.0))
+        cols  = days
+        today = datetime.date(*time.localtime(time.time())[:3])
+        if days > 7:
+            cols = int(math.ceil(float(days) / float(rows)))
+
+        # Update the timeline.
+        self.timeline.set_visible(rows == 1)
+
+        # Show captions for the day.
+        if cols == 7 or rows == 1:
+            for child in self.day_captions.get_children():
+                self.day_captions.remove(child)
+            for col in range(cols):
+                this_date = self.range[0] + datetime.timedelta(col)
+                day_name  = self.cal.model.get_day_name(this_date)
+                text      = hippo.CanvasText(text      = day_name,
+                                             xalign    = hippo.ALIGNMENT_CENTER,
+                                             size_mode = hippo.CANVAS_SIZE_ELLIPSIZE_END)
+                self.day_captions.add(text, col, col + 1, 0, 1)
+                self.day_captions.set_column_expand(col, True)
+            self.day_captions.set_visible(True)
+        else:
+            self.day_captions.set_visible(False)
+
+        # Update the grid.
+        self.grid.set_size(rows, cols)
+        for row in range(rows):
+            self.grid.set_row_expand(row, True)
+        for child in self.grid.get_children():
+            child.set_active(self.is_active(date))
+            child.set_show_title(rows != 1)
+            child.set_show_rulers(rows == 1)
+            child.set_selected(date == self.selected)
+            child.set_highlighted(date == today)
+            child.set_date(date)
+            child.update()
+            date = util.next_day(date)
+
+        if rows == 1:
+            self.update_one_row()
+        else:
+            self.update_multi_row()
+
+
+    def do_allocate_one_row(self, width, height, origin_changed):
+        grid_x, grid_y = self.gridbox.get_position(self.grid)
+        grid_w, grid_h = self.grid.get_allocation()
+
+        for cell in self.grid.get_children():
+            cell_x_off,  cell_y_off  = self.grid.get_position(cell)
+
+            # the 18 number is for the size of the scrollbar on the right
+            # (don't know how to get it)
+            try:
+                cell_x_off = math.ceil((float(cell_x_off)) * (width - grid_x - 18) / grid_w)
+            except:
+                pass
+
+            cell_w,      cell_h      = cell.get_allocation()
+            x                        = int(grid_x + cell_x_off)
+            y                        = int(grid_y + cell_y_off)
+            w                        = cell_w
+            h                        = cell_h
+            view                     = self.vevent_views[cell]
+            alloc                    = (x, y, w, h)
+
+            if self.allocs[view] == alloc:
+                continue
+            self.allocs[view] = alloc
+            self.gridbox.set_position(view, alloc[0], alloc[1])
+            view.set_properties(box_width  = alloc[2],
+                                box_height = alloc[3])
+
+
+    def do_allocate_multi_row(self, width, height, origin_changed):
+        days           = (self.range[1] - self.range[0]).days + 1
+        rows           = int(math.ceil(float(days) / 7.0))
+        cols           = days
+        grid_x, grid_y = self.gridbox.get_position(self.grid)
+        if days > 7:
+            cols = int(math.ceil(float(days) / float(rows)))
+
+        for row in self.grid.get_rows():
+            start = row[0].date
+            end   = row[-1].date
+            view  = self._get_event_view(row[0], start, end, True)
+            view.set_column_count(cols)
+
+            # Find the position of the writable area of the row.
+            grid_w,      grid_h      = self.grid.get_allocation()
+            grid_w,      grid_h      = (width, height)
+            row_x_off,   row_y_off   = self.grid.get_position(row[0])
+            cell_w,      cell_h      = row[0].get_allocation()
+            title_x_off, title_y_off = row[0].get_body_position()
+            x                        = grid_x + row_x_off
+            y                        = grid_y + row_y_off + title_y_off
+            w                        = grid_w
+            h                        = cell_h - title_y_off
+            alloc                    = (x, y, w, h)
+
+            if self.allocs[view] == alloc:
+                continue
+            self.allocs[view] = alloc
+            self.gridbox.set_position(view, alloc[0], alloc[1])
+            view.set_properties(box_width  = alloc[2],
+                                box_height = alloc[3])
+
+
+    def do_allocate(self, width, height, origin_changed):
+        days           = (self.range[1] - self.range[0]).days + 1
+        rows           = int(math.ceil(float(days) / 7.0))
+        cols           = days
+        grid_x, grid_y = self.gridbox.get_position(self.grid)
+        if days > 7:
+            cols = int(math.ceil(float(days) / float(rows)))
+
+        # Show the items for one-row mode or multi-row mode.
+        if rows == 1:
+            self.do_allocate_one_row(width, height, origin_changed)
+        else:
+            self.do_allocate_multi_row(width, height, origin_changed)
+        hippo.CanvasBox.do_allocate(self, width, height, origin_changed)
+
+
+    def on_grid_paint_one_row(self):
+        w,          h          = self.get_allocation()
+        grid_x,     grid_y     = self.gridbox.get_position(self.grid)
+        grid_w,     grid_h     = self.grid.get_allocation()
+        timeline_w, timeline_h = self.timeline.get_allocation()
+        padding_left           = timeline_w
+        padding_right          = w - timeline_w - grid_w
+
+        if self.allocs[self.padding_left][2] != padding_left:
+            self.allocs[self.padding_left] = (0, 0, padding_left, 0)
+            self.padding_left.set_properties(box_width = padding_left)
+        if self.allocs[self.padding_right][2] != padding_right:
+            self.allocs[self.padding_right] = (0, 0, padding_right, 0)
+            self.padding_right.set_properties(box_width = padding_right)
+
+
+    def on_grid_paint(self, grid, ptr, rect):
+        # catching this signal is ugly, but trying to do this
+        # in do_allocate() will result in painful to avoid event
+        # loops.
+        days = (self.range[1] - self.range[0]).days + 1
+        rows = int(math.ceil(float(days) / 7.0))
+
+        if rows == 1:
+            self.on_grid_paint_one_row()
+
+
+gobject.type_register(CanvasDayRange)
+
+gobject.signal_new('day-clicked',
+                   CanvasDayRange,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+
+gobject.signal_new('time-clicked',
+                   CanvasDayRange,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+
+gobject.signal_new('event-clicked',
+                   CanvasDayRange,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasEvent.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasEvent.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasEvent.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+#
+##############################################################################
+
+import hippo
+import gobject
+from SpiffGtkWidgets import color
+from CanvasRectangle import CanvasRectangle
+
+class CanvasEvent(CanvasRectangle):
+    """
+    A canvas item representing a day.
+    """
+    def __init__(self, cal, event, **kwargs):
+        """
+        Constructor.
+        """
+        self.cal    = cal
+        self.event  = event
+        self.rulers = []
+        CanvasRectangle.__init__(self, **kwargs)
+        # Create canvas items.
+        self.text = hippo.CanvasText(xalign    = hippo.ALIGNMENT_CENTER,
+                                     yalign    = hippo.ALIGNMENT_CENTER,
+                                     size_mode = hippo.CANVAS_SIZE_ELLIPSIZE_END)
+        self.append(self.text, hippo.PACK_EXPAND)
+
+    def set_text(self, text, description = ''):
+        self.text.set_property('text', text + ', ' + description)
+
+
+    def set_text_color(self, newcolor):
+        self.text.props.color = color.to_int(newcolor)
+
+
+    def set_text_properties(self, **kwargs):
+        self.text.set_properties(**kwargs)
+
+
+gobject.type_register(CanvasEvent)

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasEventView.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasEventView.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasEventView.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+import datetime
+import util
+from SpiffGtkWidgets import color
+from CanvasEvent import CanvasEvent
+
+class CanvasEventView(hippo.CanvasBox):
+    """
+    A canvas item that shows a range of events.
+    """
+    def __init__(self, cal, start = None, end = None, **kwargs):
+        """
+        Constructor.
+        """
+        hippo.CanvasBox.__init__(self, **kwargs)
+
+        self.cal         = cal
+        self.model       = cal.model
+        self.range       = None
+        self.event_items = {}
+        self.allocs      = {}
+        self.set_range(start, end)
+        self.connect_after('button-press-event', self.on_button_press_event)
+        self.model.connect('event-added',   self.on_model_event_added)
+        self.model.connect('event-removed', self.on_model_event_removed)
+
+
+    def on_model_event_added(self, model, event):
+        self.update()
+
+
+    def on_model_event_removed(self, model, event):
+        if event is not None \
+            and event not in self.event_items:
+            return
+        self.update()
+
+
+    def on_button_press_event(self, widget, event):
+        w, h  = self.get_allocation()
+        days  = (self.range[1] - self.range[0]).days + 1
+        day_w = w / float(days)
+        day   = int(event.x / day_w)
+        date  = self.range[0] + datetime.timedelta(1) * day
+        time  = 24 / float(h) * event.y
+        hour  = int(time)
+        min   = (time - hour) * 60
+        date += datetime.timedelta(0, 0, 0, 0, min, hour)
+        self.emit('time-clicked', widget, event, date)
+
+
+    def on_event_button_press_event(self, widget, event):
+        self.emit('event-clicked', widget, event)
+        return True
+
+
+    def on_event_button_release_event(self, widget, event):
+        self.emit('event-released', widget, event)
+        return True
+
+
+    def set_range(self, start, end):
+        if start is None or end is None:
+            return
+        range = datetime.datetime(*start.timetuple()[:3]), \
+                datetime.datetime(*end.timetuple()[:7])
+
+        # Update end if it's a `datetime.date' and not a `datetime.datetime',
+        # because day ranges are inclusive (so day must _end_ at 23:59:59)
+        if isinstance(end, datetime.date):
+            range = range[0], util.end_of_day(range[1])
+
+        if self.range is not None \
+          and self.range[0] == range[0] \
+          and self.range[1] == range[1]:
+            return
+        self.range = range
+        self.update()
+
+
+gobject.type_register(CanvasEventView)
+
+gobject.signal_new('time-clicked',
+                   CanvasEventView,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT,
+                    gobject.TYPE_PYOBJECT,
+                    gobject.TYPE_PYOBJECT))
+
+gobject.signal_new('event-clicked',
+                   CanvasEventView,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+
+gobject.signal_new('event-released',
+                   CanvasEventView,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasGrid.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasGrid.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasGrid.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+import calendar
+import pango
+import util
+from CanvasTable import CanvasTable
+
+class CanvasGrid(CanvasTable):
+    """
+    A table item that automatically retrieves the cell content from a given
+    data provider.
+    """
+    def __init__(self, provider, **kwargs):
+        """
+        Constructor.
+        """
+        CanvasTable.__init__(self, 1, 1)
+        self.provider = provider
+
+
+    def _new_cell(self):
+        cell = self.provider()
+        return cell
+
+
+    def _add_line(self, length):
+        rows, cols = self.get_size()
+        for colnum in range(length):
+            self.set_column_expand(colnum, True)
+            self.add(self._new_cell(), colnum, colnum + 1, rows, rows + 1)
+        self.set_row_expand(rows, True)
+
+
+    def _add_column(self):
+        rows, cols = self.get_size()
+        for rownum in range(rows):
+            self.add(self._new_cell(), cols, cols + 1, rownum, rownum + 1)
+
+
+    def set_size(self, rows, cols):
+        old_rows, old_cols = self.get_size()
+
+        # Create new cells if the new size is bigger.
+        if cols > old_cols:
+            for x in range(old_cols, cols):
+                self._add_column()
+        if rows > old_rows:
+            for rownum in range(old_rows, rows):
+                self._add_line(cols)
+
+        # Remove cells if the new size is smaller.
+        self.shrink(rows, cols)
+        CanvasTable.set_size(self, rows, cols)

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasHEventView.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasHEventView.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasHEventView.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+import pango
+import datetime
+import util
+from SpiffGtkWidgets   import color
+from CanvasEvent       import CanvasEvent
+from CanvasEventView   import CanvasEventView
+from CanvasMagnetTable import CanvasMagnetTable
+
+class CanvasHEventView(CanvasEventView, hippo.CanvasItem):
+    """
+    A canvas item that shows a range of events.
+    """
+    def __init__(self, cal, start = None, end = None, **kwargs):
+        """
+        Constructor.
+        """
+        CanvasEventView.__init__(self, cal, **kwargs)
+        self.table = CanvasMagnetTable(align = CanvasMagnetTable.ALIGN_TOP)
+        self.table.set_homogeneus_columns(True)
+        self.append(self.table, hippo.PACK_EXPAND)
+        self.show_normal = True  # whether to show normal events
+        self.overflow_indic = []
+        self.columns = 0
+
+
+    def _format_time(self, event):
+        hour, minute = event.start.timetuple()[3:5]
+#        if minute == 0:
+#            text = '%d %s' % (hour, event.caption)
+#        else:
+        text = '%d:%02d %s' % (hour, minute, event.caption)
+        return text
+
+
+    def set_column_count(self, count):
+        if count == self.columns:
+            return
+        self.columns = count
+
+        self.table.set_column_count(count)
+
+        # Hide all overflow indicators.
+        for child in self.get_children():
+            if child != self.table:
+                self.remove(child)
+
+        for col in range(count):
+            font = self.cal.font.copy()
+            font.set_style(pango.STYLE_ITALIC)
+            text = hippo.CanvasText(text   = ' ',
+                                    font   = font.to_string(),
+                                    xalign = hippo.ALIGNMENT_CENTER)
+            self.append(text, hippo.PACK_FIXED)
+            self.overflow_indic.append(text)
+            text.set_visible(False)
+            self.allocs[text] = (0, 0, 0, 0)
+
+
+    def _add_event(self, event):
+        event_start      = max(event.start, self.range[0])
+        event_end        = min(event.end,   self.range[1])
+        event_off_days   = (event_start - self.range[0]).days
+        event_width_days = (event_end - event_start).days + 1
+
+        # Create the event.
+        item = CanvasEvent(self.cal, event)
+        self.event_items[event] = item
+        item.connect('button-press-event',   self.on_event_button_press_event)
+        item.connect('button-release-event', self.on_event_button_release_event)
+        self.table.add(item,
+                       event_off_days,
+                       event_off_days + event_width_days,
+                       len(self.event_items))
+        item.set_text(event.caption, event.description)
+        item.set_property('color', color.to_int(event.bg_color))
+        if self.show_normal and not util.same_day(event.start, event.end):
+            item.set_property('color', color.to_int(event.bg_color))
+        elif not event.all_day:
+            time = self._format_time(event)
+            item.set_text(time, event.description)
+            item.set_text_properties(xalign = hippo.ALIGNMENT_START)
+        if event.text_color is not None:
+            item.set_text_color(event.text_color)
+        radius_top_left     = 10
+        radius_top_right    = 10
+        radius_bottom_left  = 10
+        radius_bottom_right = 10
+        if event.end > self.range[1]:
+            radius_top_right    = 0
+            radius_bottom_right = 0
+        if event.start < self.range[0]:
+            radius_top_left    = 0
+            radius_bottom_left = 0
+        item.set_properties(radius_top_left     = radius_top_left,
+                            radius_top_right    = radius_top_right,
+                            radius_bottom_left  = radius_bottom_left,
+                            radius_bottom_right = radius_bottom_right)
+
+
+    def update(self):
+        # Don't crash if we didn't have a range now
+        if self.range is None:
+            return
+
+        days = (self.range[1] - self.range[0]).days + 1
+        self.table.set_column_count(days)
+        self.table.set_row_count(-1)
+
+        # Remove old events.
+        for item in self.table.get_children():
+            self.table.remove(item)
+
+        # Add events.
+        if self.range is None:
+            return
+        for event in self.model.get_all_day_events(self.range[0],
+                                                   self.range[1],
+                                                   self.show_normal == True):
+            self._add_event(event)
+        if self.show_normal:
+            for event in self.model.get_normal_events(self.range[0],
+                                                      self.range[1],
+                                                      False):
+                self._add_event(event)
+
+        # Force all children to be visible, to fix 'overflow' positioning.
+        for child in self.get_children():
+            child.set_visible(True)
+
+        # Change to fixed sizing.
+        rows, cols = self.table.get_size()
+        self.table.set_size(rows, cols)
+
+
+    def do_allocate(self, width, height, origin_changed):
+        CanvasEventView.do_allocate(self, width, height, origin_changed)
+
+        # Hide all overflow indicators.
+        for child in self.get_children():
+            if child != self.table:
+                child.set_visible(False)
+
+        rows, cols = self.table.get_size()
+        if min(rows, width, height) <= 0:
+            return
+
+        # Measure the height of the first event.
+        children = self.table.get_children()
+        if len(children) == 0:
+            return
+        first = children[0]
+        min_row_h, row_h = first.get_height_request(width / cols)
+        if row_h <= 0:
+            return
+
+        # Hide events that do not fit into the box.
+        max_rows = height / row_h
+        matrix   = self.table.get_matrix()
+        for colnum, col in enumerate(matrix.get_columns()):
+            # Count rows that are already hidden.
+            hidden = 0
+            for child in col:
+                if not child.get_visible():
+                    hidden += 1
+
+            # No need to hide anything if the box is large enough.
+            if len(col) <= max_rows:
+                for child in col:
+                    child.set_visible(True)
+                continue
+
+            # Hide enough rows to make room for an overflow indicator.
+            to_hide = len(col) - max_rows + 1
+            hidden  = 0
+            for row in reversed(col):
+                if hidden >= to_hide:
+                    row.set_visible(True)
+                    continue
+                if not row.get_visible():
+                    hidden += 1
+                    continue
+                row.set_visible(False)
+                hidden += 1
+
+            # Show overflow indicator
+            indic   = self.overflow_indic[colnum]
+            caption = '%d more' % hidden
+            alloc   = (width / cols * colnum, height - row_h, width / cols, hidden)
+            indic.set_visible(True)
+
+            if self.allocs[indic] == alloc:
+                continue
+            self.allocs[indic] = alloc
+            indic.set_properties(text        = caption,
+                                 box_width   = alloc[2])
+            self.set_position(indic, alloc[0], alloc[1])
+        CanvasEventView.do_allocate(self, width, height, origin_changed)
+
+
+gobject.type_register(CanvasHEventView)

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasMagnetTable.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasMagnetTable.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasMagnetTable.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+import sys
+import util
+from SpiffGtkWidgets import color
+from CanvasTable import CanvasTable
+
+class CanvasMagnetTable(CanvasTable):
+    """
+    A table that works similar to four-in-a-row. It has a number homogeneous
+    columns, and every child is dragged towards the top of the column.
+    The table also allows for adding children that span multiple columns.
+    """
+    ALIGN_TOP    = 1
+    ALIGN_BOTTOM = 2
+    ALIGN_LEFT   = 4
+    ALIGN_RIGHT  = 8
+
+    __gproperties__ = {
+        'align':    (gobject.TYPE_LONG,
+                     'the alignment',
+                     'the direction into which children are pulled. one of'
+                   + ' ALIGN_TOP, ALIGN_BOTTOM, ALIGN_LEFT or ALIGN_RIGHT',
+                     1,
+                     4,
+                     ALIGN_TOP,
+                     gobject.PARAM_READWRITE)
+    }
+    def __init__(self, **kwargs):
+        """
+        Constructor.
+        """
+        self.property_names = ('align',)
+        self.align          = self.ALIGN_TOP
+        CanvasTable.__init__(self, **kwargs)
+
+
+    def do_get_property(self, property):
+        if property.name in self.property_names:
+            return self.__getattribute__(property.name.replace('-', '_'))
+        else:
+            raise AttributeError, 'unknown property %s' % property.name
+
+
+    def do_set_property(self, property, value):
+        if property.name in self.property_names:
+            return self.__setattr__(property.name.replace('-', '_'), value)
+        else:
+            raise AttributeError, 'unknown property %s' % property.name
+
+
+    def _shift(self, matrix, move_func):
+        for cell in matrix.get_cells():
+            old_pos = matrix.get_pos(cell)
+            new_pos = move_func(cell)
+            #print "OLD", old_pos, "NEW", new_pos, cell.event.caption
+            if old_pos != new_pos:
+                self.remove(cell)
+                self.add(cell, new_pos[0], new_pos[2], new_pos[1], new_pos[3])
+
+
+    def add(self, child, left=None, right=None, top=None, bottom=None, flags=0):
+        CanvasTable.add(self, child, left, right, top, bottom, flags)
+        matrix = self.get_matrix()
+        if self.align == self.ALIGN_TOP:
+            self._shift(matrix, matrix.move_top)
+        elif self.align == self.ALIGN_BOTTOM:
+            self._shift(matrix, matrix.move_bottom)
+        if self.align == self.ALIGN_LEFT:
+            self._shift(matrix, matrix.move_left)
+        elif self.align == self.ALIGN_RIGHT:
+            self._shift(matrix, matrix.move_right)
+
+gobject.type_register(CanvasMagnetTable)

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasRectangle.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasRectangle.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasRectangle.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+from SpiffGtkWidgets import color
+import sys
+
+class CanvasRectangle(hippo.CanvasBox):
+    """
+    A canvas item that draws a rectangle, optionally with rounded corners.
+    """
+    __gproperties__ = {
+        'radius-top-left':     (gobject.TYPE_LONG,
+                                'top left radius',
+                                'radius of the top left corner',
+                                0,
+                                sys.maxint,
+                                10,
+                                gobject.PARAM_READWRITE),
+        'radius-top-right':    (gobject.TYPE_LONG,
+                                'top right radius',
+                                'radius of the top right corner',
+                                0,
+                                sys.maxint,
+                                10,
+                                gobject.PARAM_READWRITE),
+        'radius-bottom-left':  (gobject.TYPE_LONG,
+                                'bottom left radius',
+                                'radius of the bottom left corner',
+                                0,
+                                sys.maxint,
+                                10,
+                                gobject.PARAM_READWRITE),
+        'radius-bottom-right': (gobject.TYPE_LONG,
+                                'bottom right radius',
+                                'radius of the bottom right corner',
+                                0,
+                                sys.maxint,
+                                10,
+                                gobject.PARAM_READWRITE),
+    }
+
+    def __init__(self, **kwargs):
+        """
+        Constructor.
+        """
+        self.property_names = ('radius-top-left',
+                               'radius-top-right',
+                               'radius-bottom-left',
+                               'radius-bottom-right')
+        self.radius_top_left     = 10
+        self.radius_top_right    = 10
+        self.radius_bottom_left  = 10
+        self.radius_bottom_right = 10
+        hippo.CanvasBox.__init__(self, **kwargs)
+
+
+    def do_get_property(self, property):
+        if property.name in self.property_names:
+            return self.__getattribute__(property.name.replace('-', '_'))
+        else:
+            raise AttributeError, 'unknown property %s' % property.name
+
+
+    def do_set_property(self, property, value):
+        if property.name in self.property_names:
+            return self.__setattr__(property.name.replace('-', '_'), value)
+        else:
+            raise AttributeError, 'unknown property %s' % property.name
+
+
+    def do_paint_below_children(self, ctx, rect):
+        ctx.set_source_rgba(*color.to_rgba(self.props.color))
+        ctx.rectangle(rect.x, rect.y, rect.width, rect.height)
+        ctx.clip()
+        rtl  = self.props.radius_top_left
+        rtr  = self.props.radius_top_right
+        rbl  = self.props.radius_bottom_left
+        rbr  = self.props.radius_bottom_right
+        x, y = 0, 0
+        w, h = self.get_allocation()
+
+        #  A****BQ
+        # H      C
+        # *      *
+        # G      D
+        #  F****E
+        ctx.move_to(x+rtl,y)                      # A
+        ctx.line_to(x+w-rtr,y)                    # B
+        ctx.curve_to(x+w,y,x+w,y,x+w,y+rtr)       # C, both control points at Q
+        ctx.line_to(x+w,y+h-rbr)                  # D
+        ctx.curve_to(x+w,y+h,x+w,y+h,x+w-rbr,y+h) # E
+        ctx.line_to(x+rbl,y+h)                    # F
+        ctx.curve_to(x,y+h,x,y+h,x,y+h-rbl)       # G
+        ctx.line_to(x,y+rtl)                      # H
+        ctx.curve_to(x,y,x,y,x+rtl,y)             # A
+        ctx.fill()
+
+gobject.type_register(CanvasRectangle)

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasTable.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasTable.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasTable.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import gtk
+import gobject
+import hippo
+from TableLayout import TableLayout
+from Matrix      import Matrix
+
+class CanvasTable(hippo.CanvasBox):
+    def __init__(self, column_spacing=0, row_spacing=0, **kwargs):
+        hippo.CanvasBox.__init__(self, **kwargs)
+
+        self.__layout = TableLayout(column_spacing=column_spacing, row_spacing=row_spacing)
+        self.set_layout(self.__layout)
+
+    def add(self, child, left=None, right=None, top=None, bottom=None, flags=0):
+        self.__layout.add(child, left, right, top, bottom, flags)
+
+    def remove(self, child):
+        hippo.CanvasBox.remove(self, child)
+
+    def set_homogeneus_rows(self, homogeneus):
+        self.__layout.set_homogeneus_rows(homogeneus)
+
+    def set_homogeneus_columns(self, homogeneus):
+        self.__layout.set_homogeneus_columns(homogeneus)
+
+    def set_column_expand(self, column, expand):
+        self.__layout.set_column_expand(column, expand)
+
+    def set_row_expand(self, row, expand):
+        self.__layout.set_row_expand(row, expand)
+
+    def set_row_count(self, rows):
+        self.__layout.set_row_count(rows)
+
+    def set_column_count(self, cols):
+        self.__layout.set_column_count(cols)
+
+    def set_size(self, rows, cols):
+        self.__layout.set_size(rows, cols)
+
+    def get_size(self):
+        rows = self.__layout.get_row_count()
+        cols = self.__layout.get_column_count()
+        return rows, cols
+
+    def get_total_row_spacing(self):
+        return self.__layout.get_total_row_spacing()
+
+    def get_total_column_spacing(self):
+        return self.__layout.get_total_column_spacing()
+
+    def shrink(self, rows, cols):
+        for child in self.get_children():
+            box = self.find_box_child(child)
+            if box.bottom > rows or box.right > cols:
+                self.remove(child)
+
+    def get_matrix(self):
+        rows, cols = self.get_size()
+        matrix     = Matrix(rows, cols)
+        for child in self.get_children():
+            box = self.find_box_child(child)
+            matrix.set(child, box.left, box.top, box.right, box.bottom)
+        return matrix
+
+    def get_rows(self):
+        matrix = self.get_matrix()
+        return matrix.get_rows()
+
+    def remove_empty_rows(self):
+        pass #FIXME

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasTimeline.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasTimeline.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasTimeline.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+import calendar
+import pango
+
+class CanvasTimeline(hippo.CanvasBox):
+    """
+    A canvas item representing a timeline.
+    """
+    def __init__(self, cal, **kwargs):
+        """
+        Constructor.
+        """
+        hippo.CanvasBox.__init__(self, **kwargs)
+
+        self.cal  = cal
+        self.text = {}
+
+        # Create canvas items.
+        for n in range(0, 24):
+            if n == -1:
+                caption = ' '
+            else:
+                caption = '%d' % n
+            box     = hippo.CanvasGradient(padding_right = 5)
+            text    = hippo.CanvasText(text   = caption,
+                                       xalign = hippo.ALIGNMENT_END,
+                                       yalign = hippo.ALIGNMENT_START)
+            box.append(text, hippo.PACK_EXPAND)
+            self.append(box, hippo.PACK_EXPAND)
+
+
+    def _set_color(self, box, color):
+        box.set_property('start-color', color)
+        box.set_property('end-color',   color)
+
+
+    def update(self):
+        line_height = self.height / 24
+
+        # Draw the timeline.
+        for n, box in enumerate(self.get_children()):
+            text = box.get_children()[0]
+            self._set_color(box, self.cal.colors['border'])
+            text.set_property('font',  self.cal.font.to_string())
+            text.set_property('color', self.cal.colors['text'])

=== added file 'bin/SpiffGtkWidgets/Calendar/CanvasVEventView.py'
--- bin/SpiffGtkWidgets/Calendar/CanvasVEventView.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/CanvasVEventView.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import hippo
+import gobject
+import datetime
+import util
+from SpiffGtkWidgets import color
+from math              import ceil
+from CanvasEvent       import CanvasEvent
+from CanvasEventView   import CanvasEventView
+from CanvasMagnetTable import CanvasMagnetTable
+
+class CanvasVEventView(CanvasEventView):
+    """
+    A canvas item that shows a range of events.
+    """
+    def __init__(self, cal, start = None, end = None, **kwargs):
+        """
+        Constructor.
+        """
+        self.table = CanvasMagnetTable(align = CanvasMagnetTable.ALIGN_LEFT)
+        self.table.set_row_count(24 * 4)
+        self.table.set_homogeneus_rows(True)
+        CanvasEventView.__init__(self, cal, start, end, **kwargs)
+        self.append(self.table, hippo.PACK_EXPAND)
+
+
+    def _add_event(self, event):
+        rows, cols     = self.table.get_size()
+        event_start    = max(event.start, self.range[0])
+        event_end      = min(event.end,   self.range[1])
+        event_off      = (event_start   - self.range[0]).seconds
+        event_len      = (event_end     - event_start).seconds
+        range_len      = self.range[1] - self.range[0]
+        seconds        = range_len.days * 24 * 60 * 60 + range_len.seconds
+        row_seconds    = ceil(seconds / float(rows))
+        event_off_rows = int(ceil(event_off / float(row_seconds)))
+        event_len_rows = int(ceil(event_len / float(row_seconds)))
+        event_end_rows = event_off_rows + event_len_rows
+
+        # Create the event.
+        item = CanvasEvent(self.cal, event)
+        self.event_items[event] = item
+        item.connect('button-press-event',   self.on_event_button_press_event)
+        item.connect('button-release-event', self.on_event_button_release_event)
+        self.table.set_column_expand(cols, True)
+        self.table.add(item, cols + 1, cols + 2, event_off_rows, event_end_rows)
+        item.set_text(event.caption, event.description)
+        item.set_property('color', color.to_int(event.bg_color))
+        if event.text_color is not None:
+            item.set_text_color(event.text_color)
+
+        radius_top_left     = 10
+        radius_top_right    = 10
+        radius_bottom_left  = 10
+        radius_bottom_right = 10
+        if event.end > self.range[1]:
+            radius_bottom_left  = 0
+            radius_bottom_right = 0
+        if event.start < self.range[0]:
+            radius_top_left  = 0
+            radius_top_right = 0
+        item.set_properties(radius_top_left     = radius_top_left,
+                            radius_top_right    = radius_top_right,
+                            radius_bottom_left  = radius_bottom_left,
+                            radius_bottom_right = radius_bottom_right)
+
+
+    def update(self):
+        # Don't crash if we didn't have a range now
+        if self.range is None:
+            return
+
+        # Remove old events.
+        for item in self.table.get_children():
+            self.table.remove(item)
+        self.table.set_column_count(0)
+
+        if self.range is None:
+            return
+        for event in self.model.get_normal_events(*self.range):
+            self._add_event(event)
+
+
+gobject.type_register(CanvasVEventView)

=== added file 'bin/SpiffGtkWidgets/Calendar/Event.py'
--- bin/SpiffGtkWidgets/Calendar/Event.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/Event.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import util
+import datetime
+
+class Event(object):
+    """
+    This class represents an event that can be displayed in the calendar.
+    """
+
+    def __init__(self, caption, start, end = None, **kwargs):
+        """
+        Constructor.
+
+        start -- datetime
+        end -- datetime
+        """
+        assert caption is not None
+        assert start   is not None
+        self.id         = None
+        self.caption    = caption
+        self.start      = start
+        self.end        = end
+        self.all_day    = kwargs.get('all_day',    False)
+        self.text_color = kwargs.get('text_color', None)
+        self.bg_color   = kwargs.get('bg_color',   'orangered')
+        self.description = kwargs.get('description','')
+
+        if end is None:
+            self.all_day = True
+            self.end     = start
+
+        if end is not None:
+            # Check if end date (deadline) have a time set to 00:00:00,
+            # this means the event should really end on the day before,
+            # so remove 'one' second.
+            end_day = datetime.datetime(*end.timetuple()[:3])
+            end_day_seconds = datetime.datetime(*end.timetuple()[:6])
+            if end_day == end_day_seconds:
+                self.end = end - datetime.timedelta(seconds=1)
+
+        if self.all_day:
+            self.end = util.end_of_day(self.end)

=== added file 'bin/SpiffGtkWidgets/Calendar/Matrix.py'
--- bin/SpiffGtkWidgets/Calendar/Matrix.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/Matrix.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,241 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+class Matrix(object):
+    def __init__(self, rows = -1, cols = -1):
+        self.rows   = rows
+        self.cols   = cols
+        self.matrix = []
+        self.resize(max(self.rows, 1), max(self.cols, 1))
+
+
+    def resize(self, rows, cols):
+        old_rows,  old_cols  = self.get_size()
+        diff_rows, diff_cols = rows - old_rows, cols - old_cols
+        while diff_rows > 0:
+            self.matrix.append([None for f in range(old_cols)])
+            diff_rows -= 1
+        while diff_rows < 0:
+            self.matrix.pop()
+            diff_rows += 1
+        for i in range(rows):
+            diff = diff_cols
+            while diff > 0:
+                self.matrix[i].append(None)
+                diff -= 1
+            while diff < 0:
+                self.matrix[i].pop()
+                diff += 1
+
+
+    def set(self, value, x1, y1, x2, y2):
+        assert x1 <= x2
+        assert y1 <= y2
+
+        # Make sure that the table is big enough.
+        rows, cols = self.get_size()
+        if x2 > cols and self.cols != -1:
+            msg = 'Adding a cell at column %s in a matrix with %s columns'
+            raise Exception(msg % (x2, cols))
+        elif y2 > rows and self.rows != -1:
+            msg = 'Adding a cell at row %s in a matrix with %s rows'
+            raise Exception(msg % (y2, rows))
+
+        # Resize if necessary.
+        if x2 > cols or y2 > rows:
+            cols = max(cols, x2)
+            rows = max(rows, y2)
+            self.resize(rows, cols)
+
+        # Allocate the cells to the new child.
+        for rownum in range(y1, y2):
+            for colnum in range(x1, x2):
+                self.matrix[rownum][colnum] = value
+
+
+    def unset(self, child):
+        free_rows = []
+        for rownum, row in enumerate(self.matrix):
+            for cellnum, cell in enumerate(row):
+                if cell == child:
+                    row[cellnum] = None
+
+        # Remove free rows from the end of the table.
+        if self.rows == -1:
+            free_rows = 0
+            for rows in reversed(self.matrix):
+                if not self._row_is_free(rows):
+                    break
+                free_rows += 1
+            for row in range(free_rows):
+                self.matrix.pop()
+
+        # Remove free columns from the end of the table.
+        if self.cols == -1:
+            free_cols = 0
+            for rows in reversed(self.matrix):
+                free_cells = 0
+                for cell in reversed(row):
+                    if cell is not None:
+                        break
+                    free_cells += 1
+                free_cols = min(free_cells, free_cols)
+                if free_cols == 0:
+                    break
+            for row in self.matrix:
+                for cell in range(free_cols):
+                    row.pop()
+
+
+    def get_size(self):
+        rows = len(self.matrix)
+        if rows == 0:
+            return 0, 0
+        return rows, len(self.matrix[0])
+
+
+    def get_pos(self, child):
+        x1, y1, x2, y2 = -1, -1, -1, -1
+        for rownum, row in enumerate(self.matrix):
+            for colnum, cell in enumerate(row):
+                if cell != child:
+                    continue
+                if x1 == -1:
+                    x1 = colnum
+                else:
+                    x1 = min(x1, colnum)
+                if y1 == -1:
+                    y1 = rownum
+                else:
+                    y1 = min(y1, rownum)
+                x2 = max(x2, colnum + 1)
+                y2 = max(y2, rownum + 1)
+        return x1, y1, x2, y2
+
+
+    def move_top(self, child):
+        x1, y1, x2, y2 = self.get_pos(child)
+        while True:
+            if y1 <= 0:
+                break
+            if not self.is_free(x1, y1 - 1, x2, y1):
+                break
+            self.set(child, x1, y1 - 1, x2, y1)
+            self.set(None,  x1, y2 - 1, x2, y2)
+            y1 -= 1
+            y2 -= 1
+        return x1, y1, x2, y2
+
+
+    def move_bottom(self, child):
+        rows, cols     = self.get_size()
+        x1, y1, x2, y2 = self.get_pos(child)
+        while True:
+            if y2 >= rows:
+                break
+            if not self.is_free(x1, y2, x2, y2 + 1):
+                break
+            self.set(child, x1, y2, x2, y2 + 1)
+            self.set(None,  x1, y1, x2, y1 + 1)
+            y1 += 1
+            y2 += 1
+        return x1, y1, x2, y2
+
+
+    def move_left(self, child):
+        x1, y1, x2, y2 = self.get_pos(child)
+        while True:
+            if x1 <= 0:
+                break
+            if not self.is_free(x1 - 1, y1, x1, y2):
+                break
+            self.set(child, x1 - 1, y1, x1, y2)
+            self.set(None,  x2 - 1, y1, x2, y2)
+            x1 -= 1
+            x2 -= 1
+        return x1, y1, x2, y2
+
+
+    def move_right(self, child):
+        rows, cols     = self.get_size()
+        x1, y1, x2, y2 = self.get_pos(child)
+        while True:
+            if x2 >= cols:
+                break
+            if not self.is_free(x2, y1, x2 + 1, y2):
+                break
+            self.set(child, x2, y1, x2 + 1, y2)
+            self.set(None,  x1, y1, x1 + 1, y2)
+            x1 += 1
+            x2 += 1
+        return x1, y1, x2, y2
+
+
+    def get_cells(self):
+        cells = []
+        for row in self.matrix:
+            for cell in row:
+                if cell is not None and cell not in cells:
+                    cells.append(cell)
+        return cells
+
+
+    def get_rows(self):
+        rows = []
+        for row in self.matrix:
+            cells = []
+            for cell in row:
+                if cell is not None and cell not in cells:
+                    cells.append(cell)
+            rows.append(cells)
+        return rows
+
+
+    def get_columns(self):
+        rows, cols = self.get_size()
+        result     = [[] for col in range(cols)]
+        for row in self.matrix:
+            for colnum, cell in enumerate(row):
+                if cell is not None and cell not in result[colnum]:
+                    result[colnum].append(cell)
+        return result
+
+
+    def is_free(self, x1, y1, x2, y2):
+        for rownum in range(y1, y2):
+            for colnum in range(x1, x2):
+                if self.matrix[rownum][colnum] is not None:
+                    return False
+        return True
+
+
+    def row_is_free(self, row):
+        for cell in row:
+            if cell is not None:
+                return False
+        return True
+
+
+    def dump(self):
+        for row in self.matrix:
+            for cell in row:
+                if cell is None:
+                    print "None",
+                else:
+                    print "CELL",
+            print

=== added file 'bin/SpiffGtkWidgets/Calendar/Model.py'
--- bin/SpiffGtkWidgets/Calendar/Model.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/Model.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import gobject
+import datetime
+import calendar
+import util
+from MyCalendar import MyCalendar
+
+class Model(gobject.GObject):
+    __gsignals__ = {
+        'event-removed': (gobject.SIGNAL_RUN_FIRST,
+                          gobject.TYPE_NONE,
+                          (gobject.TYPE_PYOBJECT,)),
+        'event-added': (gobject.SIGNAL_RUN_FIRST,
+                        gobject.TYPE_NONE,
+                        (gobject.TYPE_PYOBJECT,))
+    }
+
+    def __init__(self):
+        """
+        Constructor.
+
+        start -- datetime
+        end -- datetime
+        """
+        self.__gobject_init__()
+        self.next_event_id = 0
+        self.calendar      = MyCalendar(calendar.SUNDAY)
+        self.events        = {}
+
+
+    def get_week(self, date):
+        """
+        Returns a tuple (start, end), where "start" points to the first day
+        of the given week, and "end" points to the last day of given week.
+        """
+        return self.calendar.get_week(date)
+
+
+    def get_month(self, date):
+        """
+        Returns a tuple (start, end), where "start points to the first day
+        of the given month and "end" points to the last day of the given
+        month.
+        """
+        return self.calendar.get_month(date)
+
+
+    def get_month_weeks(self, date, fill = True):
+        """
+        Returns a tuple (start, end), where "start" points to the first day
+        of the first week of given month, and "end" points to the last day of
+        the last week of the same month.
+        """
+        return self.calendar.get_month_weeks(date, fill)
+
+
+    def get_day_name(self, date):
+        """
+        Returns the name of the given week day.
+        """
+        return self.calendar.get_day_name(date)
+
+
+    def get_month_name(self, date):
+        """
+        Returns the name of the given month.
+        """
+        return self.calendar.get_month_name(date)
+
+
+    def remove_event(self, event):
+        assert event is not None
+        if event.id is None:
+            return
+        del self.events[event.id]
+        self.emit('event-removed', event)
+
+
+    def add_event(self, event):
+        assert event    is not None
+        assert event.id is None
+        self.events[self.next_event_id] = event
+        event.id = self.next_event_id
+        self.next_event_id += 1
+        self.emit('event-added', event)
+
+
+    def get_events(self, start, end):
+        """
+        Returns a list of all events that intersect with the given start
+        and end times.
+        """
+        events = []
+        for event in self.events.values():
+            if util.event_intersects(event, start, end):
+                events.append(event)
+        events.sort(util.event_days, reverse = True)
+        return events
+
+
+    def get_all_day_events(self, start, end, include_timed_events = False):
+        # Get a list of all-day events and sort them by length.
+        events = []
+        for event in self.get_events(start, end):
+            if event.all_day:
+                events.append(event)
+                continue
+            if include_timed_events \
+              and not util.same_day(event.start, event.end):
+                events.append(event)
+        return events
+
+
+    def get_normal_events(self, start, end, include_multi_day_events = True):
+        # Get a list of non-all-day events and sort them by length.
+        events = []
+        for event in self.get_events(start, end):
+            if not include_multi_day_events \
+              and not util.same_day(event.start, event.end):
+                continue
+            if not event.all_day:
+                events.append(event)
+        return events

=== added file 'bin/SpiffGtkWidgets/Calendar/MyCalendar.py'
--- bin/SpiffGtkWidgets/Calendar/MyCalendar.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/MyCalendar.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import datetime
+import calendar
+import locale
+
+
+class MyCalendar(object):
+    """
+    A wrapper around Python's calendar.Calendar() that doesn't suck.
+    """
+
+    def __init__(self, week_start = calendar.SUNDAY):
+        """
+        Constructor.
+
+        week_start -- the first day of the week, e.g. calendar.SUNDAY
+        """
+        assert week_start is not None
+        self.calendar = calendar.Calendar(week_start)
+
+
+    def get_week(self, date):
+        """
+        Returns a tuple (start, end), where "start" points to the first day
+        of the given week, and "end" points to the last day of given week.
+        """
+        month_tuple = date.timetuple()[:2]
+        week_tuple  = date.timetuple()[:3]
+        weeks       = self.calendar.monthdatescalendar(*month_tuple)
+        for week in weeks:
+            if week_tuple not in [d.timetuple()[:3] for d in week]:
+                continue
+            return week[0], week[-1]
+        raise Exception('No such week')
+
+
+    def get_month(self, date):
+        """
+        Returns a tuple (start, end), where "start points to the first day
+        of the given month and "end" points to the last day of the given
+        month.
+        """
+        date_tuple = date.timetuple()
+        year       = date_tuple[0]
+        month      = date_tuple[1]
+        last_day   = calendar.monthrange(year, month)[1]
+        start      = datetime.date(year, month, 1)
+        end        = datetime.date(year, month, last_day)
+        return start, end
+
+
+    def get_month_weeks(self, date, fill = True):
+        """
+        Returns a tuple (start, end), where "start" points to the first day
+        of the first week of given month, and "end" points to the last day of
+        the last week of the same month.
+
+        If "fill" is True, this function always returns 6 weeks per month by
+        appending another week if the time span is shorter.
+        """
+        weeks = self.calendar.monthdatescalendar(*date.timetuple()[:2])
+        start = weeks[0][0]
+        end   = weeks[-1][-1]
+        if fill:
+            end += datetime.timedelta(7) * (6 - len(weeks))
+        return start, end
+
+
+    def get_day_name(self, date):
+        day = calendar.weekday(*date.timetuple()[:3])
+        lang, encoding =  locale.getlocale()
+        return calendar.day_name[day].decode(encoding)
+
+
+    def get_month_name(self, date):
+        return calendar.month_name[date.timetuple()[1]]

=== added file 'bin/SpiffGtkWidgets/Calendar/TableLayout.py'
--- bin/SpiffGtkWidgets/Calendar/TableLayout.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/TableLayout.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,391 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import copy
+import gobject
+import hippo
+
+def compute_homogeneus(width, count):
+    lengths = []
+    while count > 0:
+        length = int(float(width) / count)
+        lengths.append(length)
+        width -= length
+        count -= 1
+    lengths.append(width)
+    return lengths
+
+
+def compute_lengths(allocated, min_lengths, natural_lengths, expand_map=None):
+    count         = len(min_lengths)
+    total_natural = sum(natural_lengths)
+    to_shrink     = total_natural - allocated
+
+    if to_shrink > 0:
+        lengths = copy.copy(natural_lengths)
+        # We were allocated less than our natural height. We want to shrink lines
+        # as equally as possible, but no line more than it's maximum shrink.
+        #
+        # To do this, we process the lines in order of the available shrink from
+        # least available shrink to most
+        #
+        shrinks = []
+        for i in xrange(0, count):
+            shrinks.append((i, natural_lengths[i] - min_lengths[i]))
+        shrinks.sort(key=lambda t: t[1])
+
+        lines_remaining = count
+        for (i, shrink) in shrinks:
+            # If we can shrink the rest of the lines equally, do that. Otherwise
+            # shrink this line as much as possible
+            if shrink * lines_remaining >= to_shrink:
+                shrink = to_shrink // lines_remaining
+
+            lengths[i] -= shrink
+            lines_remaining -= 1
+            to_shrink -= shrink
+
+        return lengths
+    elif to_shrink < 0 and expand_map != None and len(expand_map) > 0:
+        expand_count = len(expand_map)
+        lengths = copy.copy(natural_lengths)
+        to_grow = -to_shrink
+
+        for i in xrange(0, count):
+            if i in expand_map:
+                delta = to_grow // expand_count
+                lengths[i] += delta
+                to_grow -= delta
+                expand_count -= 1
+
+        return lengths
+    else:
+        return natural_lengths
+
+
+class TableLayout(gobject.GObject,hippo.CanvasLayout):
+    """
+    A Canvas Layout manager that arranges items in a grid
+    """
+
+    def __init__(self, column_spacing=0, row_spacing=0):
+        """
+        Create a new TableLayout object
+
+        Arguments:
+        column_spacing: Spacing between columns of items
+        row_spacing: Spacing between rows. This is added between all rows, whether
+           they are rows of items or header rows. You can add more spacing above
+           or below a header item by setting i's padding.
+
+        """
+
+        gobject.GObject.__init__(self)
+        self.__box = None
+        self.__column_spacing = column_spacing
+        self.__row_spacing = row_spacing
+        self.__rows = -1
+        self.__cols = -1
+        self.__homogeneous_rows = False
+        self.__homogeneous_cols = False
+
+        self.__expand_rows = {}
+        self.__expand_columns = {}
+
+    def add(self, child, left=None, right=None, top=None, bottom=None, flags=0):
+        """
+        Add an item to the layout.
+
+        Arguments:
+        left:
+        right:
+        top:
+        bottom:
+        flags: flags to pass to hippo.CanvasBox.append(). Currently, all flags
+           passed in are ignored by this layout manager. (default=0)
+        """
+        if self.__box == None:
+            raise Exception("Layout must be set on a box before adding children")
+
+        if left == None and right == None:
+            raise Exception("Either right or left must be specified")
+
+        if left == None:
+            left = right - 1
+        elif right == None:
+            right = left + 1
+
+        if left < 0:
+            raise Exception("Left attach is < 0")
+        if right <= left:
+            raise Exception("Right attach is <= left attach")
+
+        if top == None and bottom == None:
+            raise Exception("Either bottom or top must be specified")
+
+        if top == None:
+            top = bottom - 1
+        elif bottom == None:
+            bottom = top + 1
+
+        if top < 0:
+            raise Exception("Top attach is < 0")
+        if bottom <= top:
+            raise Exception("Bottom attach is <= top")
+
+        self.__box.append(child, flags=flags)
+        box_child = self.__box.find_box_child(child)
+        box_child.left = left
+        box_child.right = right
+        box_child.top = top
+        box_child.bottom = bottom
+
+    def __set_expand(self, expands, i, expand):
+        if expand:
+            expands[i] = True
+        else:
+            try:
+                del expands[column]
+            except KeyError:
+                pass
+
+    def set_column_expand(self, column, expand):
+        self.__set_expand(self.__expand_columns, column, expand)
+
+    def set_row_expand(self, row, expand):
+        self.__set_expand(self.__expand_rows, row, expand)
+
+    def set_row_count(self, rows):
+        self.__rows = rows
+
+        # Remove items from the expand map, if necessary.
+        expand = self.__expand_rows
+        self.__expand_rows = {}
+        for row in expand:
+            if row < self.__rows:
+                self.__expand_rows[row] = True
+
+    def set_column_count(self, cols):
+        self.__cols = cols
+
+        # Remove items from the expand map, if necessary.
+        expand = self.__expand_columns
+        self.__expand_columns = {}
+        for col in expand:
+            if col < self.__cols:
+                self.__expand_columns[col] = True
+
+    def set_size(self, rows, cols):
+        self.set_row_count(rows)
+        self.set_column_count(cols)
+
+    def set_homogeneus_rows(self, homogeneus):
+        self.__homogeneous_rows = homogeneus
+
+    def set_homogeneus_columns(self, homogeneus):
+        self.__homogeneous_cols = homogeneus
+
+    def do_set_box(self, box):
+        self.__box = box
+
+    def get_column_count(self):
+        columns = max(0, self.__cols)
+
+        for box_child in self.__box.get_layout_children():
+            columns = max(columns, box_child.right)
+
+        for column in self.__expand_columns:
+            columns = max(columns, column + 1)
+
+        return columns
+
+    def get_row_count(self):
+        rows = max(0, self.__rows)
+
+        for box_child in self.__box.get_layout_children():
+            rows = max(rows, box_child.bottom)
+
+        for row in self.__expand_rows:
+            rows = max(rows, row + 1)
+
+        return rows
+
+    def __get_total_column_spacing(self, count):
+        if count > 1:
+            return (count - 1) * self.__column_spacing
+        else:
+            return 0
+
+    def __get_total_row_spacing(self, count):
+        if count > 1:
+            return (count - 1) * self.__row_spacing
+        else:
+            return 0
+
+    def get_total_column_spacing(self):
+        return self.__get_total_column_spacing(self.get_column_count())
+
+    def get_total_row_spacing(self):
+        return self.__get_total_row_spacing(self.get_row_count())
+
+    def __get_request(self, count, get_start_end, get_request):
+        min_lengths = [0 for i in xrange(0,count)]
+        natural_lengths = [0 for i in xrange(0,count)]
+
+        # First process non-spanned children
+        for box_child in self.__box.get_layout_children():
+            start, end = get_start_end(box_child)
+            if end != start + 1:
+                continue
+
+            (min_length, natural_length) = get_request(box_child)
+
+            min_lengths[start] = max(min_lengths[start], min_length)
+            natural_lengths[start] = max(natural_lengths[start], natural_length)
+
+        # Then process spanned children
+        for box_child in self.__box.get_layout_children():
+            start, end = get_start_end(box_child)
+
+            if end == start + 1:
+                continue
+
+            (min_length, natural_length) = get_request(box_child)
+
+            current_min_length = 0
+            current_natural_length = 0
+            for i in range(start, end):
+                current_min_length += min_lengths[i]
+                current_natural_length += natural_lengths[i]
+
+            if current_min_length < min_length:
+                excess = min_length - current_min_length
+                child_count = end - start
+
+                for i in range(start, end):
+                    delta = excess // child_count
+                    min_lengths[i] += delta
+                    excess -= delta
+                    child_count -= 1
+
+            if current_natural_length < natural_length:
+                excess = natural_length - current_natural_length
+                child_count = end - start
+
+                for i in range(start, end):
+                    delta = excess // child_count
+                    natural_lengths[i] += delta
+                    excess -= delta
+                    child_count -= 1
+
+        return (min_lengths, natural_lengths)
+
+    def do_get_width_request(self):
+        column_count         = self.get_column_count()
+        total_column_spacing = self.__get_total_column_spacing(column_count)
+
+        (min_widths, natural_widths) = self.__get_request(column_count,
+                                                          lambda box_child: (box_child.left, box_child.right),
+                                                          lambda box_child: box_child.get_width_request())
+
+        if self.__homogeneous_cols:
+            max_min_width = 0
+            for width in min_widths:
+                max_min_width = max(max_min_width, width)
+            max_nat_width = 0
+            for width in natural_widths:
+                max_nat_width = max(max_nat_width, width)
+            min_widths     = [max_min_width] * column_count
+            natural_widths = [max_nat_width] * column_count
+
+        self.__min_widths     = min_widths
+        self.__natural_widths = natural_widths
+
+        return (sum(self.__min_widths) + total_column_spacing, sum(self.__natural_widths) + total_column_spacing)
+
+    def __compute_column_widths(self, width):
+        cols = max(self.__cols, len(self.__min_widths))
+        if self.__homogeneous_cols and width > 0 and cols > 0:
+            return compute_homogeneus(width, cols)
+        return compute_lengths(width, self.__min_widths, self.__natural_widths, self.__expand_columns)
+
+    def do_get_height_request(self, width):
+        row_count            = self.get_row_count()
+        total_row_spacing    = self.__get_total_row_spacing(row_count)
+        total_column_spacing = self.get_total_column_spacing()
+        widths               = self.__compute_column_widths(width - total_column_spacing)
+
+        def get_child_height_request(box_child):
+            child_width = 0
+            for i in xrange(box_child.left, box_child.right):
+                child_width += widths[i]
+
+            return box_child.get_height_request(child_width)
+
+        (min_heights, natural_heights) = self.__get_request(row_count,
+                                                            lambda box_child: (box_child.top, box_child.bottom),
+                                                            get_child_height_request)
+
+        self.__min_heights = min_heights
+        self.__natural_heights = natural_heights
+
+#        _logger.debug("height_request: min_heights=%s, natural_heights=%s", min_heights, natural_heights)
+
+        return (sum(self.__min_heights) + total_row_spacing, sum(self.__natural_heights) + total_row_spacing)
+
+    def __compute_row_heights(self, height):
+        rows = self.get_row_count()
+        if self.__homogeneous_rows and height > 0 and rows > 0:
+            return compute_homogeneus(height, rows)
+        return compute_lengths(height, self.__min_heights, self.__natural_heights, self.__expand_rows)
+
+    def do_allocate(self, x, y, width, height, requested_width, requested_height, origin_changed):
+        column_count = len(self.__min_widths)
+        total_column_spacing = self.__get_total_column_spacing(column_count)
+
+        row_count = len(self.__min_heights)
+        total_row_spacing = self.__get_total_row_spacing(row_count)
+
+        widths = self.__compute_column_widths(width - total_column_spacing)
+        heights = self.__compute_row_heights(height - total_row_spacing)
+
+#        _logger.debug("allocate: widths=%s, heights=%s", widths, heights)
+
+        x = 0
+        xs = []
+        for width in widths:
+            xs.append(x)
+            x += width + self.__column_spacing
+        xs.append(x)
+
+        y = 0
+        ys = []
+        for height in heights:
+            ys.append(y)
+            y += height + self.__row_spacing
+        ys.append(y)
+
+        for box_child in self.__box.get_layout_children():
+            x = xs[box_child.left]
+            width = xs[box_child.right] - x - self.__column_spacing
+            y = ys[box_child.top]
+            height = ys[box_child.bottom] - y - self.__row_spacing
+
+            box_child.allocate(x, y, width, height, origin_changed)
+
+gobject.type_register(TableLayout)

=== added file 'bin/SpiffGtkWidgets/Calendar/__init__.py'
--- bin/SpiffGtkWidgets/Calendar/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+from Calendar import Calendar
+from Model    import Model
+from Event    import Event
+
+import inspect
+__all__ = [name for name, obj in locals().items()
+           if not (name.startswith('_') or inspect.ismodule(obj))]

=== added file 'bin/SpiffGtkWidgets/Calendar/util.py'
--- bin/SpiffGtkWidgets/Calendar/util.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/Calendar/util.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (C) 2008-2011 Samuel Abels
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# version 3 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>
+#
+##############################################################################
+
+import datetime
+
+def time_delta(datetime1, datetime2):
+    delta = datetime1 - datetime2
+    if delta < datetime.timedelta():
+        return -delta
+    return delta
+
+
+def same_day(date1, date2):
+    return date1.timetuple()[:3] == date2.timetuple()[:3]
+
+
+def end_of_day(date):
+    start = datetime.datetime(*date.timetuple()[:3])
+    return start + datetime.timedelta(1) - datetime.timedelta(0, 0, 0, 1)
+
+
+def previous_day(date):
+    return date - datetime.timedelta(1)
+
+
+def next_day(date):
+    return date + datetime.timedelta(1)
+
+
+def previous_week(date):
+    return date - datetime.timedelta(7)
+
+
+def next_week(date):
+    return date + datetime.timedelta(7)
+
+
+def previous_month(cal, date):
+    year, month, day = date.timetuple()[:3]
+    if month == 1:
+        year  -= 1
+        month  = 12
+    else:
+        month -= 1
+    prev_month_days = [d for d in cal.itermonthdays(year, month)]
+    if day not in prev_month_days:
+        day = max(prev_month_days)
+    return datetime.datetime(year, month, day)
+
+
+def next_month(cal, date):
+    year, month, day = date.timetuple()[:3]
+    if month == 12:
+        year  += 1
+        month  = 1
+    else:
+        month += 1
+    next_month_days = [d for d in cal.itermonthdays(year, month)]
+    if day not in next_month_days:
+        day = max(next_month_days)
+    return datetime.datetime(year, month, day)
+
+
+def event_days(event1, event2):
+    return time_delta(event1.start, event1.end).days \
+         - time_delta(event2.start, event2.end).days
+
+
+def event_intersects(event, start, end = None):
+    if end is None:
+        end = start
+    return (event.start >= start and event.start < end) \
+        or (event.end > start and event.end <= end) \
+        or (event.start < start and event.end > end)
+
+
+def get_intersection_list(list, start, end):
+    intersections = []
+    for event in list:
+        if event_intersects(event, start, end):
+            intersections.append(event)
+    return intersections
+
+
+def count_intersections(list, start, end):
+    intersections = 0
+    for event in list:
+        if event_intersects(event, start, end):
+            intersections += 1
+    return intersections
+
+
+def count_parallel_events(list, start, end):
+    """
+    Given a list of events, this function returns the maximum number of
+    parallel events in the given timeframe.
+    """
+    parallel = 0
+    i        = 0
+    for i, event1 in enumerate(list):
+        if not event_intersects(event1, start, end):
+            continue
+        parallel = max(parallel, 1)
+        for f in range(i + 1, len(list)):
+            event2    = list[f]
+            new_start = max(event1.start, event2.start)
+            new_end   = min(event1.end,   event2.end)
+            if event_intersects(event2, start, end) \
+                and event_intersects(event2, new_start, new_end):
+                n = count_parallel_events(list[f:], new_start, new_end)
+                parallel = max(parallel, n + 1)
+    return parallel

=== added file 'bin/SpiffGtkWidgets/__init__.py'
--- bin/SpiffGtkWidgets/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import Calendar
+import color
+
+import inspect 
+__all__ = [name for name, obj in locals().items()
+           if not (name.startswith('_') or inspect.ismodule(obj))]

=== added file 'bin/SpiffGtkWidgets/color.py'
--- bin/SpiffGtkWidgets/color.py	1970-01-01 00:00:00 +0000
+++ bin/SpiffGtkWidgets/color.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import gtk.gdk
+
+########################
+# Explicit converters.
+########################
+def str2gdk(name):
+    return gtk.gdk.color_parse(name)
+
+def int2gdk(i):
+    red   = (i >> 24) & 0xff
+    green = (i >> 16) & 0xff
+    blue  = (i >>  8) & 0xff
+    return gtk.gdk.Color(red * 256, green * 256, blue * 256)
+
+def rgb2gdk(color):
+    red   = color[0] * 65535.0
+    green = color[1] * 65535.0
+    blue  = color[2] * 65535.0
+    return gtk.gdk.Color(red, green, blue)
+
+def rgba2gdk(color):
+    red   = color[0] * 65535.0
+    green = color[1] * 65535.0
+    blue  = color[2] * 65535.0
+    value = color[3] * 65535.0 # not supported by gdk.Color
+    return gtk.gdk.Color(red, green, blue)
+
+def gdk2int(color):
+    return (color.red   / 256 << 24) \
+         | (color.green / 256 << 16) \
+         | (color.blue  / 256 <<  8) \
+         | 0xff
+
+def gdk2rgb(color):
+    return (color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0)
+
+def gdk2rgba(color):
+    return (color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0, 1)
+
+########################
+# Automatic converters.
+########################
+def to_gdk(color):
+    if isinstance(color, gtk.gdk.Color):
+        return color
+    elif type(color) == type(0) or type(color) == type(0l):
+        return int2gdk(color)
+    elif type(color) == type(''):
+        return str2gdk(color)
+    elif type(color) == type(()) and len(color) == 3:
+        return rgb2gdk(color)
+    elif type(color) == type(()) and len(color) == 4:
+        return rgba2gdk(color)
+    else:
+        raise TypeError('%s is not a known color type' % type(color))
+
+def to_int(color):
+    return gdk2int(to_gdk(color))
+
+def to_rgb(color):
+    return gdk2rgb(to_gdk(color))
+
+def to_rgba(color):
+    return gdk2rgba(to_gdk(color))

=== added file 'bin/__init__.py'
--- bin/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added directory 'bin/common'
=== added file 'bin/common/__init__.py'
--- bin/common/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/common/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+from common import *
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/common/common.py'
--- bin/common/common.py	1970-01-01 00:00:00 +0000
+++ bin/common/common.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,714 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import gtk
+from gtk import glade
+import gobject
+from cgi import escape
+import tools
+import gettext
+
+import os
+import sys
+import platform
+import release
+import common
+import logging
+from options import options
+import service
+import locale
+import ConfigParser
+
+import threading
+import time
+import pango
+import rpc
+
+class action_tips(object):
+    def __init__(self, help):
+        self.help = help
+        self.help_frame = False
+        self.create_action_tip()
+
+    def close_or_disable_tips(self, button, disable_all=False):
+        if self.help_frame:
+            if disable_all:
+                rpc.session.rpc_exec_auth('/object', 'execute', 'res.users', 'write',
+                                          [rpc.session.uid], {'menu_tips':False})
+            self.help_frame.destroy()
+            self.help_frame = False
+        return True
+
+    def create_action_tip(self):
+        if self.help.get('msg', False):
+            msg = self.help.get('msg', '')
+            msg = msg.replace('\n',' ').replace('\t',' ')
+            if len(msg) < 80:
+                msg = '\t\t \t \t' + msg
+            title = self.help.get('title', '')
+
+            help_label = gtk.Label()
+            help_label.set_use_markup(True)
+            def size_allocate(label, allocation):
+                label.set_size_request( allocation.width - 2, -1 )
+            help_label.connect( "size-allocate", size_allocate )
+            help_label.set_label('<span font_desc="italic" foreground="black">%s</span>'% (msg))
+
+            help_label.set_alignment(0.3, 1)
+            help_label.set_line_wrap(True)
+            help_label.set_justify(gtk.JUSTIFY_FILL)
+            layout = help_label.get_layout()
+            layout.set_wrap(pango.WRAP_WORD_CHAR)
+
+            table = gtk.Table(1, 8)
+            table.set_homogeneous(False)
+            table.set_col_spacings(40)
+            table.attach(help_label, 3, 6, 0, 1, ypadding=10)
+            label_box = gtk.EventBox()
+            label_box.add(table)
+
+            # Close current tip button
+            closebtn = gtk.Button(_('Close current tip'))
+            closebtn.set_tooltip_markup(_('''<span foreground="darkred"><b>Close Current Tip:</b></span>
+This will hide the current tip. It will be displayed again next time you open this menu item, unless you disable all tips using the <b>'Menu Tips'</b> option in the user preferences.'''))
+            image = gtk.Image()
+            image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
+            closebtn.set_image(image)
+            closebtn.set_relief(gtk.RELIEF_NONE)
+            closebtn.unset_flags(gtk.CAN_FOCUS)
+            closebtn.connect('clicked', self.close_or_disable_tips)
+
+             # Disable button
+            disablebtn = gtk.Button(_('Disable all tips'))
+            disablebtn.set_tooltip_markup(_('''<span foreground="darkred"><b>Disable all tips:</b></span>
+This will disable the display of tips on all menu items.
+To re-enable tips you need to check the <b>'Menu Tips'</b> option in the user preferences.'''))
+            image1 = gtk.Image()
+            image1.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
+            disablebtn.set_image(image1)
+            disablebtn.set_relief(gtk.RELIEF_NONE)
+            disablebtn.unset_flags(gtk.CAN_FOCUS)
+            disablebtn.connect('clicked', self.close_or_disable_tips, True)
+
+            # frame Title with the two above created buttons
+            box = gtk.HBox()
+            box_label = gtk.Label()
+            box_label.set_use_markup(True)
+            tip_title = '<b>' + _('Tips') + '</b>'
+            if title:
+                tip_title = '<b> %s - %s</b>' % ( to_xml(title),  _('Tips') )
+            box_label.set_label(tip_title)
+            box.pack_start(box_label, True, True)
+            box.pack_end(disablebtn, False, False)
+            box.pack_end(closebtn, False, False)
+            box.show_all()
+            # finally the frame
+            self.help_frame = gtk.Frame()
+            self.help_frame.set_label_widget(box)
+            self.help_frame.set_label_align(0.5,0.5)
+            self.help_frame.add(label_box)
+            self.help_frame.show_all()
+            return True
+        return False
+
+
+def OpenERP_Progressbar(parent=None, title=_('OpenERP Computing')):
+    if not parent:
+        parent = service.LocalService('gui.main').window
+
+    win = gtk.Dialog('OpenERP', parent, gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
+    win.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
+    win.set_title(_(title))
+    win.set_resizable(False)
+    vbox = gtk.VBox(False, 0)
+
+    hbox = gtk.HBox(False, 13)
+    hbox.set_border_width(10)
+
+    img = gtk.Image()
+    img.set_from_stock('gtk-dialog-info', gtk.ICON_SIZE_DIALOG)
+    hbox.pack_start(img, expand=True, fill=False)
+
+    vbox2 = gtk.VBox(False, 0)
+    label = gtk.Label()
+    label.set_markup('<b>'+_('Operation in progress')+'</b>')
+    label.set_alignment(0.0, 0.5)
+    vbox2.pack_start(label, expand=True, fill=False)
+    vbox2.pack_start(gtk.HSeparator(), expand=True, fill=True)
+    vbox2.pack_start(gtk.Label(_("Please wait,\nthis operation may take a while...")), expand=True, fill=False)
+    hbox.pack_start(vbox2, expand=True, fill=True)
+    vbox.pack_start(hbox)
+
+    pb = gtk.ProgressBar()
+    pb.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)
+    vbox.pack_start(pb, expand=True, fill=False)
+
+    win.vbox.pack_start(vbox, expand=True, fill=True)
+    win.set_has_separator(False)
+    win.set_transient_for(parent)
+    win.show_all()
+    return win, pb
+
+def _search_file(file, dir='path.share'):
+    tests = [
+        lambda x: os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), x),
+        lambda x: os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), 'pixmaps', x),
+        lambda x: os.path.join(options[dir], x),
+    ]
+    for func in tests:
+        x = func(file)
+        if os.path.exists(x):
+            return x
+    return file
+
+terp_path = _search_file
+terp_path_pixmaps = lambda x: _search_file(x, 'path.pixmaps')
+
+try:
+    OPENERP_ICON = gtk.gdk.pixbuf_new_from_file(terp_path_pixmaps('openerp-icon.png'))
+except gobject.GError, e:
+    log = logging.getLogger('init')
+    log.fatal(str(e))
+    log.fatal(_('Ensure that the file %s is correct') % options.rcfile)
+    exit(1)
+
+def selection(title, values, alwaysask=False, parent=None):
+    if not values or len(values)==0:
+        return None
+    elif len(values)==1 and (not alwaysask):
+        key = values.keys()[0]
+        return (key, values[key])
+
+    xml = glade.XML(terp_path("openerp.glade"), "win_selection", gettext.textdomain())
+    win = xml.get_widget('win_selection')
+    if not parent:
+        parent = service.LocalService('gui.main').window
+    win.set_icon(OPENERP_ICON)
+    win.set_transient_for(parent)
+
+    label = xml.get_widget('win_sel_title')
+    if title:
+        label.set_text(title)
+
+    list = xml.get_widget('win_sel_tree')
+    list.get_selection().set_mode('single')
+    cell = gtk.CellRendererText()
+    column = gtk.TreeViewColumn("Widget", cell, text=0)
+    list.append_column(column)
+    list.set_search_column(0)
+    model = gtk.ListStore(gobject.TYPE_STRING)
+    keys = values.keys()
+    keys.sort()
+
+    for val in keys:
+        model.append([val])
+
+    list.set_model(model)
+    list.connect('row-activated', lambda x,y,z: win.response(gtk.RESPONSE_OK) or True)
+
+    ok = False
+    while not ok:
+        response = win.run()
+        ok = True
+        res = None
+        if response == gtk.RESPONSE_OK:
+            sel = list.get_selection().get_selected()
+            if sel:
+                (model, iter) = sel
+                if iter:
+                    res = model.get_value(iter, 0)
+                    try:
+                        res = (res, values[res.decode('utf8')])
+                    except:
+                        res = (res, values[res])
+                else:
+                    ok = False
+            else:
+                ok = False
+        else:
+            res = None
+    parent.present()
+    win.destroy()
+    return res
+
+class upload_data_thread(threading.Thread):
+    def __init__(self, email, data, type, supportid):
+        self.args = [('email',email),('type',type),('supportid',supportid),('data',data)]
+        super(upload_data_thread,self).__init__()
+    def run(self):
+        try:
+            import urllib
+            args = urllib.urlencode(self.args)
+            fp = urllib.urlopen('http://www.openerp.com/scripts/survey.php', args)
+            fp.read()
+            fp.close()
+        except:
+            pass
+
+def upload_data(email, data, type='SURVEY', supportid=''):
+    a = upload_data_thread(email, data, type, supportid)
+    a.start()
+    return True
+
+def file_selection(title, filename='', parent=None, action=gtk.FILE_CHOOSER_ACTION_OPEN, preview=True, multi=False, filters=None):
+    if action == gtk.FILE_CHOOSER_ACTION_OPEN:
+        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)
+    else:
+        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)
+
+    win = gtk.FileChooserDialog(title, None, action, buttons)
+    if not parent:
+        parent = service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(OPENERP_ICON)
+    win.set_current_folder(options['client.default_path'])
+    win.set_select_multiple(multi)
+    win.set_default_response(gtk.RESPONSE_OK)
+    if filters is not None:
+        for filter in filters:
+            win.add_filter(filter)
+    if filename:
+        win.set_current_name(filename)
+
+    def update_preview_cb(win, img):
+        filename = win.get_preview_filename()
+        try:
+            pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename, 128, 128)
+            img.set_from_pixbuf(pixbuf)
+            have_preview = True
+        except:
+            have_preview = False
+        win.set_preview_widget_active(have_preview)
+        return
+
+    if preview:
+        img_preview = gtk.Image()
+        win.set_preview_widget(img_preview)
+        win.connect('update-preview', update_preview_cb, img_preview)
+
+    button = win.run()
+    if button!=gtk.RESPONSE_OK:
+        win.destroy()
+        return False
+    if not multi:
+        filepath = win.get_filename()
+        if filepath:
+            filepath = filepath.decode('utf-8')
+            try:
+                options['client.default_path'] = os.path.dirname(filepath)
+            except:
+                pass
+        parent.present()
+        win.destroy()
+        return filepath
+    else:
+        filenames = win.get_filenames()
+        if filenames:
+            filenames = [x.decode('utf-8') for x in filenames]
+            try:
+                options['client.default_path'] = os.path.dirname(filenames[0])
+            except:
+                pass
+        parent.present()
+        win.destroy()
+        return filenames
+
+def support(*args):
+    wid_list = ['email_entry','id_entry','name_entry','phone_entry','company_entry','error_details','explanation_textview','remark_textview']
+    required_wid = ['email_entry', 'name_entry', 'company_name', 'id_entry']
+    support_id = options['support.support_id']
+    recipient = options['support.recipient']
+
+    sur = glade.XML(terp_path("openerp.glade"), "dia_support",gettext.textdomain())
+    win = sur.get_widget('dia_support')
+    parent = service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(OPENERP_ICON)
+    win.show_all()
+    sur.get_widget('id_entry1').set_text(support_id)
+
+    response = win.run()
+    if response == gtk.RESPONSE_OK:
+        fromaddr = sur.get_widget('email_entry1').get_text()
+        id_contract = sur.get_widget('id_entry1').get_text()
+        name =  sur.get_widget('name_entry1').get_text()
+        phone =  sur.get_widget('phone_entry1').get_text()
+        company =  sur.get_widget('company_entry1').get_text()
+
+        urgency = sur.get_widget('urgency_combo1').get_active_text()
+
+        buffer = sur.get_widget('explanation_textview1').get_buffer()
+        explanation = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
+
+        buffer = sur.get_widget('remark_textview').get_buffer()
+        remarks = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
+
+        content = name +"(%s, %s, %s)"%(id_contract, company, phone) + _(" has reported the following bug:\n") + explanation + "\n" + _("remarks") + ":\n" + remarks
+
+        if upload_data(fromaddr, content, 'support', id_contract):
+            common.message(_('Support request sent !'), parent=win)
+
+    parent.present()
+    win.destroy()
+    return True
+
+def error(title, message, details='', parent=None, disconnected_mode=False):
+    """
+    Show an error dialog with the support request or the maintenance
+    """
+    log = logging.getLogger('common.message')
+    details = get_client_environment() + details
+    log.error('Message %s: %s' % (str(message),details))
+
+    show_message = True
+
+    if not disconnected_mode:
+        try:
+            maintenance = rpc.session.rpc_exec_auth_try('/object', 'execute', 'maintenance.contract', 'status')
+        except:
+            maintenance = False
+        if maintenance:
+            if maintenance['status'] == 'none':
+                maintenance_contract_message=_("""
+<b>An unknown error has been reported.</b>
+
+<b>You do not have a valid OpenERP publisher warranty contract !</b>
+If you are using OpenERP in production, it is highly suggested to subscribe
+a publisher warranty program.
+
+The OpenERP publisher warranty contract provides you a bugfix guarantee and an
+automatic migration system so that we can fix your problems within a few
+hours. If you had a publisher warranty contract, this error would have been sent
+to the quality team of the OpenERP editor.
+
+The publisher warranty program offers you:
+* Automatic migrations on new versions,
+* A bugfix guarantee,
+* Monthly announces of potential bugs and their fixes,
+* Security alerts by email and automatic migration,
+* Access to the customer portal.
+
+You can use the link bellow for more information. The detail of the error
+is displayed on the second tab.
+""")
+            elif maintenance['status'] == 'partial':
+                maintenance_contract_message=_("""
+<b>An unknown error has been reported.</b>
+
+Your publisher warranty contract does not cover all modules installed in your system !
+If you are using OpenERP in production, it is highly suggested to upgrade your
+contract.
+
+If you have developed your own modules or installed third party module, we
+can provide you an additional publisher warranty contract for these modules. After
+having reviewed your modules, our quality team will ensure they will migrate
+automatically for all future stable versions of OpenERP at no extra cost.
+
+Here is the list of modules not covered by your publisher warranty contract:
+%s
+
+You can use the link bellow for more information. The detail of the error
+is displayed on the second tab.""") % (", ".join(maintenance['uncovered_modules']), )
+            else:
+                show_message = False
+
+        else:
+            maintenance_contract_message=_("""
+<b>An unknown error has been reported.</b>
+
+<b>You do not have a valid OpenERP publisher warranty contract !</b>
+If you are using OpenERP in production, it is highly suggested to subscribe
+a publisher warranty program.
+
+The OpenERP publisher warranty contract provides you a bugfix guarantee and an
+automatic migration system so that we can fix your problems within a few
+hours. If you had a publisher warranty contract, this error would have been sent
+to the quality team of the OpenERP editor.
+
+The publisher warranty program offers you:
+* Automatic migrations on new versions,
+* A bugfix guarantee,
+* Monthly announces of potential bugs and their fixes,
+* Security alerts by email and automatic migration,
+* Access to the customer portal.
+
+You can use the link bellow for more information. The detail of the error
+is displayed on the second tab.
+""")
+
+    xmlGlade = glade.XML(terp_path('win_error.glade'), 'dialog_error', gettext.textdomain())
+    win = xmlGlade.get_widget('dialog_error')
+    if not parent:
+        parent=service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(OPENERP_ICON)
+    win.set_title("OpenERP - %s" % title)
+
+    if not isinstance(message, basestring):
+        message = str(message)
+    xmlGlade.get_widget('title_error').set_markup("<i>%s</i>" % escape(message))
+
+    details_buffer = gtk.TextBuffer()
+    details_buffer.set_text(details)
+    xmlGlade.get_widget('details_explanation').set_buffer(details_buffer)
+    if show_message:
+        xmlGlade.get_widget('maintenance_explanation').set_markup(maintenance_contract_message)
+
+    xmlGlade.get_widget('notebook').remove_page(int(show_message))
+    if not show_message:
+        def send(widget):
+            def get_text_from_text_view(textView):
+                """Retrieve the buffer from a text view and return the content of this buffer"""
+                buffer = textView.get_buffer()
+                return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
+
+            # Use details_buffer
+            tb = get_text_from_text_view(xmlGlade.get_widget('details_explanation'))
+            explanation = get_text_from_text_view(xmlGlade.get_widget('explanation_textview'))
+            remarks = get_text_from_text_view(xmlGlade.get_widget('remarks_textview'))
+            summary = xmlGlade.get_widget('summary_entry').get_text()
+
+            if rpc.session.rpc_exec_auth_try('/object', 'execute', 'maintenance.contract', 'send', tb, explanation, remarks, summary):
+                common.message(_('Your problem has been sent to the quality team !\nWe will recontact you after analysing the problem.'), parent=win)
+                win.destroy()
+            else:
+                common.message(_('Your problem could *NOT* be sent to the quality team !\nPlease report this error manually at:\n\t%s') % ('http://openerp.com/report_bug.html',), title=_('Error'), type=gtk.MESSAGE_ERROR, parent=win)
+
+        xmlGlade.signal_connect('on_button_send_clicked', send)
+        xmlGlade.signal_connect('on_closebutton_clicked', lambda x : win.destroy())
+    response = win.run()
+    parent.present()
+    win.destroy()
+    return True
+
+def message(msg, title=None, type=gtk.MESSAGE_INFO, parent=None):
+    if not parent:
+        parent=service.LocalService('gui.main').window
+    dialog = gtk.MessageDialog(parent,
+      gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+      type, gtk.BUTTONS_OK)
+    msg = to_xml(msg)
+    if title is not None:
+        msg = '<b>%s</b>\n\n%s' % (to_xml(title), msg)
+    dialog.set_icon(OPENERP_ICON)
+    dialog.set_markup(msg)
+    dialog.show_all()
+    dialog.run()
+    parent.present()
+    dialog.destroy()
+    return True
+
+def to_xml(s):
+    from cgi import escape
+    return escape(s)
+
+def message_box(title, msg, parent=None):
+    dia = glade.XML(terp_path("openerp.glade"), "dia_message_box",gettext.textdomain())
+    win = dia.get_widget('dia_message_box')
+    l = dia.get_widget('msg_title')
+    l.set_text(title)
+
+    msg_area = dia.get_widget('msg_tv')
+    buffer = msg_area.get_buffer()
+    iter_start = buffer.get_start_iter()
+    buffer.insert(iter_start, msg)
+    msg_area.set_sensitive(False)
+
+    if not parent:
+        parent=service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(OPENERP_ICON)
+
+    response = win.run()
+    parent.present()
+    win.destroy()
+    return True
+
+
+def warning(msg, title=None, parent=None):
+    return message(msg=msg, title=title, type=gtk.MESSAGE_WARNING, parent=parent)
+
+def sur(msg, parent=None):
+    if not parent:
+        parent=service.LocalService('gui.main').window
+    sur = glade.XML(terp_path("openerp.glade"), "win_sur",gettext.textdomain())
+    win = sur.get_widget('win_sur')
+    win.set_transient_for(parent)
+    win.show_all()
+    l = sur.get_widget('lab_question')
+    l.set_text(msg)
+
+    if not parent:
+        parent=service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(OPENERP_ICON)
+
+    response = win.run()
+    parent.present()
+    win.destroy()
+    return response == gtk.RESPONSE_OK
+
+def sur_3b(msg, parent=None):
+    sur = glade.XML(terp_path("openerp.glade"), "win_quest_3b",gettext.textdomain())
+    win = sur.get_widget('win_quest_3b')
+    l = sur.get_widget('label')
+    l.set_text(msg)
+
+    if not parent:
+        parent=service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(OPENERP_ICON)
+
+    response = win.run()
+    parent.present()
+    win.destroy()
+    if response == gtk.RESPONSE_YES:
+        return 'ok'
+    elif response == gtk.RESPONSE_NO:
+        return 'ko'
+    elif response == gtk.RESPONSE_CANCEL:
+        return 'cancel'
+    else:
+        return 'cancel'
+
+def ask(question, parent=None):
+    dia = glade.XML(terp_path('openerp.glade'), 'win_quest', gettext.textdomain())
+    win = dia.get_widget('win_quest')
+    label = dia.get_widget('label1')
+    label.set_text(question)
+    entry = dia.get_widget('entry')
+
+    if not parent:
+        parent=service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(OPENERP_ICON)
+
+    response = win.run()
+    parent.present()
+    # grab a safe copy of the entered text before destroy()
+    #to avoid GTK bug https://bugzilla.gnome.org/show_bug.cgi?id=613241
+    value = entry.get_text()
+    win.destroy()
+    if response == gtk.RESPONSE_CANCEL:
+        return None
+    else:
+        return value
+
+def concurrency(resource, id, context, parent=None):
+    dia = glade.XML(common.terp_path("openerp.glade"),'dialog_concurrency_exception',gettext.textdomain())
+    win = dia.get_widget('dialog_concurrency_exception')
+
+    if not parent:
+        parent=service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(OPENERP_ICON)
+
+    res= win.run()
+    parent.present()
+    win.destroy()
+
+    if res == gtk.RESPONSE_OK:
+        return True
+    if res == gtk.RESPONSE_APPLY:
+        obj = service.LocalService('gui.window')
+        obj.create(False, resource, id, [], 'form', None, context,'form,tree')
+    return False
+
+def open_file(value, parent):
+    filetype = {}
+    if options['client.filetype']:
+        if isinstance(options['client.filetype'], str):
+            filetype = eval(options['client.filetype'])
+        else:
+            filetype = options['client.filetype']
+    root, ext = os.path.splitext(value)
+    cmd = False
+    if ext[1:] in filetype:
+        cmd = filetype[ext[1:]] % (value)
+    if not cmd:
+        cmd = file_selection(_('Open with...'),
+                parent=parent)
+        if cmd:
+            cmd = cmd + ' %s'
+            filetype[ext[1:]] = cmd
+            options['client.filetype'] = filetype
+            options.save()
+            cmd = cmd % (value)
+    if cmd:
+        pid = os.fork()
+        if not pid:
+            pid = os.fork()
+            if not pid:
+                prog, args = cmd.split(' ', 1)
+                args = [os.path.basename(prog)] + args.split(' ')
+                try:
+                    os.execvp(prog, args)
+                except:
+                    pass
+            time.sleep(0.1)
+            sys.exit(0)
+        os.waitpid(pid, 0)
+
+
+# Color set
+
+colors = {
+    'invalid':'#ff6969',
+    'readonly':'#eeebe7',
+    'required':'#d2d2ff',
+    'normal':'white'
+}
+
+def get_client_environment():
+    try:
+        rev_id = os.popen('bzr revision-info').read()
+        if not rev_id:
+            rev_id = 'Bazaar Package not Found !'
+    except Exception,e:
+        rev_id = 'Exception: %s\n' % (tools.ustr(e))
+
+    os_lang = '.'.join( [x for x in locale.getdefaultlocale() if x] )
+    if not os_lang:
+        os_lang = 'NOT SET'
+
+    environment = '\nEnvironment Information : \n' \
+                     'System : %s\n' \
+                     'OS Name : %s\n' \
+                     %(platform.platform(), platform.os.name)
+    if os.name == 'posix':
+      if platform.system() == 'Linux':
+         lsbinfo = os.popen('lsb_release -a').read()
+         environment += '%s'%(lsbinfo)
+      else:
+         environment += 'Your System is not lsb compliant\n'
+    environment += 'Operating System Release : %s\n' \
+                   'Operating System Version : %s\n' \
+                   'Operating System Architecture : %s\n' \
+                   'Operating System Locale : %s\n'\
+                   'Python Version : %s\n'\
+                   'OpenERP-Client Version : %s\n'\
+                   'Last revision No. & ID :%s'\
+                    %(platform.release(), platform.version(), platform.architecture()[0],
+                      os_lang, platform.python_version(),release.version,rev_id)
+    return environment
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added directory 'bin/docs'
=== added file 'bin/docs/README'
--- bin/docs/README	1970-01-01 00:00:00 +0000
+++ bin/docs/README	2011-04-25 08:38:27 +0000
@@ -0,0 +1,109 @@
+Screen
+------
+
+1 model
+Liste de vues - vue active
+
+	def sélection de la vue
+
+	screen.model
+	screel.view
+
+Parser_form
+	pour chaque form
+	cherche la vue
+
+
+Pouvoir faire:
+	<field name="order_lines">
+		<form>
+
+
+		</form>
+	</field>
+
+
+View (observater)
+-----------------
+
+view_widget
+	valeur par défaut
+	self.model_widget
+	self.view_form
+
+view_form
+	self.model_form
+
+
+Model (observateur)
+-------------------
+
+model_list
+	save
+	load
+	self.valid:
+		ok
+
+model_form
+	validate
+	save
+	reload
+
+model_field
+	set
+	set_client
+	get
+	get_client
+
+
+Observater
+----------
+
+	connect(nom signal, objet, **kwargs, *args2)
+	signal
+
+
+============================================================================
+Parent ou fils ?
+Bon voila, j'ai dessiné une structure de classe ultra-basique de comment les
+trucs devraient s'agencer dans le client (à mon avis).
+
+Les specs:
+
+    - Un changement dans un modèle doit se voir dans toutes les vues qui lui
+      sont associées
+
+    - On doit pouvoir composer les vues facilement, autrement dit on doit
+      pouvoir mixer vue en arbre et vue en forme.
+
+
+Comment je propose de le faire:
+
+    - MVC
+
+    - Un modèle quand il est mis à jour averti l'observateur qui à son tour
+      averti les vues qui surveille cet observateur.
+
+    - Une vue ou une mégavue, c'est la même interface : elles doivent donc être
+      interchangeable
+
+    - Une vue comprend deux objets: self.form et self.tree qui sont les deux
+      façons de la représenter dans le contexte définit par la mégavue.
+
+
+J'hésite sur les objets observés: modèle ou mégamodèle ? Sur les modèles c'est
+ma première idée car ils sont à la base des informations. Le problème c'est que
+fait on quand il y a création/effacement ? Passer par un observateur sur le
+mégamodèle ? C'est une solution, à la création/l'effacement le mégamodèle
+change et passe alors un message, les vues qui le doivent l'interceptant.
+
+Quel genre de messages sont envoyés via l'observateur:
+
+    - id de la ressource
+    - ressource (res.partner)
+    - champ (name, address_id)
+
+Ainsi les vues savent si elles doivent agir ou pas, et poum un petit reload
+fait l'affaire.
+
+

=== added file 'bin/docs/tiny-client.fig'
--- bin/docs/tiny-client.fig	1970-01-01 00:00:00 +0000
+++ bin/docs/tiny-client.fig	2011-04-25 08:38:27 +0000
@@ -0,0 +1,237 @@
+#FIG 3.2  Produced by xfig version 3.2.5-alpha5
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+0 32 #cccccc
+0 33 #8e8e8e
+0 34 #7c7c7c
+0 35 #c9aaa1
+0 36 #b6b6b6
+0 37 #ac9478
+0 38 #808080
+0 39 #c6b797
+0 40 #eff8ff
+0 41 #dccba6
+0 42 #404040
+0 43 #c0c0c0
+0 44 #e0e0e0
+0 45 #8e8f8e
+0 46 #868286
+0 47 #c7c3c7
+0 48 #e7e3e7
+0 49 #444444
+0 50 #868686
+0 51 #c7c7c7
+0 52 #e7e7e7
+0 53 #f7f7f7
+0 54 #9e9e9e
+0 55 #dbdbdb
+0 56 #a1a1b7
+0 57 #9c0000
+0 58 #ededed
+0 59 #86acff
+0 60 #7070ff
+0 61 #bebebe
+0 62 #515151
+0 63 #000049
+0 64 #797979
+0 65 #303430
+0 66 #c7b696
+0 67 #d7d7d7
+0 68 #aeaeae
+0 69 #aaaaaa
+0 70 #555555
+0 71 #85807d
+0 72 #d2d2d2
+0 73 #3a3a3a
+0 74 #4573aa
+0 75 #000000
+0 76 #94949a
+0 77 #d6d7d6
+0 78 #7b79a5
+0 79 #666666
+0 80 #e2e2ee
+0 81 #565151
+0 82 #effbff
+0 83 #414141
+0 84 #414541
+0 85 #717571
+0 86 #73758c
+0 87 #635dce
+0 88 #8c8c8c
+0 89 #424242
+0 90 #8c8c8c
+0 91 #424242
+0 92 #8c8c8c
+0 93 #424242
+0 94 #8c8c8c
+0 95 #424242
+0 96 #8c8c8c
+0 97 #424242
+0 98 #8c8c8c
+0 99 #424242
+6 450 -2700 8415 -1845
+6 585 -2520 2880 -2070
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 585 -2225 2871 -2225
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 585 -2153 2871 -2153
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 585 -2510 2871 -2510 2871 -2082 585 -2082 585 -2510
+4 0 0 100 0 16 12 0.0000 4 150 525 585 -2296 View1\001
+-6
+6 3285 -2520 5625 -2070
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 3319 -2225 5605 -2225
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 3319 -2153 5605 -2153
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 3319 -2510 5605 -2510 5605 -2082 3319 -2082 3319 -2510
+4 0 0 100 0 16 12 0.0000 4 150 525 3390 -2296 View2\001
+-6
+6 5985 -2520 8325 -2070
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 6019 -2225 8305 -2225
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 6019 -2153 8305 -2153
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 6019 -2510 8305 -2510 8305 -2082 6019 -2082 6019 -2510
+4 0 0 100 0 16 12 0.0000 4 150 525 6090 -2296 View3\001
+-6
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+	 450 -2700 8415 -2700 8415 -1845 450 -1845 450 -2700
+-6
+6 5850 -450 8190 1800
+6 5850 -450 8190 0
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5884 -155 8170 -155
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5884 -83 8170 -83
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 5884 -440 8170 -440 8170 -12 5884 -12 5884 -440
+4 0 0 100 0 16 12 0.0000 4 150 630 5955 -226 Model1\001
+-6
+6 5850 450 8190 900
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5884 745 8170 745
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5884 817 8170 817
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 5884 460 8170 460 8170 888 5884 888 5884 460
+4 0 0 100 0 16 12 0.0000 4 150 630 5955 674 Model2\001
+-6
+6 5850 1350 8190 1800
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5884 1645 8170 1645
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5884 1717 8170 1717
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 5884 1360 8170 1360 8170 1788 5884 1788 5884 1360
+4 0 0 100 0 16 12 0.0000 4 150 630 5955 1574 Model3\001
+-6
+-6
+6 585 -1710 2880 -360
+6 585 -1710 2880 -1260
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 585 -1415 2871 -1415
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 585 -1343 2871 -1343
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 585 -1700 2871 -1700 2871 -1272 585 -1272 585 -1700
+4 0 0 100 0 16 12 0.0000 4 150 525 585 -1486 View1\001
+-6
+6 585 -810 2880 -360
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 585 -515 2871 -515
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 585 -443 2871 -443
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 585 -800 2871 -800 2871 -372 585 -372 585 -800
+4 0 0 100 0 16 12 0.0000 4 150 525 585 -586 View1\001
+-6
+-6
+6 5850 -1350 8190 -900
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5884 -1055 8170 -1055
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 5884 -983 8170 -983
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 5884 -1340 8170 -1340 8170 -912 5884 -912 5884 -1340
+4 0 0 100 0 16 12 0.0000 4 150 960 5955 -1126 Observator\001
+-6
+6 9450 -1350 11790 -900
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9484 -1055 11770 -1055
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9484 -983 11770 -983
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 9484 -1340 11770 -1340 11770 -912 9484 -912 9484 -1340
+4 0 0 100 0 16 12 0.0000 4 150 825 9555 -1126 Controller\001
+-6
+6 9450 0 11790 450
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9484 295 11770 295
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9484 367 11770 367
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 9484 10 11770 10 11770 438 9484 438 9484 10
+4 0 0 100 0 16 12 0.0000 4 195 1110 9555 224 MegaModel1\001
+-6
+6 9450 -2700 11790 -2250
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9484 -2405 11770 -2405
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2
+	 9484 -2333 11770 -2333
+2 2 0 1 0 7 101 0 20 4.000 0 0 7 0 0 5
+	 9484 -2690 11770 -2690 11770 -2262 9484 -2262 9484 -2690
+4 0 0 100 0 16 12 0.0000 4 195 1005 9555 -2476 MegaView1\001
+-6
+3 2 0 1 0 7 50 -1 -1 0.000 0 0 1 3
+	1 1 3.00 30.00 60.00
+	 2880 -2340 4230 -1350 5850 -1170
+	 0.000 -1.000 0.000
+3 2 0 1 0 7 50 -1 -1 0.000 0 0 1 3
+	1 1 3.00 30.00 60.00
+	 2880 -1575 4860 -945 5850 -1170
+	 0.000 -1.000 0.000
+3 2 0 1 0 7 50 -1 -1 0.000 0 0 1 3
+	1 1 3.00 30.00 60.00
+	 2880 -630 4950 -585 5850 -1170
+	 0.000 -1.000 0.000
+3 2 0 1 0 7 50 -1 -1 0.000 0 1 0 3
+	1 1 3.00 30.00 60.00
+	 8415 -2385 8865 -2655 9495 -2475
+	 0.000 -1.000 0.000
+3 2 0 1 0 7 50 -1 -1 4.000 0 1 0 2
+	1 1 1.00 60.00 120.00
+	 10620 -2250 10620 -1350
+	 0.000 0.000
+3 2 0 1 0 7 50 -1 -1 4.000 0 1 0 2
+	1 1 1.00 60.00 120.00
+	 10665 -900 10665 0
+	 0.000 0.000
+3 2 0 1 0 7 50 -1 -1 4.000 0 1 0 4
+	1 1 1.00 60.00 120.00
+	 8190 -1170 9045 -900 9810 -450 10215 0
+	 0.000 -1.000 -1.000 0.000
+3 2 0 1 0 7 50 -1 -1 4.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 9450 180 8685 -270 8145 -270
+	 0.000 -1.000 0.000
+3 2 0 1 0 7 50 -1 -1 4.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 9450 225 8685 630 8145 675
+	 0.000 -1.000 0.000
+3 2 0 1 0 7 50 -1 -1 4.000 0 1 0 3
+	1 1 1.00 60.00 120.00
+	 9450 225 9135 945 8190 1530
+	 0.000 -1.000 0.000
+3 2 0 1 0 7 50 -1 -1 4.000 0 1 0 2
+	1 1 1.00 60.00 120.00
+	 6930 -450 6930 -900
+	 0.000 0.000

=== added file 'bin/environment_info.py'
--- bin/environment_info.py	1970-01-01 00:00:00 +0000
+++ bin/environment_info.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import os
+import sys
+import platform
+import locale
+import optparse
+import xmlrpclib
+import release
+import tools
+import logging
+
+class environment(object):
+    def __init__(self, login, password, dbname, host='localhost', port=8069):
+        self.login = login
+        self.passwd = password
+        self.db = dbname
+        self.host = host
+        self.port = port
+        self.log = logging.getLogger('environment')
+
+    def get_with_server_info(self):
+        try:
+            login_socket = xmlrpclib.ServerProxy('http://%s:%s/xmlrpc/common' % (self.host, self.port))
+            self.uid = login_socket.login(self.db, self.login, self.passwd)
+            if self.uid:
+                self.log.info(login_socket.get_server_environment() + self.get_client_info())
+                login_socket.logout(self.db, self.login, self.passwd)
+            else:
+                self.log.info("bad login or password from "+self.login+" using database "+self.db)
+        except Exception, e:
+                self.log.exception(e)
+        return True
+
+    def get_client_info(self):
+        try:
+            rev_id = os.popen('bzr revision-info').read()
+            if not rev_id:
+                rev_id = 'Bazaar Package not Found !'
+        except Exception,e:
+            rev_id = 'Exception: %s\n' % (tools.ustr(e))
+        environment = 'OpenERP-Client Version : %s\n'\
+                      'Last revision No. & ID :%s'\
+                      %(release.version,rev_id)
+        return environment
+
+if __name__=="__main__":
+    uses ="""%prog [options]
+
+Note:
+    This script will provide you the full environment information of OpenERP-Client
+    If login,password and database are given then it will also give OpenERP-Server Information
+
+Examples:
+[1] python environment_info.py
+[2] python environment_info.py -l admin -p admin -d test
+"""
+
+    parser = optparse.OptionParser(uses)
+    parser.add_option("-l", "--login", dest="login", help="Login of the user in OpenERP")
+    parser.add_option("-p", "--password", dest="password", help="Password of the user in OpenERP")
+    parser.add_option("-d", "--database", dest="dbname", help="Database name")
+    parser.add_option("-P", "--port", dest="port", help="Port",default=8069)
+    parser.add_option("-H", "--host", dest="host", help="Host",default='localhost')
+
+    (options, args) = parser.parse_args()
+    parser = environment(options.login, options.password, dbname = options.dbname, host = options.host, port = options.port)
+    if not(options.login and options.password and options.dbname):
+        client_info = parser.get_client_info()
+
+        os_lang = '.'.join( [x for x in locale.getdefaultlocale() if x] )
+        if not os_lang:
+            os_lang = 'NOT SET'
+
+        environment = '\nEnvironment Information : \n' \
+                     'System : %s\n' \
+                     'OS Name : %s\n' \
+                     %(platform.platform(), platform.os.name)
+        if os.name == 'posix':
+          if platform.system() == 'Linux':
+             lsbinfo = os.popen('lsb_release -a').read()
+             environment += '%s'%(lsbinfo)
+          else:
+             environment += 'Your System is not lsb compliant\n'
+        environment += 'Operating System Release : %s\n' \
+                    'Operating System Version : %s\n' \
+                    'Operating System Architecture : %s\n' \
+                    'Operating System Locale : %s\n'\
+                    'Python Version : %s\n'\
+                    %(platform.release(), platform.version(), platform.architecture()[0],
+                      os_lang, platform.python_version())
+
+        parser.log.info(environment + client_info)
+        parser.log.info('\nFor server Information you need to pass database(-d), login(-l),password(-p)')
+        sys.exit(1)
+    else:
+        parser.get_with_server_info()

=== added directory 'bin/icons'
=== added file 'bin/icons/accessories-archiver+.png'
Binary files bin/icons/accessories-archiver+.png	1970-01-01 00:00:00 +0000 and bin/icons/accessories-archiver+.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/accessories-archiver-minus.png'
Binary files bin/icons/accessories-archiver-minus.png	1970-01-01 00:00:00 +0000 and bin/icons/accessories-archiver-minus.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/accessories-archiver.png'
Binary files bin/icons/accessories-archiver.png	1970-01-01 00:00:00 +0000 and bin/icons/accessories-archiver.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/account.png'
Binary files bin/icons/account.png	1970-01-01 00:00:00 +0000 and bin/icons/account.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/administration.png'
Binary files bin/icons/administration.png	1970-01-01 00:00:00 +0000 and bin/icons/administration.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/calendar.png'
Binary files bin/icons/calendar.png	1970-01-01 00:00:00 +0000 and bin/icons/calendar.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/call-start.png'
Binary files bin/icons/call-start.png	1970-01-01 00:00:00 +0000 and bin/icons/call-start.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/camera_test.png'
Binary files bin/icons/camera_test.png	1970-01-01 00:00:00 +0000 and bin/icons/camera_test.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/check.png'
Binary files bin/icons/check.png	1970-01-01 00:00:00 +0000 and bin/icons/check.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/crm.png'
Binary files bin/icons/crm.png	1970-01-01 00:00:00 +0000 and bin/icons/crm.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/dialog-close.png'
Binary files bin/icons/dialog-close.png	1970-01-01 00:00:00 +0000 and bin/icons/dialog-close.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/document-new.png'
Binary files bin/icons/document-new.png	1970-01-01 00:00:00 +0000 and bin/icons/document-new.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/dolar.png'
Binary files bin/icons/dolar.png	1970-01-01 00:00:00 +0000 and bin/icons/dolar.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/dolar_ok!.png'
Binary files bin/icons/dolar_ok!.png	1970-01-01 00:00:00 +0000 and bin/icons/dolar_ok!.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/emblem-documents.png'
Binary files bin/icons/emblem-documents.png	1970-01-01 00:00:00 +0000 and bin/icons/emblem-documents.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/emblem-important.png'
Binary files bin/icons/emblem-important.png	1970-01-01 00:00:00 +0000 and bin/icons/emblem-important.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/face-plain.png'
Binary files bin/icons/face-plain.png	1970-01-01 00:00:00 +0000 and bin/icons/face-plain.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/folder-blue.png'
Binary files bin/icons/folder-blue.png	1970-01-01 00:00:00 +0000 and bin/icons/folder-blue.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/folder-green.png'
Binary files bin/icons/folder-green.png	1970-01-01 00:00:00 +0000 and bin/icons/folder-green.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/folder-orange.png'
Binary files bin/icons/folder-orange.png	1970-01-01 00:00:00 +0000 and bin/icons/folder-orange.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/folder-violet.png'
Binary files bin/icons/folder-violet.png	1970-01-01 00:00:00 +0000 and bin/icons/folder-violet.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/folder-yellow.png'
Binary files bin/icons/folder-yellow.png	1970-01-01 00:00:00 +0000 and bin/icons/folder-yellow.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gdu-smart-failing.png'
Binary files bin/icons/gdu-smart-failing.png	1970-01-01 00:00:00 +0000 and bin/icons/gdu-smart-failing.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gnome-cpu-frequency-applet+.png'
Binary files bin/icons/gnome-cpu-frequency-applet+.png	1970-01-01 00:00:00 +0000 and bin/icons/gnome-cpu-frequency-applet+.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/go-home.png'
Binary files bin/icons/go-home.png	1970-01-01 00:00:00 +0000 and bin/icons/go-home.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/go-month.png'
Binary files bin/icons/go-month.png	1970-01-01 00:00:00 +0000 and bin/icons/go-month.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/go-today.png'
Binary files bin/icons/go-today.png	1970-01-01 00:00:00 +0000 and bin/icons/go-today.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/go-week.png'
Binary files bin/icons/go-week.png	1970-01-01 00:00:00 +0000 and bin/icons/go-week.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/go-year.png'
Binary files bin/icons/go-year.png	1970-01-01 00:00:00 +0000 and bin/icons/go-year.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/graph.png'
Binary files bin/icons/graph.png	1970-01-01 00:00:00 +0000 and bin/icons/graph.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gtk-go-back-ltr.png'
Binary files bin/icons/gtk-go-back-ltr.png	1970-01-01 00:00:00 +0000 and bin/icons/gtk-go-back-ltr.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gtk-go-back-rtl.png'
Binary files bin/icons/gtk-go-back-rtl.png	1970-01-01 00:00:00 +0000 and bin/icons/gtk-go-back-rtl.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gtk-jump-to-ltr.png'
Binary files bin/icons/gtk-jump-to-ltr.png	1970-01-01 00:00:00 +0000 and bin/icons/gtk-jump-to-ltr.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gtk-jump-to-rtl.png'
Binary files bin/icons/gtk-jump-to-rtl.png	1970-01-01 00:00:00 +0000 and bin/icons/gtk-jump-to-rtl.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gtk-media-pause.png'
Binary files bin/icons/gtk-media-pause.png	1970-01-01 00:00:00 +0000 and bin/icons/gtk-media-pause.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gtk-select-all.png'
Binary files bin/icons/gtk-select-all.png	1970-01-01 00:00:00 +0000 and bin/icons/gtk-select-all.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/gtk-stop.png'
Binary files bin/icons/gtk-stop.png	1970-01-01 00:00:00 +0000 and bin/icons/gtk-stop.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/hr.png'
Binary files bin/icons/hr.png	1970-01-01 00:00:00 +0000 and bin/icons/hr.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/idea.png'
Binary files bin/icons/idea.png	1970-01-01 00:00:00 +0000 and bin/icons/idea.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/locked.png'
Binary files bin/icons/locked.png	1970-01-01 00:00:00 +0000 and bin/icons/locked.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/mail-.png'
Binary files bin/icons/mail-.png	1970-01-01 00:00:00 +0000 and bin/icons/mail-.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/mail-forward.png'
Binary files bin/icons/mail-forward.png	1970-01-01 00:00:00 +0000 and bin/icons/mail-forward.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/mail-message-new.png'
Binary files bin/icons/mail-message-new.png	1970-01-01 00:00:00 +0000 and bin/icons/mail-message-new.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/mail-replied.png'
Binary files bin/icons/mail-replied.png	1970-01-01 00:00:00 +0000 and bin/icons/mail-replied.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/mail_delete.png'
Binary files bin/icons/mail_delete.png	1970-01-01 00:00:00 +0000 and bin/icons/mail_delete.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/marketing.png'
Binary files bin/icons/marketing.png	1970-01-01 00:00:00 +0000 and bin/icons/marketing.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/mrp.png'
Binary files bin/icons/mrp.png	1970-01-01 00:00:00 +0000 and bin/icons/mrp.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/partner.png'
Binary files bin/icons/partner.png	1970-01-01 00:00:00 +0000 and bin/icons/partner.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/personal+.png'
Binary files bin/icons/personal+.png	1970-01-01 00:00:00 +0000 and bin/icons/personal+.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/personal-.png'
Binary files bin/icons/personal-.png	1970-01-01 00:00:00 +0000 and bin/icons/personal-.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/personal.png'
Binary files bin/icons/personal.png	1970-01-01 00:00:00 +0000 and bin/icons/personal.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/product.png'
Binary files bin/icons/product.png	1970-01-01 00:00:00 +0000 and bin/icons/product.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/project.png'
Binary files bin/icons/project.png	1970-01-01 00:00:00 +0000 and bin/icons/project.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/purchase.png'
Binary files bin/icons/purchase.png	1970-01-01 00:00:00 +0000 and bin/icons/purchase.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/rating-rated.png'
Binary files bin/icons/rating-rated.png	1970-01-01 00:00:00 +0000 and bin/icons/rating-rated.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/referer.png'
Binary files bin/icons/referer.png	1970-01-01 00:00:00 +0000 and bin/icons/referer.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/report.png'
Binary files bin/icons/report.png	1970-01-01 00:00:00 +0000 and bin/icons/report.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/sale.png'
Binary files bin/icons/sale.png	1970-01-01 00:00:00 +0000 and bin/icons/sale.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/stage.png'
Binary files bin/icons/stage.png	1970-01-01 00:00:00 +0000 and bin/icons/stage.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/stock.png'
Binary files bin/icons/stock.png	1970-01-01 00:00:00 +0000 and bin/icons/stock.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/stock_align_left_24.png'
Binary files bin/icons/stock_align_left_24.png	1970-01-01 00:00:00 +0000 and bin/icons/stock_align_left_24.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/stock_effects-object-colorize.png'
Binary files bin/icons/stock_effects-object-colorize.png	1970-01-01 00:00:00 +0000 and bin/icons/stock_effects-object-colorize.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/stock_format-default.png'
Binary files bin/icons/stock_format-default.png	1970-01-01 00:00:00 +0000 and bin/icons/stock_format-default.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/stock_format-scientific.png'
Binary files bin/icons/stock_format-scientific.png	1970-01-01 00:00:00 +0000 and bin/icons/stock_format-scientific.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/stock_symbol-selection.png'
Binary files bin/icons/stock_symbol-selection.png	1970-01-01 00:00:00 +0000 and bin/icons/stock_symbol-selection.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/stock_zoom.png'
Binary files bin/icons/stock_zoom.png	1970-01-01 00:00:00 +0000 and bin/icons/stock_zoom.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/tools.png'
Binary files bin/icons/tools.png	1970-01-01 00:00:00 +0000 and bin/icons/tools.png	2011-04-25 08:38:27 +0000 differ
=== added file 'bin/icons/translate.png'
Binary files bin/icons/translate.png	1970-01-01 00:00:00 +0000 and bin/icons/translate.png	2011-04-25 08:38:27 +0000 differ
=== added directory 'bin/modules'
=== added file 'bin/modules/__init__.py'
--- bin/modules/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/modules/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import spool
+import gui
+import action
+
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added directory 'bin/modules/action'
=== added file 'bin/modules/action/__init__.py'
--- bin/modules/action/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/modules/action/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import main
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/action/main.py'
--- bin/modules/action/main.py	1970-01-01 00:00:00 +0000
+++ bin/modules/action/main.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,206 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import os
+import copy
+import time
+import base64
+import datetime
+import service
+import rpc
+import wizard
+import printer
+import common
+import tools
+import options
+from widget.view.form_gtk.many2one import dialog
+from lxml import etree
+
+class main(service.Service):
+    def __init__(self, name='action.main'):
+        service.Service.__init__(self, name)
+
+    def exec_report(self, name, data, context={}):
+        datas = data.copy()
+        ids = datas['ids']
+        del datas['ids']
+        if not ids:
+            ids =  rpc.session.rpc_exec_auth('/object', 'execute', datas['model'], 'search', datas.get('_domain',[]))
+            if ids == []:
+                common.message(_('Nothing to print!'))
+                return False
+            datas['id'] = ids[0]
+        ctx = rpc.session.context.copy()
+        ctx.update(context)
+        report_id = rpc.session.rpc_exec_auth('/report', 'report', name, ids, datas, ctx)
+        state = False
+        attempt = 0
+        max_attemps = int(options.options.get('client.timeout') or 0)
+        while not state:
+            val = rpc.session.rpc_exec_auth('/report', 'report_get', report_id)
+            if not val:
+                return False
+            state = val['state']
+            if not state:
+                time.sleep(1)
+                attempt += 1
+            if attempt>max_attemps:
+                common.message(_('Printing aborted, too long delay !'))
+                return False
+        printer.print_data(val)
+        return True
+
+    def execute(self, act_id, datas, type=None, context={}):
+        act_id = int(act_id)
+        ctx = rpc.session.context.copy()
+        ctx.update(context)
+        if type is None:
+            res = rpc.session.rpc_exec_auth('/object', 'execute', 'ir.actions.actions', 'read', int(act_id), ['type'], ctx)
+            if not (res and len(res)):
+                raise Exception, 'ActionNotFound'
+            type=res['type']
+
+        res = rpc.session.rpc_exec_auth('/object', 'execute', type, 'read', act_id, False, ctx)
+        self._exec_action(res,datas,context)
+
+    def _exec_action(self, action, datas, context={}):
+        if isinstance(action, bool) or 'type' not in action:
+            return
+        # Updating the context : Adding the context of action in order to use it on Views called from buttons
+        if datas.get('id',False):
+            context.update( {'active_id': datas.get('id',False), 'active_ids': datas.get('ids',[]), 'active_model': datas.get('model',False)})
+        context.update(tools.expr_eval(action.get('context','{}'), context.copy()))
+        if action['type'] in ['ir.actions.act_window', 'ir.actions.submenu']:
+            for key in ('res_id', 'res_model', 'view_type', 'view_mode',
+                    'limit', 'auto_refresh', 'search_view', 'auto_search', 'search_view_id'):
+                datas[key] = action.get(key, datas.get(key, None))
+
+            datas['auto_search'] = action.get('auto_search', True)
+            if not datas['search_view'] and datas['search_view_id']:
+                 datas['search_view'] = str(rpc.session.rpc_exec_auth('/object', 'execute', datas['res_model'], 'fields_view_get', isinstance(datas['search_view_id'], (tuple, list)) and datas['search_view_id'][0] or datas['search_view_id'], 'search', context))
+
+            if datas['limit'] is None or datas['limit'] == 0:
+                datas['limit'] = 100
+
+            view_ids=False
+            if action.get('views', []):
+                if isinstance(action['views'],list):
+                    view_ids=[x[0] for x in action['views']]
+                    datas['view_mode']=",".join([x[1] for x in action['views']])
+                else:
+                    if action.get('view_id', False):
+                        view_ids=[action['view_id'][0]]
+            elif action.get('view_id', False):
+                view_ids=[action['view_id'][0]]
+
+            if not action.get('domain', False):
+                action['domain']='[]'
+            domain_ctx = context.copy()
+            domain_ctx['time'] = time
+            domain_ctx['datetime'] = datetime
+            domain = tools.expr_eval(action['domain'], domain_ctx)
+            help = {}
+            if action.get('display_menu_tip', False):
+                msg = action.get('help', False)
+                title = action.get('name', False)
+                if msg and len(msg):
+                    help['msg'] =  msg
+                    help['title'] = title or ''
+            if datas.get('domain', False):
+                domain.append(datas['domain'])
+            if action.get('target', False)=='new':
+                dia = dialog(datas['res_model'], id=datas.get('res_id',None),
+                             window=datas.get('window',None), domain=domain,
+                             context=context, view_ids=view_ids,target=True,
+                             view_type=datas.get('view_mode', 'tree').split(','), help=help)
+                if dia.dia.get_has_separator():
+                    dia.dia.set_has_separator(False)
+                dia.run()
+                dia.destroy()
+            else:
+                obj = service.LocalService('gui.window')
+                obj.create(view_ids, datas['res_model'], datas['res_id'], domain,
+                        action['view_type'], datas.get('window',None), context,
+                        datas['view_mode'], name=action.get('name', False), help=help,
+                        limit=datas['limit'], auto_refresh=datas['auto_refresh'], auto_search = datas['auto_search'], search_view = datas['search_view'])
+
+        elif action['type']=='ir.actions.server':
+            res = rpc.session.rpc_exec_auth('/object', 'execute', 'ir.actions.server', 'run', [action['id']], context)
+            if res:
+                if not isinstance(res, list):
+                    res = [res]
+                for r in res:
+                    self._exec_action(r, datas, context)
+
+        elif action['type']=='ir.actions.wizard':
+            win=None
+            if 'window' in datas:
+                win=datas['window']
+                del datas['window']
+            wizard.execute(action['wiz_name'], datas, parent=win, context=context)
+
+        elif action['type']=='ir.actions.report.custom':
+            if 'window' in datas:
+                win=datas['window']
+                del datas['window']
+            datas['report_id'] = action['report_id']
+            self.exec_report('custom', datas, context)
+
+        elif action['type']=='ir.actions.report.xml':
+            if 'window' in datas:
+                win=datas['window']
+                del datas['window']
+            if not datas:
+                datas = action.get('datas',[])
+            self.exec_report(action['report_name'], datas, context)
+
+        elif action['type']=='ir.actions.act_url':
+            tools.launch_browser(action.get('url',''))
+
+    def exec_keyword(self, keyword, data={}, adds={}, context={}, warning=True):
+        actions = None
+        if 'id' in data:
+            try:
+                id = data.get('id', False)
+                actions = rpc.session.rpc_exec_auth('/object', 'execute',
+                        'ir.values', 'get', 'action', keyword,
+                        [(data['model'], id)], False, rpc.session.context)
+                actions = map(lambda x: x[2], actions)
+            except rpc.rpc_exception, e:
+#               common.error(_('Error: ')+str(e.type), e.message, e.data)
+                return False
+        keyact = {}
+        for action in actions:
+            action_name = action.get('name') or ''
+            keyact[action_name.encode('utf8')] = action
+        keyact.update(adds)
+        res = common.selection(_('Select your action'), keyact)
+        if res:
+            (name,action) = res
+            context.update(rpc.session.context)
+            self._exec_action(action, data, context=context)
+            return (name, action)
+        return False
+
+main()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/action/wizard.py'
--- bin/modules/action/wizard.py	1970-01-01 00:00:00 +0000
+++ bin/modules/action/wizard.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import gtk
+from gtk import glade
+import gettext
+import copy
+
+import service
+import rpc
+import common
+import thread
+import time
+
+from widget.screen import Screen
+
+
+class dialog(object):
+    def __init__(self, arch, fields, state, name, parent=None):
+        buttons = []
+        self.states=[]
+        default=-1
+        if not parent:
+            parent = service.LocalService('gui.main').window
+        self.dia = gtk.Dialog('OpenERP', parent,
+            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
+        for x in state:
+            but = gtk.Button(x[1])
+            but.show()
+            if len(x) >= 3:
+                icon = gtk.Image()
+                icon.set_from_stock(x[2], gtk.ICON_SIZE_BUTTON)
+                but.set_image(icon)
+            self.dia.add_action_widget(but, len(self.states))
+            if len(x) >= 4 and x[3]:
+                but.set_flags(gtk.CAN_DEFAULT)
+                default = len(self.states)
+            self.states.append(x[0])
+        if default >= 0:
+            self.dia.set_default_response(default)
+
+        val = {}
+        for f in fields:
+            if 'value' in fields[f]:
+                val[f] = fields[f]['value']
+
+        self.screen = Screen('wizard.'+name, view_type=[], window=self.dia, is_wizard=True)
+        self.screen.new(default=False)
+        self.screen.add_view_custom(arch, fields, display=True)
+        self.screen.current_model.set(val)
+
+        x,y = self.screen.screen_container.size_get()
+        width, height = parent.get_size()
+        self.screen.widget.set_size_request(min(width - 20, x + 20),
+                min(height - 60, y + 25))
+        self.screen.widget.show()
+
+        self.dia.vbox.pack_start(self.screen.widget)
+        self.dia.set_title(self.screen.current_view.title)
+        self.dia.show()
+
+    def run(self, datas={}):
+        while True:
+            res = self.dia.run()
+            self.screen.current_view.set_value()
+            if self.screen.current_model.validate() or (res<0) or (self.states[res]=='end'):
+                break
+            self.screen.display()
+        if res<len(self.states) and res>=0:
+            datas.update(self.screen.get())
+            self.dia.destroy()
+            return (self.states[res], datas)
+        else:
+            self.dia.destroy()
+            self.screen.destroy()
+            return False
+
+def execute(action, datas, state='init', parent=None, context=None):
+    if context is None:
+        context = {}
+    if not 'form' in datas:
+        datas['form'] = {}
+    wiz_id = rpc.session.rpc_exec_auth('/wizard', 'create', action)
+
+    while state!='end':
+        class wizard_progress(object):
+            def __init__(self, parent=None):
+                self.res = None
+                self.error = False
+                self.parent = parent
+                self.exception = None
+
+            def run(self):
+                def go(wiz_id, datas, state):
+                    ctx = context.copy()
+                    ctx.update(rpc.session.context)
+                    try:
+                        self.res = rpc.session.rpc_exec_auth_try('/wizard', 'execute', wiz_id, datas, state, ctx)
+                    except Exception, e:
+                        self.error = True
+                        self.res = False
+                        self.exception = e
+                        return True
+                    if not self.res:
+                        self.error = True
+                    return True
+
+                thread.start_new_thread(go, (wiz_id, datas, state))
+
+                i = 0
+                win = None
+                pb = None
+                while (not self.res) and (not self.error):
+                    time.sleep(0.1)
+                    i += 1
+                    if i > 10:
+                        if not win or not pb:
+                            win, pb = common.OpenERP_Progressbar(self.parent)
+                        pb.pulse()
+                        gtk.main_iteration()
+                if win:
+                    win.destroy()
+                    gtk.main_iteration()
+                if self.exception:
+                    import xmlrpclib
+                    import socket
+                    from rpc import rpc_exception, CONCURRENCY_CHECK_FIELD
+                    import tiny_socket
+                    try:
+                        raise self.exception
+                    except socket.error, e:
+                        common.message(str(e), title=_('Connection refused !'), type=gtk.MESSAGE_ERROR, parent=self.parent)
+                    except xmlrpclib.Fault, err:
+                        a = rpc_exception(err.faultCode, err.faultString)
+                        if a.type in ('warning', 'UserError'):
+                            if a.message in ('ConcurrencyException') and len(args) > 4:
+                                if common.concurrency(args[0], args[2][0], args[4]):
+                                    if CONCURRENCY_CHECK_FIELD in args[4]:
+                                        del args[4][CONCURRENCY_CHECK_FIELD]
+                                    return self.rpc_exec_auth(obj, method, *args)
+                            else:
+                                common.warning(a.data, a.message, parent=self.parent)
+                        else:
+                            common.error(_('Application Error'), err.faultCode, err.faultString)
+                    except tiny_socket.Myexception, err:
+                        a = rpc_exception(err.faultCode, err.faultString)
+                        if a.type in ('warning', 'UserError'):
+                            common.warning(a.data, a.message, parent=self.parent)
+                        else:
+                            common.error(_('Application Error'), err.faultCode, err.faultString)
+                    except Exception, e:
+                        common.error(_('Application Error'), _('View details'), str(e))
+                return self.res
+
+        wp = wizard_progress(parent)
+        res = wp.run()
+        if not res:
+            return False
+
+        if 'datas' in res:
+            datas['form'].update( res['datas'] )
+        if res['type']=='form':
+            dia = dialog(res['arch'], res['fields'], res['state'], action, parent)
+            dia.screen.current_model.set( datas['form'] )
+            res = dia.run(datas['form'])
+            if not res:
+                break
+            (state, new_data) = res
+            for d in new_data:
+                if new_data[d]==None:
+                    del new_data[d]
+            datas['form'].update(new_data)
+            del new_data
+        elif res['type']=='action':
+            obj = service.LocalService('action.main')
+            obj._exec_action(res['action'],datas)
+            state = res['state']
+        elif res['type']=='print':
+            obj = service.LocalService('action.main')
+            datas['report_id'] = res.get('report_id', False)
+            if res.get('get_id_from_action', False):
+                backup_ids = datas['ids']
+                datas['ids'] = datas['form']['ids']
+                win = obj.exec_report(res['report'], datas)
+                datas['ids'] = backup_ids
+            else:
+                win = obj.exec_report(res['report'], datas)
+            state = res['state']
+        elif res['type']=='state':
+            state = res['state']
+        #common.error('Wizard Error:'+ str(e.type), e.message, e.data)
+        #state = 'end'
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added directory 'bin/modules/gui'
=== added file 'bin/modules/gui/__init__.py'
--- bin/modules/gui/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import main
+import window
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/main.py'
--- bin/modules/gui/main.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/main.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,1607 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import time
+import os
+import gettext
+import urlparse
+
+import gobject
+import gtk
+from gtk import glade
+from pango import parse_markup
+import translate
+
+import rpc
+
+import service
+import options
+import common
+
+from window import win_preference, win_extension
+import tools
+import re
+import xmlrpclib
+import base64
+
+import thread
+import gc
+
+RESERVED_KEYWORDS=['absolute', 'action', 'all', 'alter', 'analyse', 'analyze', 'and', 'any', 'as', 'asc', 'authorization', 'between', 'binary', 'both',
+            'case', 'cast', 'check', 'collate', 'column','constraint', 'create', 'cross', 'current_date', 'current_time', 'current_timestamp',
+            'current_user','default', 'deferrable', 'desc', 'distinct', 'do', 'else', 'end', 'except', 'false', 'for', 'foreign', 'freeze',
+            'from', 'full', 'grant', 'group', 'having', 'ilike', 'in', 'initially','inner', 'intersect', 'into', 'is', 'isnull', 'join', 'leading',
+            'left', 'like', 'limit', 'localtime', 'localtimestamp', 'natural', 'new', 'not', 'notnull', 'null', 'off', 'offset', 'old',
+             'on', 'only', 'or', 'order', 'outer', 'overlaps', 'placing', 'primary', 'references', 'right','select', 'session_user', 'similar',
+             'some', 'sysid', 'table', 'then', 'to', 'trailing', 'true', 'union', 'unique', 'user', 'using', 'verbose', 'when', 'where']
+
+def check_ssl():
+    try:
+        from OpenSSL import SSL
+        import socket
+
+        return hasattr(socket, 'ssl')
+    except:
+        return False
+
+class StockButton(gtk.Button):
+    def __init__(self, label, stock):
+        gtk.Button.__init__(self, label)
+        self.icon = gtk.Image()
+        self.icon.set_from_stock(stock, gtk.ICON_SIZE_MENU)
+        self.set_image(self.icon)
+
+class DatabaseDialog(gtk.Dialog):
+    def __init__(self, label, parent):
+        gtk.Dialog.__init__(
+            self, label, parent,
+            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+             gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
+        )
+
+        self.set_icon(common.OPENERP_ICON)
+        self.set_default_response(gtk.RESPONSE_ACCEPT)
+        self.set_response_sensitive(gtk.RESPONSE_ACCEPT, False)
+
+        self.table = gtk.Table(3, 2, False)
+        self.table.set_row_spacings(5)
+        self.table.set_col_spacings(5)
+
+        self.messageLabel = gtk.Label('<b>'+_('Could not connect to server !')+'</b>')
+        self.messageLabel.set_use_markup(True)
+        self.messageLabel.hide()
+
+        lbl = gtk.Label(_("Server:"))
+        lbl.set_alignment(1.0, 0.5)
+        self.table.attach(lbl, 0, 1, 0, 1)
+        hbox = gtk.HBox(spacing=5)
+        self.serverEntry = gtk.Entry()
+        self.serverEntry.connect('changed', self.on_server_entry_changed, self.messageLabel)
+        self.serverEntry.set_text(self.default_server_url())
+        self.serverEntry.set_sensitive(False)
+
+        hbox.pack_start(self.serverEntry, False, False)
+
+        but_server = StockButton(_("Change"), gtk.STOCK_NETWORK)
+        but_server.connect_after('clicked', lambda *a: _server_ask(self.serverEntry, parent))
+        hbox.pack_start(but_server, False, False)
+        self.table.attach(hbox, 1, 2, 0, 1)
+
+        self.table.attach(self.messageLabel, 0, 2, 1, 2)
+
+        lbl = gtk.Label(_("Super Administrator Password:"))
+        lbl.set_alignment(1.0, 0.5)
+        self.table.attach(lbl, 0, 1, 2, 3)
+        self.adminPwdEntry = gtk.Entry()
+        self.adminPwdEntry.set_visibility(False)
+        self.adminPwdEntry.set_activates_default(True)
+        self.table.attach(self.adminPwdEntry, 1, 2, 2, 3)
+
+        self.vbox.add(self.table)
+
+    def run(self):
+        self.show_all()
+        self.messageLabel.hide()
+        res = super(DatabaseDialog, self).run()
+        if res == gtk.RESPONSE_ACCEPT:
+            self.run_thread()
+
+        self.destroy()
+
+    def on_server_entry_changed(self, entry, message):
+        try:
+            rpc.session.about(entry.get_text())
+            self.clear_screen()
+            self.on_server_entry_changed_after(entry)
+            self.set_response_sensitive(gtk.RESPONSE_ACCEPT, True)
+        except Exception, ex:
+            self.clear_screen()
+            message.show()
+
+    def default_server_url(self):
+        return "%(protocol)s%(host)s:%(port)d" % {
+            'protocol' : options.options['login.protocol'],
+            'host' : options.options['login.server'],
+            'port' : int(options.options['login.port']),
+        }
+
+    def run_thread(self):
+        import thread
+        self.result = None
+        self.error = False
+        self.exception = None
+        def go():
+            try:
+                self.on_response_accept()
+                self.result = True
+            except Exception, e:
+                self.result = True
+                self.exception = e
+            return True
+
+        thread.start_new_thread(go, ())
+
+        i = 0
+        win = None
+        pb = None
+        while not self.result:
+            time.sleep(0.1)
+            i += 1
+
+            if i > 10:
+                if not win or not pb:
+                    win, pb = common.OpenERP_Progressbar(self)
+                pb.pulse()
+                gtk.main_iteration()
+        if win:
+            win.destroy()
+            gtk.main_iteration()
+
+        if self.exception:
+            import xmlrpclib
+            import socket
+            import tiny_socket
+            from rpc import rpc_exception
+            try:
+                raise self.exception
+            except socket.error, e:
+                common.message(str(e), title=_('Connection refused !'), type=gtk.MESSAGE_ERROR)
+            except (tiny_socket.Myexception, xmlrpclib.Fault), err:
+                a = rpc_exception(err.faultCode, err.faultString)
+                if a.type in ('warning', 'UserError'):
+                    common.warning(a.data, a.message, parent=self)
+                elif a.type == 'AccessDenied':
+                    common.warning(_('Bad Super Administrator Password'), self.get_title(), parent=self)
+                else:
+                    common.error(_('Application Error'), err.faultCode, err.faultString, disconnected_mode=True)
+            except Exception, e:
+                import sys
+                import traceback
+                tb = sys.exc_info()
+                tb_s = "".join(traceback.format_exception(*tb))
+                common.error(_('Application Error'), str(e), tb_s, disconnected_mode=True)
+        else:
+            if hasattr(self, 'message') and self.message:
+                common.message(self.message, self.get_title())
+
+    def on_response_accept(self):
+        pass
+
+    def on_server_entry_changed_after(self, entry):
+        pass
+
+    def clear_screen(self):
+        self.messageLabel.hide()
+
+class RetrieveMigrationScriptDialog(DatabaseDialog):
+    def __init__(self, parent):
+        DatabaseDialog.__init__(self, _("Migration Scripts"), parent)
+        self.table.resize(5, 2)
+
+        lbl = gtk.Label(_("Contract ID:"))
+        lbl.set_alignment(1.0, 0.5)
+        self.table.attach(lbl, 0, 1, 3, 4)
+        self.contractIdEntry = gtk.Entry()
+        self.table.attach(self.contractIdEntry, 1, 2, 3, 4)
+
+        lbl = gtk.Label(_("Contract Password:"))
+        lbl.set_alignment(1.0, 0.5)
+        self.table.attach(lbl, 0, 1, 4, 5)
+        self.contractPwdEntry = gtk.Entry()
+        self.contractPwdEntry.set_visibility(False)
+        self.table.attach(self.contractPwdEntry, 1, 2, 4, 5)
+
+    def on_response_accept(self):
+        au = rpc.session.get_available_updates(
+            self.serverEntry.get_text(),
+            self.adminPwdEntry.get_text(),
+            self.contractIdEntry.get_text(),
+            self.contractPwdEntry.get_text(),
+        )
+        if not au:
+            self.message = _("You already have the latest version")
+            return
+
+        au = ["%s: %s" % (k, v) for k, v in au.items()]
+        au.sort()
+        au.insert(0, _("The following updates are available:"))
+        msg = "\n * ".join(au)
+        if not common.sur(msg):
+            return
+
+        # The OpenERP server fetchs the migration scripts
+        rpc.session.get_migration_scripts(
+            self.serverEntry.get_text(),
+            self.adminPwdEntry.get_text(),
+            self.contractIdEntry.get_text(),
+            self.contractPwdEntry.get_text(),
+        )
+        self.message = _("You can now migrate your databases.")
+
+class MigrationDatabaseDialog(DatabaseDialog):
+    def __init__(self, parent):
+        self.model = gtk.ListStore(bool, str)
+        DatabaseDialog.__init__(self, _("Migrate Database"), parent)
+
+        sw = gtk.ScrolledWindow()
+        sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+        treeview = gtk.TreeView(self.model)
+        treeview.set_rules_hint(True)
+        treeview.set_size_request(300, 380)
+
+        # Add the boolean column (apply)
+        renderer = gtk.CellRendererToggle()
+        renderer.set_property('activatable', True)
+        renderer.connect('toggled', self._on_toggle_renderer__toggled, 0)
+        col = gtk.TreeViewColumn("Apply", renderer, active=0)
+        treeview.append_column(col)
+
+        # Add the text column (database name)
+        renderer = gtk.CellRendererText()
+        col = gtk.TreeViewColumn(_("Database"), renderer, text=1)
+        treeview.append_column(col)
+        sw.add(treeview)
+        self.table.attach(sw, 0, 2, 3, 4)
+
+    def on_response_accept(self):
+        databases = [ item[1] for item in self.model if bool(item[0]) ]
+        if databases:
+            rpc.session.migrate_databases(self.serverEntry.get_text(),
+                                          self.adminPwdEntry.get_text(),
+                                          databases)
+            if len(databases) == 1:
+                self.message = _("Your database has been upgraded.")
+            else:
+                self.message = _("Your databases have been upgraded.")
+        else:
+            self.message = _("You have not selected a database")
+
+    def _on_toggle_renderer__toggled(self, renderer, path, col_index):
+        row = self.model[path]
+        row[col_index] = not row[col_index]
+
+    def clear_screen(self):
+        super(MigrationDatabaseDialog, self).clear_screen()
+        self.model.clear()
+
+    def on_server_entry_changed_after(self, entry):
+        self.clear_screen()
+        try:
+            result = rpc.session.list_db(entry.get_text())
+        except:
+            return
+        if result:
+            for db_num, db_name in enumerate(result):
+                self.model.set( self.model.append(), 0, False, 1, db_name)
+
+def _get_db_name_from_url(url):
+    if not url:
+        return ''
+    url = url.split('://', 1)[1].rsplit(':', 1)[0]
+    if '.' in url:
+        import socket
+        try:
+            socket.inet_aton(url)
+        except socket.error:
+            return url.split('.', 1)[0]
+    return ''
+
+def _refresh_dblist(db_widget, entry_db, label, butconnect, url, dbtoload=None):
+    if not dbtoload:
+        dbtoload = options.options['login.db'] or ''
+        if not dbtoload:
+            dbtoload = _get_db_name_from_url(url)
+
+    label.hide()
+
+    liststore = db_widget.get_model()
+    liststore.clear()
+    try:
+        result = rpc.session.list_db(url)
+    except:
+        label.set_label('<b>'+_('Could not connect to server !')+'</b>')
+        db_widget.hide()
+        entry_db.hide()
+        label.show()
+        if butconnect:
+            butconnect.set_sensitive(False)
+        return False
+
+    if result is None:
+        entry_db.show()
+        entry_db.set_text(dbtoload)
+        entry_db.grab_focus()
+        db_widget.hide()
+        if butconnect:
+            butconnect.set_sensitive(True)
+    else:
+        entry_db.hide()
+
+        if not result:
+            label.set_label('<b>'+_('No database found, you must create one !')+'</b>')
+            label.show()
+            db_widget.hide()
+            if butconnect:
+                butconnect.set_sensitive(False)
+        else:
+            db_widget.show()
+            index = 0
+            for db_num, db_name in enumerate(result):
+                liststore.append([db_name])
+                if db_name == dbtoload:
+                    index = db_num
+            db_widget.set_active(index)
+            if butconnect:
+                butconnect.set_sensitive(True)
+
+    lm = rpc.session.login_message(url)
+    if lm:
+        try:
+            parse_markup(lm)
+        except:
+            pass
+        else:
+            label.set_label(lm)
+            label.show()
+
+    return True
+
+def _refresh_langlist(lang_widget, url):
+    liststore = lang_widget.get_model()
+    liststore.clear()
+    lang_list = rpc.session.db_exec_no_except(url, 'list_lang')
+    lang = rpc.session.context.get('lang', options.options.get('client.lang', 'en_US'))
+    active_idx = -1
+    for index, (key,val) in enumerate(lang_list):
+        if key == lang:
+            active_idx = index
+        liststore.append((val,key))
+    if active_idx != -1:
+        lang_widget.set_active(active_idx)
+    return lang_list
+
+def _server_ask(server_widget, parent=None):
+    result = False
+    win_gl = glade.XML(common.terp_path("openerp.glade"),"win_server",gettext.textdomain())
+    win = win_gl.get_widget('win_server')
+    if not parent:
+        parent = service.LocalService('gui.main').window
+    win.set_transient_for(parent)
+    win.set_icon(common.OPENERP_ICON)
+    win.show_all()
+    win.set_default_response(gtk.RESPONSE_OK)
+    host_widget = win_gl.get_widget('ent_host')
+    port_widget = win_gl.get_widget('ent_port')
+    protocol_widget = win_gl.get_widget('protocol')
+
+    protocol = {
+        'XML-RPC (port : 8069)': 'http://',
+        'NET-RPC (faster)(port : 8070)': 'socket://',
+    }
+
+    if check_ssl():
+        protocol['XML-RPC secure'] ='https://'
+
+    listprotocol = gtk.ListStore(str)
+    protocol_widget.set_model(listprotocol)
+
+    m = re.match('^(http[s]?://|socket://)([\w.-]+):(\d{1,5})$', server_widget.get_text())
+    if m:
+        host_widget.set_text(m.group(2))
+        port_widget.set_text(m.group(3))
+
+    index = 0
+    i = 0
+    for p in protocol:
+        listprotocol.append([p])
+        if m and protocol[p] == m.group(1):
+            index = i
+        i += 1
+    protocol_widget.set_active(index)
+
+    res = win.run()
+    if res == gtk.RESPONSE_OK:
+        protocol = protocol[protocol_widget.get_active_text()]
+        url = '%s%s:%s' % ((protocol).strip(), (host_widget.get_text()).strip(), (port_widget.get_text()).strip())
+        server_widget.set_text(url)
+        result = url
+    parent.present()
+    win.destroy()
+    return result
+
+
+class db_login(object):
+    def __init__(self):
+        self.win_gl = glade.XML(common.terp_path("openerp.glade"),"win_login",gettext.textdomain())
+        self.win = self.win_gl.get_widget('win_login')
+
+    def refreshlist(self, widget, db_widget, entry_db, label, url, butconnect=False):
+
+        def check_server_version(url):
+            try:
+                import release
+                full_server_version = rpc.session.db_exec_no_except(url, 'server_version')
+                server_version = full_server_version.split('.')
+                client_version = release.version.split('.')
+                return (server_version[:2] == client_version[:2], full_server_version, release.version)
+            except:
+                # the server doesn't understand the request. It's mean that it's an old version of the server
+                return (False, _('Unknown'), release.version)
+
+        if _refresh_dblist(db_widget, entry_db, label, butconnect, url):
+            is_same_version, server_version, client_version = check_server_version(url)
+            if not is_same_version:
+                common.warning(_('The versions of the server (%s) and the client (%s) missmatch. The client may not work properly. Use it at your own risks.') % (server_version, client_version,),parent=self.win)
+
+    def refreshlist_ask(self,widget, server_widget, db_widget, entry_db, label, butconnect = False, url=False, parent=None):
+        url = _server_ask(server_widget, parent) or url
+        return self.refreshlist(widget, db_widget, entry_db, label, url, butconnect)
+
+    def run(self, dbname=None, parent=None):
+        uid = 0
+        win = self.win_gl.get_widget('win_login')
+        if not parent:
+            parent = service.LocalService('gui.main').window
+        win.set_transient_for(parent)
+        win.set_icon(common.OPENERP_ICON)
+        win.set_resizable(False)
+        win.show_all()
+        img = self.win_gl.get_widget('image_tinyerp')
+        img.set_from_file(common.terp_path_pixmaps('openerp.png'))
+        login = self.win_gl.get_widget('ent_login')
+        passwd = self.win_gl.get_widget('ent_passwd')
+        server_widget = self.win_gl.get_widget('ent_server')
+        but_connect = self.win_gl.get_widget('button_connect')
+        combo_db = self.win_gl.get_widget('combo_db')
+        entry_db = self.win_gl.get_widget('ent_db')
+        change_button = self.win_gl.get_widget('but_server')
+        label = self.win_gl.get_widget('combo_label')
+#        db_entry = self.win_gl.get_widget('ent_db')
+        label.hide()
+        entry_db.hide()
+
+        host = options.options['login.server']
+        port = options.options['login.port']
+        protocol = options.options['login.protocol']
+
+        url = '%s%s:%s' % (protocol, host, port)
+        server_widget.set_text(url)
+        login.set_text(options.options['login.login'])
+
+        # construct the list of available db and select the last one used
+        liststore = gtk.ListStore(str)
+        combo_db.set_model(liststore)
+        cell = gtk.CellRendererText()
+        combo_db.pack_start(cell, True)
+        combo_db.add_attribute(cell, 'text', 0)
+
+        res = self.refreshlist(None, combo_db, entry_db, label, url, but_connect)
+        change_button.connect_after('clicked', self.refreshlist_ask, server_widget, combo_db, entry_db, label, but_connect, url, win)
+
+        if dbname:
+            iter = liststore.get_iter_root()
+            while iter:
+                if liststore.get_value(iter, 0)==dbname:
+                    combo_db.set_active_iter(iter)
+                    break
+                iter = liststore.iter_next(iter)
+
+        res = win.run()
+        m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', server_widget.get_text() or '')
+        if m:
+            if combo_db.flags() & gtk.VISIBLE:
+                dbname = combo_db.get_active_text()
+            else:
+                dbname = entry_db.get_text()
+
+            options.options['login.server'] = m.group(2)
+            options.options['login.login'] = login.get_text()
+            options.options['login.port'] = m.group(3)
+            options.options['login.protocol'] = m.group(1)
+            options.options['login.db'] = dbname
+            result = (login.get_text(), passwd.get_text(), m.group(2), m.group(3), m.group(1), dbname)
+        else:
+            parent.present()
+            win.destroy()
+            raise Exception('QueryCanceled')
+        if res <> gtk.RESPONSE_OK:
+            parent.present()
+            win.destroy()
+            raise Exception('QueryCanceled')
+        parent.present()
+        win.destroy()
+        return result
+
+class db_create(object):
+    def set_sensitive(self, sensitive):
+        self.dialog.get_widget('button_db_ok').set_sensitive(False)
+        return sensitive
+
+    def server_change(self, widget=None, parent=None):
+        url = _server_ask(self.server_widget)
+        if self.lang_widget and url:
+            _refresh_langlist(self.lang_widget, url)
+        return url
+
+    def __init__(self, sig_login, terp_main):
+        self.dialog = glade.XML(common.terp_path("openerp.glade"), "win_createdb", gettext.textdomain())
+        self.sig_login = sig_login
+        self.terp_main = terp_main
+
+    def entry_changed(self, *args):
+        up1 = self.dialog.get_widget('ent_user_pass1').get_text()
+        up2 = self.dialog.get_widget('ent_user_pass2').get_text()
+        self.dialog.get_widget('button_db_ok').set_sensitive(bool(up1 and (up1==up2)))
+
+    def run(self, parent=None):
+        win = self.dialog.get_widget('win_createdb')
+        self.dialog.signal_connect('on_ent_user_pass1_changed', self.entry_changed)
+        self.dialog.signal_connect('on_ent_user_pass2_changed', self.entry_changed)
+        win.set_default_response(gtk.RESPONSE_OK)
+        if not parent:
+            parent = service.LocalService('gui.main').window
+        win.set_transient_for(parent)
+        win.show_all()
+        lang_dict = {}
+        pass_widget = self.dialog.get_widget('ent_password_new')
+        self.server_widget = self.dialog.get_widget('ent_server_new')
+        change_button = self.dialog.get_widget('but_server_new')
+        self.lang_widget = self.dialog.get_widget('db_create_combo')
+        self.db_widget = self.dialog.get_widget('ent_db_new')
+        demo_widget = self.dialog.get_widget('check_demo')
+        demo_widget.set_active(True)
+
+        change_button.connect_after('clicked', self.server_change, win)
+        protocol = options.options['login.protocol']
+        url = '%s%s:%s' % (protocol, options.options['login.server'], options.options['login.port'])
+
+        self.server_widget.set_text(url)
+        liststore = gtk.ListStore(str, str)
+        self.lang_widget.set_model(liststore)
+        try:
+            _refresh_langlist(self.lang_widget, url)
+        except:
+            self.set_sensitive(False)
+
+        while True:
+            res = win.run()
+            db_name = self.db_widget.get_text().lower()
+            if (res==gtk.RESPONSE_OK) and (db_name in RESERVED_KEYWORDS):
+                common.warning(_("Sorry,'" +db_name + "' cannot be the name of the database,it's a Reserved Keyword."), _('Bad database name !'), parent=win)
+                continue
+            if (res==gtk.RESPONSE_OK) and ((not db_name) or (not re.match('^[a-zA-Z0-9][a-zA-Z0-9_]+$', db_name))):
+                common.warning(_('The database name must contain only normal characters or "_".\nYou must avoid all accents, space or special characters.'), _('Bad database name !'), parent=win)
+
+            else:
+                break
+        demo_data = demo_widget.get_active()
+
+        langidx = self.lang_widget.get_active_iter()
+        langreal = langidx and self.lang_widget.get_model().get_value(langidx,1)
+        passwd = pass_widget.get_text()
+        user_pass = self.dialog.get_widget('ent_user_pass1').get_text()
+        url = self.server_widget.get_text()
+        m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url or '')
+        if m:
+            options.options['login.server'] = m.group(2)
+            options.options['login.port'] = m.group(3)
+            options.options['login.protocol'] = m.group(1)
+            options.options['login.db'] = db_name
+        parent.present()
+        win.destroy()
+
+        if res == gtk.RESPONSE_OK:
+            try:
+                id = rpc.session.db_exec(url, 'list')
+                if db_name in id:
+                    raise Exception('DbExist')
+                id = rpc.session.db_exec(url, 'create', passwd, db_name, demo_data, langreal, user_pass)
+                win, pb = common.OpenERP_Progressbar(parent, title='OpenERP Database Installation')
+                self.timer = gobject.timeout_add(1000, self.progress_timeout, pb, url, passwd, id, win, db_name, parent)
+                self.terp_main.glade.get_widget('but_menu').set_sensitive(True)
+                self.terp_main.glade.get_widget('user').set_sensitive(True)
+                self.terp_main.glade.get_widget('form').set_sensitive(True)
+                self.terp_main.glade.get_widget('plugins').set_sensitive(True)
+            except Exception, e:
+                if e.args == ('DbExist',):
+                    common.warning(_("Could not create database."),_('Database already exists !'), parent=parent)
+                elif (getattr(e,'faultCode',False)=='AccessDenied') or str(e)=='AccessDenied':
+                    common.warning(_('Bad database administrator password !'), _("Could not create database."), parent=parent)
+                else:
+                    common.warning(_("Could not create database."),_('Error during database creation !'), parent=parent)
+
+    def progress_timeout(self, pbar, url, passwd, id, win, dbname, parent=None):
+        try:
+            progress,users = rpc.session.db_exec_no_except(url, 'get_progress', passwd, id)
+        except:
+            win.destroy()
+            common.warning(_("The server crashed during installation.\nWe suggest you to drop this database."),_("Error during database creation !"))
+            return False
+
+        pbar.pulse()
+        if progress == 1.0:
+            win.destroy()
+            m = re.match('^(http[s]?://|socket://)([\w.]+):(\d{1,5})$', url)
+            ok = False
+            for x in users:
+                if x['login']=='admin' and m:
+                    res = [x['login'], x['password']]
+                    res.append( m.group(2) )
+                    res.append( m.group(3) )
+                    res.append( m.group(1) )
+                    res.append( dbname )
+                    log_response = rpc.session.login(*res)
+                    if log_response == 1:
+                        options.options['login.login'] = x['login']
+                        id = self.terp_main.sig_win_menu(quiet=False)
+                        ok = True
+                        break
+            if not ok:
+                self.sig_login(dbname=dbname)
+            return False
+        return True
+
+    def process(self):
+        return False
+
+
+class terp_main(service.Service):
+    def __init__(self, name='gui.main', audience='gui.*'):
+
+        service.Service.__init__(self, name, audience)
+        self.exportMethod(self.win_add)
+
+        self._handler_ok = True
+        self.glade = glade.XML(common.terp_path("openerp.glade"),"win_main",gettext.textdomain())
+        self.status_bar_main = self.glade.get_widget('hbox_status_main')
+        self.status_bar_main.show()
+        self.toolbar = self.glade.get_widget('main_toolbar')
+        self.sb_company = self.glade.get_widget('sb_company')
+        self.sb_requests = self.glade.get_widget('sb_requests')
+        self.sb_username = self.glade.get_widget('sb_user_name')
+        self.sb_servername = self.glade.get_widget('sb_user_server')
+        id = self.sb_servername.get_context_id('message')
+        self.sb_servername.push(id, _('Press Ctrl+O to login'))
+        self.secure_img = self.glade.get_widget('secure_img')
+        self.secure_img.hide()
+
+        window = self.glade.get_widget('win_main')
+        window.connect("destroy", self.sig_quit)
+        window.connect("delete_event", self.sig_delete)
+        self.window = window
+        self.window.set_icon(common.OPENERP_ICON)
+
+        self.notebook = gtk.Notebook()
+        self.notebook.set_scrollable(True)
+        self.sig_id = self.notebook.connect_after('switch-page', self._sig_page_changed)
+        if gtk.pygtk_version >= (2, 10, 0):
+            self.notebook.connect('page-reordered', self._sig_page_reordered)
+        vbox = self.glade.get_widget('vbox_main')
+        vbox.pack_start(self.notebook, expand=True, fill=True)
+
+        self.shortcut_menu = self.glade.get_widget('shortcut')
+
+        #
+        # Default Notebook
+        #
+
+        self.notebook.show()
+        self.pages = []
+        self.current_page = 0
+        self.last_page = 0
+
+        callbacks_dict = {
+            'on_login_activate': self.sig_login,
+            'on_logout_activate': self.sig_logout,
+            'on_win_next_activate': self.sig_win_next,
+            'on_win_prev_activate': self.sig_win_prev,
+            'on_plugin_execute_activate': self.sig_plugin_execute,
+            'on_quit_activate': self.sig_close,
+            'on_but_menu_clicked': self.sig_win_menu,
+            'on_win_new_activate': self.sig_win_menu,
+            'on_win_home_activate': self.sig_home_new,
+            'on_win_close_activate': self.sig_win_close,
+            'on_support_activate': common.support,
+            'on_preference_activate': self.sig_user_preferences,
+            'on_change_passwd_activate':lambda x:self.sig_db_password('user'),
+            'on_read_requests_activate': self.sig_request_open,
+            'on_send_request_activate': self.sig_request_new,
+            'on_request_wait_activate': self.sig_request_wait,
+            'on_opt_save_activate': lambda x: options.options.save(),
+            'on_menubar_icons_activate': lambda x: self.sig_menubar('icons'),
+            'on_menubar_text_activate': lambda x: self.sig_menubar('text'),
+            'on_menubar_both_activate': lambda x: self.sig_menubar('both'),
+            'on_opt_form_tab_top_activate': lambda x: self.sig_form_tab('top'),
+            'on_opt_form_tab_left_activate': lambda x: self.sig_form_tab('left'),
+            'on_opt_form_tab_right_activate': lambda x: self.sig_form_tab('right'),
+            'on_opt_form_tab_bottom_activate': lambda x: self.sig_form_tab('bottom'),
+            'on_opt_form_tab_orientation_horizontal_activate': lambda x: self.sig_form_tab_orientation(0),
+            'on_opt_form_tab_orientation_vertical_activate': lambda x: self.sig_form_tab_orientation(90),
+            'on_opt_debug_mode_activate':self.sig_debug_mode_tooltip,
+            'on_help_index_activate': self.sig_help_index,
+            'on_help_contextual_activate': self.sig_help_context,
+            'on_help_licence_activate': self.sig_licence,
+            'on_about_activate': self.sig_about,
+            'on_shortcuts_activate' : self.sig_shortcuts,
+            'on_db_new_activate': self.sig_db_new,
+            'on_db_restore_activate': self.sig_db_restore,
+            'on_db_backup_activate': self.sig_db_dump,
+            'on_db_drop_activate': self.sig_db_drop,
+            'on_admin_password_activate': lambda x:self.sig_db_password('admin'),
+            'on_extension_manager_activate': self.sig_extension_manager,
+            'on_db_migrate_retrieve_script_activate': self.sig_db_migrate_retrieve_script,
+            'on_db_migrate_activate' : self.sig_db_migrate,
+        }
+
+        self.glade.signal_autoconnect(callbacks_dict)
+
+        self.buttons = {}
+        for button in ('but_new', 'but_save', 'but_remove', 'but_search', 'but_previous', 'but_next', 'but_action', 'but_open', 'but_print', 'but_close', 'but_reload', 'but_switch','but_attach',
+                       'radio_tree','radio_form','radio_graph','radio_calendar','radio_diagram', 'radio_gantt'):
+            self.glade.signal_connect('on_'+button+'_clicked', self._sig_child_call, button)
+            self.buttons[button]=self.glade.get_widget(button)
+
+        menus = {
+            'form_del': 'but_remove',
+            'form_new': 'but_new',
+            'form_copy': 'but_copy',
+            'form_reload': 'but_reload',
+            'form_log': 'but_log',
+            'form_open': 'but_open',
+            'form_search': 'but_search',
+            'form_previous': 'but_previous',
+            'form_next': 'but_next',
+            'form_save': 'but_save',
+            'goto_id': 'but_goto_id',
+            'form_print': 'but_print',
+            'form_print_html': 'but_print_html',
+            'form_save_as': 'but_save_as',
+            'form_import': 'but_import',
+            'form_filter': 'but_filter',
+            'form_repeat': 'but_print_repeat'
+        }
+        for menu in menus:
+            self.glade.signal_connect('on_'+menu+'_activate', self._sig_child_call, menus[menu])
+
+        spool = service.LocalService('spool')
+        spool.subscribe('gui.window', self.win_add)
+
+
+        # we now create the icon for the attachment button when there are attachments
+        self.__img_no_attachments = gtk.Image()
+        pxbf = self.window.render_icon(self.buttons['but_attach'].get_stock_id(), self.toolbar.get_icon_size())
+        self.__img_no_attachments.set_from_pixbuf(pxbf)
+        self.__img_no_attachments.show()
+
+        pxbf = pxbf.copy()
+        w, h = pxbf.get_width(), pxbf.get_height()
+        overlay = self.window.render_icon(gtk.STOCK_APPLY, gtk.ICON_SIZE_MENU)
+        ow, oh = overlay.get_width(), overlay.get_height()
+        overlay.composite(pxbf,
+                        0, h - oh,
+                        ow, oh,
+                        0, h - oh,
+                        1.0, 1.0,
+                        gtk.gdk.INTERP_NEAREST,
+                        255)
+
+        self.__img_attachments = gtk.Image()
+        self.__img_attachments.set_from_pixbuf(pxbf)
+        self.__img_attachments.show()
+
+        self.sb_set()
+
+        settings = gtk.settings_get_default()
+        settings.set_long_property('gtk-button-images', 1, 'OpenERP:gui.main')
+
+        def fnc_menuitem(menuitem, opt_name):
+            options.options[opt_name] = menuitem.get_active()
+        dict = {
+            'on_opt_print_preview_activate': (fnc_menuitem, 'printer.preview', 'opt_print_preview'),
+            'on_opt_form_toolbar_activate': (fnc_menuitem, 'form.toolbar', 'opt_form_toolbar'),
+        }
+        self.glade.get_widget('menubar_'+(options.options['client.toolbar'] or 'both')).set_active(True)
+        self.sig_menubar(options.options['client.toolbar'] or 'both')
+        self.glade.get_widget('opt_form_tab_'+(options.options['client.form_tab'] or 'left')).set_active(True)
+        self.sig_form_tab(options.options['client.form_tab'] or 'left')
+        self.glade.get_widget('opt_form_tab_orientation_'+(str(options.options['client.form_tab_orientation']) or '0')).set_active(True)
+        self.sig_form_tab_orientation(options.options['client.form_tab_orientation'] or 0)
+        self.sig_debug_mode_tooltip()
+        for signal in dict:
+            self.glade.signal_connect(signal, dict[signal][0], dict[signal][1])
+            self.glade.get_widget(dict[signal][2]).set_active(int(bool(options.options[dict[signal][1]])))
+
+        # Adding a timer the check to requests
+        gobject.timeout_add(15 * 60 * 1000, self.request_set)
+
+
+    def shortcut_edit(self, widget, model='ir.ui.menu'):
+        obj = service.LocalService('gui.window')
+        domain = [('user_id', '=', rpc.session.uid), ('resource', '=', model)]
+        obj.create(False, 'ir.ui.view_sc', res_id=None, domain=domain, view_type='form', mode='tree,form')
+
+    def shortcut_set(self, sc=None):
+        def _action_shortcut(widget, action):
+            if action:
+                ctx = rpc.session.context.copy()
+                obj = service.LocalService('action.main')
+                if not isinstance(action, int):
+                    action = action[0]
+                obj.exec_keyword('tree_but_open', {'model': 'ir.ui.menu', 'id': action,
+                    'ids': [action], 'report_type': 'pdf', 'window': self.window}, context=ctx)
+
+        if sc is None:
+            uid = rpc.session.uid
+            sc = rpc.session.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'get_sc', uid, 'ir.ui.menu', rpc.session.context) or []
+
+        menu = gtk.Menu()
+        for s in sc:
+            menuitem = gtk.MenuItem(s['name'])
+            menuitem.connect('activate', _action_shortcut, s['res_id'])
+            menu.add(menuitem)
+
+        menu.add(gtk.SeparatorMenuItem())
+        menuitem = gtk.MenuItem(_('Edit'))
+        menuitem.connect('activate', self.shortcut_edit)
+        menu.add(menuitem)
+
+        menu.show_all()
+        self.shortcut_menu.set_submenu(menu)
+        self.shortcut_menu.set_sensitive(True)
+
+    def shortcut_unset(self):
+        menu = gtk.Menu()
+        menu.show_all()
+        self.shortcut_menu.set_submenu(menu)
+        self.shortcut_menu.set_sensitive(False)
+
+    def sig_menubar(self, option):
+        options.options['client.toolbar'] = option
+        if option=='both':
+            self.toolbar.set_style(gtk.TOOLBAR_BOTH)
+        elif option=='text':
+            self.toolbar.set_style(gtk.TOOLBAR_TEXT)
+        elif option=='icons':
+            self.toolbar.set_style(gtk.TOOLBAR_ICONS)
+
+    def sig_form_tab(self, option):
+        options.options['client.form_tab'] = option
+
+    def sig_form_tab_orientation(self, option):
+        options.options['client.form_tab_orientation'] = option
+
+    def sig_win_next(self, args):
+        pn = self.notebook.get_current_page()
+        if pn == len(self.pages)-1:
+            pn = -1
+        self.notebook.set_current_page(pn+1)
+
+    def sig_win_prev(self, args):
+        pn = self.notebook.get_current_page()
+        self.notebook.set_current_page(pn-1)
+
+    def sig_user_preferences(self, *args):
+        win = win_preference.win_preference(parent=self.window)
+        win.run()
+        id = self.sb_company.get_context_id('message')
+        comp = self.company_set()
+        if comp:
+            self.sb_company.push(id, comp)
+        else:
+            self.sb_company.push(id, '')
+        return True
+
+    def sig_win_close(self, *args):
+        if len(args) >= 2:
+            button = args[1].button
+            if (isinstance(args[0], gtk.Button) and button in [1,2]) \
+                    or (isinstance(args[0], gtk.EventBox) and button == 2):
+                page_num = self.notebook.page_num(args[2])
+                self._sig_child_call(args[0], 'but_close', page_num)
+        elif len(args) and isinstance(args[0], gtk.ImageMenuItem):
+            self._sig_child_call(args[0], 'but_close', None)
+
+    def sig_request_new(self, args=None):
+        obj = service.LocalService('gui.window')
+        try:
+            return obj.create(None, 'res.request', False,
+                    [('act_from', '=', rpc.session.uid)], 'form',
+                    mode='form,tree', window=self.window,
+                    context={'active_test': False})
+        except:
+            return False
+
+    def sig_request_open(self, args=None):
+        ids,ids2 = self.request_set()
+        obj = service.LocalService('gui.window')
+        try:
+            return obj.create(False, 'res.request', ids,
+                    [('act_to', '=', rpc.session.uid), ('active', '=', True)],
+                    'form', mode='tree,form', window=self.window,
+                    context={'active_test': False})
+        except:
+            return False
+
+    def sig_request_wait(self, args=None):
+        ids,ids2 = self.request_set()
+        obj = service.LocalService('gui.window')
+        try:
+            return obj.create(False, 'res.request', ids,
+                    [('act_from', '=', rpc.session.uid),
+                        ('state', '=', 'waiting'), ('active', '=', True)],
+                    'form', mode='tree,form', window=self.window,
+                    context={'active_test': False})
+        except:
+            return False
+
+    def company_set(self):
+        try:
+            uid = rpc.session.uid
+            ids = rpc.session.rpc_exec_auth_try('/object', 'execute',
+                    'res.users', 'get_current_company')
+            if len(ids):
+                message = _('%s') % ids[0][1]
+            else:
+                message = _('No Company')
+            id = self.sb_company.get_context_id('message')
+            self.sb_company.push(id, message)
+            return ids[0][1]
+        except:
+            return []
+
+    def request_set(self):
+        try:
+            uid = rpc.session.uid
+            ids,ids2 = rpc.session.rpc_exec_auth_try('/object', 'execute',
+                    'res.request', 'request_get')
+            if len(ids):
+                message = _('%s request(s)') % len(ids)
+            else:
+                message = _('No request')
+            if len(ids2):
+                message += _(' - %s request(s) sent') % len(ids2)
+            id = self.sb_requests.get_context_id('message')
+            self.sb_requests.push(id, message)
+            return (ids,ids2)
+        except:
+            return ([],[])
+
+    def sig_login(self, widget=None, dbname=False):
+        RES_OK = 1
+        RES_BAD_PASSWORD = -2
+        RES_CNX_ERROR = -1
+        RES_NO_DATABASE = 0
+        try:
+            log_response = RES_BAD_PASSWORD
+            res = None
+            while log_response == RES_BAD_PASSWORD:
+                try:
+                    l = db_login()
+                    res = l.run(dbname=dbname, parent=self.window)
+                except Exception, e:
+                    if e.args == ('QueryCanceled',):
+                        return False
+                    raise
+                service.LocalService('gui.main').window.present()
+                self.sig_logout(widget)
+                log_response = rpc.session.login(*res)
+                if log_response == RES_OK:
+                    options.options.save()
+                    id = self.sig_win_menu(quiet=False)
+                    if id:
+                        self.sig_home_new(quiet=True, except_id=id)
+                    if res[4] == 'https://':
+                        self.secure_img.show()
+                    else:
+                        self.secure_img.hide()
+                    self.request_set()
+                    self.company_set()
+                elif log_response == RES_NO_DATABASE:
+                    common.warning( _('Please double-check the database name or contact your administrator to verify the database status.'), _('Database cannot be accessed or does not exist'))
+                    self.sig_login(dbname=dbname)
+                    return True
+                elif log_response == RES_CNX_ERROR:
+                    common.message(_('Connection error !\nUnable to connect to the server !'))
+                elif log_response == RES_BAD_PASSWORD:
+                    common.message(_('Authentication error !\nBad Username or Password !'))
+
+        except rpc.rpc_exception:
+            rpc.session.logout()
+            raise
+        self.glade.get_widget('but_menu').set_sensitive(True)
+        self.glade.get_widget('user').set_sensitive(True)
+        self.glade.get_widget('form').set_sensitive(True)
+        self.glade.get_widget('plugins').set_sensitive(True)
+
+        title = tools.format_connection_string(*res)
+        sbid = self.sb_servername.get_context_id('message')
+        self.sb_servername.push(sbid, title)
+        self.window.set_title(_('OpenERP - %s') % title )
+        return True
+
+    def sig_logout(self, widget):
+        res = True
+        while res:
+            wid = self._wid_get()
+            if wid:
+                if 'but_close' in wid.handlers:
+                    res = wid.handlers['but_close']()
+                if not res:
+                    return False
+                res = self._win_del()
+            else:
+                res = False
+        id = self.sb_requests.get_context_id('message')
+        self.sb_requests.push(id, '')
+        id = self.sb_company.get_context_id('message')
+        self.sb_company.push(id, '')
+        id = self.sb_username.get_context_id('message')
+        self.sb_username.push(id, _('Not logged !'))
+        id = self.sb_servername.get_context_id('message')
+        self.sb_servername.push(id, _('Press Ctrl+O to login'))
+        self.secure_img.hide()
+        self.shortcut_unset()
+        self.glade.get_widget('but_menu').set_sensitive(False)
+        self.glade.get_widget('user').set_sensitive(False)
+        self.glade.get_widget('form').set_sensitive(False)
+        self.glade.get_widget('plugins').set_sensitive(False)
+        self.window.set_title(_('OpenERP') )
+        rpc.session.logout()
+        return True
+
+    def sig_debug_mode_tooltip(self, widget=None):
+        if widget:
+            options.options['debug_mode_tooltips'] = widget.get_active()
+        else:
+            mode = options.options['logging.level']
+            if mode in ('debug', 'debug_rpc','debug_rpc_answer'):
+                options.options['debug_mode_tooltips'] = True
+                self.glade.get_widget('opt_debug_mode_tooltip').set_active(True)
+
+
+    def sig_help_index(self, widget):
+        tools.launch_browser(options.options['help.index'])
+
+    def sig_help_context(self, widget):
+        model = self._wid_get().model
+        l = rpc.session.context.get('lang','en_US')
+        getvar = {
+            'model': model,
+            'lang': l,
+        }
+        tools.launch_browser(options.options['help.context'] % getvar)
+
+    def sig_licence(self, widget):
+        dialog = glade.XML(common.terp_path("openerp.glade"), "win_licence", gettext.textdomain())
+        dialog.signal_connect("on_but_ok_pressed", lambda obj: dialog.get_widget('win_licence').destroy())
+
+        win = dialog.get_widget('win_licence')
+        win.set_transient_for(self.window)
+        win.show_all()
+
+    def sig_about(self, widget):
+        about = glade.XML(common.terp_path("openerp.glade"), "win_about", gettext.textdomain())
+        buffer = about.get_widget('textview2').get_buffer()
+        about_txt = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
+        buffer.set_text(about_txt % openerp_version)
+        about.signal_connect("on_but_ok_pressed", lambda obj: about.get_widget('win_about').destroy())
+
+        win = about.get_widget('win_about')
+        win.set_transient_for(self.window)
+        win.show_all()
+
+    def sig_shortcuts(self, widget):
+        shortcuts_win = glade.XML(common.terp_path('openerp.glade'), 'shortcuts_dia', gettext.textdomain())
+        shortcuts_win.signal_connect("on_but_ok_pressed", lambda obj: shortcuts_win.get_widget('shortcuts_dia').destroy())
+
+        win = shortcuts_win.get_widget('shortcuts_dia')
+        win.set_transient_for(self.window)
+        win.show_all()
+
+    def sig_win_menu(self, widget=None, quiet=True):
+        for p in range(len(self.pages)):
+            if self.pages[p].model=='ir.ui.menu':
+                self.notebook.set_current_page(p)
+                return True
+        res = self.sig_win_new(widget, type='menu_id', quiet=quiet)
+        if not res:
+            return self.sig_win_new(widget, type='action_id', quiet=quiet)
+        return res
+
+    def sig_win_new(self, widget=None, type='menu_id', quiet=True, except_id=False):
+        try:
+            act_id = rpc.session.rpc_exec_auth('/object', 'execute', 'res.users',
+                    'read', [rpc.session.uid], [type,'name'], rpc.session.context)
+        except:
+            return False
+        id = self.sb_username.get_context_id('message')
+        self.sb_username.push(id, act_id[0]['name'] or '')
+        if not act_id[0][type]:
+            if quiet:
+                return False
+            common.warning(_("You can not log into the system !\nAsk the administrator to verify\nyou have an action defined for your user."),'Access Denied !')
+            rpc.session.logout()
+            return False
+        act_id = act_id[0][type][0]
+        if except_id and act_id == except_id:
+            return act_id
+        obj = service.LocalService('action.main')
+        obj.execute(act_id, {'window':self.window})
+        try:
+            user = rpc.session.rpc_exec_auth_wo('/object', 'execute', 'res.users',
+                    'read', [rpc.session.uid], [type,'name'], rpc.session.context)
+            if user[0][type]:
+                act_id = user[0][type][0]
+        except:
+            pass
+        return act_id
+
+    def sig_home_new(self, widget=None, quiet=True, except_id=False):
+        open_menu = self.sig_win_new(widget, type='action_id', quiet=quiet,
+                except_id=except_id)
+        if not open_menu and widget:
+            self.sig_win_menu()
+
+    def sig_plugin_execute(self, widget):
+        import plugins
+        pn = self.notebook.get_current_page()
+        datas = {'model': self.pages[pn].model, 'ids':self.pages[pn].ids_get(), 'id' : self.pages[pn].id_get()}
+        plugins.execute(datas)
+
+    def sig_quit(self, widget):
+        options.options.save()
+        gtk.main_quit()
+
+    def sig_close(self, widget):
+        if common.sur(_("Do you really want to quit ?"), parent=self.window):
+            if not self.sig_logout(widget):
+                return False
+            options.options.save()
+            gtk.main_quit()
+
+    def sig_delete(self, widget, event, data=None):
+        if common.sur(_("Do you really want to quit ?"), parent=self.window):
+            if not self.sig_logout(widget):
+                return True
+            return False
+        return True
+
+    def win_add(self, win, datas):
+        """
+            Add a tab in client
+        """
+        self.pages.append(win)
+        box = gtk.HBox(False, 0)
+
+        # Draw the close button on the right
+        closebtn = gtk.Button()
+        image = gtk.Image()
+        image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
+        w, h = image.size_request()
+
+        closebtn.set_image(image)
+        closebtn.set_relief(gtk.RELIEF_NONE)
+        closebtn.set_size_request(w + 8, h + 4)
+        closebtn.unset_flags(gtk.CAN_FOCUS)
+
+        box_label = gtk.Label(win.name)
+        event_box = gtk.EventBox()
+        event_box.add(box_label)
+        event_box.set_visible_window(False)
+        event_box.set_events(gtk.gdk.BUTTON_PRESS_MASK)
+        box.pack_start(event_box, True, True)
+        box.pack_end(closebtn, False, False)
+
+        self.notebook.append_page(win.widget, box)
+        if hasattr(self.notebook, 'set_tab_reorderable' ):
+            # since pygtk 2.10
+            self.notebook.set_tab_reorderable(win.widget, True)
+
+        event_box.connect("button_press_event", self.sig_win_close, win.widget)
+        closebtn.connect("button-press-event", self.sig_win_close, win.widget)
+        pagenum = self.notebook.page_num(win.widget)
+        pagenum = self.notebook.page_num(image)
+
+        box.show_all()
+
+        self.notebook.set_tab_label_packing(image, True, True, gtk.PACK_START)
+        self.notebook.set_tab_label(image, box)
+        image.show_all()
+        self.notebook.set_current_page(-1)
+
+    def message(self, message):
+        id = self.status_bar.get_context_id('message')
+        self.status_bar.push(id, message)
+
+    def __attachment_callback(self, view, objid):
+        current_view = self._wid_get()
+        current_id = current_view and current_view.id_get()
+        if current_view == view and objid == current_id:
+            cpt = None
+            if objid and view.screen.current_view.view_type == 'form':
+                cpt = rpc.session.rpc_exec_auth('/object', 'execute',
+                                                'ir.attachment', 'search_count',
+                                                [('res_model', '=', view.model), ('res_id', '=', objid)])
+            if cpt:
+                self.buttons['but_attach'].set_icon_widget(self.__img_attachments)
+                self.buttons['but_attach'].set_label(_('Attachments (%d)') % cpt)
+
+
+    def _update_attachment_button(self, view = None):
+        """
+        Update the attachment icon for display the number of attachments
+        """
+        if not view:
+            view = self._wid_get()
+
+        id = view and view.id_get()
+        gobject.timeout_add(1500, self.__attachment_callback, view, id)
+        self.buttons['but_attach'].set_icon_widget(self.__img_no_attachments)
+        self.buttons['but_attach'].set_label(_('Attachments'))
+
+
+    def sb_set(self, view=None):
+        if not view:
+            view = self._wid_get()
+        if view and hasattr(view, 'screen'):
+            self._handler_ok = False
+            type = view.screen.current_view.view_type
+            if type == 'dummycalendar':
+                type = 'calendar'
+            self.glade.get_widget('radio_'+type).set_active(True)
+            self._handler_ok = True
+        self._update_attachment_button(view)
+        for x in self.buttons:
+            if self.buttons[x]:
+                self.buttons[x].set_sensitive(view and (x in view.handlers))
+
+    def _win_del(self,page_num=None):
+        """
+            Del tab in Client
+        """
+        if page_num is not None:
+            pn = page_num
+        else:
+            pn = self.notebook.get_current_page()
+        if pn != -1:
+
+            self.notebook.disconnect(self.sig_id)
+            page = self.pages.pop(pn)
+
+            self.notebook.remove_page(pn)
+            self.sig_id = self.notebook.connect_after('switch-page', self._sig_page_changed)
+            self.sb_set()
+
+            page.destroy()
+            del page
+            gc.collect()
+
+        return self.notebook.get_current_page() != -1
+
+    def _wid_get(self,page_num=None):
+        if page_num is not None:
+            pn = page_num
+        else:
+            pn = self.notebook.get_current_page()
+        if pn == -1:
+            return False
+        return self.pages[pn]
+
+    def _sig_child_call(self, widget, button_name, *args):
+        page_num = None
+        if len(args):
+            page_num = args[0]
+        if not self._handler_ok:
+            return
+        wid = self._wid_get(page_num)
+        if wid:
+            res = True
+            if button_name.startswith('radio_'):
+                act = self.glade.get_widget(button_name).get_active()
+                if not act: return False
+
+            if button_name in wid.handlers:
+                res = wid.handlers[button_name]()
+                # for those buttons, we refresh the attachment button.
+                # for the "switch view" button, the action has already
+                # been called by the Screen object of the view (wid)
+                if button_name in ('but_new', 'but_remove', 'but_search', \
+                                    'but_previous', 'but_next', 'but_open', \
+                                    'but_close', 'but_reload', 'but_attach', 'but_goto_id'):
+                    self._update_attachment_button(wid)
+            if button_name=='but_close' and res:
+                self._win_del(page_num)
+
+
+    def _sig_page_changed(self, widget=None, *args):
+        self.last_page = self.current_page
+        self.current_page = self.notebook.get_current_page()
+        self.sb_set()
+
+    def _sig_page_reordered(self, notebook, child, page_num, user_param=None):
+        widget = self.pages[self.current_page]
+        self.pages.remove(widget)
+        self.pages.insert(page_num, widget)
+        self.current_page = page_num
+
+    def sig_db_new(self, widget):
+        if not self.sig_logout(widget):
+            return False
+        dia = db_create(self.sig_login, self)
+        res = dia.run(self.window)
+        if res:
+            options.options.save()
+        return res
+
+    def sig_db_drop(self, widget):
+        if not self.sig_logout(widget):
+            return False
+        url, db_name, passwd = self._choose_db_select(_('Delete a database'))
+        if not db_name:
+            return
+
+        try:
+            rpc.session.db_exec(url, 'drop', passwd, db_name)
+            common.message(_("Database dropped successfully !"), parent=self.window)
+        except Exception, e:
+            if (getattr(e,'faultCode',False)=='AccessDenied') or str(e)=='AccessDenied':
+                common.warning(_('Bad database administrator password !'),_("Could not drop database."), parent=self.window)
+            else:
+                common.warning(_("Couldn't drop database"), parent=self.window)
+
+    def sig_db_restore(self, widget):
+        filename = common.file_selection(_('Open...'), parent=self.window, preview=False)
+        if not filename:
+            return
+
+        url, db_name, passwd = self._choose_db_ent()
+        if db_name:
+            try:
+                f = file(filename, 'rb')
+                data_b64 = base64.encodestring(f.read())
+                f.close()
+                res = rpc.session.db_exec(url, 'restore', passwd, db_name, data_b64)
+                if res:
+                    common.message(_("Database restored successfully !"), parent=self.window)
+            except Exception,e:
+                if (getattr(e,'faultCode',False)=='AccessDenied') or str(e)=='AccessDenied':
+                    common.warning(_('Bad database administrator password !'),_("Could not restore database."), parent=self.window)
+                else:
+                    common.warning(_("Couldn't restore database"), parent=self.window)
+
+    def sig_db_migrate_retrieve_script(self, widget):
+        RetrieveMigrationScriptDialog(self.window).run()
+
+    def sig_db_migrate(self, widget):
+        MigrationDatabaseDialog(self.window).run()
+
+    def sig_extension_manager(self,widget):
+        win = win_extension.win_extension(self.window)
+        win.run()
+
+    def sig_db_password(self, type):
+        dialog = glade.XML(common.terp_path("openerp.glade"), "dia_passwd_change",
+                gettext.textdomain())
+        win = dialog.get_widget('dia_passwd_change')
+        win.set_icon(common.OPENERP_ICON)
+        win.set_transient_for(self.window)
+        win.show_all()
+        server_widget = dialog.get_widget('ent_server2')
+        ser_label = dialog.get_widget('label298')
+        old_pass_widget = dialog.get_widget('old_passwd')
+        new_pass_widget = dialog.get_widget('new_passwd')
+        new_pass2_widget = dialog.get_widget('new_passwd2')
+        dia_label = dialog.get_widget('label294')
+        change_button = dialog.get_widget('but_server_change1')
+        old_pass_widget.grab_focus()
+
+        if type == 'admin':
+            host = options.options['login.server']
+            port = options.options['login.port']
+            protocol = options.options['login.protocol']
+            url = '%s%s:%s' % (protocol, host, port)
+            server_widget.set_text(url)
+            change_button.connect_after('clicked', lambda a,b: _server_ask(b, win), server_widget)
+        else:
+            dia_label.set_label(_('<b>Change your password</b>'))
+            server_widget.hide()
+            ser_label.hide()
+            change_button.hide()
+        end = False
+        while not end:
+            res = win.run()
+            if res == gtk.RESPONSE_OK:
+                old_passwd = old_pass_widget.get_text()
+                new_passwd = new_pass_widget.get_text()
+                new_passwd2 = new_pass2_widget.get_text()
+                if new_passwd != new_passwd2:
+                    new_pass_widget.set_text('')
+                    new_pass_widget.grab_focus()
+                    new_pass2_widget.set_text('')
+                    common.warning(_("Confirmation password does not match " \
+                            "new password, operation cancelled!"),
+                            _("Validation Error."), parent=win)
+                else:
+                    try:
+                        if type == 'user':
+                            rpc.session.rpc_exec_auth_wo('/object', 'execute', 'res.users', 'change_password',
+                                    old_passwd, new_passwd)
+                            rpc.session._passwd = new_passwd
+                        else:
+                            url = server_widget.get_text()
+                            rpc.session.db_exec(url, 'change_admin_password',
+                                    old_passwd, new_passwd)
+                        end = True
+                    except Exception, e:
+
+                        if type == 'admin':
+                            if ('faultCode' in dir(e) and e.faultCode=="AccessDenied") \
+                                    or 'AccessDenied' in str(e):
+                                    common.warning(_("Could not change the Super Admin password."),
+                                                   _('Bad password provided !'), parent=win)
+                        else:
+                            if e.type == 'warning':
+                                 common.warning(e.data, e.message, parent=win)
+                            elif e.type == 'AccessDenied':
+                                common.warning(_("Changing password failed, please verify old password."),
+                                               _('Bad password provided !'), parent=win)
+            else:
+                end = True
+        self.window.present()
+        win.destroy()
+
+    def sig_db_dump(self, widget):
+        url, db_name, passwd = self._choose_db_select(_('Backup a database'))
+        if not db_name:
+            return
+        filename = common.file_selection(_('Save As...'),
+                action=gtk.FILE_CHOOSER_ACTION_SAVE,
+                parent=self.window,
+                preview=False,
+                filename=('%s_%s.sql' % (db_name, time.strftime('%Y%m%d_%H:%M'),)).replace(':','_'))
+
+        if filename:
+            try:
+                dump_b64 = rpc.session.db_exec(url, 'dump', passwd, db_name)
+                dump = base64.decodestring(dump_b64)
+                f = file(filename, 'wb')
+                f.write(dump)
+                f.close()
+                common.message(_("Database backed up successfully !"), parent=self.window)
+            except Exception,e:
+                if getattr(e,'faultCode',False)=='AccessDenied':
+                    common.warning(_('Bad database administrator password !'), _("Could not backup the database."),parent=self.window)
+                else:
+                    common.warning(_("Couldn't backup database."), parent=self.window)
+
+    def _choose_db_select(self, title=_("Backup a database")):
+
+        def refreshlist_ask(widget, server_widget, db_widget, entry_db, label, parent=None):
+            url = _server_ask(server_widget, parent)
+            if not url:
+                return None
+            _refresh_dblist(db_widget, entry_db, label, False, url)
+
+        dialog = glade.XML(common.terp_path("openerp.glade"), "win_db_select",
+                gettext.textdomain())
+        win = dialog.get_widget('win_db_select')
+        win.set_icon(common.OPENERP_ICON)
+        win.set_default_response(gtk.RESPONSE_OK)
+        win.set_transient_for(self.window)
+        win.show_all()
+
+        pass_widget = dialog.get_widget('ent_passwd_select')
+        server_widget = dialog.get_widget('ent_server_select')
+        db_widget = dialog.get_widget('combo_db_select')
+        entry_db = dialog.get_widget('entry_db_select')
+        label = dialog.get_widget('label_db_select')
+        entry_db.hide()
+
+        dialog.get_widget('db_select_label').set_markup('<b>'+title+'</b>')
+
+        protocol = options.options['login.protocol']
+        url = '%s%s:%s' % (protocol, options.options['login.server'], options.options['login.port'])
+        server_widget.set_text(url)
+
+        liststore = gtk.ListStore(str)
+        db_widget.set_model(liststore)
+
+        _refresh_dblist(db_widget, entry_db, label, False, url)
+        change_button = dialog.get_widget('but_server_select')
+        change_button.connect_after('clicked', refreshlist_ask, server_widget, db_widget, entry_db, label, win)
+
+        cell = gtk.CellRendererText()
+        db_widget.pack_start(cell, True)
+        db_widget.add_attribute(cell, 'text', 0)
+
+        res = win.run()
+
+        db = False
+        url = False
+        passwd = False
+        if res == gtk.RESPONSE_OK:
+            if (not db_widget.get_property('visible')) and entry_db.get_property('visible'):
+                db = entry_db.get_text()
+            else:
+                db = db_widget.get_active_text()
+            url = server_widget.get_text()
+            passwd = pass_widget.get_text()
+        self.window.present()
+        win.destroy()
+        return (url,db,passwd)
+
+    def _choose_db_ent(self):
+        dialog = glade.XML(common.terp_path("openerp.glade"), "win_db_ent", gettext.textdomain())
+        win = dialog.get_widget('win_db_ent')
+        win.set_icon(common.OPENERP_ICON)
+        win.set_transient_for(self.window)
+        win.show_all()
+
+        db_widget = dialog.get_widget('ent_db')
+        widget_pass = dialog.get_widget('ent_password')
+        widget_url = dialog.get_widget('ent_server1')
+
+        protocol = options.options['login.protocol']
+        url = '%s%s:%s' % (protocol, options.options['login.server'],
+                options.options['login.port'])
+        widget_url.set_text(url)
+
+        change_button = dialog.get_widget('but_server_change')
+        change_button.connect_after('clicked', lambda a,b: _server_ask(b, win),
+                widget_url)
+
+        res = win.run()
+
+        db = False
+        passwd = False
+        url = False
+        if res == gtk.RESPONSE_OK:
+            db = db_widget.get_text()
+            url = widget_url.get_text()
+            passwd = widget_pass.get_text()
+        self.window.present()
+        win.destroy()
+        return url, db, passwd
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added directory 'bin/modules/gui/window'
=== added file 'bin/modules/gui/window/__init__.py'
--- bin/modules/gui/window/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import service
+import rpc
+
+import common
+import form
+import tree
+
+
+class window_int(object):
+    def __init__(self, view, datas):
+        self.name = datas.get('name', _('Unknown Window'))
+
+
+class window(service.Service):
+    def __init__(self, name='gui.window'):
+        service.Service.__init__(self, name)
+    def create(self, view_ids, model, res_id=False, domain=None,
+            view_type='form', window=None, context=None, mode=None, name=False,help={},
+            limit=100, auto_refresh=False, auto_search=True, search_view=None):
+        if context is None:
+            context = {}
+        context.update(rpc.session.context)
+
+        if view_type=='form':
+            mode = (mode or 'form,tree').split(',')
+            win = form.form(model, res_id, domain, view_type=mode,
+                    view_ids = (view_ids or []), window=window,
+                    context=context, name=name, help=help, limit=limit,
+                    auto_refresh=auto_refresh, auto_search=auto_search, search_view=search_view)
+            spool = service.LocalService('spool')
+            spool.publish('gui.window', win, {})
+        elif view_type=='tree':
+            if view_ids and view_ids[0]:
+                view_base =  rpc.session.rpc_exec_auth('/object', 'execute',
+                        'ir.ui.view', 'read', [view_ids[0]],
+                        ['model', 'type'], context)[0]
+                model = view_base['model']
+                view = rpc.session.rpc_exec_auth('/object', 'execute',
+                        view_base['model'], 'fields_view_get', view_ids[0],
+                        view_base['type'],context)
+            else:
+                view = rpc.session.rpc_exec_auth('/object', 'execute', model,
+                        'fields_view_get', False, view_type, context)
+
+            win = tree.tree(view, model, res_id, domain, context,help=help,
+                    window=window, name=name)
+            spool = service.LocalService('spool')
+            spool.publish('gui.window', win, {})
+        else:
+            import logging
+            log = logging.getLogger('view')
+            log.error('unknown view type: '+view_type)
+            del log
+
+window()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/form.py'
--- bin/modules/gui/window/form.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/form.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,479 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import types
+import gettext
+
+import gtk
+import gobject
+from gtk import glade
+
+import rpc
+import win_selection
+import win_search
+import win_export
+import win_import
+import win_list
+
+from gtk.gdk import Color
+
+import common
+import service
+import options
+import copy
+
+
+from observator import oregistry
+from widget.screen import Screen
+
+class form(object):
+    def __init__(self, model, res_id=False, domain=None, view_type=None,
+            view_ids=None, window=None, context=None, name=False, help={}, limit=100,
+            auto_refresh=False, auto_search=True, search_view=None):
+        if not view_type:
+            view_type = ['form','tree']
+        if domain is None:
+            domain = []
+        if view_ids is None:
+            view_ids = []
+        if context is None:
+            context = {}
+
+        fields = {}
+        self.model = model
+        self.window = window
+        self.previous_action = None
+        self.glade = glade.XML(common.terp_path("openerp.glade"),'win_form_container',gettext.textdomain())
+        self.widget = self.glade.get_widget('win_form_container')
+        self.widget.show_all()
+        self.fields = fields
+        self.domain = domain
+        self.context = context
+        self.screen = Screen(self.model, view_type=view_type,
+                context=self.context, view_ids=view_ids, domain=domain,help=help,
+                hastoolbar=options.options['form.toolbar'], hassubmenu=options.options['form.submenu'],
+                show_search=True, window=self.window, limit=limit, readonly=bool(auto_refresh), auto_search=auto_search, search_view=search_view)
+        self.screen.signal_connect(self, 'record-message', self._record_message)
+        self.screen.widget.show()
+        oregistry.add_receiver('misc-message', self._misc_message)
+
+        if not name:
+            self.name = self.screen.current_view.title
+        else:
+            self.name = name
+        vp = gtk.Viewport()
+        vp.set_shadow_type(gtk.SHADOW_NONE)
+        vp.add(self.screen.widget)
+        vp.show()
+        self.sw = gtk.ScrolledWindow()
+        self.sw.set_shadow_type(gtk.SHADOW_NONE)
+        self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.sw.add(vp)
+        self.sw.show()
+
+        self.has_backup = False
+        self.backup = {}
+
+        self.widget.pack_start(self.sw)
+        self.handlers = {
+            'but_new': self.sig_new,
+            'but_copy': self.sig_copy,
+            'but_save': self.sig_save,
+            'but_save_as': self.sig_save_as,
+            'but_import': self.sig_import,
+            'but_print_repeat': self.sig_print_repeat,
+            'but_remove': self.sig_remove,
+            'but_search': self.sig_search,
+            'but_previous': self.sig_previous,
+            'but_next': self.sig_next,
+            'but_goto_id': self.sig_goto,
+            'but_log': self.sig_logs,
+            'but_print': self.sig_print,
+            'but_reload': self.sig_reload,
+            'but_print_html': self.sig_print_html,
+            'but_action': self.sig_action,
+            'but_switch': self.sig_switch,
+            'but_attach': self.sig_attach,
+            'but_close': self.sig_close,
+        }
+        if 'tree' in view_type:
+            self.handlers['radio_tree'] = self.sig_switch_tree
+        if 'form' in view_type:
+            self.handlers['radio_form'] =  self.sig_switch_form
+        if 'graph' in view_type:
+            self.handlers['radio_graph'] =  self.sig_switch_graph
+        if 'calendar' in view_type:
+            self.handlers['radio_calendar'] =  self.sig_switch_calendar
+        if 'diagram' in view_type:
+            self.handlers['radio_diagram'] =  self.sig_switch_diagram
+        if res_id:
+            if isinstance(res_id, (int, long,)):
+                res_id = [res_id]
+            self.screen.load(res_id)
+        else:
+            if self.screen.current_view.view_type == 'form':
+                self.sig_new(autosave=False)
+            if self.screen.current_view.view_type in ('tree', 'graph', 'calendar'):
+                self.screen.search_filter()
+
+        if auto_refresh and int(auto_refresh):
+            gobject.timeout_add(int(auto_refresh) * 1000, self.sig_reload)
+
+    def sig_switch_diagram(self, widget=None):
+        return self.sig_switch(widget, 'diagram')
+
+    def sig_switch_form(self, widget=None):
+        return self.sig_switch(widget, 'form')
+
+    def sig_switch_tree(self, widget=None):
+        return self.sig_switch(widget, 'tree')
+
+    def sig_switch_calendar(self, widget=None):
+        return self.sig_switch(widget, 'calendar')
+
+    def sig_switch_graph(self, widget=None):
+        return self.sig_switch(widget, 'graph')
+
+    def get_resource(self, widget=None, get_id=None):
+        ## This has been done due to virtual ids coming from
+        ## crm meeting. like '3-20101012155505' which are not in existence
+        ## and needed to be converted to real ids
+        if isinstance(get_id, str):
+            get_id = int(get_id.split('-')[0])
+        all_ids = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'search', [])
+        if widget:
+            get_id = int(widget.get_value())
+        if get_id in all_ids:
+            current_ids = self.screen.ids_get()
+            if get_id in current_ids:
+                self.screen.display(get_id)
+            else:
+                self.screen.load([get_id])
+            self.screen.current_view.set_cursor()
+        else:
+            if widget:
+                common.message(_('Resource ID does not exist for this object!'))
+
+    def get_event(self, widget, event, win):
+        if event.keyval in (gtk.keysyms.Return, gtk.keysyms.KP_Enter):
+            win.destroy()
+            self.get_resource(widget)
+
+    def sig_goto(self, *args):
+        if not self.modified_save():
+            return
+
+        glade2 = glade.XML(common.terp_path("openerp.glade"),'dia_goto_id',gettext.textdomain())
+        widget = glade2.get_widget('goto_spinbutton')
+        win = glade2.get_widget('dia_goto_id')
+        widget.connect('key_press_event',self.get_event,win)
+
+        win.set_transient_for(self.window)
+        win.show_all()
+
+        response = win.run()
+        win.destroy()
+
+        if response == gtk.RESPONSE_OK:
+            self.get_resource(widget)
+
+    def destroy(self):
+
+        """
+            Destroy the page object and all the child
+            (or at least should do this)
+        """
+        oregistry.remove_receiver('misc-message', self._misc_message)
+        self.screen.signal_unconnect(self)
+        self.screen.destroy()
+        self.widget.destroy()
+        self.sw.destroy()
+        del self.screen
+        del self.handlers
+
+    def ids_get(self):
+        return self.screen.ids_get()
+
+    def id_get(self):
+        return self.screen.id_get()
+
+    def sig_attach(self, widget=None):
+        id = self.id_get()
+        if id:
+            ctx = self.context.copy()
+            ctx.update(rpc.session.context)
+            action = rpc.session.rpc_exec_auth('/object', 'execute', 'ir.attachment', 'action_get', ctx)
+            action['domain'] = [('res_model', '=', self.model), ('res_id', '=', id)]
+            ctx['default_res_model'] = self.model
+            ctx['default_res_id'] = id
+            obj = service.LocalService('action.main')
+            obj._exec_action(action, {}, ctx)
+        else:
+            self.message_state(_('No record selected ! You can only attach to existing record.'), color='red')
+        return True
+
+    def sig_switch(self, widget=None, mode=None):
+        if not self.modified_save():
+            return
+        id = self.screen.id_get()
+        if mode<>self.screen.current_view.view_type:
+            self.screen.switch_view(mode=mode)
+            if id:
+                self.sig_reload()
+                self.get_resource(get_id=id)
+
+    def sig_logs(self, widget=None):
+        id = self.id_get()
+        if not id:
+            self.message_state(_('You have to select a record !'), color='red')
+            return False
+        res = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'perm_read', [id])
+        message = ''
+        for line in res:
+            todo = [
+                ('id', _('ID')),
+                ('create_uid', _('Creation User')),
+                ('create_date', _('Creation Date')),
+                ('write_uid', _('Latest Modification by')),
+                ('write_date', _('Latest Modification Date')),
+                ('xmlid', _('Internal Module Data ID'))
+            ]
+            for (key,val) in todo:
+                if line[key] and key in ('create_uid','write_uid','uid'):
+                    line[key] = line[key][1]
+                message+=val+': '+str(line[key] or '/')+'\n'
+        common.message(message)
+        return True
+
+    def sig_remove(self, widget=None):
+        if not self.id_get():
+            msg = _('Record is not saved ! \n Do you want to clear current record ?')
+        else:
+            if self.screen.current_view.view_type == 'form':
+                msg = _('Are you sure to remove this record ?')
+            else:
+                msg = _('Are you sure to remove those records ?')
+        if common.sur(msg):
+            id = self.screen.remove(unlink=True)
+            if not id:
+                self.message_state(_('Resources cleared.'), color='darkgreen')
+            else:
+                self.message_state(_('Resources successfully removed.'), color='darkgreen')
+        self.sig_reload()
+
+    def sig_import(self, widget=None):
+        fields = []
+        while(self.screen.view_to_load):
+            self.screen.load_view_to_load()
+        screen_fields = copy.deepcopy(self.screen.fields)
+        win = win_import.win_import(self.model, screen_fields, fields, parent=self.window,local_context= self.screen.context)
+        res = win.go()
+
+    def sig_save_as(self, widget=None):
+        fields = []
+        while(self.screen.view_to_load):
+            self.screen.load_view_to_load()
+        screen_fields = copy.deepcopy(self.screen.fields)
+        win = win_export.win_export(self.model, self.screen.ids_get(), screen_fields, fields, parent=self.window, context=self.context)
+        res = win.go()
+
+    def sig_new(self, widget=None, autosave=True):
+        if autosave:
+            if not self.modified_save():
+                return
+        self.screen.create_new = True
+        self.screen.new()
+        self.message_state('')
+
+    def sig_copy(self, *args):
+        if not self.modified_save():
+            return
+        res_id = self.id_get()
+        ctx = self.context.copy()
+        ctx.update(rpc.session.context)
+        new_id = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'copy', res_id, {}, ctx)
+        if new_id:
+            self.screen.load([new_id])
+            self.screen.current_view.set_cursor()
+            self.message_state(_('Working now on the duplicated document !'))
+        self.sig_reload()
+
+    def _form_save(self, auto_continue=True):
+        pass
+
+    def sig_save(self, widget=None, sig_new=True, auto_continue=True):
+        res = self.screen.save_current()
+        warning = False
+        if isinstance(res,dict):
+            id = res.get('id',False)
+            warning = res.get('warning',False)
+        else:
+            id = res
+        if id:
+            self.message_state(_('Document Saved.'), color="darkgreen")
+        elif len(self.screen.models.models) and res != None:
+            common.warning(_('Invalid form, correct red fields !'),_('Error !'), parent=self.screen.current_view.window)
+            self.message_state(_('Invalid form, correct red fields !'), color="red")
+        if warning:
+            common.warning(warning,_('Warning !'), parent=self.screen.current_view.window)
+        return bool(id)
+
+    def sig_previous(self, widget=None):
+        if not self.modified_save():
+            return
+        self.screen.display_prev()
+        self.message_state('')
+
+    def sig_next(self, widget=None):
+        if not self.modified_save():
+            return
+        self.screen.display_next()
+        self.message_state('')
+
+    def sig_reload(self, test_modified=True):
+        if not hasattr(self, 'screen'):
+            return False
+        if test_modified and self.screen.is_modified():
+            res = common.sur_3b(_('This record has been modified\n' \
+                    'do you want to save it ?'))
+            if res == 'ok':
+                self.sig_save()
+            elif res == 'ko':
+                pass
+            else:
+                return False
+        if self.screen.current_view.view_type == 'form':
+            self.screen.cancel_current()
+            self.screen.display()
+        else:
+            id = self.screen.id_get()
+            self.screen.search_filter()
+            for model in self.screen.models:
+                if model.id == id:
+                    self.screen.current_model = model
+                    self.screen.display()
+                    break
+        self.message_state('')
+        return True
+
+    def sig_action(self, keyword='client_action_multi', previous=False, report_type='pdf', adds={}):
+        ids = self.screen.ids_get()
+        group_by = self.screen.context.get('group_by')
+        if self.screen.current_model:
+            id = self.screen.current_model.id
+        else:
+            id = False
+        if self.screen.current_view.view_type == 'form':
+            id = self.screen.save_current()
+            if not id:
+                return False
+            ids = [id]
+        if self.screen.current_view.view_type == 'tree':
+            self.modified_save()
+            sel_ids = self.screen.sel_ids_get()
+            if sel_ids:
+                ids = sel_ids
+        if len(ids) or group_by:
+            obj = service.LocalService('action.main')
+            data = {'model':self.screen.resource,
+                    'id': id or False,
+                    'ids':ids,
+                    'report_type': report_type,
+                    '_domain':self.screen.domain
+                   }
+            # When group by header is selected add it's children as a active_ids
+            if group_by:
+                self.screen.context.update({'active_id':id, 'active_ids':ids})
+            if previous and self.previous_action:
+                obj._exec_action(self.previous_action[1], data, self.screen.context)
+            else:
+                res = obj.exec_keyword(keyword, data, adds, self.screen.context)
+                if res:
+                    self.previous_action = res
+            self.sig_reload(test_modified=False)
+        else:
+            self.message_state(_('You must select one or several records !'),color='red')
+
+    def sig_print_repeat(self):
+        self.sig_action('client_print_multi', True)
+
+    def sig_print_html(self):
+        self.sig_action('client_print_multi', report_type='html')
+
+    def sig_print(self):
+        self.sig_action('client_print_multi', adds={_('Print Screen').encode('utf8'): {'report_name':'printscreen.list', 'name':_('Print Screen'), 'type':'ir.actions.report.xml'}})
+
+    def sig_search(self, widget=None):
+        if not self.modified_save():
+            return
+        dom = self.domain
+        win = win_search.win_search(self.model, domain=self.domain, context=self.context, parent=self.window)
+        res = win.go()
+        if res:
+            self.screen.clear()
+            self.screen.load(res)
+
+    def message_state(self, message, context='message', color=None):
+        sb = self.glade.get_widget('stat_state')
+        if color is not None:
+            message = '<span foreground="%s">%s</span>' % (color, message)
+        sb.set_label(message)
+
+    def _record_message(self, screen, signal_data):
+        if not signal_data[3]:
+            msg = _('No record selected')
+        else:
+            name = '_'
+            if signal_data[0]>=0:
+                name = str(signal_data[0]+1)
+            name2 = _('New document')
+            if signal_data[3]:
+                name2 = _('Editing document (id: ')+str(signal_data[3])+')'
+            # Total Records should never change
+            tot_count = signal_data[2] < signal_data[1] and  str(signal_data[1]) or str(signal_data[2])
+            msg = _('Record: ') + name + ' / ' + str(signal_data[1]) + \
+                    _(' of ') + str(tot_count) + ' - ' + name2
+        sb = self.glade.get_widget('stat_form')
+        cid = sb.get_context_id('message')
+        sb.push(cid, msg)
+
+    def _misc_message(self, obj, message, color=None):
+        self.message_state(message, color=color)
+
+    def modified_save(self, reload=True):
+        if self.screen.is_modified():
+            value = common.sur_3b(_('This record has been modified\ndo you want to save it ?'))
+            if value == 'ok':
+                return self.sig_save()
+            elif value == 'ko':
+                if reload:
+                    self.sig_reload(test_modified=False)
+                return True
+            else:
+                return False
+        return True
+
+    def sig_close(self, urgent=False):
+        res = self.modified_save(reload=False)
+        return res
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/tree.py'
--- bin/modules/gui/window/tree.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/tree.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,328 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import gtk
+from gtk import glade
+import gettext
+import xmlrpclib
+
+import common
+import service
+import view_tree
+import rpc
+import options
+import win_export
+import copy
+
+class tree(object):
+    def __init__(self, view, model, res_id=False, domain=[], context={}, help={}, window=None, name=False):
+        self.glade = glade.XML(common.terp_path("openerp.glade"),'win_tree_container',gettext.textdomain())
+        self.widget = self.glade.get_widget('win_tree_container')
+        self.widget.show_all()
+        self.model = view['model']
+        self.domain2 = domain
+        if view.get('field_parent', False):
+            self.domain = []
+        else:
+            self.domain = domain
+        self.view = view
+        self.window=window
+
+        self.context=context
+
+        self.tree_res = view_tree.view_tree(view, [], res_id, True, context=context)
+        self.tree_res.view.connect('row-activated', self.sig_open)
+
+        sel = self.tree_res.view.get_selection()
+        sel.connect('changed', self.expand_one)
+
+        if not name:
+            self.name = self.tree_res.name
+        else:
+            self.name = name
+        self.vp = self.glade.get_widget('main_tree_sw')
+
+        wid = self.glade.get_widget('widget_vbox')
+        wid.show()
+
+        widget_sc = self.glade.get_widget('win_tree_sc')
+
+        widget_sc.connect('row-activated', self.sc_go)
+        self.tree_sc = view_tree.view_tree_sc(widget_sc, self.model)
+        self.handlers = {
+            'but_reload': self.sig_reload,
+            'but_switch': self.sig_edit,
+            'but_chroot': self.sig_chroot,
+            'but_open': self.sig_action,
+            'but_action': self.sig_action,
+            'but_print': self.sig_print,
+            'but_print_html': self.sig_print_html,
+            'but_close': self.sig_close,
+            'but_save_as': self.sig_save_as,
+        }
+        dict = {
+            'on_but_sc_go_clicked': self.sc_go,
+            'on_but_sc_add_clicked': self.sc_add,
+            'on_but_sc_del_clicked': self.sc_del,
+            'on_but_expand_collapse_clicked': self.expand_collapse_all,
+            'on_tbsc_clicked': self.sc_btn,
+        }
+
+        self.help = help
+        self.help_frame = False
+        wid = self.tree_res.widget_get()
+        if self.help:
+            action_tips = common.action_tips(self.help)
+            self.help_frame = action_tips.help_frame
+            if self.help_frame:
+                vbox = gtk.VBox()
+                vbox.pack_start(self.help_frame, expand=False, fill=False, padding=2)
+                vbox.pack_end(wid)
+                vbox.show_all()
+                wid = vbox
+        if self.help_frame:
+            self.vp.add_with_viewport(wid)
+        else:
+            self.vp.add(wid)
+        self.sig_reload()
+
+        for signal in dict:
+            self.glade.signal_connect(signal, dict[signal])
+        self.expand = True
+
+    def sig_reload(self, widget=None):
+        self.tree_sc.update()
+        ids = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'search', self.domain2)
+        if self.tree_res.toolbar:
+            icon_name = 'icon'
+            wid = self.glade.get_widget('tree_toolbar')
+            for w in wid.get_children():
+                wid.remove(w)
+            c = {}
+            c.update(rpc.session.context)
+            res_ids = rpc.session.rpc_exec_auth_try('/object', 'execute', self.view['model'], 'read', ids, ['name',icon_name], c)
+            rb = None
+            for r in res_ids:
+                rb = gtk.RadioToolButton(group=rb)
+                l = gtk.Label(r['name'])
+                rb.set_label_widget(l)
+
+                icon = gtk.Image()
+                if icon_name in r:
+                    if hasattr(r[icon_name], 'startswith') and r[icon_name].startswith('STOCK_'):
+                        icon.set_from_stock(getattr(gtk, r[icon_name]), gtk.ICON_SIZE_BUTTON)
+                    else:
+                        try:
+                            icon.set_from_stock(r[icon_name], gtk.ICON_SIZE_BUTTON)
+                        except:
+                            pass
+
+                hb = gtk.HBox(spacing=6)
+                hb.pack_start(icon)
+                hb.pack_start(gtk.Label(r['name']))
+                rb.set_icon_widget(hb)
+                rb.show_all()
+                rb.set_data('id', r['id'])
+                rb.connect('clicked', self.menu_main_clicked)
+                self.menu_main_clicked(rb)
+                wid.insert(rb, -1)
+        else:
+            self.tree_res.ids = ids
+            self.tree_res.reload()
+            wid = self.glade.get_widget('widget_vbox')
+            wid.hide()
+
+    def menu_main_clicked(self, widget):
+        if widget.get_active():
+            id = widget.get_data('id')
+
+            ids = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'read', [id], [self.view['field_parent']])[0][self.view['field_parent']]
+
+            self.tree_res.ids = ids
+            self.tree_res.reload()
+
+            self.expand = False
+            self.expand_collapse_all( self.glade.get_widget('button7') )
+
+        return False
+
+    def expand_collapse_all(self, widget):
+        if self.expand:
+            self.tree_res.view.expand_all()
+        else:
+            self.tree_res.view.collapse_all()
+        self.expand = not self.expand
+        if self.expand:
+            widget.set_stock_id('gtk-goto-bottom')
+        else:
+            widget.set_stock_id('gtk-goto-top')
+
+    def expand_one(self, selection):
+        model,iter = selection.get_selected_rows()
+        if iter:
+            self.tree_res.view.expand_row(iter[0],False)
+
+    def sig_print_html(self, widget=None, keyword='client_print_multi', id=None):
+        self.sig_action(keyword='client_print_multi', report_type='html')
+
+    def sig_print(self, widget=None, keyword='client_print_multi', id=None):
+        self.sig_action(keyword='client_print_multi')
+
+    def sig_action(self, widget=None, keyword='tree_but_action', id=None, report_type='pdf', warning=True):
+        ids = self.ids_get()
+
+        if not id and ids and len(ids):
+            id = ids[0]
+        if id:
+            ctx = self.context.copy()
+            if 'active_ids' in ctx:
+                del ctx['active_ids']
+            if 'active_id' in ctx:
+                del ctx['active_id']
+            obj = service.LocalService('action.main')
+            return obj.exec_keyword(keyword, {'model':self.model, 'id':id,
+                'ids':ids, 'report_type':report_type, 'window': self.window}, context=ctx,
+                warning=warning)
+        else:
+            common.message(_('No resource selected!'))
+        return False
+
+    def sig_open(self, widget, iter, path):
+        if not self.sig_action(widget, 'tree_but_open', warning=False):
+            if self.tree_res.view.row_expanded(iter):
+                self.tree_res.view.collapse_row(iter)
+            else:
+                self.tree_res.view.expand_row(iter, False)
+
+
+    def sig_remove(self, widget=None):
+        ids = self.ids_get()
+        if len(ids):
+            if common.sur(_('Are you sure you want\nto remove this record?')):
+                try:
+                    rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'unlink', ids)
+                    self.sig_reload()
+                except xmlrpclib.Fault, err:
+                    common.message(_('Error removing resource!'))
+
+    # TODO: improve with domain expr
+    def sig_chroot(self, widget=None):
+        ids = self.ids_get()
+        if len(ids) and self.domain:
+            id = ids[0]
+            datas = {'domain_field': self.domain[0][0], 'domain_value': id[0], 'res_id':id[0]}
+            obj = service.LocalService('gui.window')
+            obj.create(self.view, self.model, id[0], (self.domain[0],id[0]) )
+        else:
+            common.message(_('Unable to chroot: no tree resource selected'))
+
+    def sig_new(self, widget=None):
+        #datas = {'res_model':self.model, 'domain_field': self.domain[0], 'domain_value': self.id_get(), 'res_id':None}
+#       domain = self.domain
+#       if self.domain:
+#           id = self.id_get()
+#           if id:
+#               domain=(domain[0],id)
+        obj = service.LocalService('gui.window')
+        obj.create(None, self.model, None, self.domain)
+
+    def sig_edit(self, widget=None):
+        id = False
+        ids = self.ids_get()
+        if ids:
+            id = ids[0]
+        elif self.tree_res.toolbar:
+            wid = self.glade.get_widget('tree_toolbar')
+            for w in wid.get_children():
+                if w.get_active():
+                    id = w.get_data('id')
+        if id:
+            obj = service.LocalService('gui.window')
+            obj.create(None, self.model, id, self.domain)
+        else:
+            common.message(_('No resource selected!'))
+
+    def domain_id_get(self, tree=False):
+        filter = []
+        if self.domain and self.view.get('field_parent', False):
+            filter = self.domain
+        res = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'search', filter)
+        return res
+
+    def sig_printscreen(self, widget=None):
+        ids = self.tree_res.ids
+        pass
+
+    def sc_btn(self, widget):
+        main = service.LocalService('gui.main')
+        main.shortcut_edit(widget, self.model)
+
+    def sc_del(self, widget):
+        id = self.tree_sc.sel_id_get()
+        if id!=None:
+            sc_id = int(self.tree_sc.value_get(2))
+            rpc.session.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'unlink', [sc_id])
+        self.tree_sc.update()
+
+    def sc_add(self, widget):
+        ids = self.tree_res.sel_ids_get()
+        if len(ids):
+            res = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'name_get', ids, rpc.session.context)
+            for (id,name) in res:
+                uid = rpc.session.uid
+                rpc.session.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'create', {'resource':self.model, 'user_id':uid, 'res_id':id, 'name':name})
+        self.tree_sc.update()
+
+    def sc_go(self, widget=None, *args):
+        id = self.tree_sc.sel_id_get()
+        if id!=None:
+            self.sig_action(None, 'tree_but_open', id)
+
+    def ids_get(self):
+        res = self.tree_res.sel_ids_get()
+        return res
+
+    def id_get(self):
+        try:
+            if hasattr(self, 'search'):
+                return self.search[self.search_pos]
+            else:
+                return None
+        except IndexError:
+            return None
+
+    def destroy(self):
+        #TODO destroy gui.window.tree
+        pass
+
+    def sig_close(self, urgent=False):
+        return True
+
+    def sig_save_as(self, widget=None):
+        fields = []
+        tree_fields = copy.deepcopy(self.tree_res.fields)
+        win = win_export.win_export(self.model, self.tree_res.sel_ids_get(),
+                tree_fields, [], parent=self.window, context=self.context)
+        res = win.go()
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added directory 'bin/modules/gui/window/view_sel'
=== added file 'bin/modules/gui/window/view_sel/__init__.py'
--- bin/modules/gui/window/view_sel/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/view_sel/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added directory 'bin/modules/gui/window/view_tree'
=== added file 'bin/modules/gui/window/view_tree/__init__.py'
--- bin/modules/gui/window/view_tree/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/view_tree/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+
+from view_tree import *
+from view_tree_sc import *
+
+import parse
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/view_tree/parse.py'
--- bin/modules/gui/window/view_tree/parse.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/view_tree/parse.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import gtk
+import gobject
+from xml.parsers import expat
+
+import gettext
+
+class parse(object):
+    def __init__(self, fields):
+        self.fields = fields
+        self.pixbufs = {}
+
+    def _psr_start(self, name, attrs):
+        if name == 'tree':
+            self.title = attrs.get('string',_('Tree'))
+            self.toolbar = bool(attrs.get('toolbar',False))
+            self.colors = {}
+            for color_spec in attrs.get('colors', '').split(';'):
+                if color_spec:
+                    colour, test = color_spec.split(':')
+                    self.colors.setdefault(colour,[])
+                    self.colors[colour].append(test)
+        elif name == 'field':
+            if attrs.get('invisible', False):
+                self.invisible_fields.append(str(attrs['name']))
+                return True
+            type = self.fields[attrs['name']]['type']
+            field_name = attrs.get('string', self.fields[attrs['name']]['string'])
+            if type!='boolean':
+                column = gtk.TreeViewColumn(field_name)
+                if 'icon' in attrs:
+                    render_pixbuf = gtk.CellRendererPixbuf()
+                    column.pack_start(render_pixbuf, expand=False)
+                    column.add_attribute(render_pixbuf, 'pixbuf', self.pos)
+                    self.fields_order.append(str(attrs['icon']))
+                    self.pixbufs[self.pos] = True
+                    self.pos += 1
+                cell = gtk.CellRendererText()
+                cell.set_fixed_height_from_font(1)
+                if type=='float':
+                    cell.set_property('xalign', 1.0)
+                column.pack_start(cell, expand=False)
+                column.add_attribute(cell, 'text', self.pos)
+            else:
+                cell = gtk.CellRendererToggle()
+                column = gtk.TreeViewColumn (field_name, cell, active=self.pos)
+            self.pos += 1
+            column.set_resizable(1)
+            self.fields_order.append(str(attrs['name']))
+            self.tree.append_column(column)
+        else:
+            import logging
+            log = logging.getLogger('view')
+            log.error('unknown tag: '+str(name))
+            del log
+    def _psr_end(self, name):
+        pass
+    def _psr_char(self, char):
+        pass
+    def parse(self, xml_data, tree):
+        cell = gtk.CellRendererText()
+        cell.set_fixed_height_from_font(1)
+        column = gtk.TreeViewColumn('ID', cell, text=0)
+        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        column.set_fixed_width(60)
+        column.set_visible(False)
+        tree.append_column(column)
+        self.tree = tree
+        self.pos = 1
+
+        self.fields_order = []
+        self.invisible_fields = []
+
+        psr = expat.ParserCreate()
+        psr.StartElementHandler = self._psr_start
+        psr.EndElementHandler = self._psr_end
+        psr.CharacterDataHandler = self._psr_char
+        psr.Parse(xml_data)
+        return self.pos
+
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/view_tree/view_tree.py'
--- bin/modules/gui/window/view_tree/view_tree.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/view_tree/view_tree.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,432 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import gtk
+import gobject
+
+import time
+import datetime as DT
+import copy
+import math
+import locale
+import gettext
+
+from xml.parsers import expat
+
+import options
+import rpc
+import parse
+
+import tools
+from tools import user_locale_format, datetime_util
+
+DT_FORMAT = '%Y-%m-%d'
+DHM_FORMAT = '%Y-%m-%d %H:%M:%S'
+
+# BUG: ids = []
+#
+# Tree struct:  [ id, values, children, children_id ]
+#
+#    values: [...]
+#    children: [ tree_struct ]
+#            [] for no children
+#            None for undevelopped (with children!)
+#        assert: no children => []
+#
+# Node struct: [list of (pos, list) ]
+#
+class view_tree_model(gtk.GenericTreeModel, gtk.TreeSortable):
+    def __init__(self, ids, view, fields, fields_type, invisible_fields=[], context={}, pixbufs={}, treeview=None, colors='black'):
+        gtk.GenericTreeModel.__init__(self)
+        self.fields = fields
+        self.fields_type = fields_type
+        self.invisible_fields = invisible_fields
+        self.view = view
+        self.roots = ids
+        self.colors = colors
+        self.color_ids = {}
+        self.context = context
+        self.tree = self._node_process(self.roots)
+        self.pixbufs = pixbufs
+        self.treeview = treeview
+
+    def get_color(self,result):
+        color_ids = {}
+        for res in result:
+            color_ids[res['id']] = 'black'
+            res_lower = {}
+            for key, vals in res.iteritems():
+                if self.fields_type.get(key, False) and vals != 'False':
+                    type = self.fields_type[key]['type']
+                    if type == 'date':
+                        res_lower[key] = datetime_util.local_to_server_timestamp(vals,
+                                         user_locale_format.get_date_format(), DT_FORMAT, tz_offset=False)
+                        continue
+                    elif type == 'datetime':
+                        res_lower[key] = datetime_util.local_to_server_timestamp(vals,
+                                         user_locale_format.get_datetime_format(True), DT_FORMAT)
+                        continue
+                if isinstance(vals, (str, unicode)):
+                    res_lower[key]= vals.lower()
+                else:
+                    res_lower[key] = vals
+            for color, expt in self.colors.iteritems():
+                val = False
+                for cond in expt:
+                    if isinstance(cond, basestring):
+                        val = tools.expr_eval(cond, res_lower)
+                    if val:
+                        color_ids[res_lower['id']] = color
+                        break
+                if val:
+                    break
+        return color_ids
+
+    def _read(self, ids, fields):
+        c = {}
+        c.update(rpc.session.context)
+        c.update(self.context)
+        if self.invisible_fields:
+            fields += self.invisible_fields
+        try:
+            res_ids = rpc.session.rpc_exec_auth_try('/object', 'execute',
+                    self.view['model'], 'read', ids, fields, c)
+        except:
+            res_ids = []
+            for id in ids:
+                val = {'id': id}
+                for f in fields:
+                    if self.fields_type[f]['type'] in ('one2many','many2many'):
+                        val[f] = []
+                    else:
+                        val[f] = ''
+                res_ids.append(val)
+        for field in self.fields + self.invisible_fields:
+            for x in res_ids:
+                if self.fields_type[field]['type'] in ('date',):
+                    display_format = user_locale_format.get_date_format()
+                    if x[field]:
+                        x[field] = datetime_util.server_to_local_timestamp(x[field],
+                                    DT_FORMAT, display_format, tz_offset=False)
+                    else:
+                        x[field] = str(x[field])
+                elif self.fields_type[field]['type'] in ('datetime',):
+                    display_format = user_locale_format.get_datetime_format(True)
+                    if x[field]:
+                        x[field] = datetime_util.server_to_local_timestamp(x[field],
+                                    DHM_FORMAT, display_format)
+                    else:
+                        x[field] = str(x[field])
+                elif self.fields_type[field]['type'] in ('one2one','many2one'):
+                    if x[field]:
+                        x[field] = x[field][1]
+                elif self.fields_type[field]['type'] in ('selection'):
+                    if x[field]:
+                        x[field] = dict(self.fields_type[field]['selection']).get(x[field],'')
+                elif self.fields_type[field]['type'] in ('float',):
+                    interger, digit = self.fields_type[field].get('digits', (16,2))
+                    x[field] = user_locale_format.format('%.' + str(digit) + 'f', x[field] or 0.0)
+                elif self.fields_type[field]['type'] in ('integer',):
+                    x[field] = int(user_locale_format.format('%d', int(x[field]) or 0))
+                elif self.fields_type[field]['type'] in ('float_time',):
+                    val = datetime_util.float_time_convert(x[field])
+                    if x[field] < 0:
+                        val = '-' + val
+                    x[field] = val
+        return res_ids
+
+    def _node_process(self, ids):
+        tree = []
+        if self.view.get('field_parent', False):
+            res = self._read(ids, self.fields+[self.view['field_parent']])
+            self.color_ids.update(self.get_color(res))
+            for x in res:
+                tree.append( [ x['id'], None, [], x[self.view['field_parent']] ] )
+                tree[-1][1] = [ x[ y ] for y in self.fields]
+                if len(x[self.view['field_parent']]):
+                    tree[-1][2] = None
+        else:
+            res = self._read(ids, self.fields)
+            for x in res:
+                tree.append( [ x['id'],  [ x[y] for y in self.fields], [] ])
+        return tree
+
+    def _node_expand(self, node):
+        node[2] = self._node_process(node[3])
+        del node[3]
+
+    def on_get_flags(self):
+        return 0
+
+    def on_get_n_columns(self):
+        return len(self.fields)+1
+
+    def on_get_column_type(self, index):
+        if index in self.pixbufs:
+            return gtk.gdk.Pixbuf
+        return fields_list_type.get(self.fields_type[self.fields[index-1]]['type'],
+                gobject.TYPE_STRING)
+
+    def on_get_path(self, node):
+        '''returns the tree path (a tuple of indices)'''
+        return tuple([ x[0] for x in node ])
+
+    def on_get_iter(self, path):
+        '''returns the node corresponding to the given path.'''
+        node = []
+        tree = self.tree
+        if self.tree==[]:
+            return None
+        for x in path:
+            node.append( (x, tree) )
+            if x <= (len(tree)-1):
+                tree = tree[x] and tree[x][2] or None
+        return node
+
+    def on_get_value(self, node, column):
+        (n, list) = node[-1]
+        if column:
+            value = list[n][1][column-1]
+        else:
+            value = list[n][0]
+
+        if value==None or (value==False and type(value)==bool):
+            res = ''
+        else:
+            res = value
+        if (column in self.pixbufs) and res:
+            if res.startswith('STOCK_'):
+                res = getattr(gtk, res)
+            return self.treeview.render_icon(stock_id=res, size=gtk.ICON_SIZE_MENU, detail=None)
+        return res
+
+    def on_iter_next(self, node):
+        '''returns the next node at this level of the tree'''
+        node = node[:]
+        (n, list) = node[-1]
+        if n<len(list)-1:
+            node[-1] = (n+1, list)
+            return node
+        return None
+
+    def on_iter_children(self, node):
+        '''returns the first child of this node'''
+        if node==None:                    # added
+            return [ (0, self.tree) ]     # added
+        node = node[:]
+        (n, list) = node[-1]
+        if list[n][2]==None:
+            self._node_expand(list[n])
+        if list[n][2]==[]:
+            return None
+        node.append( (0, list[n][2]) )
+        return node
+
+    def on_iter_has_child(self, node):
+        '''returns true if this node has children'''
+        (n, list) = node[-1]
+        return list[n][2]!=[]
+
+    def on_iter_n_children(self, node):
+        '''returns the number of children of this node'''
+        if node==None:                    # changed
+            return len(self.tree)         # changed
+        (n, list) = node[-1]
+        if list[n][2]==None:
+            self._node_expand(list[n])
+        return len(list[n][2])
+
+    def on_iter_nth_child(self, node, child):
+        '''returns the nth child of this node'''
+        if node==None:
+            if child<len(self.tree):
+                return [ (child, self.tree) ]
+            return None
+        else:
+            (n, list) = node[-1]
+            if list[n][2]==None:
+                self._node_expand(list[n])
+            if child<len(list[n][2]):
+                node = node[:]
+                node.append( (child, list[n][2]) )
+                return node
+            return None
+
+    def on_iter_parent(self, node):
+        '''returns the parent of this node'''
+        if node==None:
+            return None
+        return node[:-1]
+
+    def cus_refresh(self):
+        tree = self.tree
+        tree[0][2] = None
+
+    def _cus_row_find(self, ids_res):
+        tree = self.tree
+        try:
+            ids = ids_res[:]
+            while len(ids)>0:
+                if ids[-1] in self.roots:
+                    ids.pop()
+                    break
+                ids.pop()
+            path = []
+            while ids!=[]:
+                path.append(0)
+                val = ids.pop()
+                i = iter(tree)
+                while True:
+                    node = i.next()
+                    if node[0]==val:
+                        break
+                    path[-1]+=1
+                if (node[2]==None) and (ids!=[]):
+                    return None
+                tree = node[2]
+            return (tuple(path), node)
+        except:
+            return None
+
+class view_tree(object):
+    def __init__(self, view_info, ids, res_id=None, sel_multi=False, context={}):
+        self.view = gtk.TreeView()
+        self.view.set_headers_visible(True)
+        self.context = {}
+        self.context.update(rpc.session.context)
+        self.context.update(context)
+        self.fields = rpc.session.rpc_exec_auth('/object', 'execute', view_info['model'], 'fields_get', False, self.context)
+        p = parse.parse(self.fields)
+        p.parse(view_info['arch'], self.view)
+        self.toolbar = p.toolbar
+        self.pixbufs = p.pixbufs
+        self.colors = p.colors
+        self.name = p.title
+        self.sel_multi = sel_multi
+
+        if sel_multi:
+            self.view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+        else:
+            self.view.get_selection().set_mode(gtk.SELECTION_SINGLE)
+        self.view.set_expander_column(self.view.get_column(1))
+        self.view.set_enable_search(False)
+        self.view.get_column(0).set_visible(False)
+
+        self.ids=ids
+        self.view_info = view_info
+        self.fields_order = p.fields_order
+        self.invisible_fields = p.invisible_fields
+        self.model = None
+        self.reload()
+
+        self.view.show_all()
+        self.search=[]
+        self.next=0
+
+    def reload(self):
+        del self.model
+        self.context.update(rpc.session.context)
+        self.model = view_tree_model(self.ids, self.view_info, self.fields_order, self.fields, self.invisible_fields, context=self.context, pixbufs=self.pixbufs, treeview=self.view , colors=self.colors)
+        self.view.set_model(self.model)
+        local ={}
+        def render_column(column, cell, model, iter):
+            if not isinstance(cell, gtk.CellRendererPixbuf):
+                    list = zip(self.model.color_ids.keys(),self.model.color_ids.values())
+                    color_by ={}
+                    for k,v in list:
+                        color_by.setdefault(v,[]).append(str(k))
+                    for k ,v in color_by.items():
+                        if model.get_value(iter,0) in v and not isinstance(cell,gtk.CellRendererToggle):
+                            cell.set_property('foreground',k)
+
+        for column in self.view.get_columns():
+            renderers  = column.get_cell_renderers()
+            for ren in renderers:
+                column.set_cell_data_func(ren, render_column)
+
+    def widget_get(self):
+        return self.view
+
+    def sel_ids_get(self):
+        sel = self.view.get_selection()
+        if not sel:
+            return None
+        sel = sel.get_selected_rows()
+        if not sel:
+            return []
+        (model, iters) = sel
+        return map(lambda x: int(model.get_value(model.get_iter(x), 0)), iters)
+
+    def sel_id_get(self):
+        sel = self.view.get_selection().get_selected()
+        if sel==None:
+            return None
+        (model, iter) = sel
+        if not iter:
+            return None
+        res = model.get_value(iter, 0)
+        if res!=None:
+            return int(res)
+        return res
+
+    def value_get(self, col):
+        sel = self.view.get_selection().get_selected_rows()
+        if sel==None:
+            return None
+        (model, iter) = sel
+        if not iter:
+            return None
+        return model.get_value(iter, col)
+
+    def go(self, id):
+        return
+        ids = com_rpc.xrpc.exec_auth('res_path_get', id, self.root)
+        if not len(ids):
+            raise Exception, 'IdNotFound'
+        self.view.collapse_all()
+        model = self.view.get_model()
+        iter = model.get_iter_root()
+        while len(ids)>0:
+            if ids[-1]==model.root:
+                break
+            ids.pop()
+        if ids!=[]:
+            ids.pop()
+            while ids!=[]:
+                val = ids.pop()
+                while True:
+                    if int(model.get_value(iter,0)) == val:
+                        self.view.expand_row( model.get_path(iter), False)
+                        break
+                    if not model.iter_next(iter):
+                        raise Exception, 'IdNotFound'
+                if ids!=[]:
+                    iter = model.iter_children(iter)
+            self.view.get_selection().select_iter(iter)
+
+fields_list_type = {
+    'boolean': gobject.TYPE_BOOLEAN,
+#    'integer': gobject.TYPE_INT,
+}
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/view_tree/view_tree_sc.py'
--- bin/modules/gui/window/view_tree/view_tree_sc.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/view_tree/view_tree_sc.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,176 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import rpc
+import gobject
+import gtk
+
+import gettext
+import service
+
+class view_tree_sc(object):
+    ( COLUMN_RES_ID, COLUMN_NAME, COLUMN_ID ) = range(3)
+    def __init__(self, tree, model):
+        self.last_iter = None
+        self.model = model
+        self.tree = tree
+        self.tree.connect( 'key-press-event', self.on_key_press_event )
+        self.tree.get_selection().set_mode('single')
+
+        self.tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, [("shortcuts", 0, 0)], gtk.gdk.ACTION_COPY)
+        self.tree.enable_model_drag_dest([("shortcuts", 0, 0)], gtk.gdk.ACTION_COPY)
+        self.tree.connect("drag_data_received", self.on_drag_data_received)
+
+        column = gtk.TreeViewColumn (_('ID'), gtk.CellRendererText(), text=0)
+        self.tree.append_column(column)
+        column.set_visible(False)
+        cell = gtk.CellRendererText()
+        cell.connect( 'edited', self.on_cell_edited )
+
+        column = gtk.TreeViewColumn (_('Description'), cell, text=1)
+        self.tree.append_column(column)
+#        self.update()
+
+    def update(self):
+        store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
+        uid =  rpc.session.uid
+        sc = rpc.session.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'get_sc', uid, self.model, rpc.session.context) or []
+        for s in sc:
+            num = store.append()
+            store.set(num,
+              self.COLUMN_RES_ID, s['res_id'],
+              self.COLUMN_NAME, s['name'],
+              self.COLUMN_ID, s['id']
+            )
+            self.last_iter = num
+
+        self.tree.set_model(store)
+        if self.model == 'ir.ui.menu':
+            service.LocalService('gui.main').shortcut_set(sc)
+
+    def remove(self, id):
+        self.update()
+
+    def add(self, id):
+        self.update()
+
+    def value_get(self, col):
+        sel = self.tree.get_selection().get_selected()
+        if not sel:
+            return None
+        (model, iter) = sel
+        if not iter:
+            return None
+        return model.get_value(iter, col)
+
+    def sel_id_get(self):
+        res = self.value_get(0)
+        res = eval(str(res))
+        if res:
+            return int(res[0]) if isinstance(res,(list,tuple)) else res
+        return None
+
+    def serv_update(self, ids, action):
+        if (action==2):
+            self.update()
+
+    def on_cell_edited(self, cell, path_string, new_text):
+        model = self.tree.get_model()
+        iter = model.get_iter_from_string(path_string)
+        old_text = model.get_value( iter, self.COLUMN_NAME )
+        if old_text <> new_text:
+            res_id = int( model.get_value( iter, self.COLUMN_ID ) )
+            rpc.session.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'write', res_id, { 'name' : new_text }, rpc.session.context )
+            model.set(iter, self.COLUMN_NAME, new_text)
+
+        cell.set_property( 'editable', False )
+
+    def on_key_press_event( self, widget, event ):
+        if event.keyval == gtk.keysyms.F2:
+            column = self.tree.get_column( self.COLUMN_NAME )
+            cell = column.get_cell_renderers()[0]
+            cell.set_property( 'editable', True )
+
+            selected_row = widget.get_selection().get_selected()
+            if selected_row and selected_row[1]:
+                (model, iter) = selected_row
+                path = model.get_path( iter )
+                self.tree.set_cursor_on_cell( path, column, cell, True )
+
+    def check_sanity(self, model, iter_to_copy, target_iter):
+        path_of_iter_to_copy = model.get_path(iter_to_copy)
+        path_of_target_iter = model.get_path(target_iter)
+        return not ( path_of_target_iter[0:len(path_of_iter_to_copy)] == path_of_iter_to_copy )
+
+    def iter_copy(self, treeview, model, iter_to_copy, target_iter, pos):
+        data_column_0 = model.get_value(iter_to_copy, self.COLUMN_RES_ID)
+        data_column_1 = model.get_value(iter_to_copy, self.COLUMN_NAME)
+        data_column_2 = model.get_value(iter_to_copy, self.COLUMN_ID)
+        if (pos == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE) or (pos == gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
+            new_iter = model.prepend(None)
+        elif pos == gtk.TREE_VIEW_DROP_BEFORE:
+            new_iter = model.insert_before(target_iter, None)
+        elif pos == gtk.TREE_VIEW_DROP_AFTER:
+            new_iter = model.insert_after(target_iter, None)
+        else:
+            new_iter = model.append()
+        model.set_value(new_iter, self.COLUMN_RES_ID, data_column_0)
+        model.set_value(new_iter, self.COLUMN_NAME, data_column_1)
+        model.set_value(new_iter, self.COLUMN_ID, data_column_2)
+
+    def on_drag_data_received(self, treeview, drag_context, x, y, selection, info, eventtime):
+        drop_info = treeview.get_dest_row_at_pos(x,y)
+        modified = False
+        if drop_info:
+            path, pos = drop_info
+            model, iter_to_copy = treeview.get_selection().get_selected()
+            target_iter = model.get_iter(path)
+            if target_iter <> iter_to_copy:
+                if self.check_sanity(model, iter_to_copy, target_iter):
+                    self.iter_copy(treeview, model, iter_to_copy, target_iter, pos)
+                    drag_context.finish(True, True, eventtime)
+                    treeview.expand_all()
+                    modified = True
+                else:
+                    drag_context.finish(False, False, eventtime)
+            else:
+                drag_context.finish(False, False, eventtime)
+        else:
+            model, iter_to_copy = treeview.get_selection().get_selected()
+            if iter_to_copy <> self.last_iter:
+                self.iter_copy( treeview, model, iter_to_copy, None, None )
+                drag_context.finish(True, True, eventtime)
+                modified = True
+            else:
+                drag_context.finish(False, False, eventtime)
+
+        if modified == True:
+            model = treeview.get_model()
+            iter = model.get_iter_first()
+            counter = 0
+            while iter:
+                res_id = int(model.get_value( iter, self.COLUMN_ID ))
+                rpc.session.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'write', res_id, { 'sequence' : counter }, rpc.session.context )
+                counter = counter + 1
+                iter = model.iter_next( iter )
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/win_export.py'
--- bin/modules/gui/window/win_export.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/win_export.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,367 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import gtk
+from gtk import glade
+import gobject
+import gettext
+import common
+
+import rpc
+
+import service
+import types
+import os
+
+def export_csv(fname, fields, result, write_title=False):
+    import csv
+    try:
+        fp = file(fname, 'wb+')
+        writer = csv.writer(fp, quoting=csv.QUOTE_ALL)
+        if write_title:
+            writer.writerow(fields)
+        for data in result:
+            row = []
+            for d in data:
+                if type(d)==types.StringType:
+                    row.append(d.replace('\n',' ').replace('\t',' '))
+                else:
+                    row.append(d or '')
+            writer.writerow(row)
+        fp.close()
+        common.message(str(len(result))+_(' record(s) saved !'))
+        return True
+    except IOError, (errno, strerror):
+        common.message(_("Operation failed !\nI/O error")+"(%s)" % (errno,))
+        return False
+
+def open_excel(fields, result):
+    if os.name == 'nt':
+        try:
+            if len(fields) > 0:
+                from win32com.client import Dispatch
+                import pywintypes
+                xlApp = Dispatch("Excel.Application")
+                xlApp.Workbooks.Add()
+                for col in range(len(fields)):
+                    xlApp.ActiveSheet.Cells(1,col+1).Value = fields[col]
+                sht = xlApp.ActiveSheet
+                for a in result:
+                    for b in range(len(a)):
+                        if type(a[b]) == type(''):
+                            a[b]=a[b].decode('utf-8','replace')
+                        elif type(a[b]) == type([]):
+                            if len(a[b])==2:
+                                a[b] = a[b][1].decode('utf-8','replace')
+                            else:
+                                a[b] = ''
+                sht.Range(sht.Cells(2, 1), sht.Cells(len(result)+1, len(fields))).Value = result
+                xlApp.Visible = 1
+        except:
+            common.error(_('Error Opening Excel !'),'')
+    else:
+#        common.message(_("Function only available for MS Office !\nSorry, OOo users :("))
+        try:
+            import subprocess
+            import time
+            retcode = subprocess.call(["soffice", "-accept=socket,host=localhost,port=2002;urp;", "-nodefault"], shell=False)
+            from oootools import OOoTools
+            for i in range(10):
+                ooo = OOoTools('localhost', 2002)
+                if ooo and ooo.desktop:
+                    break
+                time.sleep(1)
+            doc = ooo.desktop.loadComponentFromURL("private:factory/scalc",'_blank',0,())
+            sheet = doc.CurrentController.ActiveSheet
+            for col in range(len(fields)):
+                cell = sheet.getCellByPosition(col, 0)
+                cell.String = fields[col]
+            if len(fields) > 0:
+                cellrange = sheet.getCellRangeByPosition(0, 1, len(fields) - 1, len(result))
+                tresult = []
+                for i in range(len(result)):
+                    tresult.append(tuple(result[i]))
+                    tresult = tuple(tresult)
+                cellrange.setDataArray(tresult)
+        except:
+            common.error(_('Error Opening Excel !'),'')
+
+def datas_read(ids, model, fields, fields_view, prefix='', context=None):
+    ctx = context.copy()    
+    ctx.update(rpc.session.context)
+    datas = rpc.session.rpc_exec_auth('/object', 'execute', model, 'export_data', ids, fields, ctx)
+    return datas
+
+class win_export(object):
+    def __init__(self, model, ids, fields, preload = [], parent=None, context=None):
+        self.glade = glade.XML(common.terp_path("openerp.glade"), 'win_save_as',
+                gettext.textdomain())
+        self.win = self.glade.get_widget('win_save_as')
+        self.ids = ids
+        self.model = model
+        self.fields_data = {}
+        self.fields = {}
+        if context is None:
+            context = {}
+        self.context = context
+
+        if parent is None:
+            parent = service.LocalService('gui.main').window
+        self.win.set_transient_for(parent)
+        self.win.set_icon(common.OPENERP_ICON)
+        self.parent = parent
+
+        self.view1 = gtk.TreeView()
+        self.view1.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+        self.glade.get_widget('exp_vp1').add(self.view1)
+        self.view2 = gtk.TreeView()
+        self.view2.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+        self.glade.get_widget('exp_vp2').add(self.view2)
+        self.view1.set_headers_visible(False)
+        self.view2.set_headers_visible(False)
+
+        cell = gtk.CellRendererText()
+        column = gtk.TreeViewColumn(_('Field Name'), cell, text=0, background=2)
+        self.view1.append_column(column)
+
+        cell = gtk.CellRendererText()
+        column = gtk.TreeViewColumn(_('Field Name'), cell, text=0)
+        self.view2.append_column(column)
+
+        #for f in preload:
+        #    self.model2.set(self.model2.append(), 0, f[1], 1, f[0])
+        self.wid_import_compatible = self.glade.get_widget('import_compatible')
+
+        self.model1 = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
+        self.model2 = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
+
+        self.fields_original = fields
+        self.model_populate(self.fields_original)
+
+        self.view1.set_model(self.model1)
+        self.view2.set_model(self.model2)
+        self.view1.show_all()
+        self.view2.show_all()
+
+
+        self.wid_action = self.glade.get_widget('win_saveas_combo')
+        self.wid_write_field_names = self.glade.get_widget('add_field_names_cb')
+        action = self.wid_action.set_active(os.name!='nt')
+        
+        if os.name != 'nt':
+            self.wid_action.remove_text(0)
+        else:
+            try:
+                from win32com.client import Dispatch
+                import pywintypes
+                xlApp = Dispatch("Excel.Application")
+            except Exception,e:
+                if isinstance(e, pywintypes.com_error):
+                    action = self.wid_action.set_active(isinstance(e, pywintypes.com_error))
+                    self.wid_action.remove_text(0)
+                else:
+                    pass
+
+        self.glade.signal_connect('on_but_unselect_all_clicked', self.sig_unsel_all)
+        self.glade.signal_connect('on_but_select_all_clicked', self.sig_sel_all)
+        self.glade.signal_connect('on_but_select_clicked', self.sig_sel)
+        self.glade.signal_connect('on_but_unselect_clicked', self.sig_unsel)
+        self.glade.signal_connect('on_but_predefined_clicked', self.add_predef)
+        self.glade.signal_connect('on_but_delpredefined_clicked', self.del_export_list_btn)
+        self.glade.signal_connect('on_import_toggled', self.import_toggled)
+
+        # Creating the predefined export view
+        self.pref_export = gtk.TreeView()
+        self.pref_export.append_column(gtk.TreeViewColumn('Export name', gtk.CellRendererText(), text=1))
+        self.pref_export.append_column(gtk.TreeViewColumn('Exported fields', gtk.CellRendererText(), text=2))
+        self.glade.get_widget('predefined_exports').add(self.pref_export)
+
+        self.pref_export.connect("row-activated", self.sel_predef)
+        self.pref_export.connect('key_press_event', self.del_export_list_key)
+
+        # Fill the predefined export tree view and show everything
+        self.fill_predefwin()
+        self.pref_export.show_all()
+
+    def model_populate(self, fields, prefix_node='', prefix=None, prefix_value='', level=2):
+        import_comp = self.wid_import_compatible.get_active()
+        fields = fields.copy()
+        fields.update({'id':{'string':_('ID')}})
+        fields.update({'.id':{'string':_('Database ID')}}) 
+
+        fields_order = fields.keys()
+        fields_order.sort(lambda x,y: -cmp(fields[x].get('string', ''), fields[y].get('string', '')))
+        for field in fields_order:
+            if import_comp and fields[field].get('readonly', False):
+                ok = False
+                for sl in fields[field].get('states', {}).values():
+                    for s in sl:
+                        ok = ok or (s==('readonly',False))
+                if not ok: continue
+            self.fields_data[prefix_node+field] = fields[field]
+            if prefix_node:
+                self.fields_data[prefix_node + field]['string'] = '%s%s' % (prefix_value, self.fields_data[prefix_node + field]['string'])
+            st_name = fields[field]['string'] or field 
+            node = self.model1.insert(prefix, 0, [st_name, prefix_node+field, (fields[field].get('required', False) and '#ddddff') or 'white'])
+            self.fields[prefix_node+field] = (st_name, fields[field].get('relation', False))
+            if fields[field].get('relation', False) and level>0:
+                if (fields[field]['type'] in ['many2many']) and import_comp:
+                    pass
+                else:
+                    if (not import_comp) or (fields[field]['type'] in ['one2many']):
+                        fields2 = rpc.session.rpc_exec_auth('/object', 'execute', fields[field]['relation'], 'fields_get', False, rpc.session.context)
+                        self.model_populate(fields2, prefix_node+field+'/', node, st_name+'/', level-1)
+                    else:
+                        self.model_populate({}, prefix_node+field+'/', node, st_name+'/', level-1)
+
+    def del_export_list_key(self,widget, event, *args):
+        if event.keyval==gtk.keysyms.Delete:
+            self.del_selected_export_list()
+    
+    def del_export_list_btn(self, widget=None):
+        self.del_selected_export_list()
+
+    def del_selected_export_list(self):
+        store, paths = self.pref_export.get_selection().get_selected_rows()
+        for p in paths:
+            export_fields= store.get_value(store.__getitem__(p[0]).iter,0)
+            export_name= store.get_value(store.__getitem__(p[0]).iter,1)
+
+            ir_export = rpc.RPCProxy('ir.exports')
+            ir_export_line = rpc.RPCProxy('ir.exports.line')
+
+            export_ids=ir_export.search([('name','=',export_name)])
+
+            for id in export_ids:
+                fields=[]
+                line_ids=ir_export_line.search([('export_id','=',id)])
+
+                obj_line=ir_export_line.read(line_ids)
+                for i in range(0,len(obj_line)):
+                    fields.append(obj_line[i]['name'])
+
+                if fields==export_fields:
+                    ir_export.unlink(id)
+                    ir_export_line.unlink(line_ids)
+                    store.remove(store.get_iter(p))
+                    break
+
+    def sig_sel_all(self, widget=None):
+        self.model2.clear()
+        for field, relation in self.fields.keys():
+            if not relation:
+                self.model2.set(self.model2.append(), 0, self.fields[field], 1, field)
+
+    def sig_sel(self, widget=None):
+        sel = self.view1.get_selection()
+        sel.selected_foreach(self._sig_sel_add)
+
+    def _sig_sel_add(self, store, path, iter):
+        name, relation = self.fields[store.get_value(iter,1)]
+        #if relation:
+        #    return
+        num = self.model2.append()
+        self.model2.set(num, 0, store.get_value(iter,0), 1, store.get_value(iter,1))
+
+    def sig_unsel(self, widget=None):
+        store, paths = self.view2.get_selection().get_selected_rows()
+        for p in paths:
+            store.remove(store.get_iter(p))
+
+    def import_toggled(self, widget=None):
+        self.model1.clear()
+        self.model2.clear()
+        self.model_populate(self.fields_original)
+
+    def sig_unsel_all(self, widget=None):
+        self.model2.clear()
+    
+    def fill_predefwin(self):
+        self.predef_model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_STRING)
+        ir_export = rpc.RPCProxy('ir.exports')
+        ir_export_line = rpc.RPCProxy('ir.exports.line')
+        export_ids = ir_export.search([('resource', '=', self.model)])
+        for export in ir_export.read(export_ids):
+            fields = ir_export_line.read(export['export_fields'])
+            try:
+                self.predef_model.append(([f['name'] for f in fields], export['name'], ', '.join([self.fields_data[f['name']]['string'] for f in fields])))
+            except:
+                pass
+        self.pref_export.set_model(self.predef_model)
+
+    def add_predef(self, button):
+        name = common.ask(_('What is the name of this export ?'))
+        if not name:
+            return 
+        ir_export = rpc.RPCProxy('ir.exports')
+        iter = self.model2.get_iter_root()
+        fields = []
+        while iter:
+            field_name = self.model2.get_value(iter, 1)
+            fields.append(field_name)
+            iter = self.model2.iter_next(iter)
+        ir_export.create({'name' : name, 'resource' : self.model, 'export_fields' : [(0, 0, {'name' : f}) for f in fields]})
+        self.predef_model.append((fields, name, ','.join([self.fields_data[f]['string'] for f in fields])))
+    
+    def sel_predef(self, treeview, path, column):
+        self.model2.clear()
+        for field in self.predef_model[path[0]][0]:
+            self.model2.append((self.fields_data[field]['string'], field))
+
+    def go(self):
+        button = self.win.run()
+        if button==gtk.RESPONSE_OK:
+            fields = []
+            fields2 = []
+            iter = self.model2.get_iter_root()
+            while iter:
+                fields.append(self.model2.get_value(iter, 1).replace('/.id','.id'))
+                fields2.append(self.model2.get_value(iter, 0))
+                iter = self.model2.iter_next(iter)
+            action = self.wid_action.get_active()
+            self.parent.present()
+            self.win.destroy()
+            import_comp = self.wid_import_compatible.get_active()
+            result = datas_read(self.ids, self.model, fields, self.fields_data, context=self.context)
+            if result.get('warning',False):
+                common.message_box(_('Exportation Error !'), unicode(result.get('warning',False)))
+                return False
+            result = result.get('datas',[])
+            if import_comp:
+                fields2 = fields
+            if self.wid_action.get_active_text() == "Open in Excel":
+                open_excel(fields2, result)
+            else:
+                fname = common.file_selection(_('Save As...'),
+                        parent=self.parent, action=gtk.FILE_CHOOSER_ACTION_SAVE)
+                if fname:
+                    export_csv(fname, fields2, result, self.wid_write_field_names.get_active())
+            return True
+        else:
+            self.parent.present()
+            self.win.destroy()
+            return False
+
+
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/win_extension.py'
--- bin/modules/gui/window/win_extension.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/win_extension.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import os
+import gettext
+import gobject
+import gtk
+import gtk.glade
+
+import service
+import common
+import options
+
+class win_extension(object):
+    def __init__(self, parent=None):
+        glade = gtk.glade.XML(common.terp_path('openerp.glade'), 'win_extension', gettext.textdomain())
+        self.win = glade.get_widget('win_extension')
+        self.win.set_transient_for(parent)
+        self.win.set_icon(common.OPENERP_ICON)
+        model = gtk.ListStore( str, str, str )
+
+        self.treeview = glade.get_widget('treeview_extension')
+        self.treeview.set_model(model)
+
+        for index, text in enumerate([_('Extension'), _('Application'), _('Print Processor')]):
+            renderer = gtk.CellRendererText()
+            renderer.set_property( 'editable', True )
+            renderer.connect( 'edited', self._on_cell_renderer_edited )
+            column = gtk.TreeViewColumn( text, renderer, text=index )
+            column.set_resizable( True )
+            self.treeview.append_column( column )
+
+        dict = {
+            'on_button5_clicked' : self._sig_add,
+            'on_button6_clicked' : self._sig_remove,
+        }
+
+        for signal in dict:
+            glade.signal_connect( signal, dict[signal] )
+
+        self.load_from_file()
+
+    def load_from_file(self):
+        try:
+            filetypes = eval(options.options['extensions.filetype'][:])
+            for ext, (app, app_print) in filetypes.items():
+                self.add_to_model( ext, app, app_print )
+        except Exception, ex:
+            pass
+
+    def save_to_file(self):
+        value = dict( [ [ ext, ( app, printable ) ] for ext, app, printable in self.treeview.get_model() ] )
+        options.options['extensions.filetype'] = str(value)
+        options.options.save()
+
+    def add_to_model( self, extension, application, printable ):
+        model = self.treeview.get_model()
+        iter = model.append()
+        model.set( iter, 0, extension, 1, application, 2, printable )
+        return model.get_path( iter )
+
+    def _sig_add( self, button, data = None ):
+        path = self.add_to_model( '', '', '' )
+        self.treeview.set_cursor( path, self.treeview.get_column(0), True )
+
+    def _sig_remove( self, button, data = None ):
+        selection = self.treeview.get_selection()
+        model, iter = selection.get_selected()
+
+        if iter:
+            path = model.get_path(iter)
+            model.remove( iter )
+            selection.select_path( path )
+
+            if not selection.path_is_selected( path ):
+                row = path[0]-1
+                if row >= 0:
+                    selection.select_path((row,))
+
+    def _on_cell_renderer_edited( self, cell, path_string, new_text ):
+        model = self.treeview.get_model()
+        iter = model.get_iter_from_string(path_string)
+
+        (path,column) = self.treeview.get_cursor()
+
+        column_id = self.treeview.get_columns().index(column)
+
+        old_text = model.get_value( iter, column_id )
+
+        if column_id == 0:
+            old_text = old_text.lower()
+            new_text = new_text.lower()
+        if old_text <> new_text:
+            if column_id == 0:
+                if new_text in [ ext for ext, app, app_print in model ]:
+                    common.warning(_('This extension is already defined'), _('Extension Manager'), parent=self.win)
+                    return
+                else:
+                    model.set(iter, column_id, new_text)
+            else:
+                model.set(iter, column_id, new_text)
+
+    def run(self):
+        res = self.win.run()
+        if res == -5:
+            self.save_to_file()
+        self.win.destroy()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'bin/modules/gui/window/win_import.py'
--- bin/modules/gui/window/win_import.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/win_import.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,268 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import gtk
+from gtk import glade
+import gobject
+import gettext
+import common
+
+import rpc
+
+import csv
+import cStringIO
+
+import options
+import service
+
+#
+# TODO: make it works with references
+#
+def import_csv(csv_data, f, model, fields, context=None, parent=None):
+    fname = csv_data['fname']
+    content = file(fname,'rb').read()
+    input=cStringIO.StringIO(content)
+    input.seek(0)
+    data = list(csv.reader(input, quotechar=csv_data['del'] or '"', delimiter=csv_data['sep']))[int(csv_data['skip']):]
+    datas = []
+    for line in data:
+        if not line:
+            continue
+        datas.append(map(lambda x:x.decode(csv_data['combo']).encode('utf-8'), line))
+    if not datas:
+        common.warning(_('The file is empty !'), _('Importation !'), parent=parent)
+        return False
+    try:
+        res = rpc.session.rpc_exec_auth('/object', 'execute', model, 'import_data', f, datas, 'init', '', False, context)
+    except Exception, e:
+        common.warning(str(e), _('XML-RPC error !'), parent=parent)
+        return False
+    result = res[0]
+    if result>=0:
+        if result == 1:
+            common.message(_('Imported one object !'))
+        else:
+            common.message(_('Imported %d objects !') % (result,))
+    else:
+        d = ''
+        for key,val in res[1].items():
+            d+= ('\t%s: %s\n' % (str(key),str(val)))
+        error = _(u'Error trying to import this record:\n%s\nError Message:\n%s\n\n%s') % (d,res[2],res[3])
+        common.message_box(_('Importation Error !'), unicode(error))
+    return True
+
+class win_import(object):
+    def __init__(self, model, fields, preload = [], parent=None, local_context=None):
+        self.glade = glade.XML(common.terp_path("openerp.glade"), 'win_import',
+                gettext.textdomain())
+        self.glade.get_widget('import_csv_combo').set_active(0)
+        self.win = self.glade.get_widget('win_import')
+        self.model = model
+        self.fields_data = {}
+        self.invert = False
+        self.context = local_context or {}
+        if parent is None:
+            parent = service.LocalService('gui.main').window
+        self.win.set_transient_for(parent)
+        self.win.set_icon(common.OPENERP_ICON)
+        self.parent = parent
+        self.autodetect_btn = self.glade.get_widget('button_autodetect')
+        self.filechooser = self.glade.get_widget('import_csv_file')
+        self.filechooser.set_current_folder(
+                options.options['client.default_path'])
+        self.filechooser.connect('selection-changed',self.file_changed)
+        self.view1 = gtk.TreeView()
+        self.view1.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+        self.glade.get_widget('import_vp_left').add(self.view1)
+        self.view2 = gtk.TreeView()
+        self.view2.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+        self.glade.get_widget('import_vp_right').add(self.view2)
+        self.view1.set_headers_visible(False)
+        self.view2.set_headers_visible(False)
+
+        cell = gtk.CellRendererText()
+        column = gtk.TreeViewColumn(_('Field name'), cell, text=0, background=2)
+        self.view1.append_column(column)
+
+        cell = gtk.CellRendererText()
+        column = gtk.TreeViewColumn(_('Field name'), cell, text=0)
+        self.view2.append_column(column)
+
+        self.model1 = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
+        self.model2 = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
+
+        for f in preload:
+            self.model2.set(self.model2.append(), 0, f[1], 1, f[0])
+
+        self.fields = {}
+        self.fields_invert = {}
+        def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2):
+            fields_order = fields.keys()
+            fields_order.sort(lambda x,y: -cmp(fields[x].get('string', ''), fields[y].get('string', '')))
+            for field in fields_order:
+                if (fields[field].get('type','') not in ('reference',)) \
+                        and (not fields[field].get('readonly', False) \
+                        or not dict(fields[field].get('states', {}).get(
+                            'draft', [('readonly', True)])).get('readonly', True)\
+                        or not dict(fields[field].get('states', {}).get(
+                            field, [('readonly', True)])).get('readonly', True)):
+                    self.fields_data[prefix_node+field] = fields[field]
+                    st_name = prefix_value+fields[field]['string'] or field
+                    node = self.model1.insert(prefix, 0, [st_name, prefix_node+field,
+                        (fields[field].get('required', False) and '#ddddff') or 'white'])
+                    self.fields[prefix_node+field] = st_name
+                    self.fields_invert[st_name] = prefix_node+field
+                    if fields[field].get('type','') == 'one2many' and level>0:
+                        fields2 = rpc.session.rpc_exec_auth('/object', 'execute', fields[field]['relation'], 'fields_get', False, rpc.session.context)
+                        model_populate(fields2, prefix_node+field+'/', node, st_name+'/', level-1)
+                    if fields[field].get('relation',False) and level>0:
+                        #self.fields[field+':id'] = fields[field]['string']
+                        #self.fields_invert[fields[field]['string']] = field+':id'
+                        model_populate({'/id':{'string':'ID'},'.id':{'string':_('Database ID')}}, \
+                                       prefix_node+field, node, st_name+'/', level-1)
+        fields.update({'id':{'string':'ID'},'.id':{'string':_('Database ID')}})
+        model_populate(fields)
+
+        #for f in fields:
+        #   self.model1.set(self.model1.append(), 1, f, 0, fields[f].get('string', 'unknown'))
+
+        self.view1.set_model(self.model1)
+        self.view2.set_model(self.model2)
+        self.view1.show_all()
+        self.view2.show_all()
+
+        self.glade.signal_connect('on_but_unselect_all_clicked', self.sig_unsel_all)
+        self.glade.signal_connect('on_but_select_all_clicked', self.sig_sel_all)
+        self.glade.signal_connect('on_but_select_clicked', self.sig_sel)
+        self.glade.signal_connect('on_but_unselect_clicked', self.sig_unsel)
+        self.glade.signal_connect('on_but_autodetect_clicked', self.sig_autodetect)
+        
+    def file_changed(self, widget=None):
+        fname= self.filechooser.get_filename()
+        if not fname:
+            self.autodetect_btn.set_sensitive(False)
+        else:
+            self.autodetect_btn.set_sensitive(True)
+          
+    def sig_autodetect(self, widget=None):
+        fname= self.filechooser.get_filename()
+        if not fname:
+            common.message('You must select an import file first !')
+            return True
+        csvsep= self.glade.get_widget('import_csv_sep').get_text()
+        csvdel= self.glade.get_widget('import_csv_del').get_text()
+        csvcode= self.glade.get_widget('import_csv_combo').get_active_text() or 'UTF-8'
+
+        self.glade.get_widget('import_csv_skip').set_value(1)
+        try:
+            data = csv.reader(file(fname), quotechar=csvdel or '"', delimiter=csvsep)
+        except:
+            common.warning(_('Error opening .CSV file'), _('Input Error.'), parent=self.win)
+            return True
+        self.sig_unsel_all()
+        word=''
+        try:
+            for line in data:
+                for word in line:
+                    word = word.decode(csvcode)
+                    if not csvcode.lower() == 'utf-8':
+                        word = word.encode('utf-8')
+                    if (word in self.fields):
+                        num = self.model2.append()
+                        self.model2.set(num, 0, self.fields[word], 1, word)
+                    elif word in self.fields_invert:
+                        self.invert = True
+                        num = self.model2.append()
+                        self.model2.set(num, 0, word, 1, word)
+                    else:
+                        raise Exception(_("You cannot import this field %s, because we cannot auto-detect it"))
+                break
+        except:
+            common.warning(_('Error processing your first line of the file.\nField %s is unknown !') % (word,), _('Import Error.'), parent=self.win)
+        return True
+
+    def sig_sel_all(self, widget=None):
+        self.model2.clear()
+        for field in self.fields.keys():
+            self.model2.set(self.model2.append(), 0, self.fields[field], 1, field)
+
+    def sig_sel(self, widget=None):
+        sel = self.view1.get_selection()
+        sel.selected_foreach(self._sig_sel_add)
+
+    def _sig_sel_add(self, store, path, iter):
+        num = self.model2.append()
+        self.model2.set(num, 0, store.get_value(iter,0), 1, store.get_value(iter,1))
+
+    def sig_unsel(self, widget=None):
+        def _sig_sel_del(store, path, iter):
+            store.remove(iter)
+        (store,paths) = self.view2.get_selection().get_selected_rows()
+        for p in paths:
+            store.remove(store.get_iter(p))
+
+    def sig_unsel_all(self, widget=None):
+        self.model2.clear()
+
+    def go(self):
+        while True:
+            button = self.win.run()
+            if button == gtk.RESPONSE_OK:
+                if not len(self.model2):
+                    common.warning(_("You have not selected any fields to import"), parent=self.win)
+                    continue
+
+                fields = []
+                fields2 = []
+                iter = self.model2.get_iter_root()
+                while iter:
+                    fields.append(self.model2.get_value(iter, 1))
+                    fields2.append(self.model2.get_value(iter, 0))
+                    iter = self.model2.iter_next(iter)
+
+                csv = {
+                    'fname': self.glade.get_widget('import_csv_file').get_filename(),
+                    'sep': self.glade.get_widget('import_csv_sep').get_text(),
+                    'del': self.glade.get_widget('import_csv_del').get_text(),
+                    'skip': self.glade.get_widget('import_csv_skip').get_value(),
+                    'combo': self.glade.get_widget('import_csv_combo').get_active_text() or 'UTF-8'
+                }
+                self.parent.present()
+                self.win.destroy()
+                if csv['fname']:
+                    if self.invert:
+                        inverted = []
+                        for f in fields:
+                            for key, value in self.fields_invert.items():
+                                if key.encode('utf8') == f:
+                                    inverted.append(value)
+                        return import_csv(csv, inverted, self.model, self.fields_invert, context=self.context, parent=self.win)
+                    else:
+                        return import_csv(csv, fields, self.model, self.fields, context=self.context, parent=self.win)
+                return False
+            else:
+                self.parent.present()
+                self.win.destroy()
+                return False
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/win_list.py'
--- bin/modules/gui/window/win_list.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/win_list.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import gtk
+from gtk import glade
+import gobject
+import gettext
+
+
+#from view_tree import parse
+import rpc
+
+
+fields_list_type = {
+    'checkbox': gobject.TYPE_BOOLEAN,
+    'integer': gobject.TYPE_INT,
+    'float': gobject.TYPE_FLOAT
+}
+
+class win_list(object):
+    def __init__(self, model, sel_multi=True, context={}, search=False):
+        self.sel_multi = sel_multi
+        self.context = context
+        self.context.update(rpc.session.context)
+
+        self.model_name = model
+        view = rpc.session.rpc_exec_auth('/object', 'execute', model, 'fields_view_get', False, 'tree', context)
+        self.view_data = view
+        self.tree = widget.tree(view['arch'], view['fields'], model, sel_multi=sel_multi, search=search)
+        self.tree.context = context
+        self.fields = view['fields']
+        self.widget = self.tree.widget
+        self.view = self.tree.widget
+        self.fields_order = self.tree.fields_order
+
+    def destroy(self):
+        self.tree.destroy()
+        del self.fields_order
+        del self.widget
+        del self.view
+
+    def reload(self, ids):
+        res = rpc.session.rpc_exec_auth('/object', 'execute', self.model_name, 'read', ids, self.fields_order, self.context)
+        self.tree.value = res
+
+    def sel_pos_set(self, num):
+        sel = self.view.get_selection()
+        sel.unselect_all()
+        sel.select_path((num,))
+        self.view.scroll_to_cell((num,))
+
+    def sel_ids_get(self):
+        return self.tree.sel_ids_get()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/win_preference.py'
--- bin/modules/gui/window/win_preference.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/win_preference.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import gettext
+
+import gtk
+from gtk import glade
+
+import rpc
+
+import common
+import win_search
+import copy
+import service
+from widget.screen import Screen
+
+import form
+
+import options
+import translate
+
+
+class win_preference(object):
+    def __init__(self, parent=None):
+        self.glade = glade.XML(common.terp_path("openerp.glade"),'win_preference', gettext.textdomain())
+        self.win = self.glade.get_widget('win_preference')
+        self.win.set_icon(common.OPENERP_ICON)
+        if not parent:
+            parent = service.LocalService('gui.main').window
+        self.win.set_transient_for(parent)
+        self.parent = parent
+
+        action_id = rpc.session.rpc_exec_auth('/object', 'execute', 'res.users', 'action_get', {})
+        action = rpc.session.rpc_exec_auth('/object', 'execute', 'ir.actions.act_window', 'read', [action_id], False, rpc.session.context)[0]
+
+        view_ids = []
+        if action.get('views', []):
+            view_ids = [x[0] for x in action['views']]
+        elif action.get('view_id', False):
+            view_ids = [action['view_id'][0]]
+
+        self.screen = Screen('res.users', view_type=[], window=parent)
+        self.screen.add_view_id(view_ids[0], 'form', display=True)
+        self.screen.load([rpc.session.uid])
+        self.screen.display(rpc.session.uid)
+
+        vbox = self.glade.get_widget('preference_vbox')
+        vbox.pack_start(self.screen.widget)
+
+        self.win.set_title(_('Preferences'))
+        self.win.show_all()
+
+    def run(self, datas={}):
+        lang = rpc.session.context.get('lang', 'en_US')
+        end = False
+        while not end:
+            res = self.win.run()
+            end = (res != gtk.RESPONSE_OK) or self.screen.current_model.validate()
+            if not end:
+                self.screen.display()
+                self.screen.current_view.set_cursor()
+        if res == gtk.RESPONSE_OK:
+            values = self.screen.get()
+            rpc.session.rpc_exec_auth('/object', 'execute', 'res.users', 'write', [rpc.session.uid], values)
+            rpc.session.context_reload()
+            new_lang = rpc.session.context.get('lang', 'en_US')
+            if lang != new_lang:
+                common.message(_("The default language of the interface has been modified, do not forget to restart " \
+                                 "the client to have the interface in your language"),
+                               _("Default language modified !"), parent=self.win)
+        self.parent.present()
+        self.win.destroy()
+        return True
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/modules/gui/window/win_search.py'
--- bin/modules/gui/window/win_search.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/win_search.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,210 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import gtk
+from gtk import glade
+from copy import deepcopy
+import gobject
+import gettext
+import common
+import service
+
+import rpc
+
+from widget.screen import Screen
+import widget_search
+
+fields_list_type = {
+    'checkbox': gobject.TYPE_BOOLEAN
+}
+
+class dialog(object):
+    def __init__(self, model, domain=None, context=None, window=None, target=False):
+        if domain is None:
+            domain = []
+        if context is None:
+            context = {}
+
+        if not window:
+            window = service.LocalService('gui.main').window
+
+        self.dia = gtk.Dialog(_('OpenERP - Link'), window,
+                gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
+        self.window = window
+        if not target:
+            self.dia.set_property('default-width', 760)
+            self.dia.set_property('default-height', 500)
+            self.dia.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
+            self.dia.set_icon(common.OPENERP_ICON)
+
+            self.accel_group = gtk.AccelGroup()
+            self.dia.add_accel_group(self.accel_group)
+
+            self.but_cancel = self.dia.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
+            self.but_cancel.add_accelerator('clicked', self.accel_group, gtk.keysyms.Escape, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
+
+            self.but_ok = self.dia.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
+            self.but_ok.add_accelerator('clicked', self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
+
+        scroll = gtk.ScrolledWindow()
+        scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scroll.set_placement(gtk.CORNER_TOP_LEFT)
+        scroll.set_shadow_type(gtk.SHADOW_NONE)
+        self.dia.vbox.pack_start(scroll, expand=True, fill=True)
+
+        vp = gtk.Viewport()
+        vp.set_shadow_type(gtk.SHADOW_NONE)
+        scroll.add(vp)
+        self.screen = Screen(model, view_ids=None, domain=domain, context=context, window=self.dia, view_type=['form'])
+        self.screen.new()
+        vp.add(self.screen.widget)
+
+        x,y = self.screen.screen_container.size_get()
+        width, height = window.get_size()
+        vp.set_size_request(min(width - 20, x + 20),min(height - 60, y + 25))
+        self.dia.show_all()
+        self.screen.display()
+
+    def run(self, datas={}):
+        while True:
+            try:
+                res = self.dia.run()
+                if res == gtk.RESPONSE_OK:
+                    if self.screen.current_model.validate() and self.screen.save_current():
+                        return self.screen.current_model.id
+                    else:
+                        self.screen.display()
+                        self.screen.current_view.set_cursor()
+                else:
+                    break
+            except Exception:
+                # Passing all exceptions, most preferably the one of sql_constraint
+                pass
+        return False
+
+    def destroy(self):
+        self.window.present()
+        self.dia.destroy()
+        self.screen.destroy()
+
+
+class win_search(object):
+    def __init__(self, model, sel_multi=True, ids=[], context={}, domain = [], parent=None):
+        self.model = model
+        self.sel_multi = sel_multi
+        self.ids = ids
+        self.glade = glade.XML(common.terp_path("openerp.glade"),'win_search',gettext.textdomain())
+        self.win = self.glade.get_widget('win_search')
+        self.win.set_icon(common.OPENERP_ICON)
+        if not parent:
+            parent = service.LocalService('gui.main').window
+        self.parent = parent
+        self.win.set_transient_for(parent)
+        self.screen = Screen(model, view_type=['tree'], show_search=True, domain=domain,
+                             context=context, parent=self.win, win_search=True)
+        self.view = self.screen.current_view
+        if self.screen.filter_widget.focusable:
+            self.screen.filter_widget.focusable.grab_focus()
+        self.title = _('OpenERP Search: %s') % self.screen.name
+        self.title_results = _('OpenERP Search: %s (%%d result(s))') % (self.screen.name,)
+        self.win.set_title(self.title)
+        self.view.unset_editable()
+        sel = self.view.widget_tree.get_selection()
+        if not sel_multi:
+            sel.set_mode('single')
+        else:
+            sel.set_mode(gtk.SELECTION_MULTIPLE)
+        self.view.widget_tree.connect('row_activated', self.sig_activate)
+        self.view.widget_tree.connect('button_press_event', self.sig_button)
+        self.screen.win_search_callback = self.update_title
+        vp = gtk.Viewport()
+        vp.set_shadow_type(gtk.SHADOW_NONE)
+        vp.add(self.screen.widget)
+        vp.show()
+        self.sw = gtk.ScrolledWindow()
+        self.sw.set_shadow_type(gtk.SHADOW_NONE)
+        self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.sw.add(vp)
+        self.sw.show()
+        self.wid = self.glade.get_widget('win_search_vbox')
+        self.wid.pack_start(self.sw)
+        self.wid.show_all()
+
+    def sig_activate(self, treeview, path, column, *args):
+        self.view.widget_tree.emit_stop_by_name('row_activated')
+        if not self.sel_multi:
+            self.win.response(gtk.RESPONSE_OK)
+        return False
+
+    def sig_button(self, view, event):
+        if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
+            self.win.response(gtk.RESPONSE_OK)
+        return False
+
+    def find(self, widget=None, *args):
+        self.screen.search_filter()
+        self.update_title()
+
+    def update_title(self):
+        self.ids = self.screen.win_search_ids
+        self.reload()
+        self.win.set_title(self.title_results % len(self.ids))
+
+    def reload(self):
+        sel = self.view.widget_tree.get_selection()
+        if sel.get_mode() == gtk.SELECTION_MULTIPLE:
+            sel.select_all()
+
+
+    def sel_ids_get(self):
+        return self.screen.sel_ids_get()
+
+    def destroy(self):
+        self.parent.present()
+        self.win.destroy()
+
+    def go(self):
+        ## This is if the user has set some filters by default with search_default_XXX
+        if self.ids:
+            self.screen.win_search_domain += [('id','in', self.ids)]
+            self.find()
+        else:
+            self.screen.update_scroll()
+        end = False
+        while not end:
+            button = self.win.run()
+            if button == gtk.RESPONSE_OK:
+                res = self.sel_ids_get() or self.ids
+                end = True
+            elif button== gtk.RESPONSE_APPLY:
+                self.find()
+            else:
+                res = None
+                end = True
+        self.destroy()
+        if button == gtk.RESPONSE_ACCEPT:
+            dia = dialog(self.model, window=self.parent, domain=self.screen.domain_init ,context=self.screen.context)
+            id = dia.run()
+            res = id and [id] or None
+            dia.destroy()
+        return res
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'bin/modules/gui/window/win_selection.py'
--- bin/modules/gui/window/win_selection.py	1970-01-01 00:00:00 +0000
+++ bin/modules/gui/window/win_selection.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import gtk
+from gtk import glade
+import gettext
+import common
+
+from view_tree import parse
+import gobject
+import rpc
+
+fields_list_type = {
+    'checkbox': gobject.TYPE_BOOLEAN
+}
+
+#
+# Should be replaced by win_browse
+#
+
+class win_selection_class(object):
+    def __init__(self, ids, model, view=None):
+        self.glade = glade.XML(common.terp_path("openerp.glade"), "win_selection",gettext.textdomain())
+        self.win = self.glade.get_widget('win_selection')
+        self.win.set_icon(common.OPENERP_ICON)
+        self.parent = service.LocalService('gui.main').window
+        self.win.set_transient_for(parent)
+
+        self.ids = ids
+        self.view = self.glade.get_widget('win_sel_tree')
+        self.view.get_selection().set_mode('single')
+        if view==None:
+            fields = { 'name': {'type': 'char', 'string':_('Name')} }
+            xml = '''<?xml version="1.0"?>
+<tree string="%s">
+    <field name="name" string="%s"></field>
+</tree>''' % (_('Ressource Name'), _('Names'))
+        else:
+            fields = None
+            xml = None
+
+        p = parse.parse(fields)
+        p.parse(xml, self.view)
+        self.view.set_expander_column(self.view.get_column(1))
+        self.fields_order = p.fields_order
+
+        types=[ gobject.TYPE_STRING ]
+        for x in self.fields_order:
+            types.append( fields_list_type.get(fields[x]['type'], gobject.TYPE_STRING))
+        self.model = gtk.ListStore(*types)
+
+        if view==None:
+            res_ids = rpc.session.rpc_exec_auth('/object', 'execute', model, 'name_get', self.ids, rpc.session.context)
+            for res in res_ids:
+                num = self.model.append()
+                self.model.set(num, 0, res[0], 1, res[1])
+        else:
+            pass # Todo
+
+        self.view.set_model(self.model)
+        self.view.show_all()
+
+    def id_name_get(self):
+        id = self.value_get(0)
+        if id:
+            return (id, self.value_get(1))
+        return None
+
+    def value_get(self, col):
+        sel = self.view.get_selection().get_selected()
+        if sel==None:
+            return None
+        (model, iter) = sel
+        return model.get_value(iter, col)
+
+    def go(self):
+        button = self.win.run()
+        if button==gtk.RESPONSE_OK:
+            res = self.id_name_get()
+        else:
+            res=None
+        self.parent.present()
+        self.win.destroy()
+        return res
+
+def win_selection_h(from_resource, ids, model, view=None):
+    return win_selection(ids, model, view)
+
+def win_selection(ids, model, view=None):
+    if len(ids)==1:
+        return rpc.session.rpc_exec_auth('/object', 'execute', model, 'name_get', ids, rpc.session.context)[0]
+    win = win_selection_class(ids, model, view)
+    res = win.go()
+    return res
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added directory 'bin/modules/spool'
=== added file 'bin/modules/spool/__init__.py'
--- bin/modules/spool/__init__.py	1970-01-01 00:00:00 +0000
+++ bin/modules/spool/__init__.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import service
+
+class spool(service.Service):
+    def __init__(self, name='spool'):
+        service.Service.__init__(self, name, '*')
+        self.obj_sub = {}
+        self.report = {}
+
+    def publish(self, name, obj, datas, trigger=True):
+        if name not in self.report:
+            self.report[name]=[]
+        self.report[name].append((obj, datas))
+        if trigger:
+            return self.trigger(name)
+        return 0
+
+    def subscribe(self, name, method, datas={}):
+        if name not in self.obj_sub:
+            self.obj_sub[name]=[]
+        self.obj_sub[name].append( (method, datas) )
+
+    def trigger(self, name):
+        nbr = 0
+        while len(self.report[name]):
+            (obj, datas) = self.report[name].pop()
+            if name in self.obj_sub:
+                for i in self.obj_sub[name]:
+                    new_datas = datas.copy()
+                    new_datas.update(i[1])
+                    i[0](obj, new_datas)
+                    nbr +=1
+        return nbr
+spool()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/observator.py'
--- bin/observator.py	1970-01-01 00:00:00 +0000
+++ bin/observator.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+try:
+    set
+except NameError:
+    from sets import Set as set
+
+
+class ObservatorRegistry(object):
+    def __new__(cls):
+        if not hasattr(cls, '_inst'):
+            cls._inst = object.__new__(cls)
+        return cls._inst
+
+    def __init__(self):
+        self._observables = {}
+        self._receivers = {}
+
+    def add_observable(self, oid, obj):
+        self._observables[oid] = obj
+
+    def add_receiver(self, signal, callable):
+        if signal not in self._receivers:
+            self._receivers[signal] = []
+        self._receivers[signal].append(callable)
+    
+    def remove_receiver(self, signal, callable):
+        self._receivers[signal].remove(callable)
+
+    def warn(self, *args):
+        for receiver in self._receivers.get(args[0], []):
+            receiver(*args[1:])
+
+oregistry = ObservatorRegistry()
+
+
+class Observable(object):
+    def __init__(self):
+        oregistry.add_observable(id(self), self)
+
+    def warn(self, *args):
+        oregistry.warn(args[0], self, *args[1:])
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/openerp-client.py'
--- bin/openerp-client.py	1970-01-01 00:00:00 +0000
+++ bin/openerp-client.py	2011-04-25 08:38:27 +0000
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+"""
+OpenERP - Client
+OpenERP is an ERP+CRM program for small and medium businesses.
+
+The whole source code is distributed under the terms of the
+GNU Public Licence.
+
+(c) 2003-TODAY, Fabien Pinckaers - Tiny sprl
+"""
+
+
+import sys
+import os
+import release
+__author__ = release.author
+__version__ = release.version
+
+import __builtin__
+__builtin__.__dict__['openerp_version'] = __version__
+
+import logging
+arguments = {}
+if sys.platform == 'win32':
+    arguments['filename'] = os.path.join(os.environ['USERPROFILE'], 'openerp-client.log')
+
+logging.basicConfig(**arguments)
+
+from distutils.sysconfig import get_python_lib
+terp_path = os.path.join(get_python_lib(), 'openerp-client')
+sys.path.append(terp_path)
+
+if os.name == 'nt':
+    sys.path.insert(0, os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), 'GTK\\bin'))
+    sys.path.insert(0, os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), 'GTK\\lib'))
+    os.environ['PATH'] = os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), 'GTK\\lib') + ";" + os.environ['PATH']
+    os.environ['PATH'] = os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), 'GTK\\bin') + ";" + os.environ['PATH']
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gtk.glade
+
+#gtk.gdk.threads_init() # causes the GTK client to block everything.
+
+import locale
+import gettext
+
+import atk
+import gtk._gtk
+import pango
+
+if os.name == 'nt':
+    sys.path.insert(0, os.path.join(os.getcwd(), os.path.dirname(sys.argv[0])))
+    os.environ['PATH'] = os.path.join(os.getcwd(), os.path.dirname(sys.argv[0])) + ";" + os.environ['PATH']
+
+import translate
+translate.setlang()
+
+import options
+
+# On first run, client won't have a language option,
+# so try with the LANG environ, or fallback to english
+client_lang = options.options['client.lang']
+if not client_lang:
+    client_lang = os.environ.get('LANG', '').split('.')[0]
+
+translate.setlang(client_lang)
+
+
+# add new log levels below DEBUG
+logging.DEBUG_RPC = logging.DEBUG - 1
+logging.addLevelName(logging.DEBUG_RPC, 'DEBUG_RPC')
+logging.Logger.debug_rpc = lambda self, msg, *args, **kwargs: self.log(logging.DEBUG_RPC, msg, *args, **kwargs)
+
+logging.DEBUG_RPC_ANSWER = logging.DEBUG - 2
+logging.addLevelName(logging.DEBUG_RPC_ANSWER, 'DEBUG_RPC_ANSWER')
+logging.Logger.debug_rpc_answer = lambda self, msg, *args, **kwargs: self.log(logging.DEBUG_RPC_ANSWER, msg, *args, **kwargs)
+
+logging.getLogger().setLevel(getattr(logging, options.options['logging.level'].upper()))
+
+
+
+import modules
+import common
+
+items = [('terp-flag', '_Translation', gtk.gdk.CONTROL_MASK, ord('t'), '')]
+gtk.stock_add (items)
+
+factory = gtk.IconFactory ()
+factory.add_default ()
+
+pix_file = os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), 'icons')
+if not os.path.isdir(pix_file):
+    pix_file = os.path.join(options.options['path.pixmaps'],'icons')
+
+for fname in os.listdir(pix_file):
+    ffname = os.path.join(pix_file,fname)
+    if not os.path.isfile(ffname):
+        continue
+    iname = os.path.splitext(fname)[0]
+    try:
+        pixbuf = gtk.gdk.pixbuf_new_from_file(ffname)
+    except:
+        pixbuf = None
+        continue
+    if pixbuf:
+        icon_set = gtk.IconSet (pixbuf)
+        factory.add('terp-'+iname, icon_set)
+
+try:
+    win = modules.gui.main.terp_main()
+    if options.options.rcexist:
+        win.sig_login()
+    if os.name == 'nt':
+        from tools.win32 import get_systemfont_style
+        gtk.rc_parse_string(get_systemfont_style())
+    gtk.main()
+except KeyboardInterrupt, e:
+    log = logging.getLogger('common')
+    log.info(_('Closing OpenERP, KeyboardInterrupt'))
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+

=== added file 'bin/openerp.glade'
--- bin/openerp.glade	1970-01-01 00:00:00 +0000
+++ bin/openerp.glade	2011-04-25 08:38:27 +0000
@@ -0,0 +1,8424 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <widget class="GtkDialog" id="win_login">
+    <property name="title" translatable="yes">OpenERP - Login</property>
+    <property name="modal">True</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkVBox" id="vbox30">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkImage" id="image_tinyerp">
+                <property name="width_request">500</property>
+                <property name="height_request">71</property>
+                <property name="visible">True</property>
+                <property name="stock">gtk-missing-image</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkTable" id="table1">
+                <property name="visible">True</property>
+                <property name="border_width">10</property>
+                <property name="n_rows">4</property>
+                <property name="n_columns">3</property>
+                <property name="column_spacing">3</property>
+                <property name="row_spacing">3</property>
+                <child>
+                  <widget class="GtkEntry" id="ent_passwd">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="has_focus">True</property>
+                    <property name="visibility">False</property>
+                    <property name="activates_default">True</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="ent_login">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="activates_default">True</property>
+                    <property name="text">admin</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label2">
+                    <property name="width_request">117</property>
+                    <property name="visible">True</property>
+                    <property name="xalign">1</property>
+                    <property name="xpad">3</property>
+                    <property name="ypad">3</property>
+                    <property name="label" translatable="yes">Server:</property>
+                  </widget>
+                  <packing>
+                    <property name="x_options"></property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="ent_server">
+                    <property name="visible">True</property>
+                    <property name="sensitive">False</property>
+                    <property name="editable">False</property>
+                    <property name="activates_default">True</property>
+                    <property name="width_chars">16</property>
+                    <property name="text">localhost</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="but_server">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Change</property>
+                    <property name="use_underline">True</property>
+                    <property name="response_id">0</property>
+                    <signal name="activate" handler="on_but_server_activate"/>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="x_options"></property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label266">
+                    <property name="visible">True</property>
+                    <property name="xalign">1</property>
+                    <property name="xpad">3</property>
+                    <property name="ypad">3</property>
+                    <property name="label" translatable="yes">Database:</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label4">
+                    <property name="visible">True</property>
+                    <property name="xalign">1</property>
+                    <property name="xpad">3</property>
+                    <property name="ypad">3</property>
+                    <property name="label" translatable="yes">User:</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label5">
+                    <property name="visible">True</property>
+                    <property name="xalign">1</property>
+                    <property name="xpad">3</property>
+                    <property name="ypad">3</property>
+                    <property name="label" translatable="yes">Password:</property>
+                    <property name="justify">GTK_JUSTIFY_RIGHT</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkVBox" id="vbox57">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkComboBox" id="combo_db">
+                        <property name="visible">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="combo_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.0099999997764825821</property>
+                        <property name="yalign">1</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                        <widget class="GtkEntry" id="ent_db">
+                            <property name="visible">True</property>
+                        </widget>
+                        <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                        </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="okbutton1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="button_connect">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="label">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-5</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkWindow" id="win_main">
+    <property name="width_request">600</property>
+    <property name="height_request">400</property>
+    <property name="visible">True</property>
+    <property name="title">OpenERP</property>
+    <property name="window_position">GTK_WIN_POS_CENTER</property>
+    <property name="default_width">920</property>
+    <property name="default_height">780</property>
+    <signal name="destroy_event" handler="on_win_main_destroy_event"/>
+    <child>
+      <widget class="GtkVBox" id="vbox_main">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkAlignment" id="alignment52">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkMenuBar" id="menubar1">
+                <property name="visible">True</property>
+                <child>
+                  <widget class="GtkMenuItem" id="menuitem1">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_File</property>
+                    <property name="use_underline">True</property>
+                    <child>
+                      <widget class="GtkMenu" id="menuitem1_menu">
+                        <child>
+                          <widget class="GtkImageMenuItem" id="login">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Connect...</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_login_activate"/>
+                            <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4209">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-connect</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="logout">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Disconnect</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_logout_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4210">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-disconnect</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="separator9">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="Databases">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Databases</property>
+                            <property name="use_underline">True</property>
+                            <child>
+                              <widget class="GtkMenu" id="Databases_menu">
+                                <child>
+                                  <widget class="GtkImageMenuItem" id="db_new">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">_New database</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_db_new_activate"/>
+                                    <child internal-child="image">
+                                      <widget class="GtkImage" id="image4212">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-new</property>
+                                        <property name="icon_size">1</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkImageMenuItem" id="db_restore">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">_Restore database</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_db_restore_activate"/>
+                                    <child internal-child="image">
+                                      <widget class="GtkImage" id="image4213">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-open</property>
+                                        <property name="icon_size">1</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkImageMenuItem" id="db_backup">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">_Backup database</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_db_backup_activate"/>
+                                    <child internal-child="image">
+                                      <widget class="GtkImage" id="image4214">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-save-as</property>
+                                        <property name="icon_size">1</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkImageMenuItem" id="db_drop">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Dro_p database</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_db_drop_activate"/>
+                                    <child internal-child="image">
+                                      <widget class="GtkImage" id="image4215">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-delete</property>
+                                        <property name="icon_size">1</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkSeparatorMenuItem" id="separator77">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkImageMenuItem" id="db_migrate_retrieve_script">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">_Download Migrations Code</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_db_migrate_retrieve_script_activate"/>
+                                    <child internal-child="image">
+                                      <widget class="GtkImage" id="image_retrieve_script">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-sort-descending</property>
+                                        <property name="icon_size">1</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkImageMenuItem" id="db_migrate">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">_Migrate Database(s)</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_db_migrate_activate"/>
+                                    <child internal-child="image">
+                                      <widget class="GtkImage" id="image_migrate">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-refresh</property>
+                                        <property name="icon_size">1</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkSeparatorMenuItem" id="separator7">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkImageMenuItem" id="admin_password">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Administrator Password</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_admin_password_activate"/>
+                                    <child internal-child="image">
+                                      <widget class="GtkImage" id="image4216">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-dialog-question</property>
+                                        <property name="icon_size">1</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                              </widget>
+                            </child>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4211">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-network</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="separator2">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="quitter1">
+                            <property name="visible">True</property>
+                            <property name="label">gtk-quit</property>
+                            <property name="use_underline">True</property>
+                            <property name="use_stock">True</property>
+                            <signal name="activate" handler="on_quit_activate"/>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkMenuItem" id="user">
+                    <property name="visible">True</property>
+                    <property name="sensitive">False</property>
+                    <property name="label" translatable="yes">_User</property>
+                    <property name="use_underline">True</property>
+                    <signal name="activate" handler="on_requests_activate"/>
+                    <child>
+                      <widget class="GtkMenu" id="user_menu">
+                        <child>
+                          <widget class="GtkImageMenuItem" id="preference">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Preferences</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_preference_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4217">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-preferences</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="change_passwd">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Change password</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_change_passwd_activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="item2">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="send_request">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Send a request</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_send_request_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4218">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-new</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="read_requests">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Read my requests</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_read_requests_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4219">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-find</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="séparateur12">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="request_wait">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Waiting Requests</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_request_wait_activate"/>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkMenuItem" id="form">
+                    <property name="visible">True</property>
+                    <property name="sensitive">False</property>
+                    <property name="label" translatable="yes">For_m</property>
+                    <property name="use_underline">True</property>
+                    <signal name="activate" handler="on_form_activate"/>
+                    <child>
+                      <widget class="GtkMenu" id="form_menu">
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_new">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_New</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_new_activate"/>
+                            <accelerator key="N" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4220">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-new</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_save">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Save</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_save_activate"/>
+                            <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4221">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-save</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="copy">
+                            <property name="visible">True</property>
+                            <property name="tooltip" translatable="yes">Copy this resource</property>
+                            <property name="label" translatable="yes">_Duplicate</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_copy_activate"/>
+                            <accelerator key="D" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4222">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-copy</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_delete">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Delete</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_del_activate"/>
+                            <accelerator key="D" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4223">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-delete</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="séparateur1">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_search">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Find</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_search_activate"/>
+                            <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4224">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-find</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_next">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Ne_xt</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_next_activate"/>
+                            <accelerator key="Page_Down" modifiers="" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4225">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-go-forward</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_previous">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Pre_vious</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_previous_activate"/>
+                            <accelerator key="Page_Up" modifiers="" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4226">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-go-back</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="switch_lf">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Switch to list/form</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_but_switch_clicked"/>
+                            <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4227">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-justify-fill</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="win_new">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Menu</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_win_new_activate"/>
+                            <accelerator key="T" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4228">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-index</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="separator8">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="win_home">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_New Home Tab</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_win_home_activate"/>
+                            <accelerator key="H" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4229">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-home</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="win_close">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Close Tab</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_win_close_activate"/>
+                            <accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4230">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-close</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="win_prev">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Previous Tab</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_win_prev_activate"/>
+                            <accelerator key="Page_Up" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="win_next">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Next Tab</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_win_next_activate"/>
+                            <accelerator key="Page_Down" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="séparateur9">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="form_log">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">View _logs</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_log_activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="goto_id">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Go to resource ID...</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_goto_id_activate"/>
+                            <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="separator6">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_open">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Open</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_open_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4231">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-open</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_reload">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Reloa_d / Undo</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_reload_activate"/>
+                            <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4232">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-redo</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="séparateur5">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="form_repeat">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Repeat latest _action</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_repeat_activate"/>
+                            <accelerator key="Y" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="séparateur7">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_print">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Preview in PDF</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_print_activate"/>
+                            <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4233">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-print-preview</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_print_html">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Previe_w in editor</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_print_html_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4234">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-print-preview</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="séparateur8">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="form_save_as">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Expor_t data...</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_save_as_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4235">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-save-as</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="form_import">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">I_mport data...</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_form_import_activate"/>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkMenuItem" id="options">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_Options</property>
+                    <property name="use_underline">True</property>
+                    <child>
+                      <widget class="GtkMenu" id="options_menu">
+                        <child>
+                          <widget class="GtkMenuItem" id="extension_manager">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Extension Manager</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_extension_manager_activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="menubar">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Menubar</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_menubar_activate"/>
+                            <child>
+                              <widget class="GtkMenu" id="menubar_menu">
+                                <child>
+                                  <widget class="GtkRadioMenuItem" id="menubar_both">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Text _and Icons</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_menubar_both_activate"/>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkRadioMenuItem" id="menubar_icons">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">_Icons only</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="group">menubar_both</property>
+                                    <signal name="activate" handler="on_menubar_icons_activate"/>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkRadioMenuItem" id="menubar_text">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">_Text only</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="group">menubar_both</property>
+                                    <signal name="activate" handler="on_menubar_text_activate"/>
+                                  </widget>
+                                </child>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="forms1">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Forms</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_forms1_activate"/>
+                            <child>
+                              <widget class="GtkMenu" id="forms1_menu">
+                                <child>
+                                  <widget class="GtkCheckMenuItem" id="opt_form_toolbar">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Right Toolbar</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_opt_form_toolbar_activate"/>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkMenuItem" id="forms_tab">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Tabs default position</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_forms_tabs_activate"/>
+                                    <child>
+                                      <widget class="GtkMenu" id="forms_tab_menu">
+                                        <child>
+                                          <widget class="GtkRadioMenuItem" id="opt_form_tab_top">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">Top</property>
+                                            <property name="use_underline">True</property>
+                                            <signal name="activate" handler="on_opt_form_tab_top_activate"/>
+                                          </widget>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkRadioMenuItem" id="opt_form_tab_left">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">Left</property>
+                                            <property name="use_underline">True</property>
+                                            <property name="group">opt_form_tab_top</property>
+                                            <signal name="activate" handler="on_opt_form_tab_left_activate"/>
+                                          </widget>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkRadioMenuItem" id="opt_form_tab_right">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">Right</property>
+                                            <property name="use_underline">True</property>
+                                            <property name="group">opt_form_tab_top</property>
+                                            <signal name="activate" handler="on_opt_form_tab_right_activate"/>
+                                          </widget>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkRadioMenuItem" id="opt_form_tab_bottom">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">Bottom</property>
+                                            <property name="use_underline">True</property>
+                                            <property name="group">opt_form_tab_top</property>
+                                            <signal name="activate" handler="on_opt_form_tab_bottom_activate"/>
+                                          </widget>
+                                        </child>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkMenuItem" id="forms_tab_orientation">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Tabs default orientation</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_forms_tabs_orientation_activate"/>
+                                    <child>
+                                      <widget class="GtkMenu" id="forms_tab_orientation_menu">
+                                        <child>
+                                          <widget class="GtkRadioMenuItem" id="opt_form_tab_orientation_0">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">Horizontal</property>
+                                            <property name="use_underline">True</property>
+                                            <signal name="activate" handler="on_opt_form_tab_orientation_horizontal_activate"/>
+                                          </widget>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkRadioMenuItem" id="opt_form_tab_orientation_90">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">Vertical</property>
+                                            <property name="use_underline">True</property>
+                                            <property name="group">opt_form_tab_orientation_0</property>
+                                            <signal name="activate" handler="on_opt_form_tab_orientation_vertical_activate"/>
+                                          </widget>
+                                        </child>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                </child>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="printer">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Print</property>
+                            <property name="use_underline">True</property>
+                            <child>
+                              <widget class="GtkMenu" id="printer_menu">
+                                <child>
+                                  <widget class="GtkCheckMenuItem" id="opt_print_preview">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Previe_w before print</property>
+                                    <property name="use_underline">True</property>
+                                    <signal name="activate" handler="on_opt_print_preview_activate"/>
+                                  </widget>
+                                </child>
+                              </widget>
+                            </child>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4238">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-print</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="separator5">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="opt_save">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Save options</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_opt_save_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4239">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-save</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkMenuItem" id="plugins">
+                    <property name="visible">True</property>
+                    <property name="sensitive">False</property>
+                    <property name="label" translatable="yes">_Plugins</property>
+                    <property name="use_underline">True</property>
+                    <signal name="activate" handler="on_plugins_activate"/>
+                    <child>
+                      <widget class="GtkMenu" id="plugins_menu">
+                        <child>
+                          <widget class="GtkImageMenuItem" id="plugin_execute">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Execute a plugin</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_plugin_execute_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4240">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-network</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkMenuItem" id="shortcut">
+                    <property name="visible">True</property>
+                    <property name="sensitive">False</property>
+                    <property name="label" translatable="yes">_Shortcuts</property>
+                    <property name="use_underline">True</property>
+                    <signal name="activate" handler="on_shortcut_activate"/>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkMenuItem" id="help">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_Help</property>
+                    <property name="use_underline">True</property>
+                    <child>
+                      <widget class="GtkMenu" id="help_menu">
+                      <child>
+                          <widget class="GtkCheckMenuItem" id="opt_debug_mode_tooltip">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Enable Debug Mode Tooltips</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_opt_debug_mode_activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="support">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Support Request</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_support_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4241">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-help</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="séparateur13">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="help_index">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">User _Manual</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_help_index_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4242">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-zoom-fit</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="help_contextual">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_Contextual Help</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_help_contextual_activate"/>
+                            <accelerator key="H" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4243">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-jump-to</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkMenuItem" id="shortcuts">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Keyboard Shortcuts</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_shortcuts_activate"/>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkSeparatorMenuItem" id="séparateur11">
+                            <property name="visible">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="help_licence">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_License</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_help_licence_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4245">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-justify-center</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkImageMenuItem" id="about1">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">_About...</property>
+                            <property name="use_underline">True</property>
+                            <signal name="activate" handler="on_about_activate"/>
+                            <child internal-child="image">
+                              <widget class="GtkImage" id="image4246">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-about</property>
+                                <property name="icon_size">1</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolbar" id="main_toolbar">
+            <property name="visible">True</property>
+            <property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
+            <child>
+              <widget class="GtkToolButton" id="but_new">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Create a new resource</property>
+                <property name="stock_id">gtk-new</property>
+                <signal name="clicked" handler="on_but_new_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_save">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Edit / Save this resource</property>
+                <property name="stock_id">gtk-save</property>
+                <signal name="clicked" handler="on_but_save_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkSeparatorToolItem" id="separatortoolitem5">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_remove">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Delete this resource</property>
+                <property name="stock_id">gtk-delete</property>
+                <signal name="clicked" handler="on_but_remove_clicked"/>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorToolItem" id="separatortoolitem1">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_previous">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Go to previous matched search</property>
+                <property name="label" translatable="yes">Previous</property>
+                <property name="stock_id">gtk-go-back</property>
+                <signal name="clicked" handler="on_but_previous_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_next">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Go to next matched resource</property>
+                <property name="label" translatable="yes">Next</property>
+                <property name="stock_id">gtk-go-forward</property>
+                <signal name="clicked" handler="on_but_next_clicked"/>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorToolItem" id="separatortoolitem888">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkRadioToolButton" id="radio_tree">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">List</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-find</property>
+                <signal name="toggled" handler="on_radio_tree_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioToolButton" id="radio_form">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Form</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-new</property>
+                <property name="group">radio_tree</property>
+                <signal name="clicked" handler="on_radio_form_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioToolButton" id="radio_calendar">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Calendar</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-select-color</property>
+                <property name="group">radio_tree</property>
+                <signal name="clicked" handler="on_radio_calendar_clicked"/>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkRadioToolButton" id="radio_diagram">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Diagram</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-select-color</property>
+                <property name="group">radio_tree</property>
+                <signal name="clicked" handler="on_radio_diagram_clicked"/>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkRadioToolButton" id="radio_graph">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Graph</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-spell-check</property>
+                <property name="group">radio_tree</property>
+                <signal name="clicked" handler="on_radio_graph_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioToolButton" id="radio_gantt">
+                <property name="visible">False</property>
+                <property name="label" translatable="yes">Gantt</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-justify-right</property>
+                <property name="group">radio_tree</property>
+                <signal name="clicked" handler="on_radio_gantt_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkSeparatorToolItem" id="separatortoolitem9">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_print">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Print documents</property>
+                <property name="stock_id">gtk-print</property>
+                <signal name="clicked" handler="on_but_print_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_action">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Launch actions about this resource</property>
+                <property name="label" translatable="yes">Action</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-execute</property>
+                <signal name="clicked" handler="on_but_action_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_attach">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Add an attachment to this resource</property>
+                <property name="label" translatable="yes">Attachment</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-paste</property>
+                <signal name="clicked" handler="on_but_attach_clicked"/>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorToolItem" id="separatortoolitem10">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_menu">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="tooltip" translatable="yes">Menu</property>
+                <property name="label" translatable="yes">Menu</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-index</property>
+                <signal name="clicked" handler="on_but_menu_clicked"/>
+              </widget>
+              <packing>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkSeparatorToolItem" id="separatortoolitem8">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_reload">
+                  <property name="visible">True</property>
+                  <property name="tooltip" translatable="yes">Reload</property>
+                  <property name="label" translatable="yes">Reload</property>
+                  <property name="use_underline">True</property>
+                  <property name="stock_id">gtk-redo</property>
+                  <signal name="clicked" handler="on_form_reload_activate"/>
+              </widget>
+              <packing>
+                  <property name="expand">False</property>
+              </packing>
+            </child>
+            <child>
+                <widget class="GtkSeparatorToolItem" id="separatortoolitem19">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkToolButton" id="but_close">
+                <property name="visible">True</property>
+                <property name="tooltip" translatable="yes">Close this window</property>
+                <property name="stock_id">gtk-close</property>
+                <signal name="clicked" handler="on_but_close_clicked"/>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <widget class="GtkHBox" id="hbox_status_main">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkStatusbar" id="sb_user_server">
+                <property name="width_request">150</property>
+                <property name="visible">True</property>
+                <property name="border_width">2</property>
+                <property name="has_resize_grip">False</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkStatusbar" id="sb_user_name">
+                <property name="width_request">130</property>
+                <property name="visible">True</property>
+                <property name="border_width">2</property>
+                <property name="has_resize_grip">False</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label54">
+                <property name="visible">True</property>
+                <property name="xpad">5</property>
+                <property name="label" translatable="yes">Company:</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkStatusbar" id="sb_company">
+                <property name="width_request">125</property>
+                <property name="visible">True</property>
+                <property name="border_width">2</property>
+                <property name="has_resize_grip">False</property>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label56">
+                <property name="visible">True</property>
+                <property name="xpad">5</property>
+                <property name="label" translatable="yes">Requests:</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkStatusbar" id="sb_requests">
+                <property name="width_request">120</property>
+                <property name="visible">True</property>
+                <property name="border_width">2</property>
+                <property name="has_resize_grip">False</property>
+              </widget>
+              <packing>
+                <property name="position">5</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="req_search_but">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="tooltip" translatable="yes">Read my Requests</property>
+                <property name="relief">GTK_RELIEF_NONE</property>
+                <property name="response_id">0</property>
+                <signal name="clicked" handler="on_read_requests_activate"/>
+                <child>
+                  <widget class="GtkImage" id="image376">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-find</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">6</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="request_new_but">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="tooltip" translatable="yes">Send a new request</property>
+                <property name="relief">GTK_RELIEF_NONE</property>
+                <property name="response_id">0</property>
+                <signal name="clicked" handler="on_send_request_activate"/>
+                <child>
+                  <widget class="GtkImage" id="image377">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-new</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">7</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkImage" id="secure_img">
+                <property name="visible">True</property>
+                <property name="stock">gtk-dialog-authentication</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">6</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkWindow" id="win_tree">
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">OpenERP - Tree Resources</property>
+    <child>
+      <widget class="GtkVBox" id="win_tree_container">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkHPaned" id="hp_tree">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="position">250</property>
+            <child>
+              <widget class="GtkVBox" id="widget_vbox">
+                <property name="visible">True</property>
+                <child>
+                  <widget class="GtkVPaned" id="tree_vpaned">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="position">400</property>
+                    <child>
+                      <widget class="GtkToolbar" id="tree_toolbar">
+                        <property name="visible">True</property>
+                        <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
+                        <property name="toolbar_style">GTK_TOOLBAR_BOTH_HORIZ</property>
+                      </widget>
+                      <packing>
+                        <property name="resize">True</property>
+                        <property name="shrink">True</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkScrolledWindow" id="scrolledwindow3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <child>
+                          <widget class="GtkViewport" id="viewport1">
+                            <property name="visible">True</property>
+                            <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+                            <child>
+                              <widget class="GtkVBox" id="vbox13">
+                                <property name="visible">True</property>
+                                <child>
+                                  <widget class="GtkHBox" id="hbox1">
+                                    <property name="visible">True</property>
+                                    <child>
+                                      <widget class="GtkToolbar" id="toolbar10">
+                                        <property name="visible">True</property>
+                                        <property name="toolbar_style">GTK_TOOLBAR_ICONS</property>
+                                        <property name="show_arrow">False</property>
+                                        <child>
+                                          <widget class="GtkToolButton" id="tbsc">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">Shortcuts</property>
+                                            <property name="use_underline">True</property>
+                                            <signal name="clicked" handler="on_tbsc_clicked"/>
+                                          </widget>
+                                        </child>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkToolbar" id="toolbar1">
+                                        <property name="visible">True</property>
+                                        <property name="toolbar_style">GTK_TOOLBAR_ICONS</property>
+                                        <child>
+                                          <widget class="GtkToolButton" id="button4">
+                                            <property name="visible">True</property>
+                                            <property name="stock_id">gtk-add</property>
+                                            <signal name="clicked" handler="on_but_sc_add_clicked"/>
+                                          </widget>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkToolButton" id="button6">
+                                            <property name="visible">True</property>
+                                            <property name="stock_id">gtk-remove</property>
+                                            <signal name="clicked" handler="on_but_sc_del_clicked"/>
+                                          </widget>
+                                          <packing>
+                                            <property name="homogeneous">True</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkSeparatorToolItem" id="separatortoolitem7">
+                                            <property name="visible">True</property>
+                                          </widget>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkToolButton" id="button7">
+                                            <property name="visible">True</property>
+                                            <property name="stock_id">gtk-goto-bottom</property>
+                                            <signal name="clicked" handler="on_but_expand_collapse_clicked"/>
+                                          </widget>
+                                          <packing>
+                                            <property name="homogeneous">True</property>
+                                          </packing>
+                                        </child>
+                                      </widget>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkTreeView" id="win_tree_sc">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="headers_visible">False</property>
+                                    <property name="reorderable">True</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="resize">True</property>
+                        <property name="shrink">True</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="resize">False</property>
+                <property name="shrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkScrolledWindow" id="main_tree_sw">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </widget>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkWindow" id="win_form">
+    <property name="visible">True</property>
+    <property name="extension_events">GDK_EXTENSION_EVENTS_ALL</property>
+    <property name="title" translatable="yes">OpenERP - Forms</property>
+    <child>
+      <widget class="GtkVBox" id="win_form_container">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <widget class="GtkHBox" id="hbox2">
+            <property name="visible">True</property>
+            <property name="border_width">3</property>
+            <child>
+              <widget class="GtkStatusbar" id="stat_form">
+                <property name="width_request">103</property>
+                <property name="visible">True</property>
+                <property name="border_width">1</property>
+                <property name="has_resize_grip">False</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label37">
+                <property name="visible">True</property>
+                <property name="xpad">10</property>
+                <property name="label" translatable="yes">State:</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="stat_state">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="use_markup">True</property>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="win_about">
+    <property name="width_request">430</property>
+    <property name="height_request">360</property>
+    <property name="title" translatable="yes">OpenERP - About</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkVBox" id="vbox7">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkLabel" id="label36">
+                <property name="visible">True</property>
+                <property name="xpad">5</property>
+                <property name="ypad">8</property>
+                <property name="label" translatable="yes">&lt;b&gt;About OpenERP&lt;/b&gt;
+&lt;i&gt;The most advanced Open Source ERP &amp;amp; CRM !&lt;/i&gt;</property>
+                <property name="use_markup">True</property>
+                <property name="justify">GTK_JUSTIFY_CENTER</property>
+                <property name="wrap">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkHSeparator" id="hseparator4">
+                <property name="visible">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="padding">2</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkNotebook" id="notebook2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <child>
+                  <widget class="GtkScrolledWindow" id="scrolledwindow14">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="window_placement">GTK_CORNER_BOTTOM_LEFT</property>
+                    <child>
+                      <widget class="GtkTextView" id="textview2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="editable">False</property>
+                        <property name="justification">GTK_JUSTIFY_CENTER</property>
+                        <property name="text" translatable="yes">
+OpenERP - GTK Client - v%s
+
+OpenERP is an Open Source ERP+CRM
+for small to medium businesses.
+
+The whole source code is distributed under
+the terms of the GNU Public Licence.
+
+(c) 2003-TODAY, Tiny sprl
+
+More Info on www.openerp.com !</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox30">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImage" id="image165">
+                        <property name="visible">True</property>
+                        <property name="stock">gtk-network</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label98">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">_OpenERP</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="type">tab</property>
+                    <property name="tab_fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkScrolledWindow" id="scrolledwindow15">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <child>
+                      <widget class="GtkTextView" id="textview3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="editable">False</property>
+                        <property name="justification">GTK_JUSTIFY_CENTER</property>
+                        <property name="accepts_tab">False</property>
+                        <property name="text" translatable="yes">
+(c) 2003-TODAY - Tiny sprl
+OpenERP is a product of Tiny sprl:
+
+Tiny sprl
+40 Chaussée de Namur
+1367 Gérompont
+Belgium
+
+Tel : (+32)81.81.37.00
+Mail: sales@xxxxxxx
+Web: http://tiny.be</property>
+                      </widget>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox31">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImage" id="image166">
+                        <property name="visible">True</property>
+                        <property name="stock">gtk-home</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label99">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">_Contact</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="type">tab</property>
+                    <property name="position">1</property>
+                    <property name="tab_fill">False</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="okbutton2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-close</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-5</property>
+                <signal name="pressed" handler="on_but_ok_pressed"/>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkWindow" id="win_form_widget">
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">Open ERP - Forms widget</property>
+    <child>
+      <widget class="GtkScrolledWindow" id="scrolledwindow44">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="shadow_type">GTK_SHADOW_IN</property>
+        <child>
+          <widget class="GtkViewport" id="viewport5">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkVBox" id="vbox10">
+                <property name="visible">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <widget class="GtkVBox" id="widget_textbox_tag">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkHBox" id="toolbar11">
+                        <property name="visible">True</property>
+        <child>
+                          <widget class="GtkToggleToolButton" id="toggle_underline">
+                            <property name="visible">True</property>
+                            <property name="stock_id">gtk-underline</property>
+                            <signal name="toggled" handler="on_toggle_underline_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkToggleToolButton" id="toggle_bold">
+                            <property name="visible">True</property>
+                            <property name="stock_id">gtk-bold</property>
+                            <signal name="toggled" handler="on_toggle_bold_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkToggleToolButton" id="toggle_italic">
+                            <property name="visible">True</property>
+                            <property name="stock_id">gtk-italic</property>
+                            <signal name="toggled" handler="on_toggle_italic_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+      <child>
+                          <widget class="GtkToggleToolButton" id="toggle_strikethrough">
+                            <property name="visible">True</property>
+                            <property name="stock_id">gtk-strikethrough</property>
+                            <signal name="toggled" handler="on_toggle_strike_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioToolButton" id="radioleft">
+                            <property name="visible">True</property>
+                            <property name="stock_id">gtk-justify-left</property>
+                            <signal name="toggled" handler="on_radioleft_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioToolButton" id="radiocenter">
+                            <property name="visible">True</property>
+                            <property name="stock_id">gtk-justify-center</property>
+                            <property name="group">radioleft</property>
+                            <signal name="toggled" handler="on_radiocenter_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioToolButton" id="radioright">
+                            <property name="visible">True</property>
+                            <property name="stock_id">gtk-justify-right</property>
+                            <property name="group">radioleft</property>
+                            <signal name="toggled" handler="on_radioright_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioToolButton" id="radiofill">
+                            <property name="visible">True</property>
+                            <property name="stock_id">gtk-justify-fill</property>
+                            <property name="group">radioleft</property>
+                            <signal name="toggled" handler="on_radiofill_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                          </packing>
+                        </child>
+      <child>
+                      <widget class="GtkToolbar" id="toolbar111">
+                        <property name="visible">True</property>
+                        <property name="toolbar_style">GTK_TOOLBAR_ICONS</property>
+      <child>
+        <widget class="GtkFontButton" id="font_button">
+          <property name="visible">True</property>
+          <property name="can_focus">True</property>
+          <signal name="font-set" handler="on_font_button_clicked"/>
+        </widget>
+        <packing>
+          <property name="padding">0</property>
+          <property name="expand">False</property>
+          <property name="fill">False</property>
+       </packing>
+           </child>
+           <child>
+          <widget class="GtkColorButton" id="color_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <signal name="color-set" handler="on_color_button_clicked"/>
+          </widget>
+          <packing>
+            <property name="padding">0</property>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+          </packing>
+           </child>
+          </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <widget class="GtkScrolledWindow" id="scrolledwindow31">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <property name="window_placement">GTK_CORNER_BOTTOM_LEFT</property>
+                        <child>
+                          <widget class="GtkTextView" id="widget_textbox_tag_tv">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">12</property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="win_sur">
+    <property name="width_request">450</property>
+    <property name="height_request">150</property>
+    <property name="title" translatable="yes">OpenERP - Confirmation</property>
+    <property name="modal">True</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox4">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkHBox" id="hbox28">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkImage" id="image144">
+                <property name="visible">True</property>
+                <property name="xpad">15</property>
+                <property name="ypad">15</property>
+                <property name="stock">gtk-dialog-question</property>
+                <property name="icon_size">6</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="lab_question">
+                <property name="visible">True</property>
+                <property name="label">Are you sure?</property>
+                <property name="wrap">True</property>
+                <property name="single_line_mode">False</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="padding">4</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area4">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="cancelbutton2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="okbutton4">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-5</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="win_selection">
+    <property name="title" translatable="yes">OpenERP - Selection</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="default_width">400</property>
+    <property name="default_height">400</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox6">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkVBox" id="win_selection_vbox">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkLabel" id="win_sel_title">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Your selection:</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkHSeparator" id="hseparator8">
+                <property name="visible">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="padding">3</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkScrolledWindow" id="scrolledwindow5">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                <child>
+                  <widget class="GtkTreeView" id="win_sel_tree">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="headers_visible">False</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area8">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="cancelbutton5">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+                <signal name="clicked" handler="on_cancelbutton5_clicked"/>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="okbutton6">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-5</property>
+                <signal name="clicked" handler="on_okbutton6_clicked"/>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="win_dialog">
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">OpenERP - Dialog</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dia_vbox_main">
+        <property name="visible">True</property>
+        <child>
+          <placeholder/>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dia_hbox_button">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="win_field_pref">
+    <property name="width_request">440</property>
+    <property name="height_request">268</property>
+    <property name="title" translatable="yes">OpenERP, Field Preference target</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox8">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkTable" id="table3">
+            <property name="visible">True</property>
+            <property name="border_width">4</property>
+            <property name="n_rows">5</property>
+            <property name="n_columns">2</property>
+            <property name="column_spacing">5</property>
+            <property name="row_spacing">4</property>
+            <child>
+              <widget class="GtkFrame" id="frame6">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <child>
+                  <widget class="GtkAlignment" id="alignment13">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <widget class="GtkHBox" id="hbox40">
+                        <property name="visible">True</property>
+                        <property name="border_width">6</property>
+                        <property name="homogeneous">True</property>
+                        <child>
+                          <widget class="GtkRadioButton" id="radio_user_pref">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label" translatable="yes">_only for you</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="draw_indicator">True</property>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioButton" id="radiobutton2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label" translatable="yes">for _all users</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radio_user_pref</property>
+                          </widget>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label116">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Value applicable for:&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="type">label_item</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="right_attach">2</property>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkFrame" id="frame5">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <child>
+                  <widget class="GtkAlignment" id="alignment12">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <widget class="GtkVBox" id="pref_vbox">
+                        <property name="visible">True</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label115">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Value applicable if:&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="type">label_item</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="right_attach">2</property>
+                <property name="top_attach">4</property>
+                <property name="bottom_attach">5</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label63">
+                <property name="visible">True</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Field _Name:</property>
+                <property name="use_underline">True</property>
+                <property name="justify">GTK_JUSTIFY_RIGHT</property>
+                <property name="mnemonic_widget">ent_field</property>
+              </widget>
+              <packing>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label66">
+                <property name="visible">True</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">_Domain:</property>
+                <property name="use_underline">True</property>
+                <property name="justify">GTK_JUSTIFY_RIGHT</property>
+                <property name="mnemonic_widget">ent_domain</property>
+              </widget>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="ent_field">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can_focus">True</property>
+                <property name="editable">False</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="ent_domain">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can_focus">True</property>
+                <property name="editable">False</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="ent_value">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can_focus">True</property>
+                <property name="editable">False</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label64">
+                <property name="visible">True</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Default _value:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">ent_value</property>
+              </widget>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child internal-child="action_area