# Dialogs.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 string
import os

import gnome.ui
import gnome.uiconsts

import GDK
import GTK
import GTKSCINTILLA

import Moleskine
import Languages
import findtools
from GladeDialog import GladeDialog

search_dialog = None
search_in_files_dialog = None
replace_dialog = None
options_dialog = None
langs_dialog = None

def _parse_color(color):
    import types
    if type(color) == types.IntType:
        b = (color & 0xFF0000) >> 16
        g = (color & 0x00FF00) >> 8
        r = (color & 0x0000FF)
        return (r, g, b)
    elif type(color) == types.TupleType:
        return color[2] * 65536 + color[1] * 256 + color[0]
    else:
        raise TypeError, '`color\' must be tuple or integer.'

class OptionsDialog(GladeDialog):
    def __init__(self):
        GladeDialog.__init__(self, 'options_dialog')
        self.set_transient_for(Moleskine.app)
        prefs = Moleskine.app.prefs
    
    # Misc
        if prefs['complete/auto'] is not None:
            self.auto_complete.set_active(int(prefs['complete/auto']))
        self.save_win_size.set_active(int(prefs['geometry/save_win_size']))
        self.save_session.set_active(int(prefs['history/save_session']))
    # History
        self.mru_size.set_value(int(prefs['history/mru_size']))
        self.mru_sort.set_active(int(prefs['history/mru_sort']))
    # Default style
        self.def_font.set_font_name(prefs['default style/font'])
        (r, g, b) = _parse_color(int(prefs['default style/fore']))
        self.def_fore.set_i8(r, g, b, 0xFF)
        (r, g, b) = _parse_color(int(prefs['default style/back']))
        self.def_back.set_i8(r, g, b, 0xFF)
    # Line numbers style
        self.linen_font.set_font_name(prefs['line numbers/font'])
        (r, g, b) = _parse_color(int(prefs['line numbers/fore']))
        self.linen_fore.set_i8(r, g, b, 0xFF)
        (r, g, b) = _parse_color(int(prefs['line numbers/back']))
        self.linen_back.set_i8(r, g, b, 0xFF)
    # Matched braces style
        (r, g, b) = _parse_color(int(prefs['braces style/fore_ok']))
        self.brace_ok.set_i8(r, g, b, 0xFF)
        (r, g, b) = _parse_color(int(prefs['braces style/fore_bad']))
        self.brace_bad.set_i8(r, g, b, 0xFF)
    # Selection color
        (r, g, b) = _parse_color(int(prefs['default style/selection']))
        self.selection_colour.set_i8(r, g, b, 0xFF)
    # Bookmark color
        (r, g, b) = _parse_color(int(prefs['default style/bookmark']))
        self.bookmark_colour.set_i8(r, g, b, 0xFF)
    # Caret style
        (r, g, b) = _parse_color(int(prefs['caret/fore']))
        self.caret_fore.set_i8(r, g, b, 0xFF)
        self.caret_width.set_value(int(prefs['caret/width']))
        self.caret_period.set_value(int(prefs['caret/period']))
    # Keyboard
        keys = int(prefs['keyboard/keybindings'])
        if keys == 2:
            self.keys_emacs.set_active(1)
        elif keys == 1:
            self.keys_gnome.set_active(1)
        else:
            self.keys_moleskine.set_active(1)
    # Line numbers digits
        self.show_linen.set_active(int(prefs['line numbers/show']))
        if int(prefs['line numbers/auto']):
            self.digits_auto.set_active(1)
            self.digits_num.set_sensitive(0)
        else:
            self.digits_fixed.set_active(1)
            self.digits_num.set_sensitive(1)
        self.digits_num.set_value(int(prefs['line numbers/digits']))
        
        for dirname in Moleskine.app.prefs.dirnames:
            self.dirnames_list.append(dirname)
    
    def digits_fixed_toggled(self, w, user_data=None):
        if w.get_active():
            self.digits_num.set_sensitive(1)
        else:
            self.digits_num.set_sensitive(0)
    
    def new_dir_clicked(self, w, user_data=None):
        row = self.dirnames_list.append(('', ''))
        self.dirnames_list.select_row(row, 0)
        self.name_entry.set_text('')
        self.dir_entry.set_text('')
        self.name_entry.grab_focus()
    
    def del_dir_clicked(self, w, user_data=None):
        row = self.dirnames_list.selection[0]
        self.dirnames_list.remove(row)
        self.dirnames_list.select_row(row - 1, 0)
    
    def edit_name(self, w, user_data=None):
        if len(self.dirnames_list.selection) > 0:
            row = self.dirnames_list.selection[0]
            self.dirnames_list.set_text(row, 0, w.get_text())
    
    def edit_dir(self, w, user_data=None):
        if len(self.dirnames_list.selection) > 0:
            row = self.dirnames_list.selection[0]
            self.dirnames_list.set_text(row, 1, w.get_text())
    
    def dir_selected(self, clist, row, column, event, user_data=None):
        name = clist.get_text(row, 0)
        self.name_entry.set_text(name)
        directory = clist.get_text(row, 1)
        self.dir_entry.set_text(directory)
        
        self.name_entry.set_sensitive(1)
        self.dir_entry.set_sensitive(1)
        self.del_dir.set_sensitive(1)
    
    def dir_unselected(self, clist, row, column, event, user_data=None):
        self.name_entry.set_text('')
        self.dir_entry.set_text('')
        
        self.name_entry.set_sensitive(0)
        self.dir_entry.set_sensitive(0)
        self.del_dir.set_sensitive(0)
    
    def apply(self):
        prefs = Moleskine.app.prefs
        
        prefs['complete/auto'] = self.auto_complete.get_active()
    # History
        prefs['geometry/save_win_size'] = self.save_win_size.get_active()
        prefs['history/save_session'] = self.save_session.get_active()
        prefs['history/mru_size'] = self.mru_size.get_value_as_int()
        prefs['history/mru_sort'] = self.mru_sort.get_active()
    # Default style
        prefs['default style/font'] = self.def_font.get_font_name()
        prefs['default style/fore'] = \
                _parse_color(self.def_fore.get_i8())
        prefs['default style/back'] = \
                _parse_color(self.def_back.get_i8())
    # Line numbers style
        prefs['line numbers/font'] = self.linen_font.get_font_name()
        prefs['line numbers/fore'] = \
                _parse_color(self.linen_fore.get_i8())
        prefs['line numbers/back'] = \
                _parse_color(self.linen_back.get_i8())
    # Matched braces style
        prefs['braces style/fore_ok'] = \
                _parse_color(self.brace_ok.get_i8())
        prefs['braces style/fore_bad'] = \
                _parse_color(self.brace_bad.get_i8())
    # Selection color
        prefs['default style/selection'] = \
                _parse_color(self.selection_colour.get_i8())
    # Bookmark color
        prefs['default style/bookmark'] = \
                _parse_color(self.bookmark_colour.get_i8())
    # Caret style
        prefs['caret/fore'] = \
                _parse_color(self.caret_fore.get_i8())
        prefs['caret/width'] = self.caret_width.get_value_as_int()
        prefs['caret/period'] = self.caret_period.get_value_as_int()
    # Keyboard
        if self.keys_emacs.get_active():
            prefs['keyboard/keybindings'] = 2
        elif self.keys_gnome.get_active():
            prefs['keyboard/keybindings'] = 1
        else:
            prefs['keyboard/keybindings'] = 0
    # Line numbers digits
        prefs['line numbers/show'] = self.show_linen.get_active()
        prefs['line numbers/auto'] = self.digits_auto.get_active()
        prefs['line numbers/digits'] = self.digits_num.get_value_as_int()
    # Directory names
        Moleskine.app.prefs.dirnames = []
        for row in range(self.dirnames_list.rows):
            name = self.dirnames_list.get_text(row, 0)
            directory = self.dirnames_list.get_text(row, 1)
            
            directory = os.path.expanduser(directory)
            directory = os.path.expandvars(directory)
            directory = os.path.abspath(directory)
            
            Moleskine.app.prefs.dirnames.append((name, directory))
        
        for doc in Moleskine.app.documents:
            doc.setup_line_numbers_digits()
            doc.setup_braces_style()
            doc.update_title()
        Moleskine.app.update_mru_menu()
        Moleskine.app.update_styles()
        Moleskine.app.update_title()
        Moleskine.app.prefs.save()
    
    def destroy(self, w, user_data=None):
        global options_dialog
        options_dialog = None
    
    def clicked(self, w, button, user_data=None):
        if button == 0:
            self.apply()
            self.close()
        elif button == 1:
            self.apply()
        else:
            self.close()

