# CVS.py
#
# Moleskine: a source code editor for the GNOME desktop
#
# Copyright (c) 2000 - 2002   Michele Campeotto <micampe@micampe.it>
#
# 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 Library 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.

import sys
import os
import string
import popen2
import time
import fcntl, FCNTL

import gtk
import GTK
import GDK
import gnome.ui
import gnome.uiconsts

import GTKSCINTILLA

import Moleskine

class CVS:
    def __init__(self, action=None, directory=0):
        self.action = action
        self.command = None
        self.message = None
        self.directory = directory
        
        self.doc = Moleskine.app.get_current_document()
        if not self.directory and \
           (self.doc.filepath is None or \
            (self.doc.is_modified() and self.action in ('commit', 'update'))):
            gnome.ui.GnomeMessageBox(
                    _('Cannot execute CVS command.\nYou have to save the file first.'),
                    gnome.uiconsts.MESSAGE_BOX_ERROR,
                    gnome.uiconsts.STOCK_BUTTON_OK).run_and_close()
            return
        elif self.action == 'commit':
            self.ask_log()
        elif self.action == 'remove':
            res = gnome.ui.GnomeMessageBox(
                    _('Do you want to remove your working copy first?'),
                    gnome.uiconsts.MESSAGE_BOX_QUESTION,
                    gnome.uiconsts.STOCK_BUTTON_YES,
                    gnome.uiconsts.STOCK_BUTTON_NO).run_and_close()
            if res == 1:  # No
                return
            else:         # Yes
                if os.access(self.doc.filepath, os.F_OK):
                    os.unlink(self.doc.filepath)
        
        if not self.directory:
            filepath = '"' + string.replace(os.path.dirname(self.doc.filepath), '"', '\\"') + '"'
            filename = '"' + string.replace(os.path.basename(self.doc.filepath), '"', '\\"') + '"'
        else:
            if action == 'add' or action == 'remove':
                filepath = '"' + string.replace(os.path.dirname(os.path.normpath(self.doc.filepath + '/../')), '"', '\\"') + '"'
                filename = '"' + string.replace(os.path.basename(os.path.dirname(self.doc.filepath)), '"', '\\"') + '"'
            else:
                filepath = '"' + string.replace(os.path.dirname(self.doc.filepath), '"', '\\"') + '"'
                filename = ''
        
        if action == 'commit':
            if self.message is None:
                return
            self.command = 'cd %s; cvs commit -m "%s" %s' % (filepath, string.replace(self.message, '"', '\\"'), filename)
        elif action == 'update':
            self.command = 'cd %s; cvs update -d -P %s' % (filepath, filename)
        elif action == 'status':
            self.command = 'cd %s; cvs status %s' % (filepath, filename)
        elif action == 'log':
            self.command = 'cd %s; cvs log %s' % (filepath, filename)
        elif action == 'add':
            self.command = 'cd %s; cvs add %s' % (filepath, filename)
        elif action == 'remove':
            self.command = 'cd %s; cvs remove %s' % (filepath, filename)
        elif action == 'diff':
            self.command = 'cd %s; cvs diff -u %s' % (filepath, filename)
        
        if self.command is not None:
            self.exec_command()

    def ask_log(self):
        log_label = gtk.GtkLabel(_('Enter your CVS log message:'))
        log_label.show()
        editable = gtk.GtkText()
        editable.set_usize(400, 150)
        editable.set_editable(1)
        editable.show()
        
        dialog = gnome.ui.GnomeDialog(_('CVS log message'), gnome.uiconsts.STOCK_BUTTON_OK, gnome.uiconsts.STOCK_BUTTON_CANCEL)
        dialog.set_policy(1, 1, 1)
        dialog.vbox.set_homogeneous(0)
        dialog.vbox.pack_start(log_label, expand=0, padding=4)
        dialog.vbox.pack_end(editable, padding=4)
        
        editable.grab_focus()
        button = dialog.run_and_close()
        if button == 0:
            self.message = editable.get_chars(0, editable.get_length())
        else:
            self.message = None
    
    def keypress(self, widget, event, user_data = None):
        if user_data != 'CVS':
            return
        if event.keyval == GDK.Escape:
            self.abort = 1
    
    def timeout_loop(self):
        if self.inst.poll() == -1:
            try:
                self.cvs_errormessage = self.cvs_errormessage + self.cvs_error.readline()
            except IOError, (errno, errstr):
                if errno != 11:
                    raise
            if string.find(self.cvs_errormessage, 'waiting') != -1:
                print "File is probably locked; keep waiting..."
            if string.find(self.cvs_errormessage, 'assword') != -1:
                self.abort = 1
                self.authenticated = 0
            
            try:
                self.cvs_outputmessage = self.cvs_outputmessage + self.cvs_output.read()
            except IOError, (errno, errstr):
                if errno != 11:
                    raise

            if self.abort:
                gtk.mainquit()
            
            self.ticks = 1 - self.ticks
            Moleskine.app.progress.set_value(self.ticks)
            return 1       # continue
        else:
            gtk.mainquit()
    
    def exec_command(self):
        def make_less(text = ''):
            less = gnome.ui.GnomeLess()
            less.set_fixed_font(1)
            less.set_font(gtk.load_font('-*-lucidatypewriter-medium-*-*-*-120-*-*-*-*-*-*'))
            less.set_usize(620, 450)
            less.show_string(text)
            less.show()
            return less
        
        if not self.directory and self.action == 'diff' and self.doc.is_modified():
            gnome.ui.GnomeMessageBox(
                    _('You might want to save the file before diff-ing it against CVS version.'),
                    gnome.uiconsts.MESSAGE_BOX_WARNING,
                    gnome.uiconsts.STOCK_BUTTON_OK).run_and_close()
        
        self.authenticated = 1
        progress = Moleskine.app.progress
        progress.set_activity_step(3)
        progress.set_activity_mode(1)
        progress.set_value(0)
        Moleskine.app.status.push(_('CVS working... (press ESC to abort)'))
        signal_id = Moleskine.app.connect('key-press-event', self.keypress, 'CVS')
        self.cvs_errormessage = ''
        self.cvs_outputmessage = ''
        
        self.inst = popen2.Popen3(self.command, 1, -1)
        self.cvs_output, self.cvs_input, self.cvs_error = self.inst.fromchild, self.inst.tochild, self.inst.childerr
        if sys.hexversion < 0x2020000:
            fcntl.fcntl(self.cvs_error.fileno(), FCNTL.F_SETFL, FCNTL.O_NONBLOCK)
            fcntl.fcntl(self.cvs_output.fileno(), FCNTL.F_SETFL, FCNTL.O_NONBLOCK)
        else:
            fcntl.fcntl(self.cvs_error.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
            fcntl.fcntl(self.cvs_output.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        
        self.abort = 0
        self.ticks = 0
        timer = gtk.timeout_add(50, self.timeout_loop)
        gtk.mainloop()
        gtk.timeout_remove(timer)
        
        Moleskine.app.status.pop()
        progress.set_activity_mode(0)
        progress.set_value(0)
        Moleskine.app.disconnect(signal_id)
        
        if not self.authenticated:
            gnome.ui.GnomeMessageBox(
                    _('Password protected CVS repositories are not supported at the moment. Sorry.'),
                    gnome.uiconsts.MESSAGE_BOX_ERROR,
                    gnome.uiconsts.STOCK_BUTTON_OK).run_and_close()
        
        if not self.abort:
            self.cvs_outputmessage = self.cvs_outputmessage + self.cvs_output.read()
            self.cvs_errormessage = self.cvs_errormessage + self.cvs_error.read()
        self.cvs_output.close()
        self.cvs_error.close()
        self.cvs_input.close()
        
        if (not self.directory and self.action == 'diff' and len(self.cvs_errormessage) == 0) or (self.directory and self.action == 'diff' and len(self.cvs_outputmessage) > 0):
            Moleskine.app.new_document()
            doc = Moleskine.app.get_current_document()
            doc.set_text(self.cvs_outputmessage)
            doc.set_language(Moleskine.app.langs['diff output'])
        else:
            notebook = gtk.GtkNotebook()
            less_o = make_less(self.cvs_outputmessage)
            notebook.append_page(less_o, gtk.GtkLabel(_('CVS output')))
            
            if len(self.cvs_errormessage) > 0:
                less_e = make_less(self.cvs_errormessage)
                notebook.append_page(less_e, gtk.GtkLabel(_('CVS error')))
                if string.find(self.cvs_errormessage, 'abort') != -1:
                    tab_label = notebook.get_tab_label(notebook.get_nth_page(1))
                    tab_label_style = tab_label.get_style().copy()
                    label_color = tab_label.get_colormap().alloc('red')
                    tab_label_style.fg[GTK.STATE_NORMAL] = label_color
                    tab_label.set_style(tab_label_style)
                if len(self.cvs_outputmessage) == 0:
                    notebook.set_page(1)
            
            notebook.show()
            
            dialog = gnome.ui.GnomeDialog(_('CVS output'), gnome.uiconsts.STOCK_BUTTON_OK)
            dialog.set_policy(1, 1, 1)
            dialog.vbox.pack_start(notebook, padding=4)
            dialog.run_and_close()
