# Sketch - A Python-based interactive drawing program
# Copyright (C) 1998, 1999, 2000 by Bernhard Herzog
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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 Library General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

#
#	Class MaskGroup
#
#	A special group where one object defines a clip mask for the
#	entire group
#

from app.events.warn import warn_tb, INTERNAL

from app.command import AddCmd

from app import _, IntersectRects, RegisterCommands
from compound import EditableCompound
from properties import EmptyFillStyle, EmptyLineStyle

import cids

class MaskGroup(EditableCompound):

	is_Group = 1
	is_MaskGroup = 1
	cid = cids.MASKGROUP

	_lazy_attrs = EditableCompound._lazy_attrs.copy()
	_lazy_attrs['mask_fill'] = 'update_mask_attrs'
	_lazy_attrs['mask_line'] = 'update_mask_attrs'

	commands = EditableCompound.commands[:]

	def __init__(self, objects=None, duplicate=None):
		EditableCompound.__init__(self, objects,
									duplicate=duplicate)

	def update_mask_attrs(self):
		if self.objects:
			mask = self.objects[0]
			if mask.has_properties:
				self.mask_fill = mask.Properties().Duplicate()
				self.mask_fill.AddStyle(EmptyLineStyle)
				if mask.has_line and mask.Properties().HasLine():
					self.mask_line = mask.Properties().Duplicate()
					self.mask_line.AddStyle(EmptyFillStyle)
				else:
					self.mask_line = None
			else:
				self.mask_line = self.mask_fill = None

	def update_rects(self):
		if self.objects:
			self.bounding_rect = self.objects[0].bounding_rect
			self.coord_rect = self.objects[0].coord_rect

	def ChildChanged(self, child):
		if child is self.objects[0]:
			if self.document is not None:
				self.document.AddClearRect(child.bounding_rect)
		EditableCompound.ChildChanged(self, child)

	def Info(self):
		return _("MaskGroup with %d objects") % len(self.objects)

	def Hit(self, p, rect, device):
		if self.objects[0].Hit(p, rect, device, clip=1):
			return EditableCompound.Hit(self, p, rect, device)

	def Blend(self, other, frac1, frac2):
		try:
			objs = self.objects
			oobjs = other.objects
			blended = []
			for i in range(min(len(objs), len(oobjs))):
				blended.append(Blend(objs[i], oobjs[i], frac1, frac2))
			return MaskGroup(blended)
		except:
			warn_tb(INTERNAL)
			raise MismatchError

	def Ungroup(self):
		objects = EditableCompound.GetObjects(self)
		# Move the mask, which is in objects[0] to the end of the
		# objects list, because it was on top of all other objects
		# before the group was created.
		#
		# Use a copy of the objects list or we're modifying the list
		# used by the mask group itself with unpredictable consequences
		# for undo.
		# XXX perhaps it would be better to have GetObjects return a
		# copy of the list.
		objects = objects[:]
		objects.append(objects[0])
		del objects[0]
		return objects

	def SaveToFile(self, file):
		file.BeginMaskGroup()
		for obj in self.objects:
			obj.SaveToFile(file)
		file.EndMaskGroup()

	def DrawShape(self, device, rect=None):
		if not self.objects:
			return
		mask = self.objects[0]
		if mask.has_properties:
			attr = mask.properties
			mask.properties = self.mask_fill
		device.PushClip()
		clipped = 1
		try:
			mask.DrawShape(device, rect, clip=1)
			if rect:
				rect = IntersectRects(rect, mask.bounding_rect)
				test = rect.overlaps
				for o in self.objects[1:]:
					if test(o.bounding_rect):
						o.DrawShape(device, rect)
			else:
				for obj in self.objects[1:]:
					obj.DrawShape(device)
			if self.mask_line is not None:
				device.PopClip()
				clipped = 0
				mask.properties = self.mask_line
				mask.DrawShape(device, rect)
		finally:
			if clipped:
				device.PopClip()
			if mask.has_properties:
				mask.properties = attr

	def permute_objects(self, permutation):
		# make sure the mask stays at index 0
		if permutation[0] != 0:
			permutation = list(permutation)
			permutation.remove(0)
			permutation.insert(0, 0)
		return EditableCompound.permute_objects(self, permutation)

	def Mask(self):
		return self.objects[0]

	def MaskedObjects(self):
		return self.objects[1:]

	def SelectMask(self):
		if self.document is not None:
			self.document.SelectObject(self.objects[0])
	AddCmd(commands, SelectMask, _("Select Mask"), key_stroke='m')

	context_commands = ('SelectMask',)

RegisterCommands(MaskGroup)
