diff Gtk/gui.py @ 11:fe9c55f86698

Improvements in navigation buttons
author Miguel Ángel Bárcena Rodríguez <miguelangel@obraencurso.es>
date Wed, 24 Nov 2010 22:56:02 +0100
parents d2cbc0278b30
children 60bc5117926c
line wrap: on
line diff
--- a/Gtk/gui.py	Fri Nov 19 19:17:11 2010 +0100
+++ b/Gtk/gui.py	Wed Nov 24 22:56:02 2010 +0100
@@ -74,6 +74,13 @@
     print utils.mapping(_("pyArq-Presupuestos running on $1"),
                         (globalVars.desktop["desktop"],))
 
+# Add MenutoolButton to Uimanager
+class MenuToolAction(gtk.Action):
+       __gtype_name__ = "MenuToolAction"
+
+gobject.type_register(MenuToolAction)
+MenuToolAction.set_tool_item_type(gtk.MenuToolButton)
+
 
 class MainWindow(object):
     """gui.MainWindow:
@@ -87,14 +94,14 @@
     +-- object
       +-- MainWindow
     Atributes:
-        notebook: Read. notebook widget
-        pageList: Read. page list
-        budgetList: Read. budget list
     Methods:
-        removePage
+        changeHistorySignal
+        changeActiveSignal
+        appendEmptyPage
+        updatePage
+        closePage
     """
     # TODO:* Can choose open budget in new window
-    # TODO:* gtk.Action for menu and toolbar
     # TODO:* Can choose show more than one notebook in the same window or
     # TODO:  can show basedata notebook in a side pane
     __ui = '''<ui>
@@ -106,10 +113,9 @@
       <menu action="View">
       </menu>
       <menu action="Go">
-        <menuitem action="GoUp"/>
-        <menuitem action="GoDown"/>
         <menuitem action="GoPrevious"/>
         <menuitem action="GoPosterior"/>
+        <menuitem action="GoUp"/>
         <menuitem action="GoToRoot"/>
       </menu>
       <menu action="Test">
@@ -121,10 +127,9 @@
       <toolitem action="ImportFiebdc"/>
       <toolitem action="Close"/>
       <separator name="sep1"/>
+      <toolitem action="GoPrevMenu"/>
+      <toolitem action="GoPostMenu"/>
       <toolitem action="GoUp"/>
-      <toolitem action="GoDown"/>
-      <toolitem action="GoPrevious"/>
-      <toolitem action="GoPosterior"/>
       <toolitem action="GoToRoot"/>
     </toolbar>
     </ui>'''
@@ -132,19 +137,16 @@
     def __init__(self):
         """__init__()
         
-        Initialize the atributes "__budget_list" and "__page_list" without data.
+        Initialize the atributes self.__page_list without data.
         Creates the widgets "window" and "__notebook".
         
         self.__window: gtk.Window object
-        self.__budget_temp_list: Temporal list of budgets
-        self.__budget_list: List of budgets ("base.Budget" objects)
+        self.__uimanager: gtk.UIManager object
         self.__page_list: List of pages ("Page" object)
         self.__notebook: Notebook widget ("gtk.Notebook" object)
         self.__general_action_group: "General" action group
+        self.__navigation_action_group: "Navigation" action group
         """
-        #TODO: action group for Navigation buttons
-        self.__budget_temp_list = []
-        self.__budget_list = []
         self.__page_list = []
         # Main window
         self.__window = gtk.Window(gtk.WINDOW_TOPLEVEL)
@@ -158,12 +160,11 @@
         self.__window.add(_vbox1)
         _vbox1.show()
         #Uimanager
-        _uimanager = gtk.UIManager()
-        _accelgroup = _uimanager.get_accel_group()
+        self.__uimanager = gtk.UIManager()
+        _accelgroup = self.__uimanager.get_accel_group()
         self.__window.add_accel_group(_accelgroup)