class LanguagesDialog(GladeDialog):
    def __init__(self):
        GladeDialog.__init__(self, 'languages_dialog')
        self.set_transient_for(Moleskine.app)
        
        self.styles_list.set_auto_sort(1)
        
        lexers = Languages.Lexers.keys()
        lexers.sort()
        self.lexer_combo.set_popdown_strings(lexers)
        
        lang_names = Moleskine.app.langs.keys()
        lang_names.sort()
        self.lang_name_combo.set_popdown_strings(lang_names)
        doc = Moleskine.app.get_current_document()
        self.lang_name.set_text(doc.language.name)
        
        self.rule_glob.connect('clicked', self.set_rule_type, 0)
        self.rule_name_re.connect('clicked', self.set_rule_type, 1)
        self.rule_text_re.connect('clicked', self.set_rule_type, 2)
    
    def setup_editor_tab(self, lang):
        self.tab_size.set_value(lang.tab_size)
        self.soft_tabs.set_active(lang.soft_tabs)
        self.show_indent.set_active(lang.show_indent)
        self.indent_size.set_value(lang.indent_size)
        self.auto_indent.set_active(lang.auto_indent)
        if lang.edge_indicator == 2:
            self.edge_background.set_active(1)
        elif lang.edge_indicator == 1:
            self.edge_line.set_active(1)
        else:
            self.edge_none.set_active(1)
        self.edge_column.set_value(lang.edge_column)
        (r, g, b) = _parse_color(lang.edge_color)
        self.edge_color.set_i8(r, g, b, 0xFF)
    
    def setup_highlight_tab(self, lang):
        for l in Languages.Lexers.keys():
            if Languages.Lexers[l] == lang.lexer:
                lexer_name = l
                break
        self.lexer.set_text(lexer_name)
    
    def setup_keywords_tab(self, lang):
        print lang.api_file
        if lang.api_file is not None:
            self.api_file.gtk_entry().set_text(lang.api_file)
        for kwclass in range(5):
            if lang.keywords[kwclass] is not None:
                kwlist = self['keywords%i_list' % (kwclass + 1)]
                kwlist.freeze()
                kwlist.delete_text(0, -1)
                kwlist.insert_defaults(lang.keywords[kwclass])
                kwlist.thaw()
    
    def setup_ftypes_tab(self, lang):
        self.rules_list.clear()
        self.rules_list.freeze()
        for rule in lang.rules:
            if rule[0] == 0:
                rule_type = _('Shell glob')
            elif rule[0] == 1:
                rule_type = ('Filename regexp')
            else:
                rule_type = _('Text regexp')
            row = self.rules_list.append((rule_type, rule[1]))
            self.rules_list.set_row_data(row, rule[0])
        self.rules_list.thaw()
    
    def language_changed(self, w, user_data=None):
        lang_name = self.lang_name.get_text()
        if not Moleskine.app.langs.has_key(lang_name):
            return
        lang = Moleskine.app.langs[lang_name]
        
        self.setup_editor_tab(lang)
        self.setup_highlight_tab(lang)
        self.setup_keywords_tab(lang)
        self.setup_ftypes_tab(lang)
    
    def lexer_changed(self, w, data=None):
        from copy import deepcopy
        
        lang_name = self.lang_name.get_text()
        if not Moleskine.app.langs.has_key(lang_name):
            return
        lang = Moleskine.app.langs[lang_name]
        lexer_name = self.lexer.get_text()
        lexer = Languages.Lexers[lexer_name]
        
        self.styles_list.unselect_all()
        self.styles_list.clear()
        self.styles_list.freeze()
        if Languages.Styles.has_key(lexer):
            styles = Languages.Styles[lexer]
            for s in styles.keys():
                row = self.styles_list.append((s,))
                if styles.has_key(s) and lang.styles.has_key(styles[s]):
                    self.styles_list.set_row_data(row,
                            (styles[s], deepcopy(lang.styles[styles[s]])))
        self.styles_list.thaw()
    
    def style_selected(self, clist, row, column, event, user_data=None):
        from Languages import _merge_font
        
        style = clist.get_row_data(row)
        if style is None:
            style = {'font': None,
                     'fore': None,
                     'back': None }
        else:
            style = style[1]
        
        if style['font'] is not None:
            font = _merge_font(style['font'])
        else:
            font = Moleskine.app.prefs['default style/font']
        self.style_font.set_font_name(font)
        if style['fore'] is not None:
            r, g, b = _parse_color(int(style['fore']))
        else:
            r, g, b = _parse_color(int(
                    Moleskine.app.prefs['default style/fore']))
        self.style_fore.set_i8(r, g, b, 0xFF)
        if style['back'] is not None:
            r, g, b = _parse_color(int(style['back']))
        else:
            r, g, b = _parse_color(int(
                    Moleskine.app.prefs['default style/back']))
        self.style_back.set_i8(r, g, b, 0xFF)
        
        self.style_font.set_sensitive(1)
        self.style_fore.set_sensitive(1)
        self.style_back.set_sensitive(1)
        self.style_reset.set_sensitive(1)
    
    def style_unselected(self, clist, row, column, event, user_data=None):
        from Languages import _split_font
        style = clist.get_row_data(row)
        if style is None:
            style = {'font': None,
                     'fore': None,
                     'back': None }
        else:
            style = style[1]
        
        style['font'] = _split_font(self.style_font.get_font_name())
        style['fore'] = str(_parse_color(self.style_fore.get_i8()))
        style['back'] = str(_parse_color(self.style_back.get_i8()))
        
        self.style_font.set_sensitive(0)
        self.style_fore.set_sensitive(0)
        self.style_back.set_sensitive(0)
        self.style_reset.set_sensitive(0)
    
    def reset_style_clicked(self, w, user_data=None):
        row = self.styles_list.selection[0]
        style = self.styles_list.get_row_data(row)
        style[1]['font'] = None
        style[1]['fore'] = None
        style[1]['back'] = None
        self.styles_list.select_row(row, 0)
    
    def new_rule_clicked(self, w, user_data=None):
        row = self.rules_list.append((_('Shell glob'), ''))
        self.rules_list.set_row_data(row, 0)
        self.rules_list.select_row(row, 0)
        self.rule_entry.set_text('')
        self.rule_entry.grab_focus()
    
    def del_rule_clicked(self, w, user_data=None):
        self.rules_list.remove(self.rules_list.selection[0])
    
    def edit_rule(self, w, user_data=None):
        if len(self.rules_list.selection) > 0:
            row = self.rules_list.selection[0]
            self.rules_list.set_text(row, 1, w.get_text())
    
    def set_rule_type(self, w, user_data=None):
        if w.get_active() == 1:
            row = self.rules_list.selection[0]
            if user_data == 0:
                self.rules_list.set_text(row, 0, _('Shell glob'))
            elif user_data == 1:
                self.rules_list.set_text(row, 0, _('Filename regexp'))
            else:
                self.rules_list.set_text(row, 0, _('Text regexp'))
    
    def rule_selected(self, clist, row, column, event, user_data=None):
        rule = clist.get_text(row, 1)
        self.rule_entry.set_text(rule)
        rule_type = clist.get_row_data(row)
        if rule_type == 0:
            self.rule_glob.set_active(1)
        elif  rule_type == 1:
            self.rule_name_re.set_active(1)
        else:
            self.rule_text_re.set_active(1)
        
        self.rule_entry.set_sensitive(1)
        self.del_rule.set_sensitive(1)
        self.rule_glob.set_sensitive(1)
        self.rule_name_re.set_sensitive(1)
        self.rule_text_re.set_sensitive(1)
    
    def rule_unselected(self, clist, row, column, event, user_data=None):
        self.rule_entry.set_text('')
        self.rule_glob.set_active(1)
        
        self.rule_entry.set_sensitive(0)
        self.del_rule.set_sensitive(0)
        self.rule_glob.set_sensitive(0)
        self.rule_name_re.set_sensitive(0)
        self.rule_text_re.set_sensitive(0)
    
    def on_lang_name_activate(self, w, user_data=None):
        w.set_editable(0)
        name = w.get_text()
        if not Moleskine.app.langs.has_key(name):
            Moleskine.app.langs[name] = \
                    Languages.Language(name, GTKSCINTILLA.LEXER_NULL)
            lang_names = Moleskine.app.langs.keys()
            lang_names.sort()
            self.lang_name_combo.set_popdown_strings(lang_names)
            w.set_text(name)
    
    def new_language(self, w, user_data=None):
        self.lang_name.set_text('')
        self.lang_name.set_editable(1)
        self.lang_name.grab_focus()
    
    def copy_language(self, w, user_data=None):
        pass
    
    def del_language(self, w, user_data=None):
        if self.lang_name.get_text() == 'Plain text':
            gnome.ui.GnomeMessageBox(
                    _('You can\'t remove this language.'),
                    gnome.uiconsts.MESSAGE_BOX_ERROR,
                    gnome.uiconsts.STOCK_BUTTON_OK).run_and_close()
            return
        del(Moleskine.app.langs[self.lang_name.get_text()])
        lang_names = Moleskine.app.langs.keys()
        lang_names.sort()
        self.lang_name_combo.set_popdown_strings(lang_names)
        self.lang_name.set_text('Plain text')
    
    def apply(self):
        from copy import deepcopy
        
        langs = Moleskine.app.langs
        lang_name = self.lang_name.get_text()
        lang = langs[lang_name]
        
        lang.tab_size = self.tab_size.get_value_as_int()
        lang.soft_tabs = self.soft_tabs.get_active()
        lang.indent_size = self.indent_size.get_value_as_int()
        lang.show_indent = self.show_indent.get_active()
        lang.auto_indent = self.auto_indent.get_active()
        if self.edge_none.get_active():
            lang.edge_indicator = 0
        elif self.edge_line.get_active():
            lang.edge_indicator = 1
        else:
            lang.edge_indicator = 2
        lang.edge_column = self.edge_column.get_value_as_int()
        lang.edge_color = _parse_color(self.edge_color.get_i8())
        
        lang.api_file = self.api_file.get_full_path(0)
        
        if len(self.styles_list.selection) > 0:
            self.styles_list.unselect_all()
        lang.lexer = Languages.Lexers[self.lexer.get_text()]
        for row in range(self.styles_list.rows):
            style = self.styles_list.get_row_data(row)
            if style is None:
                continue
            lang.styles[style[0]] = deepcopy(style[1])
        
        lang.keywords = [None, None, None, None, None]
        lang.keywords[0] = self.keywords1_list.get_chars(0, -1)
        lang.keywords[1] = self.keywords2_list.get_chars(0, -1)
        lang.keywords[2] = self.keywords3_list.get_chars(0, -1)
        lang.keywords[3] = self.keywords4_list.get_chars(0, -1)
        lang.keywords[4] = self.keywords5_list.get_chars(0, -1)
        
        lang.rules = []
        for row in range(self.rules_list.rows):
            type = self.rules_list.get_row_data(row)
            rule = self.rules_list.get_text(row, 1)
            lang.rules.append((type, rule))
        
        Moleskine.app.langs.save()
        for doc in Moleskine.app.documents:
            doc.reload_language()
    
    def destroy(self, w, user_data=None):
        global langs_dialog
        langs_dialog = None
    
    def clicked(self, w, button, user_data=None):
        if button == 0:
            self.apply()
            self.close()
        elif button == 1:
            self.apply()
        else:
            self.close()

