#  PMail - GNOME/GTK/Python email client
#  Copyright (C) 2000 Scott Bender <sbender@harmony-ds.com>

#  This file is part of PMail.

#  PMail 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, or (at your option)
#  any later version.

#  PMail 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 GNU Emacs; see the file COPYING.  If not, write to
#  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
#  Boston, MA 02111-1307, USA.  

from gtk import *
import GtkExtra
import string

import Base
import config
import mail
import mail.Source
import mail.Plugin
import gutil
import update
import Compose
import mail.Plugin

_defaultListHeaders =  [ 'id', 'Subject', 'From', 'Date-Received' ]
_defaultHeaderSizes = [ 40, 200, 100, 40 ]
_defaultSortHeader = 'Date-Received'
_defaultSortOrder = 1
_order_chars = [ '>', '<' ]
_flag_headers = ['D', 'A', 'F' ]
_flag_headers_sizes = [7, 7, 7]


class Window(GtkScrolledWindow, Base.PMailObject):
  def __init__(self, root, messageWindow, name='GtkMessageList'):
    GtkScrolledWindow.__init__(self)
    #GtkWindow.__init__(self)
    Base.PMailObject.__init__(self, debug=0)

    self.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)

    self._name = name
    self._messageWindow = messageWindow
    self._rootWindow = root
    self._list = None
    self._disable_selection_update = 0
    self._selected_messages = []
    self._messages = None
    self._mailbox = None
    self.load_config()
    self.update_column_headers()

    self._sortFuncs = {
      'date': [self.cmpAscDate, self.cmpDescDate],
      'Date-Received': [self.cmpAscDate, self.cmpDescDate],
      '__default' : [self.cmpAscString, self.cmpDescString]
      }
    

  def selected_messages(self):
    return self._selected_messages

  def selected_mailbox(self):
    return self._mailbox

  def name(self):
    return self._name

  def button_press_event(self, w, event):
    #print 'button_press_event:', w, event
    if event.button != 3 or not self._messages:
      return

    selected =  w.get_selection_info(event.x, event.y)
    if selected and selected[0] < len(self._messages):
      row = selected[0]
      msg = self._messages[row]

      self.select_message(msg)

      mf = GtkExtra.MenuFactory(GtkExtra.MENU_FACTORY_MENU)
      entries = [
        ('Reply', None, self._rootWindow.reply, msg),
        ('Reply/Quoted', None, self._rootWindow.reply, msg, mail.QUOTED),
        ('Reply/Inline', None, self._rootWindow.reply, msg, mail.INLINE),
        ('Reply/As Attachment', None, self._rootWindow.reply,
         msg, mail.ATTACHMENT),
        ('<separator>', None, None),
        ('Forward', None, self._rootWindow.forward, msg),
        ('Forward/Quoted', None, self._rootWindow.forward, msg, mail.QUOTED),
        ('Forward/Inline', None, self._rootWindow.forward, msg, mail.INLINE),
        ('Forward/As Attachment', None, self._rootWindow.forward,
         msg, mail.ATTACHMENT),
        ('<separator>', None, None),        
        ('Delete', None, self.delete_mail, msg),
        ('Flag', None, self.flag_mail, msg),
        ('Restore Draft', None, self.restore_draft, msg),
        ]

      entries.append(('<separator>', None, None))

      entries = entries + gutil.folder_menu_entries('Copy to', self.copy_msg,
                                                    msg)
      entries = entries + gutil.folder_menu_entries('Move to', self.move_msg,
                                                    msg)

      plugins = mail.Plugin.get_plugins()
      if len(plugins):
        entries.append(('<separator>', None, None))
        plugin_items = []
        for plugin in plugins:
          items = plugin.message_list_popup_items(msg)
          if items:
            plugin_items = plugin_items + items
        

      mf.add_entries(entries)
      menu = mf.get_menu('')

      for item in plugin_items:
        menu.append(item)
        item.show()
      
      menu.popup(None, None, None, event.button, event.time)


  def restore_draft(self, b, msg):
    Compose.restore(self._rootWindow, msg)
      
  def copy_msg(self, b, msg, box):
    msg.append(box)

  def move_msg(self, b, msg, box):
    msg.append(box)
    self.delete_mail(None, msg)

  def select_message(self, message):
    try:
      idx = self._messages.index(message)
    except:
      msg = self._mailbox.message_with_id(message.id())
      idx = self._messages.index(msg)
    self._list.unselect_all()
    self._list.select_row(idx, 0)

  def update_from_selection(self, message):
    if not self._disable_selection_update:
      if message not in self._selected_messages:
        self._selected_messages.append(message)
      if len(self._selected_messages) == 1 and self._mailbox:
        wasseen = message.seen()
        self._messageWindow.display_message(message)
        mail.Plugin.call_plugins('message_displayed', (message, self))
        self.update_message_colors(self._messages.index(message))
        self.update_status()
        if not wasseen:
          self._treeWindow.update()

  def select_row(self, clist, row, col, event):
    self.update_from_selection(self._messages[row])

  def unselect_row(self, clist, row, col, event):
    if self._messages[row] in self._selected_messages:
      self._selected_messages.remove(self._messages[row])

      
  def column_clicked(self, clist, col):
    header = self._columnHeaders[col]
    if header == self._sortHeader:
      self._sortOrder = not self._sortOrder
    self._sortHeader = header
    self.update_column_headers()
    self.list_messages()

  def column_resized(self, clist, col, width):
    self._headerSizes[col] = width

  def drag_data_get(self, widget, context, data, info, time):
    drag_data = ''
    if len(self._selected_messages):
      for msg in self._selected_messages:
        drag_data = drag_data + '%s\n' % msg.unique_name()
      data.set(data.target, 1, drag_data)

  def drag_data_delete(self, widget, context):
    self.delete_mail()

  def update_column_headers(self, need_recreated=1):
    if need_recreated:
      if self._list:
        self.remove(self._list)
        self._list.destroy()
        
      self._list = GtkCList(len(self._columnHeaders), self._columnHeaders)
      self._list.set_selection_mode(SELECTION_EXTENDED)
      self._list.column_titles_active()
      self._list.set_reorderable(1)
      self._list.set_border_width(2)

      self._list.connect("select_row", self.select_row)
      self._list.connect("unselect_row", self.unselect_row)      
      self._list.connect("click_column", self.column_clicked)
      self._list.connect("resize_column", self.column_resized)
      self._list.connect('drag_data_get', self.drag_data_get)
      self._list.connect('drag_data_delete', self.drag_data_delete)
      self._list.connect('button_press_event', self.button_press_event)      
      self._list.drag_source_set(GDK.BUTTON1_MASK,
                                 [('application/x-pmail', 0, 1)],
                                 GDK.ACTION_COPY|GDK.ACTION_MOVE)
      
      self.add(self._list)
      self._list.show()
    
    for i in range(len(self._columnHeaders)):
      s = self._columnHeaders[i]
      if string.lower(self._columnHeaders[i]) == \
         string.lower(self._sortHeader):
        s = '%s   %s' % (s,  _order_chars[self._sortOrder])
      self._list.set_column_title(i, s)
      self._list.set_column_width(i, self._headerSizes[i])
                     
  def update_message_colors(self, idx):
    msg = self._messages[idx]
    fg = self._normalfor_color
    bg = self._normalback_color
    if not msg.seen():
      if self._unread_forb:
        fg = self._unread_color
      else:
        bg = self._unread_color
        
    if msg.flagged():
      if self._flagged_forb:
        fg = self._flagged_color
      else:
        bg = self._flagged_color

    if msg.deleted():
      if self._deleted_forb:
        fg = self._deleted_color
      else:
        bg = self._deleted_color

    # this is a quick hack ...
    import re,os
    self._colours =[]
    execfile(os.path.expanduser("~/.pmail/colours"),locals())
    for c in self.colours:
      self._colours.append((c[0],
                            re.compile(c[1]),
                            apply(self.get_colormap().alloc,c[2])))

    for head,reg,col in self._colours:
      if reg.search(msg.get_header(head)):
        fg=col
        break
    # ----------------------------

    self._list.set_background(idx, bg)    
    self._list.set_foreground(idx, fg)

    if msg.deleted():
      s = 'D'
    else:
      s = ''
    self._list.set_text(idx, 0, s)
    self._list.set_text(idx, 1, msg.answered() and 'A' or '')
    self._list.set_text(idx, 2, msg.flagged() and 'F' or '')    
      
  def update_colors(self):
    for idx in range(len(self._messages)):
      self.update_message_colors(idx)
    
  def list_messages(self, mailbox = None, msgs = None, is_selection=0):

    if mailbox:
      self._mailbox = mailbox
      
    if not self._mailbox and not msgs:
      self._list.freeze()
      self._list.clear()
      self._list.thaw()
      mail.Plugin.call_plugins('message_list_updated', (self,))
      return
    
    if not msgs:
      #if self._mailbox.can_lock():
      msgs = self._mailbox.messages(is_selection=is_selection)
        #self._mailbox.unlock()
      #else:
      #  return

    self._list.freeze()
    self._list.clear()
      
    if self._mailbox \
       and self._mailbox.source()['deleteOption'] != mail.Source.MARK:
      tmsgs = []
      for m in msgs:
        if not m.deleted():
          tmsgs.append(m)
      msgs = tmsgs
    else:
      msgs = list(msgs) #copy is since we're sorting
    
    selected = self._selected_messages
    self._selected_messages = []
    
    self._messages = msgs
    
    if msgs:
      self.debug('sorting')
      sortf = self._sortFuncs.get(self._sortHeader)
      if not sortf:
        sortf = self._sortFuncs.get('__default')
      msgs.sort(sortf[self._sortOrder])
      self.debug('done.')
      self._disable_selection_update = 1
      idx = 0
      for msg in msgs:
        #this can take a while, if we don't mainiteration
        #the window manager get's locked up, why??
        update.update() 
        row = []
        for i in range(len(self._columnHeaders)):
          h = self._columnHeaders[i]
          if h == 'id':
            val = '%s' % msg.id()
          elif h == 'Date-Received':
            val = mail.prettyDate(msg, msg.date())
          elif h == 'Date':            
            val = mail.prettyDate(msg, msg.headers().getdate('date'))
          elif h in [ 'From', 'To' ]:
            val = mail.name_part(msg.get_header(h))
          elif h == 'D':
            val = msg.deleted() and 'D' or ''
          elif h == 'A':
            val = msg.answered() and 'A' or ''
          elif h == 'F':
            val = msg.flagged() and 'F' or ''
          else:
            val = msg.get_header(h)
          if not val: val = ''
          row.append(val)
        self._list.append(row)
        if msg in selected:
          self._list.select_row(idx, 0)
          self._selected_messages.append(msg)
        idx = idx + 1
    self.update_colors()
    self._disable_selection_update = 0

    mail.Plugin.call_plugins('message_list_updated', (self,))

    if not len(self._selected_messages):
      self._list.select_row(0,0)

    self._list.thaw()
      
    self.update_status()

  def update_status(self):
    if self._mailbox:
      msg = '%s{%s}: %d messages, %d unread' % (self._mailbox.source().name(),
                                                self._mailbox.path(),
                                                self._mailbox.message_count(),
                                                self._mailbox.unseen_count())
      self._rootWindow.pop_status('box')
      self._rootWindow.show_status('box', msg)

  def check_mail(self, _b=None, force_list=0):
    if self._mailbox:
      if self._mailbox.check_mail() or force_list:
        self.list_messages()
      self._treeWindow.update()

  def delete_mail(self, _b=None, msg=None):
    if msg:
      msgs = [msg]
    elif len(self._selected_messages):
      msgs = self._selected_messages        
    else:
      return

    sel_idx = self._messages.index(msgs[0])
    sel_count = len(msgs)

    for msg in msgs:
      msg.delete()
      
    if msg._source['deleteOption'] == mail.Source.MARK:
      self.update_colors()
      #print 'MARK:', sel_idx, sel_count
      if sel_count == 1 and sel_idx+1 < len(self._messages):
        self._list.unselect_row(sel_idx, 0)
        self._list.select_row(sel_idx+1, 0)
    else:
      self.list_messages()
      self._list.unselect_all()
      self._list.select_row(sel_idx, 0)
    self._treeWindow.update()

  def expunge(self, b):
    if self._mailbox:
      self._mailbox.expunge()
      self.list_messages()
      self._treeWindow.update()

  def flag_mail(self, b, msg=None):
    if msg:
      msgs = [msg]
    elif len(self._selected_messages):
      msgs = self._selected_messages
    else:
      return
    
    for msg in msgs:
      msg.set_flagged(not msg.flagged())
      self.update_message_colors(self._messages.index(msg))

  def load_config(self):
    c = config.getWindowParam(self, 'unread_color', (0, 65535, 0))
    self._unread_colort = c
    self._unread_color = self.get_colormap().alloc(c[0], c[1], c[2])
    self._unread_forb = config.getWindowParam(self, 'unread_forb', 0)
    
    c = config.getWindowParam(self, 'normalfor_color', (0, 0, 0))
    #c = (0, 0, 0)
    self._normalfor_colort = c
    self._normalfor_color = self.get_colormap().alloc(c[0], c[1], c[2])
    
    c = config.getWindowParam(self, 'normalback_color', (65535, 65535, 65535))
    #c = (65535, 65535, 65535)
    self._normalback_colort = c
    self._normalback_color = self.get_colormap().alloc(c[0], c[1], c[2])

    c = config.getWindowParam(self, 'deleted_color', (65535, 0, 0))
    self._deleted_colort = c
    self._deleted_color = self.get_colormap().alloc(c[0], c[1], c[2])
    self._deleted_forb = config.getWindowParam(self, 'deleted_forb', 0)    
    
    c = config.getWindowParam(self, 'flagged_color', (0, 0, 65535))
    self._flagged_colort = c
    self._flagged_color = self.get_colormap().alloc(c[0], c[1], c[2])
    self._flagged_forb = config.getWindowParam(self, 'flagged_forb', 0)    

    self._columnHeaders = config.getWindowParam(self, 'columnHeaders',
                                                _defaultListHeaders)
    self._sortHeader = config.getWindowParam(self, 'sortHeader',
                                        _defaultSortHeader)
    self._sortOrder = config.getWindowParam(self, 'sortOrder',
                                            _defaultSortOrder)
    self._headerSizes = config.getWindowParam(self, 'headerSizes',
                                              _defaultHeaderSizes)

    self._columnHeaders = _flag_headers + self._columnHeaders
    self._headerSizes = _flag_headers_sizes + self._headerSizes
    
  def store_config(self):
    config.setWindowParam(self, 'sortHeader', self._sortHeader)
    config.setWindowParam(self, 'sortOrder', self._sortOrder)
    config.setWindowParam(self, 'headerSizes',
                          self._headerSizes[len(_flag_headers):])

    config.setWindowParam(self, 'unread_color', self._unread_colort)
    config.setWindowParam(self, 'unread_forb', self._unread_forb)
    
    config.setWindowParam(self, 'deleted_color', self._deleted_colort)
    config.setWindowParam(self, 'deleted_forb', self._deleted_forb)

    config.setWindowParam(self, 'flagged_color', self._flagged_colort)
    config.setWindowParam(self, 'flagged_forb', self._flagged_forb)

    config.setWindowParam(self, 'normalfor_color', self._normalfor_colort)
    config.setWindowParam(self, 'normalback_color', self._normalback_colort)

    
  def cmpAscDate(self, l, r):
    ld = l.float_date()
    rd = r.float_date()
    return int(ld - rd)

  def cmpDescDate(self, l, r):
    ld = l.float_date()
    rd = r.float_date()
    return int(rd - ld)

  def cmpAscString(self, l, r):
    ls = l.get_header(self._sortHeader)
    rs = r.get_header(self._sortHeader)
    if ls == rs:
      return 0
    elif ls > rs:
      return 1
    else:
      return -1

  def cmpDescString(self, l, r):
    ls = l.get_header(self._sortHeader)
    rs = r.get_header(self._sortHeader)
    if ls == rs:
      return 0
    elif ls > rs:
      return -1
    else:
      return 1