-        _general_action_group = gtk.ActionGroup("General")
-        self.__general_action_group = _general_action_group
-        _general_action_group.add_actions(
+        self.__general_action_group = gtk.ActionGroup("General")
+        self.__general_action_group.add_actions(
             [("File", None, _("_File"), None),
              ("ImportFiebdc", gtk.STOCK_OPEN, _('_Import Fiebdc'), "",
                 _('Import FIEBDC'), self._menuitemImportFiebdc),
@@ -171,16 +172,6 @@
                 self._menuitemClose),
              ("View", None, _("_View")),
              ("Go", None, _("_Go")),
-             ("GoUp", gtk.STOCK_GO_UP, _("_Up Record"),"",
-                _("Go up record"), self._menuitemGoUp),
-             ("GoDown", gtk.STOCK_GO_DOWN, _("_Down Record"),"",
-                _("Go down record"), self._menuitemGoDown),
-             ("GoPrevious", gtk.STOCK_GO_BACK, _("_Previous Record"),"",
-                _("Go Previous record"), self._menuitemGoPrevious),
-             ("GoPosterior", gtk.STOCK_GO_FORWARD, _("_Posterior Record"),"",
-                _("Go posterior record"), self._menuitemGoPosterior),
-             ("GoToRoot", gtk.STOCK_GOTO_TOP, _("_Root"),"",
-                _("Go to root"), self._menuitemGoToRoot),
              ("Test", None, _("_Test")),
              ('ImportFiebdcPriceDatabase', gtk.STOCK_OPEN,
                 _("Import Fiebdc _price database"), "", _("Import database"),
@@ -188,14 +179,51 @@
              ("OpenPriceDatabase", gtk.STOCK_OPEN, _('_Open price database'),
                 "", _('Open Database'), self._menuitemOpenPriceDatabase),
             ])
-        _uimanager.insert_action_group(_general_action_group, 0)
-        _uimanager.add_ui_from_string(self.__ui)
-        _menu_bar = _uimanager.get_widget("/MenuBar")
+        self.__navigation_action_group = gtk.ActionGroup("Navigation")
+        self.__navigation_action_group.add_actions(
+            [("Go", None, _("_Go")),
+             ("GoPrevious", gtk.STOCK_GO_BACK, _("_Back"),"",
+                _("Go to the previous visited item"),
+                  self._menuitemGoPrevious),
+             ("GoPosterior", gtk.STOCK_GO_FORWARD, _("_Forward"),"",
+                _("Go to the next visited item"), self._menuitemGoPosterior),
+             ("GoUp", gtk.STOCK_GO_UP, _("_Up Item"),"",
+                _("Go up item"), self._menuitemGoUp),
+             ("GoToRoot", gtk.STOCK_GOTO_TOP, _("_Root"),"",
+                _("Go to root"), self._menuitemGoToRoot),
+            ])
+        self.__navigation_action_group.add_action(
+            MenuToolAction("GoPrevMenu", None ,
+                           _("Go to the previous visited item"),
+                           gtk.STOCK_GO_BACK))
+        self.__navigation_action_group.add_action(
+             MenuToolAction("GoPostMenu", None ,
+                           _("Go to the next visited item"),
+                           gtk.STOCK_GO_FORWARD))
+        self.__navigation_action_group.set_sensitive(False)
+        self.__navigation_action_group.get_action("GoPostMenu").set_sensitive(
+            False)
+        self.__navigation_action_group.get_action("GoPrevMenu").set_sensitive(
+            False)
+        self.__uimanager.insert_action_group(self.__general_action_group, 0)
+        self.__uimanager.insert_action_group(self.__navigation_action_group, 1)
+        self.__uimanager.add_ui_from_string(self.__ui)
+        _menu_bar = self.__uimanager.get_widget("/MenuBar")
         _vbox1.pack_start(_menu_bar, False, False, 0)
-        _toolbar = _uimanager.get_widget("/ToolBar")
+        _toolbar = self.__uimanager.get_widget("/ToolBar")
         _toolbar.get_settings().set_long_property("gtk-toolbar-icon-size",
             gtk.ICON_SIZE_SMALL_TOOLBAR, "pyArq-Presupuestos:toolbar")
         _vbox1.pack_start(_toolbar, False, False, 0)
+        # menuToolButton go prev
+        _go_prev_button = self.__uimanager.get_widget(
+            "/ToolBar/GoPrevMenu")
+        _go_prev_button.set_arrow_tooltip_text(_("Back history"))
+        _go_prev_button.connect('clicked', self._menuitemGoPrevious)
+        # menuToolButton go pos
+        _go_post_button = self.__uimanager.get_widget(
+            "/ToolBar/GoPostMenu")
+        _go_post_button.set_arrow_tooltip_text(_("Forward history"))
+        _go_post_button.connect('clicked', self._menuitemGoPosterior)
         # Notebook
         self.__notebook = gtk.Notebook()
         _vbox1.pack_start(self.__notebook, True, True, 0)
@@ -203,8 +231,95 @@
         self.__notebook.set_show_tabs(True)
         self.__notebook.set_show_border(True)
         self.__notebook.set_scrollable(True)
+        self.__notebook.connect("switch-page", self._switch_page)
         self.__notebook.show()
         self._main()
+        #TODO: create budget object in mainwindow?
+
+    def changeHistorySignal(self):
+        """changeHistorySignal()
+        
+        A pane emit the updateHistory signal.
+        
+        Nothing to do now
+        """
+        pass
+
+    def changeActiveSignal(self):
+        """changeActiveSignal()
+        
+        A pane emit the change-active signal.
+        
+        Chech buttons sensitive
+        """
+        self._checkButtonsSensitive(self.__notebook.get_current_page())
+
+    def _checkButtonsSensitive(self, page_num):
+        """_checkButtonsSensitive(page_num)
+        
+        page_num: page number in notebook
+        
+        Check and if necessary update the sensitive state of the navigation
+        buttons.
+        """
+        _page = self.__page_list[page_num]
+        if isinstance(_page, Page) and \
+           self.__navigation_action_group.get_sensitive():
+            # GoToRoot and GoUp actions
+            _goto_root = self.__navigation_action_group.get_action("GoToRoot")
+            _go_up = self.__navigation_action_group.get_action("GoUp")
+            if len(_page.activePathRecord) == 1 and \
+               _goto_root.get_sensitive():
+                _goto_root.set_sensitive(False)
+                _go_up.set_sensitive(False)
+            elif len(_page.activePathRecord) != 1 and \
+               not _goto_root.get_sensitive():
+                _goto_root.set_sensitive(True)
+                _go_up.set_sensitive(True)
+            # GoPrevMenu action
+            _go_Previous = self.__navigation_action_group.get_action(
+                "GoPrevious")
+            _go_prev = self.__navigation_action_group.get_action("GoPrevMenu")
+            if _page.previousPathRecord is None:
+                if _go_prev.get_sensitive():
+                    _go_prev.set_sensitive(False)
+                    _go_Previous .set_sensitive(False)
+            else:
+                if not _go_prev.get_sensitive():
+                    _go_prev.set_sensitive(True)
+                    _go_Previous.set_sensitive(True)
+            # GoPostMenu action
+            _go_Posterior = self.__navigation_action_group.get_action(
+                "GoPosterior")
+            _go_post = self.__navigation_action_group.get_action("GoPostMenu")
+            if _page.posteriorPathRecord is None:
+                if _go_post.get_sensitive():
+                    _go_post.set_sensitive(False)
+                    _go_Posterior.set_sensitive(False)
+            else:
+                if not _go_post.get_sensitive():
+                    _go_post.set_sensitive(True)
+                    _go_Posterior.set_sensitive(True)
+
+    def _switch_page(self, notebook, page, page_num,):
+        """_switch_page(notebook, page, page_num)
+         
+        Method connected to the "switch-page" signal of the notebook widget
+        
+        It changes the sensitive state of the navigation action group
+        """
+        _page = self.__page_list[page_num]
+        if isinstance(_page, EmptyPage) and \
+           self.__navigation_action_group.get_sensitive():
+            self.__navigation_action_group.set_sensitive(False)
+        elif isinstance(_page, Page):
+            if not self.__navigation_action_group.get_sensitive():
+                self.__navigation_action_group.set_sensitive(True)
+            self._checkButtonsSensitive(page_num)
+            _go_prev = self.__uimanager.get_widget("/ToolBar/GoPrevMenu")
+            _go_prev.set_menu(_page.back_menu)
+            _go_post = self.__uimanager.get_widget("/ToolBar/GoPostMenu")
+            _go_post.set_menu(_page.forward_menu)
 
     def _main(self):
         """main()
@@ -214,31 +329,32 @@
         self.__window.show()
         gtk.main()
 
-    def _addBudget(self, budget):
-        """_addBudget(budget)
-        
-        budget: "base.Budget" object
-        
-        Appends a budget in the "__budget_list"
+    def appendEmptyPage(self, emptyPage):
+        """appendEmptyPage(widget, label)
+        
+        Append a empty page to the notebook.
         """
-        if not budget is None:
-            _budget = budget
-            if _budget in self.__budget_temp_list:
-                self.__budget_temp_list.remove(_budget)
-            self.__budget_list.append(_budget)
-
-    def _appendPage(self):
-        """_appendPage()
-        
-        Creates a new page (instance of "Page class") from the last budget in
-        __budget_list, appends this page in the "__page_list" and shows the
-        page widget in the notebook widget.
+        self.__page_list.append(emptyPage)
+        self.__notebook.append_page(emptyPage.widget, emptyPage.title)
+        # TODO: reordenable and detachable Pages
+        #self.__notebook.set_tab_reorderable(emptyPage.widget, True)
+        #self.__notebook.set_tab_detachable(emptyPage.widget, True)
+
+    def updatePage(self, empty_page, page):
+        """updatePage(page)
+        
+        Update emptyPage to Page.
         """
-        _last_budget = self.__budget_list[-1]
-        _page = Page(self, _last_budget)
-        self.__notebook.append_page(_page.widget, _page.title)
-        self.__page_list.append(_page)
-
+        _page_num = self.__notebook.page_num(empty_page.widget)
+        self.__page_list[_page_num] = page
+        if self.__notebook.get_current_page() == _page_num:
+            _go_prev = self.__uimanager.get_widget("/ToolBar/GoPrevMenu")
+            _go_prev.set_menu(page.back_menu)
+            _go_post = self.__uimanager.get_widget("/ToolBar/GoPostMenu")
+            _go_post.set_menu(page.forward_menu)
+            if not self.__navigation_action_group.get_sensitive():
+                self.__navigation_action_group.set_sensitive(True)
+                self._checkButtonsSensitive(_page_num)
 
     def _menuitemImportFiebdc(self, widget):
         """_menuitemImportFiebdc(widget)
@@ -249,7 +365,6 @@
         Creates and shows a file selection window to open a budget file.
         """
         _budget = base.Budget()
-        self.__budget_temp_list.append(_budget)
         _budget_file = fiebdc.Read()
         _read_method = _budget_file.readFile
         _filename = "file"
@@ -264,10 +379,10 @@
         widget: the widget where the event is emitted from
         Callback to open a price database file.
         
-        Creates and shows a file selection window to open a price database file.
+        Creates and shows a file selection window to open a price database
+        file.
         """
         _budget = base.Budget()
-        self.__budget_temp_list.append(_budget)
         _budget_file = fiebdc.Read()
         _read_method = _budget_file.readFile
         _filename = "file"
@@ -285,7 +400,6 @@
         Creates and shows a file selection window to open a durus database
         """
         _budget = None
-        self.__budget_temp_list.append(_budget)
         _budget_file = durusdatabase.Read()
         _read_method = _budget_file.readFile
         _filename = "file"
@@ -305,25 +419,29 @@
         if _page_num != -1:
             _page = self.__page_list[_page_num]
             if isinstance(_page, EmptyPage) and _page.filetype == "durus":
-                print _("Cancel reading Durus database has not been implemented.")
+                print _("Cancel reading Durus database has not been "
+                        "implemented.")
             else:
                 _page.close()
 
-    def removePage(self, page_num):
-        """removePage(page_num)
-        
-        page_num: The number Page to be removed
+    def closePage(self, page):
+        """closePage(page)
+        
+        page: EmptyPage or Page object
         
         Removes a page from notebook and page_list.
-        Removes the budget from the budget_list if necessary.
+        Hide navigation action group if necessary
         """
-        _page = self.__page_list[page_num]
-        if isinstance(_page, Page):
-            #not loading budget
-            self.__budget_list.pop(page_num)
-        self.__page_list.pop(page_num)
-        _page.clear()
-        self.__notebook.remove_page(page_num)
+        if page in self.__page_list:
+            _page_num = self.__page_list.index(page)
+            self.__page_list.pop(_page_num)
+            page.clear()
+            self.__notebook.remove_page(_page_num)
+            if len(self.__page_list) == 0:
+                self.__navigation_action_group.set_sensitive(False)
+        else:
+            raise IndexError, _("The page is not in the page list")
+
 
     def _menuitemGoToRoot(self, widget):
         """_menuitemGoToRoot(widget)
@@ -353,29 +471,9 @@
             if isinstance(_page, Page):
                 #not loading budget
                 _active_path = _page.activePathRecord
-                if len(_active_path) > 1 and _active_path[-1] > 0:
-                    _budget = self.__budget_list[_page_num]
-                    _up_path = _active_path[:-1] + (_active_path[-1] - 1,)
-                    if _budget.hasPath(_up_path):
-                        _page.propagateMessageFrom("change_active", (-1,),
-                                                   _up_path)
-
-    def _menuitemGoDown(self, widget):
-        """_menuitemGoToDown(widget)
-        
-        widget: the widget where the event is emitted from
-        
-        Callback to go to down record.
-        """
-        _page_num = self.__notebook.get_current_page()
-        if _page_num != -1:
-            _page = self.__page_list[_page_num]
-            if isinstance(_page, Page):
-                #not loading budget
-                _active_path = _page.activePathRecord
                 if len(_active_path) > 1:
-                    _budget = self.__budget_list[_page_num]
-                    _up_path = _active_path[:-1] + (_active_path[-1] + 1,)
+                    _budget = _page.budget
+                    _up_path = _active_path[:-1]
                     if _budget.hasPath(_up_path):
                         _page.propagateMessageFrom("change_active", (-1,),
                                                    _up_path)
@@ -394,7 +492,7 @@
                 #not loading budget
                 _previous_path = _page.previousPathRecord
                 if _previous_path is not None:
-                    _budget = self.__budget_list[_page_num]
+                    _budget = _page.budget
                     if _budget.hasPath(_previous_path):
                         _page.propagateMessageFrom("change_active", (-1,),
                                                    _previous_path)
@@ -413,7 +511,7 @@
                 #not loading budget
                 _posterior_path = _page.posteriorPathRecord
                 if _posterior_path is not None:
-                    _budget = self.__budget_list[_page_num]
+                    _budget = _page.budget
                     if _budget.hasPath(_posterior_path):
                         _page.propagateMessageFrom("change_active", (-1,),
                                                    _posterior_path)
@@ -445,34 +543,6 @@
         """
         gtk.main_quit()
 
-    def _getNotebook(self):
-        """_getNotebook()
-        
-        Return the notebook widget
-        """
-        return self.__notebook
-
-    def _getPageList(self):
-        """_getPageList()
-        
-        Return the page list
-        """
-        return self.__page_list
-
-    def _getBudgetList(self):
-        """_getBudgetList()
-        
-        Return the budget list
-        """
-        return self.__budget_list
-
-    notebook = property(_getNotebook, None, None,
-                      "notebook object")
-    pageList = property(_getPageList, None, None,
-                      "Page list")
-    budgetList = property(_getBudgetList, None, None,
-                      "Budget list")
-
 
 class EmptyPage(object):
     """gui.EmptyPage:
@@ -555,8 +625,8 @@
         self.__animationThobber = gtk.gdk.PixbufAnimation(
                                   globalVars.getAppPath("THROBBER-GIF"))
         self.__quietThobber = self.__throbber.get_pixbuf()
-        self.__budget_icon = gtk.gdk.pixbuf_new_from_file_at_size(
-                             globalVars.getAppPath("BUDGET-ICON"), 16, 16)
+        self.__budget_icon = gtk.gdk.pixbuf_new_from_file(
+                             globalVars.getAppPath("BUDGET-ICON"))
         _filename = os.path.basename(filename)
         _rootfilename = os.path.splitext(_filename)[0]
         if not _rootfilename == "":
@@ -707,9 +777,7 @@
         for _child in _children:
             self.__widget.remove(_child)
         self.__widget.pack_start(_page.widget, True, True, 0)
-        _noteBook = self.__mainWindow.notebook
-        _pageIndex = _noteBook.page_num(self.__widget)
-        self.__mainWindow.pageList[_pageIndex] = _page
+        self.__mainWindow.updatePage(self, _page)
 
     def threadCanceled(self):
         """threadCanceled()
@@ -717,12 +785,10 @@
         Sets the __children atribute to None
         This causes that the timeouts is ended.
         This method is called from thread when is canceled
-        TODO: it must called threadFinished or somethig
         """
         self.__children = None
         self.stopLoading()
-        _page_num = self.__mainWindow.notebook.page_num(self.widget)
-        self.__mainWindow.removePage(_page_num)
+        self.__mainWindow.closePage(self)
 
     def close(self):
         """close()
@@ -798,6 +864,8 @@
         activePathRecord: Read. The active path record
         previousPathRecord: Read. The previous path record
         posteriorPathRecord Read. The posterior path record
+        back_menu: back menu to show in menutoolbutton
+        forward_menu: forward menu to show in menutoolbutton
     Methods:
         propagateMessageFrom
         sendMessageTo
@@ -816,6 +884,16 @@
         mainWindow: MainWindow object
         budget: "base.Budget" object
         path_record: the active path record
+        
+        self.__mainWindow: MainWindow object
+        self.__widget: a gtk.VBox
+        self.__panes_list: 
+        self.__main_item: 
+        self.__active_path_record:
+        self.__history_back:
+        self.__history_forward:
+        self.__back_menu: a gtk.Menu
+        self.__forward_menu: a gtk.Menu
         """
         #TODO: __panes_list should come from config file...
         self.__mainWindow = mainWindow
@@ -826,9 +904,25 @@
         self.__active_path_record = ()
         self.__history_back = []
         self.__history_forward = []
+        self.__back_menu = gtk.Menu()
+        self.__back_menu.show()
+        self.__forward_menu = gtk.Menu()
+        self.__forward_menu.show()
         self.budget = budget
         self._setActivePathRecord(path_record)
         self.__widget.show()
+        self.__budget_icon = gtk.gdk.pixbuf_new_from_file(
+            globalVars.getAppPath("BUDGET-ICON"))
+        self.__chapter_icon = gtk.gdk.pixbuf_new_from_file(
+            globalVars.getAppPath("CHAPTER-ICON"))
+        self.__unit_icon  = gtk.gdk.pixbuf_new_from_file(
+            globalVars.getAppPath("UNIT-ICON") )
+        self.__material_icon  = gtk.gdk.pixbuf_new_from_file(
+            globalVars.getAppPath("MATERIAL-ICON") )
+        self.__machinery_icon = gtk.gdk.pixbuf_new_from_file(
+            globalVars.getAppPath("MACHINERY-ICON"))
+        self.__labourforce_icon = gtk.gdk.pixbuf_new_from_file(
+            globalVars.getAppPath("LABOURFORCE-ICON"))
 
     def propagateMessageFrom(self, message, pane_path, arg=None):
         """propagateMessageFrom(message, pane_path, arg=None)
@@ -848,6 +942,7 @@
         if message == "change_active" and _budget.hasPath(arg):
             self.sendMessageTo(self.__main_item, message, pane_path, arg)
             self._setActivePathRecord(arg)
+            self.__mainWindow.changeActiveSignal()
         elif message == "autoclose":
             self._closeItem(pane_path)
         elif message == "split h":
@@ -872,12 +967,7 @@
         
         Close Page
         """
-        _page_list = self.__mainWindow.pageList
-        if self in _page_list:
-            _page_num = _page_list.index(self)
-            self.__mainWindow.removePage(_page_num)
-        else:
-            raise IndexError, _("The page is not in the page list")
+        self.__mainWindow.closePage(self)
 
     def clear(self):
         """clear()
@@ -885,7 +975,6 @@
         Clear atributes
         """
         self.propagateMessageFrom("clear", (0,))
-        
         del self.__budget
         del self.__panes_list
         del self.__widget
@@ -1036,26 +1125,160 @@
     def _appendHistory(self, path):
         """_appendHistory(path))
         