class SearchDialog(GladeDialog):
    def __init__(self):
        GladeDialog.__init__(self, 'search_dialog')
        self.set_transient_for(Moleskine.app)
        self.editable_enters(self.search_combo.entry)
        self.search_combo.set_case_sensitive(1)
        self.search_combo.set_use_arrows_always(1)
    
    def show(self):
        GladeDialog.show(self)
        doc = Moleskine.app.get_current_document()
        if doc.get_selection_start() != doc.get_selection_end():
            self.search_entry.set_text(doc.get_sel_text())
        else:
            self.search_entry.set_text('')
        self.search_entry.select_region(0, -1)
        self.get_window()._raise()
        self.search_entry.grab_focus()
    
    def get_flags(self):
        from Document import FindFlags
        
        FindFlags.match_case = self.match_case_check.get_active()
        FindFlags.whole_words = self.whole_words_check.get_active()
        FindFlags.word_start = self.word_start_check.get_active()
        FindFlags.regexp = self.regexp_check.get_active()
        FindFlags.backwards = self.backwards_check.get_active()
        FindFlags.text = self.search_entry.get_text()
    
    def regexp_toggled(self, w, user_data=None):
        if w.get_active():
            self.whole_words_check.set_sensitive(0)
            self.word_start_check.set_sensitive(0)
        else:
            self.whole_words_check.set_sensitive(1)
            self.word_start_check.set_sensitive(1)
    
    def search(self, info = 1):
        from Document import FindFlags
        
        self.get_flags()
        doc = Moleskine.app.get_current_document()
        if not info:
            pos = doc.get_current_pos()
            doc.goto_pos(0)
        result = doc.search_text(
            text = FindFlags.text,
            match_case = FindFlags.match_case,
            whole_words = FindFlags.whole_words,
            word_start = FindFlags.word_start,
            regexp = FindFlags.regexp,
            backwards = FindFlags.backwards)
        if result is None and info:
            gnome.ui.GnomeMessageBox(
                    _('No matches for: "%s".') % FindFlags.text,
                    gnome.uiconsts.MESSAGE_BOX_INFO,
                    gnome.uiconsts.STOCK_BUTTON_OK).run_and_close()
        elif result is not None:
            doc.ensure_visible(doc.line_from_position(result[0]))
        if result is None and not info:
            doc.goto_pos(pos)
        return result
    
    def destroy(self, w, user_data=None):
        global find_dialog
        find_dialog = None
    
    def clicked(self, w, button, user_data=None):
        if button == 0:
            self.close()
            self.search()
        else:
            self.close()

