# Copyright (c) 2001 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307
# USA.

"""
Memory View
"""

__revision__ = "$Id: MemoryView.py,v 1.40 2002/01/03 17:29:21 syt Exp $"

from gtk import *
from MemoryCodes import *
from MemoryModel import MemoryModel, E_ID, E_TYPE, E_GROUP, E_NAME 
from MemoryController import MemoryController
from HornModel import HornModel
from HornActioner import HornActioner
from narval.lib import *
from narval.gui import unicode_to_gtk
# DnD stuff
from GDK import *
TARGET_STRING = 1212
TARGET_ROOTWIN =1313
target = [('STRING', 0, TARGET_STRING),
          ('text/plain', 0, TARGET_STRING),
          ('application/x-rootwin-drop', 0, TARGET_ROOTWIN)]

class MemoryView(GtkVBox):
    """
    View of memory using singleton pattern
    """

    _DICT_ = {}
    
    def __init__(self):
        self.__dict__ = self._DICT_

        if not self.__dict__ :
            GtkVBox.__init__(self)

            ## link to model
            self.mmodel = MemoryModel()
            self.mmodel.attach_observer(self)
            ## link to controller callbacks
            self.mcontroller = MemoryController()

            ## filter
            label = GtkLabel(unicode_to_gtk(_("Filter : ")))
            # whatr. you want to see...
            self._current_filter = '*'
            # a combo to select filter
            combo = GtkCombo()
            self._xpath_filters = ['*',
                                   'recipe',
                                   'action',
                                   'transform',
                                   'plan',
                                   'element',
                                   'error',
                                   ]
            combo.set_popdown_strings(self._xpath_filters)
            combo.entry.set_text(self._xpath_filters[0])
            combo.disable_activate()
            combo.entry.connect("changed",self._filter_changed, combo)
            combo.entry.connect("activate",self._filter_changed, combo)

            # a custom button
            button = GtkButton(unicode_to_gtk(_("Custom...")))
            button.set_sensitive(FALSE)
            button.connect("clicked",self._custom_clicked,button)

            # put into a hbox
            hbox = GtkHBox()
            hbox.pack_start(label,expand=FALSE)
            hbox.pack_start(combo, expand=TRUE)
            hbox.pack_start(button, expand=FALSE)
            self.pack_start(hbox, expand=FALSE)
            self.sort_ctrl = [SORT_ASCENDING, SORT_ASCENDING, SORT_ASCENDING, -1]
            self.main_clist = GtkCList(3,[unicode_to_gtk(_('Id')),
                                          unicode_to_gtk(_('Type')),
                                          unicode_to_gtk(_('Name'))])
            self.main_clist.set_selection_mode(SELECTION_SINGLE)
            self.main_clist.set_column_auto_resize(0, TRUE)
            self.main_clist.set_column_auto_resize(1, TRUE)
            self.main_clist.connect('button-press-event',
                                    self.main_clist_clicked)
            self.main_clist.connect('key-press-event',
                                    self.main_clist_key_pressed)
            self.main_clist.drag_source_set(BUTTON1_MASK, target, ACTION_COPY)
            self.main_clist.connect('drag_data_get',
                                    self.main_clist_get_drag_data)
            self.main_clist.connect('click-column',
                                    self.main_clist_column_clicked)
            scroll = GtkScrolledWindow()
            scroll.set_policy(POLICY_AUTOMATIC,POLICY_AUTOMATIC)
            scroll.add(self.main_clist)
            self.pack_start(scroll)
            
            self._fill_elts_list()


    # Observer interface #######################################################
    def MemoryModel_update(self, observable, changes):
        for change in changes:
            if change[0] == 'add':
                if self._current_filter == '*' or \
                   self._current_filter == change[1][E_TYPE]:
                    self.main_clist.append(_format_elt(change[1]))
            elif change[0] == 'rem':
                if self._current_filter == '*' or \
                   self._current_filter == change[1][E_TYPE]:
                    eid = _format_eid(change[1][E_ID])
                    for i in xrange(self.main_clist.rows):
                        if eid == self.main_clist.get_text(i, 0):
                            self.main_clist.remove(i)
                            break
        if not changes:
            # changes is empty-> all memory have changed
            self._fill_elts_list(self._current_filter)

    ## Internal callbacks for MemoryView ######################################

    def _filter_changed(self,*args):
        filter = args[1].entry.get_text()
        if filter != self._current_filter:
            try:
                self._current_filter = filter
                self._fill_elts_list(filter)
            except Exception, e:
                from narval.gui.DialogWindow import DialogWindow
                err_dlg = DialogWindow(unicode_to_gtk(_("Update filter error")))
                err_dlg.append_dialog(GtkLabel(unicode_to_gtk(
                    _('An error occured while processing the filter:'))))
                err_dlg.append_dialog(GtkLabel(str(e)))
                err_dlg.ok_button.show()
                err_dlg.ok_button.set_flags(CAN_DEFAULT|HAS_DEFAULT)
                err_dlg.ok_button.connect_object('clicked',lambda w:w.destroy(),err_dlg)
                return

    def _custom_clicked(self):
        print unicode_to_gtk(_('not yet implemented'))
        
    def main_clist_column_clicked(self, clist, column):
        if self.sort_ctrl[-1] == column:
            self._change_sort_order(column)
        self.sort_ctrl[-1] = column
        clist.set_sort_column(column)
        clist.set_sort_type(self.sort_ctrl[column])
        clist.sort()
        
    def main_clist_clicked(self, clist, event):
        # we store  the coordinates of the click
        # so that we can have a chance to know later who was dragged
        try:
            self._click_coord  = row, col = clist.get_selection_info(event.x,   
                                                                     event.y)
        except:
            return
        eid = clist.get_text(self._click_coord [0], 0).strip()
        type = clist.get_text(self._click_coord [0], 1)
        fname = clist.get_text(self._click_coord [0], 2)
            
        if event.button == 1 and event.type == GDK._2BUTTON_PRESS:
            # we got a double click with left button
            if type == 'recipe':
                self.mcontroller.edit_recipe(None, eid)
            elif not type == 'plan':
                self.mcontroller.edit_element(None, eid)
            else:
                self.mcontroller.add_plan_to_monitor(None, eid)
                
        elif event.button == 3 and clist.get_selection_info(event.x,event.y):
            clist.select_row(row,col)
            popup = GtkMenu()
            if type=='recipe':
                item = GtkMenuItem(unicode_to_gtk(_('Edit recipe')))
                item.connect('activate',self.mcontroller.edit_recipe, eid)
                popup.append(item)
                item = GtkMenuItem(unicode_to_gtk(_('Remove recipe')))
                item.connect('activate',self.mcontroller.remove_recipe, eid)
                popup.append(item)
                item = GtkMenuItem(unicode_to_gtk(_('Save recipe')))
                item.connect('activate',self.mcontroller.save_recipe, eid)
                popup.append(item)
                if HornModel().narval is not None:
                    item = GtkMenuItem()
                    popup.append(item)
                    item = GtkMenuItem(unicode_to_gtk(_('Instanciate plan')))
                    item.connect('activate',
                                 lambda w, f=fname, ha=HornActioner(): \
                                 ha.startPlan(f), fname)
                    popup.append(item)
                    
            elif type=='plan':
                item = GtkMenuItem(unicode_to_gtk(_('Monitor plan')))
                item.connect('activate',self.mcontroller.add_plan_to_monitor,eid)
                popup.append(item)
                item = GtkMenuItem(unicode_to_gtk(_('Edit matching recipe')))
                item.connect('activate',self.mcontroller.edit_recipe_from_plan,eid)
                popup.append(item)
            elif type=='transform':
                item = GtkMenuItem(unicode_to_gtk(_("Edit transform")))
                item.connect('activate',self.mcontroller.edit_element,eid)
                popup.append(item)
                item = GtkMenuItem(unicode_to_gtk(_('Remove transform')))
                item.connect('activate',self.mcontroller.remove_element,eid)
                popup.append(item)
            elif type=='action':
                item = GtkMenuItem(unicode_to_gtk(_("Edit action")))
                item.connect('activate',self.mcontroller.edit_element,eid)
                popup.append(item)
                item = GtkMenuItem(unicode_to_gtk(_('Remove action')))
                item.connect('activate',self.mcontroller.remove_element,eid)
                popup.append(item)                    
            else:
                item = GtkMenuItem(unicode_to_gtk(_('Edit element')))
                item.connect('activate',self.mcontroller.edit_element,eid)
                popup.append(item)
                if type == 'error':
                    item = GtkMenuItem(unicode_to_gtk(_('Monitor matching plan')))
                    item.connect('activate',self.mcontroller.add_plan_from_error,eid)
                    popup.append(item)
                item = GtkMenuItem(unicode_to_gtk(_('Remove element')))
                item.connect('activate',self.mcontroller.remove_element,eid)
                popup.append(item)

            popup.show_all()
            popup.popup(None,None,None,3,event.time)

    def main_clist_get_drag_data(self, clist, context, selection_data, info, time):
        #print selection_data, info, context
        type = clist.get_text(self._click_coord [0], 1)
        #node = clist.node_get_row_data(clist.node_nth(self._click_coord[0]))
        if type in ('recipe','action','transform'):
            fname = clist.get_text(self._click_coord [0], 2)
            data = "<al:step xmlns:al='%s' target='%s'" % (AL_NS, fname)
            if type == 'recipe':
                data = "%s type='recipe'/>" % (data)
            elif type== 'action':
                data = "%s type='action'/>" % (data)
            elif type == 'transform':
                data = "%s type='transform'/>" % (data)
            selection_data.set(selection_data.target, 8, data)

    def main_clist_key_pressed(self,clist,event):
        if clist.selection and event.keyval == GDK.Delete :
            eid = clist.get_text(self._click_coord [0], 0)
            HornActioner().removeElement(eid)

    ## PRIVATE ################################################################
    def _fill_elts_list(self, filter=None):
        #print 'FILL CLIST |%s|' % filter
        self.main_clist.clear()
        for e_l in self.mmodel.get_elements_list():
            if not filter or filter == '*' \
               or filter == unicode_to_gtk(e_l[E_TYPE]):
                self.main_clist.append(_format_elt(e_l))

    def _change_sort_order(self, index):
        if self.sort_ctrl[index] == SORT_ASCENDING:
            self.sort_ctrl[index] = SORT_DESCENDING
        else:
            self.sort_ctrl[index] = SORT_ASCENDING
            
def _format_eid(eid):
    return '%4s'%unicode_to_gtk(eid)

def _format_elt(elt):
    if elt[E_GROUP]:
        return [_format_eid(elt[E_ID]),
                unicode_to_gtk(elt[E_TYPE]),
                unicode_to_gtk( '%s.%s' % (elt[E_GROUP], elt[E_NAME]))]
    else:
        return [_format_eid(elt[E_ID]),
                unicode_to_gtk(elt[E_TYPE]),
                unicode_to_gtk( '%s' % (elt[E_NAME]))]
        
