# -*- coding: utf-8 -*-
#  Color picker plugin
#  This file is part of gedit-plugins
# 
#  Copyright (C) 2006 Jesse van den Kieboom
#   
#  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.

import gedit, gtk
import gettext
import re
from gpdefs import *

try:
	gettext.bindtextdomain(GETTEXT_PACKAGE, GP_LOCALEDIR)
	_ = lambda s: gettext.dgettext(GETTEXT_PACKAGE, s);
except:
	_ = lambda s: s

ui_str = """
<ui>
  <menubar name="MenuBar">
    <menu name="ToolsMenu" action="Tools">
      <placeholder name="ToolsOps_2">
        <menuitem name="ColorPicker" action="ColorPicker"/>
      </placeholder>
    </menu>
  </menubar>
</ui>
"""

class ColorPickerPluginInstance:
	def __init__(self, plugin, window):
		self._window = window
		self._plugin = plugin
		self._activate_id = 0
		
		self.insert_menu()
		self.update()
		
		self._activate_id = self._window.connect('focus-in-event', \
				self.on_window_activate)

	def stop(self):
		self.remove_menu()

		if self._activate_id:
			self._window.handler_disconnect(self._activate_id)
		
		self._window = None
		self._plugin = None
		self._action_group = None
		self._activate_id = 0
		
	def insert_menu(self):
		manager = self._window.get_ui_manager()

		self._action_group = gtk.ActionGroup("GeditColorPickerPluginActions")
		self._action_group.add_actions( \
				[("ColorPicker", None, _("Pick _Color..."), None, \
				_("Pick a color from a dialog"), \
				lambda a: self._plugin.on_color_picker_activate(self._window))])

		manager.insert_action_group(self._action_group, -1)
		self._ui_id = manager.add_ui_from_string(ui_str)

	def remove_menu(self):
		manager = self._window.get_ui_manager()
		
		manager.remove_ui(self._ui_id)
		manager.remove_action_group(self._action_group)
		manager.ensure_update()

	def update(self):
		tab = self._window.get_active_tab()
		self._action_group.set_sensitive(tab != None)
		
		if not tab and self._plugin._dialog and \
				self._plugin._dialog.get_transient_for() == self._window:
			self._plugin._dialog.response(gtk.RESPONSE_CLOSE)
	
	def on_window_activate(self, window, event):
		self._plugin.dialog_transient_for(window)

class ColorPickerPlugin(gedit.Plugin):
	DATA_TAG = "ColorPickerPluginInstance"
	
	def __init__(self):
		gedit.Plugin.__init__(self)
		self._dialog = None

	def get_instance(self, window):
		return window.get_data(self.DATA_TAG)
	
	def set_instance(self, window, instance):
		window.set_data(self.DATA_TAG, instance)
	
	def activate(self, window):
		self.set_instance(window, ColorPickerPluginInstance(self, window))
	
	def deactivate(self, window):
		self.get_instance(window).stop()
		self.set_instance(window, None)
		
	def update_ui(self, window):
		self.get_instance(window).update()
	
	def skip_hex(self, buf, iter, next_char):
		while True:
			char = iter.get_char()
			
			if not char:
				return

			if not char.lower() in \
					('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
					'a', 'b', 'c', 'd', 'e', 'f'):
				return
			
			if not next_char(iter):
				return
	
	def get_color_position(self, buf):
		bounds = buf.get_selection_bounds()
		
		if not bounds or bounds[0].equal(bounds[1]):
			# No selection, find color in the current cursor position
			start = buf.get_iter_at_mark(buf.get_insert())
			
			end = start.copy()
			start.backward_char()
			
			self.skip_hex(buf, start, lambda iter: iter.backward_char())
			self.skip_hex(buf, end, lambda iter: iter.forward_char())
		else:
			start, end = bounds
			
		text = buf.get_text(start, end)
		
		if not re.match('#?[0-9a-zA-Z]+', text):
			return None
		
		if text[0] != '#':
			start.backward_char()
			
			if start.get_char() != '#':
				return None
		
		return start, end		
	
	def insert_color(self, text):
		window = gedit.app_get_default().get_active_window()
		view = window.get_active_view()
		
		if not view or not view.get_editable():
			return
		
		doc = view.get_buffer()
		
		if not doc:
			return
		
		# Begin user action
		doc.begin_user_action()
		
		# Get the color
		bounds = self.get_color_position(doc)
		
		if not bounds:
			doc.delete_selection(False, True)
		else:
			doc.delete(bounds[0], bounds[1])		
		
		doc.insert_at_cursor('#' + text)
		doc.end_user_action()
		
	def scale_color_component(self, component):
		return min(max(int(round(component * 255. / 65535.)), 0), 255)

	def scale_color(self, color):
		color.red = self.scale_color_component(color.red)
		color.green = self.scale_color_component(color.green)
		color.blue = self.scale_color_component(color.blue)

	def get_current_color(self):
		window = gedit.app_get_default().get_active_window()
		doc = window.get_active_document()
		
		if not doc:
			return None
		
		bounds = self.get_color_position(doc)
		
		if bounds:
			return doc.get_text(bounds[0], bounds[1])
		else:
			return None

	def dialog_transient_for(self, window):
		if self._dialog:
			self._dialog.set_transient_for(window)

	# Signal handlers
	
	def on_color_picker_activate(self, window):
		if not self._dialog:
			self._dialog = gtk.ColorSelectionDialog(_('Pick Color'))
			self._dialog.colorsel.set_has_palette(True)

			image = gtk.Image()
			image.set_from_stock(gtk.STOCK_SELECT_COLOR, gtk.ICON_SIZE_BUTTON)

			self._dialog.ok_button.set_label(_('_Insert'))
			self._dialog.ok_button.set_image(image)

			self._dialog.cancel_button.set_use_stock(True)
			self._dialog.cancel_button.set_label(gtk.STOCK_CLOSE)

			self._dialog.connect('response', self.on_dialog_response)

		color = self.get_current_color()
		
		if color:
			try:
				color = gtk.gdk.color_parse(color)
			except ValueError:
				color = None
			
			if color:
				self._dialog.colorsel.set_current_color(color)
			
		self._dialog.set_transient_for(window)
		self._dialog.present()

	def on_dialog_response(self, dialog, response):
		if response == gtk.RESPONSE_OK:
			color = dialog.colorsel.get_current_color()
			
			self.scale_color(color)
			
			self.insert_color("%02x%02x%02x" % (color.red, \
					color.green, color.blue))
		else:
			self._dialog.destroy()
			self._dialog = None