class PromptDialog(GladeDialog):
    def __init__(self, text, replace_text):
        self.text = text
        self.replace_text = replace_text
        self.buttonclicked = 0
        GladeDialog.__init__(self, 'replace_prompt_dialog')

    def show(self):
        #GladeDialog.show(self)
        self.replace_label.set_text(_('Replace "%s" with "%s"?') % (self.text, self.replace_text))
        self.get_window()._raise()
        #self.button7.grab_focus()
        self.run()
        return self.buttonclicked   # replace = 0, skip = 1, all = 2, one = 3, cancel = 4

    def clicked(self, w, button, user_data=None):
        self.buttonclicked = button
        self.close()

class ReplaceDialog(SearchDialog):
    def __init__(self):
        GladeDialog.__init__(self, 'replace_dialog')
        self.set_transient_for(Moleskine.app)
        self.editable_enters(self.search_combo.entry)
        self.search_combo.set_case_sensitive(1)
        self.search_combo.set_use_arrows_always(1)
        self.editable_enters(self.replace_combo.entry)
        self.replace_combo.set_case_sensitive(1)
        self.replace_combo.set_use_arrows_always(1)
    
    def show(self):
        SearchDialog.show(self)
        self.replace_entry.set_text('')
    
    def get_flags(self):
        from Document import FindFlags
        
        SearchDialog.get_flags(self)
        FindFlags.replace_prompt = self.replace_prompt_check.get_active()
        FindFlags.replace_all = self.replace_all_radio.get_active()
        FindFlags.replace_selection = self.replace_sel_radio.get_active()
        FindFlags.replace_text = self.replace_entry.get_text()
    
    def build_flags(self):
        from Document import FindFlags
        
        return FindFlags.backwards * GTKSCINTILLA.FIND_DOWN + \
               FindFlags.match_case * GTKSCINTILLA.FIND_MATCH_CASE + \
               FindFlags.whole_words * GTKSCINTILLA.FIND_WHOLE_WORDS + \
               FindFlags.word_start * GTKSCINTILLA.FIND_WORD_START + \
               FindFlags.regexp * GTKSCINTILLA.FIND_REGEXP
    
    def regexp_toggled(self, w, user_data=None):
        if w.get_active():
            self.whole_words_check.set_sensitive(0)
            self.word_start_check.set_sensitive(0)
        else:
            self.whole_words_check.set_sensitive(1)
            self.word_start_check.set_sensitive(1)
    
    def replace_multi(self, all=0):
        from Document import FindFlags
        
        flags = self.build_flags()
        
        doc = Moleskine.app.get_current_document()
        doc.begin_undo_action()
        
        count = 0
        target_start = target_end = 0
        
        if all:
            replace_start = 0
            replace_end = doc.get_length()
        else:
            replace_start = doc.get_selection_start()
            replace_end = doc.get_selection_end()
        
        regexp = FindFlags.regexp
        find_text = FindFlags.text
        replace_text = FindFlags.replace_text
        prompt_on = FindFlags.replace_prompt
        replace_action = 0
        
        result = doc.find_text(flags, find_text, (replace_start, replace_end))
        while result and replace_action < 3:
            target_start, target_end = result
            doc.set_selection_start(target_start)
            doc.set_selection_end(target_end)
            doc.ensure_visible(doc.line_from_position(target_start))
            
            replace_action = 0
            if prompt_on:
                prompt_dialog = PromptDialog(doc.get_sel_text(), replace_text)
                replace_action = prompt_dialog.show()
            if replace_action == 0:      # replace
                pass
            elif replace_action == 1:    # skip
                result = doc.find_text(flags, find_text,
                                       (target_end, replace_end))
                continue
            elif replace_action == 2:    # all
                prompt_on = 0
            elif replace_action == 3:    # one
                pass
            elif replace_action == 4:    # cancel
                break
            
            count = count + 1
            doc.set_target_start(target_start)
            doc.set_target_end(target_end)
            len_replace = doc.replace_target(regexp, replace_text)
            replace_start = target_start + len_replace
            if not all:
                replace_end = replace_end + \
                              (len_replace - (target_end - target_start))
            else:
                replace_end = doc.get_length()
            result = doc.find_text(flags, find_text,
                                   (replace_start, replace_end))
        doc.goto_pos(doc.get_target_end())
        doc.end_undo_action()
        if replace_dialog != 4:
            gnome.ui.GnomeMessageBox(
                        _('Done.\nReplaced %i strings.') % (count, ),
                        gnome.uiconsts.MESSAGE_BOX_INFO,
                        gnome.uiconsts.STOCK_BUTTON_OK).run_and_close()
        return count
    
    def replace(self):
        from Document import FindFlags

        doc = Moleskine.app.get_current_document()
        sel_start = doc.get_selection_start()
        sel_end = doc.get_selection_end()
        
        self.get_flags()
        
        count = 0
        if FindFlags.replace_all:
            count = self.replace_multi(all=1)
        elif FindFlags.replace_selection:
            count = self.replace_multi()
        else:
            self.search()
            search_sel_start = doc.get_selection_start()
            search_sel_end = doc.get_selection_end()
            
            if search_sel_start != search_sel_end:
                count = 1
                prompt_on = FindFlags.replace_prompt
                replace_action = 0
                if prompt_on:
                    prompt_dialog = PromptDialog(doc.get_sel_text(), FindFlags.replace_text)
                    replace_action = prompt_dialog.show()
                if replace_action == 1 or replace_action == 4:      # skip or cancel
                    return
                doc.set_target_start(search_sel_start)
                doc.set_target_end(search_sel_end)
                replace_length = doc.replace_target(FindFlags.regexp,
                                                    FindFlags.replace_text)
        doc.set_selection_start(sel_start)
        doc.set_selection_end(sel_end + count * (len(FindFlags.replace_text) - len(FindFlags.text)))
    
    def destroy(self, w, user_data=None):
        global replace_dialog
        replace_dialog = None
    
    def clicked(self, w, button, user_data=None):
        if button == 0:
        #    self.search()
        #elif button == 1:
            self.close()
            self.replace()
        else:
            self.close()

