1. Michal Sznajder
  2. thg

Source

thg / hggtk / commit.py

#
# commit.py - commit dialog for TortoiseHg
#
# Copyright 2007 Brad Schick, brad at gmail . com
# Copyright (C) 2007 TK Soh <teekaysoh@gmail.com>
#

import os
import threading
import StringIO
import sys
import shutil
import tempfile
import datetime
import cPickle

import pygtk
pygtk.require('2.0')
import gtk
import gobject
import pango

from mercurial.i18n import _
from mercurial.node import *
from mercurial import cmdutil, util, ui, hg, commands, patch
from hgext import extdiff
from shlib import shell_notify
from gdialog import *
from gtools import cmdtable
from status import GStatus
from hgcmd import CmdDialog

class GCommit(GStatus):
    """GTK+ based dialog for displaying repository status and committing changes.

    Also provides related operations like add, delete, remove, revert, refresh,
    ignore, diff, and edit.
    """

    ### Overrides of base class methods ###

    def parse_opts(self):
        GStatus.parse_opts(self)

        # Need an entry, because extdiff code expects it
        if not self.test_opt('rev'):
            self.opts['rev'] = ''

        if self.test_opt('message'):
            buffer = gtk.TextBuffer()
            buffer.set_text(self.opts['message'])
            self.text.set_buffer(buffer)

        if self.test_opt('logfile'):
            buffer = gtk.TextBuffer()
            buffer.set_text('Comment will be read from file ' + self.opts['logfile'])
            self.text.set_buffer(buffer)
            self.text.set_sensitive(False)


    def get_title(self):
        return os.path.basename(self.repo.root) + ' commit ' + ' '.join(self.pats) + ' ' + self.opts['user'] + ' ' + self.opts['date']

    def get_icon(self):
        return 'menucommit.ico'

    def auto_check(self):
        if self.test_opt('check'):
            for entry in self.model : 
                if entry[1] in 'MAR':
                    entry[0] = True


    def save_settings(self):
        settings = GStatus.save_settings(self)
        settings['gcommit'] = self._vpaned.get_position()
        return settings


    def load_settings(self, settings):
        GStatus.load_settings(self, settings)
        if settings:
            self._setting_vpos = settings['gcommit']
        else:
            self._setting_vpos = -1


    def get_tbbuttons(self):
        tbbuttons = GStatus.get_tbbuttons(self)
        tbbuttons.insert(2, self.make_toolbutton(gtk.STOCK_OK, '_Commit',
            self._commit_clicked, tip='commit'))
        return tbbuttons


    def get_body(self):
        status_body = GStatus.get_body(self)

        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)
        scroller = gtk.ScrolledWindow()
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        frame.add(scroller)
        
        self.text = gtk.TextView()
        self.text.set_wrap_mode(gtk.WRAP_WORD)
        self.text.modify_font(pango.FontDescription(self.fontcomment))
        scroller.add(self.text)
        
        self._vpaned = gtk.VPaned()
        self._vpaned.add1(frame)
        self._vpaned.add2(status_body)
        self._vpaned.set_position(self._setting_vpos)
        return self._vpaned


    def get_menu_info(self):
        """Returns menu info in this order: merge, addrem, unknown, clean, ignored, deleted
        """
        merge, addrem, unknown, clean, ignored, deleted  = GStatus.get_menu_info(self)
        return (merge + (('_commit', self._commit_file),),
                addrem + (('_commit', self._commit_file),),
                unknown + (('_commit', self._commit_file),),
                clean,
                ignored,
                deleted + (('_commit', self._commit_file),))


    def should_live(self, widget=None, event=None):
        # If there are more than a few character typed into the commit
        # message, ask if the exit should continue.
        live = False
        if self.text.get_buffer().get_char_count() > 10:
            dialog = Confirm('Exit', [], self, 'Discard commit message and exit?')
            if dialog.run() == gtk.RESPONSE_NO:
                live = True
        return live

    ### End of overridable methods ###


    def _commit_clicked(self, toolbutton, data=None):
        if not self._ready_message():
            return True

        commitable = 'MAR'
        addremove_list = self._relevant_files('?!')
        if len(addremove_list) and self._should_addremove(addremove_list):
            commitable += '?!'

        commit_list = self._relevant_files(commitable)
        if len(commit_list) > 0:
            self._hg_commit(commit_list)
        else:
            Prompt('Nothing Commited', 'No committable files selected', self).run()
        return True


    def _commit_file(self, stat, file):
        if self._ready_message():
            if stat not in '?!' or self._should_addremove([file]):
                self._hg_commit([file])
        return True


    def _should_addremove(self, files):
        if self.test_opt('addremove'):
            return True
        else:
            response = Confirm('Add/Remove', files, self).run() 
            if response == gtk.RESPONSE_YES:
                # This will stay set for further commits (meaning no more prompts). Problem?
                self.opts['addremove'] = True
                return True
        return False


    def _ready_message(self):
        begin, end = self.text.get_buffer().get_bounds()
        message = self.text.get_buffer().get_text(begin, end) 
        if not self.test_opt('logfile') and not message:
            Prompt('Nothing Commited', 'Please enter commit message', self).run()
            self.text.grab_focus()
            return False
        else:
            if not self.test_opt('logfile'):
                self.opts['message'] = message
            return True


    def _hg_commit(self, files):
        if not self.repo.ui.config('ui', 'username'):
            Prompt('Username not configured', 'Please enter a username', self).run()
            from thgconfig import ConfigDialog
            dlg = ConfigDialog(self.repo.root, False)
            dlg.show_all()
            dlg.focus_field('ui.username')
            dlg.run()
            dlg.hide()

        # call the threaded CmdDialog to do the commit, so the the large commit
        # won't get locked up by potential large commit. CmdDialog will also
        # display the progress of the commit operation.
        cmdline  = ["hg", "commit", "--verbose", "--repository", self.repo.root]
        cmdline += ['--message', self.opts['message']]
        cmdline += files
        dialog = CmdDialog(cmdline, True)
        dialog.set_transient_for(self)
        dialog.run()
        dialog.hide()

        # refresh overlay icons and commit dialog
        self.text.set_buffer(gtk.TextBuffer())
        shell_notify([self.cwd] + files)
        self.reload_status()

def launch(root='', files=[], cwd='', main=True):
    u = ui.ui()
    u.updateopts(debug=False, traceback=False)
    repo = hg.repository(u, path=root)

    ct = repo.ui.config('tortoisehg', 'commit', 'internal')
    if ct != 'internal':
        from hglib import thgdispatch
        args = ['--repository', root, ct]
        try:
            ret = thgdispatch(repo.ui, args=args)
        except SystemExit:
            pass
        return None

    cmdoptions = {
        'user':'', 'date':'',
        'modified':True, 'added':True, 'removed':True, 'deleted':True,
        'unknown':False, 'ignored':False, 
        'exclude':[], 'include':[],
        'check': False, 'git':False, 'logfile':'', 'addremove':False,
    }
    
    dialog = GCommit(u, repo, cwd, files, cmdoptions, main)
    dialog.display()
    return dialog
    
def run(root='', files=[], cwd='', **opts):
    # If no files or directories were selected, take current dir
    # TODO: Not clear if this is best; user may expect repo wide
    if not files and cwd:
        files = [cwd]
    if launch(root, files, cwd, True):
        gtk.gdk.threads_init()
        gtk.gdk.threads_enter()
        gtk.main()
        gtk.gdk.threads_leave()

if __name__ == "__main__":
    import sys
    opts = {}
    opts['root'] = len(sys.argv) > 1 and sys.argv[1] or ''
    run(**opts)