-        path: the old active path record
-        
-        Sets the old active path record
+        path: the new active path record
+        
+        Append the new active path record to history lists and update menus
+        """
+        if len(self.__history_back) > 1 and path in self.__history_back[:-1]:
+            # the new active record is in back history list
+            # then append forward history and pop back history
+            _items_num = len(self.__history_back) - \
+                         self.__history_back.index(path) -1 
+            for _item in range(_items_num):
+                _record_path = self.__history_back.pop()
+                _first_menuitem = self.__back_menu.get_children()[0]
+                self.__back_menu.remove(_first_menuitem)
+                self.__history_forward.append(_record_path)
+                _menuitem = self._menuItemFactory(_record_path)
+                _menuitem.connect_object("activate", self._menuHistoryForward,
+                    _record_path, _menuitem)
+                self.__forward_menu.prepend(_menuitem)
+            while len(self.__history_forward) > 100:
+                    # list too long
+                    self.__history_forward.pop(0)
+                    _last_menuitem = self.__forward_menu.get_children()[-1]
+                    self.__forward_menu.remove(_last_menuitem)
+        else:
+            # the new active record not is in back history list
+            if len(self.__history_forward) > 1 and \
+               path in self.__history_forward:
+                # the new active record is in history forward list
+                # then append back history and pop forward history
+                _items_num = len(self.__history_forward) - \
+                             self.__history_forward.index(path)
+                for _item in range(_items_num):
+                    _record_path = self.__history_forward.pop()
+                    _first_menuitem = self.__forward_menu.get_children()[0]
+                    self.__forward_menu.remove(_first_menuitem)
+                    self.__history_back.append(_record_path)
+                    if len(self.__history_back) > 1:
+                        _menuitem = self._menuItemFactory(
+                            self.__history_back[-2])
+                        _menuitem.connect_object("activate",
+                            self._menuHistoryBack, self.__history_back[-2],
+                            _menuitem)
+                        self.__back_menu.prepend(_menuitem)
+            else:
+                # the new active record not is in history forward list
+                # then append back history and clear forward history
+                self.__history_forward[:] = []
+                for _child in self.__forward_menu.get_children():
+                    self.__forward_menu.remove(_child)
+                self.__history_back.append(path)
+                if len(self.__history_back) > 1:
+                    _menuitem = self._menuItemFactory(self.__history_back[-2])
+                    _menuitem.connect_object("activate", self._menuHistoryBack,
+                        self.__history_back[-2], _menuitem)
+                    self.__back_menu.prepend(_menuitem)
+            while len(self.__history_back) > 100:
+                # list too long
+                self.__history_back.pop(0)
+                _last_menuitem = self.__back_menu.get_children()[-1]
+                self.__back_menu.remove(_last_menuitem)
+        self.__mainWindow.changeHistorySignal()
+
+    def _getImage(self, record):
+        """_getImage(record)
+        
+        record: record object
+        
+        Returns an image depending on the type of record
         """
-        if len(self.__history_back) > 1 and self.__history_back[-2] == path:
-            # append forward history and pop back history
-            _path = self.__history_back.pop()
-            self.__history_forward.append(_path)
-            if len(self.__history_forward) > 1000:
-                self.__history_forward.pop(0)
+        _hierarchy = record.recordType.hierarchy
+        _type = record.recordType.type
+        _subtype = record.recordType.subtype
+        if _hierarchy == 0:
+            _icon = self.__budget_icon
+        elif _hierarchy == 1:
+            _icon = self.__chapter_icon
         else:
-            # append back history and pop or delete forward history
-            if len(self.__history_forward) > 1 and \
-               self.__history_forward[-1] == path:
-                self.__history_forward.pop()
+            if _type == 0:
+                _icon = self.__unit_icon
+            elif _type == 1:
+                _icon = self.__labourforce_icon
+            elif _type == 2:
+                _icon = self.__machinery_icon
             else:
-                self.__history_forward[:] = []
-            self.__history_back.append(path)
-            if len(self.__history_back) > 1000:
-                self.__history_back.pop(0)
+                _icon = self.__material_icon
+        _image = gtk.Image()
+        _image.set_from_pixbuf(_icon)
+        return _image
+
+    def _menuFactory(self):
+        """_menuFactory()
+        
+        record: record object
+        
+        Creates menus for history back an history forward tool buttons
+        """
+
+        # Back Menu
+        # clear menu
+        for _child in self.__back_menu.get_children():
+            self.__back_menu.remove(_child)
+        # pupulate menu
+        if len(self.__history_back) > 1:
+            for _record_path in self.__history_back[:-1]:
+                _menuitem = self._menuItemFactory(_record_path)
+                _menuitem.connect_object("activate", self._menuHistoryBack,
+                    _record_path, _menuitem)
+                self.__back_menu.prepend(_menuitem)
+        # Forward Menu
+        # clear menu
+        for _child in self.__forward_menu.get_children():
+            self.__forward_menu.remove(_child)
+        # pupulate menu
+        if len(self.__history_forward) > 0:
+            for _record_path in self.__history_forward[:]:
+                _menuitem = self._menuItemFactory(_record_path)
+                _menuitem.connect_object("activate", self._menuHistoryForward,
+                    _record_path, _menuitem)
+                self.__forward_menu.prepend(_menuitem)
+
+    def _menuItemFactory(self, record_path):
+        """_menuItemFactory(record_path):
+        
+        record_path: record path
+        
+        Creates and return a menuItem to go to the record
+        """
+        _code = self.budget.getCode(record_path)
+        _record = self.budget.getRecord(_code)
+        _summary = _record.summary
+        _text = _code + " " + _summary
+        if len(_text) > 30:
+            _text = _text[:27] + "..."
+        _image = self._getImage(_record)
+        _menuitem = gtk.ImageMenuItem(_text)
+        _menuitem.set_image(_image)
+        _menuitem.show()
+        return _menuitem
+
+    def _menuHistoryBack(self, record_path, menu_item):
+        """_menuHistoryBack(record_path, menu_item)
+        
+        Go to the record selected in History Back menu
+        """
+        if self.budget.hasPath(record_path):
+            self.propagateMessageFrom("change_active", (-1,), record_path)
+
+    def _menuHistoryForward(self, record_path, menu_item):
+        """_menuHistoryForward(record_path, menu_item)
+        
+        Go to the record selected in History Forward menu
+        """
+        if self.budget.hasPath(record_path):
+            self.propagateMessageFrom("change_active", (-1,), record_path)
 
     def _getActivePathRecord(self):
         """_getActivePathRecord()
@@ -1084,6 +1307,20 @@
         else:
             return None
 
+    def _getBackMenu(self):
+        """_getBackMenu()
+        
+        Return the Back Menu
+        """
+        return self.__back_menu
+
+    def _getForwardMenu(self):
+        """_getForwardMenu()
+        
+        Return the Forward Menu
+        """
+        return self.__forward_menu
+
     def _getTitle(self):
         """_getTitle()
         
@@ -1149,7 +1386,10 @@
                       "Previous Active Path Record")
     posteriorPathRecord = property(_getPosteriorPathRecord, None, None,
                       "Posterior Active Path Record")
-
+    back_menu = property(_getBackMenu, None, None,
+                      "Back Menu")
+    forward_menu = property(_getForwardMenu, None, None,
+                      "Forward Menu")
 
 class View(object):
     """gui.View:
@@ -4599,4 +4839,4 @@
     values = property(None, _setValues, None,
         "values")
     options = property(None, _setOptions, None,
-        "options")
\ No newline at end of file
+        "options")