class SearchInFilesDialog(SearchDialog):
    def __init__(self):
        GladeDialog.__init__(self, 'search_in_files_dialog')
        self.set_transient_for(Moleskine.app)
        self.editable_enters(self.search_combo.entry)
        self.search_combo.set_case_sensitive(1)
        self.search_combo.set_use_arrows_always(1)
        self.editable_enters(self.pattern_combo.entry)
        self.pattern_combo.set_case_sensitive(1)
        self.pattern_combo.set_use_arrows_always(1)
    
    def show(self):
        if self.directory_entry.get_text() == '' and \
           Moleskine.app.get_current_document().filepath is not None:
            self.directory_entry.set_text(os.path.dirname(Moleskine.app.get_current_document().filepath) + '/')
        SearchDialog.show(self)
    
    def get_flags(self):
        from Document import FindFlags
        
        FindFlags.match_case = self.match_case_check.get_active()
        FindFlags.whole_words = self.whole_words_check.get_active()
        FindFlags.word_start = self.word_start_check.get_active()
        FindFlags.regexp = self.regexp_check.get_active()
        FindFlags.backwards = 0
        FindFlags.text = self.search_entry.get_text()
        
        FindFlags.in_open_files = self.open_files_radio.get_active()
        FindFlags.in_directory_files = self.directory_files_radio.get_active()
        FindFlags.recursive_directory = self.recursive_directory_check.get_active()
        FindFlags.in_all_files = self.all_files_radio.get_active()
        FindFlags.in_pattern_files = self.pattern_files_radio.get_active()
        FindFlags.in_files_pattern = self.pattern_entry.get_text()
        FindFlags.in_directory = self.directory_entry.get_text()
        if FindFlags.in_directory == '':
            FindFlags.in_directory = os.path.dirname(Moleskine.app.get_current_document().filepath)
        FindFlags.in_directory = os.path.expanduser(FindFlags.in_directory)
        FindFlags.in_directory = os.path.expandvars(FindFlags.in_directory)
        FindFlags.in_directory = os.path.abspath(FindFlags.in_directory)
    
    def build_flags(self):
        from Document import FindFlags
        
        return FindFlags.match_case * GTKSCINTILLA.FIND_MATCH_CASE + \
               FindFlags.whole_words * GTKSCINTILLA.FIND_WHOLE_WORDS + \
               FindFlags.word_start * GTKSCINTILLA.FIND_WORD_START + \
               FindFlags.regexp * GTKSCINTILLA.FIND_REGEXP
    
    def regexp_toggled(self, w, user_data=None):
        if w.get_active():
            self.whole_words_check.set_sensitive(0)
            self.word_start_check.set_sensitive(0)
        else:
            self.whole_words_check.set_sensitive(1)
            self.word_start_check.set_sensitive(1)
    
    def matches(self, filepath):
        from fnmatch import fnmatch
        from Document import FindFlags
        
        if fnmatch(filepath, FindFlags.in_files_pattern):
            return 1
        return 0
    
    def mark_tab(self, notebook=None, page_num=0, color='#ff0000'):
        if notebook is None:
            return
        tab_label = notebook.get_tab_label(notebook.get_nth_page(page_num))
        tab_label_style = tab_label.get_style().copy()
        label_color = tab_label.get_colormap().alloc(color)
        tab_label_style.fg[GTK.STATE_NORMAL] = label_color
        tab_label.set_style(tab_label_style)
        
    def get_dir_files(self, subdir):
        from Document import FindFlags
        
        dir_files = os.listdir(subdir)
        for j in range(len(dir_files)):
            filepath = subdir + '/' + dir_files[j]
            dir_files[j] = filepath
            if os.path.isdir(filepath):
                if FindFlags.recursive_directory and not os.path.islink(filepath):
                    dir_files = dir_files + self.get_dir_files(filepath)
        return dir_files
    
    def search_in_files(self):
        from Document import FindFlags
        from gtk import GdkColor
        
        documents = Moleskine.app.documents
        notebook = Moleskine.app.notebook
        pos = notebook.get_current_page()
        first_found = None
        times_found = 0
        
        self.get_flags()
        
        Moleskine.app.switch = 0
        if FindFlags.in_directory_files:
            directory_files = self.get_dir_files(FindFlags.in_directory)
            search_directory_files = []
            for i in range(len(directory_files)):
                ok = 1
                filepath = directory_files[i]
                if os.path.isdir(filepath) or not os.path.isfile(filepath):
                    continue
                if FindFlags.in_pattern_files:
                    if not self.matches(filepath):
                      continue
                for doc in Moleskine.app.documents:
                    if filepath == doc.filepath:
                        ok = 0
                        break
                if ok: search_directory_files.append(filepath)
            for file in search_directory_files:
                Moleskine.app.open_document(file)
        
        close = []
        if FindFlags.in_open_files or FindFlags.in_directory_files:
            for i in range(len(documents)):
                notebook.set_page(i)
                label = notebook.get_tab_label(notebook.get_nth_page(i))
                label.set_rc_style()    # Default style
                if FindFlags.in_directory_files:
                    if not FindFlags.in_directory == os.path.dirname(documents[i].filepath):
                        if FindFlags.recursive_directory and string.find(os.path.dirname(documents[i].filepath), FindFlags.in_directory) != 0:
                            continue
                if FindFlags.in_pattern_files:
                    if not self.matches(documents[i].filepath):
                        continue
                result = self.search(0)
                if result is not None:
                    if first_found is None:
                        first_found = i
                    times_found = times_found + 1
                    # Mark tab label
                    if FindFlags.in_directory_files and documents[i].filepath in search_directory_files:
                        self.mark_tab(notebook, i, 'blue')
                    else:
                        self.mark_tab(notebook, i, 'red')
                else:
                    if FindFlags.in_directory_files and documents[i].filepath in search_directory_files:
                        close.append(i)
        for i in range(len(close)):
            notebook.set_page(close[i] - i)
            Moleskine.app.close_document()
        Moleskine.app.switch = 1
        if first_found is None:
            notebook.set_page(pos)
        else:
            notebook.set_page(first_found)
        if times_found > 0:
            gnome.ui.GnomeMessageBox(
                    _('Done.\nString found in %i files.') % (times_found, ),
                    gnome.uiconsts.MESSAGE_BOX_INFO,
                    gnome.uiconsts.STOCK_BUTTON_OK).run_and_close()
        else:
            gnome.ui.GnomeMessageBox(
                    _('String not found in any files.'),
                    gnome.uiconsts.MESSAGE_BOX_INFO,
                    gnome.uiconsts.STOCK_BUTTON_OK).run_and_close()

    
    def destroy(self, w, user_data=None):
        global search_in_files_dialog
        search_in_files_dialog = None
    
    def clicked(self, w, button, user_data=None):
        if button == 0:
            self.close()
            self.search_in_files()
        else:
            self.close()

    def key_press_event(self, w, event):
        if event.keyval == GDK.Return:
            self.close()
            self.search_in_files()

def options():
    global options_dialog
    
    if options_dialog is None:
        options_dialog = OptionsDialog()
    else:
        options_dialog.get_window()._raise()

def languages():
    global langs_dialog
    
    if langs_dialog is None:
        langs_dialog = LanguagesDialog()
    else:
        langs_dialog.get_window()._raise()

def search():
    global search_dialog
    
    if search_dialog is None:
        search_dialog = SearchDialog()
    search_dialog.show()

def search_in_files():
    global search_in_files_dialog
    
    if search_in_files_dialog is None:
        search_in_files_dialog = SearchInFilesDialog()
    search_in_files_dialog.show()

def replace():
    global replace_dialog
    
    if replace_dialog is None:
        replace_dialog = ReplaceDialog()
    replace_dialog.show()
