#!/usr/bin/python
import gimp, gimpplugin
from gimpfu import *
pdb = gimp.pdb
import os

black = (0,0,0)
white = (255,255,255)
gray = (128,128,128)

neutral_color = {
    NORMAL_MODE:   -1,
    DISSOLVE_MODE: -1,
    MULTIPLY_MODE: white,
    DIVIDE_MODE:   white,
    SCREEN_MODE:   black,
    OVERLAY_MODE:  gray,
    DODGE_MODE:    black,
    BURN_MODE:     white,
    HARDLIGHT_MODE:gray,
    SOFTLIGHT_MODE:gray,
    GRAIN_EXTRACT_MODE: gray,
    GRAIN_MERGE_MODE:   gray,
    DIFFERENCE_MODE:    black,
    ADDITION_MODE: black,
    SUBTRACT_MODE: black,
    DARKEN_ONLY_MODE:   white,
    LIGHTEN_ONLY_MODE:  black,
    HUE_MODE:      -1,
    SATURATION_MODE:    -1,
    COLOR_MODE:    -1,
    VALUE_MODE:    -1,
}    

class ApplyLayer(gimpplugin.plugin):
    def query(self):
	gimp.install_procedure ('plug_in_apply_layer', 
	    'Apply the contents of the layer to the layer below, and clear this layer to have no effect.',

	    "Apply the pixels in this layer to the layer below, like 'merge down' "
            "but preserve this layer's mode, opacity, parasites... everything but pixel "
	    "content, unlike 'merge down', which destroys the layer being merged down. \n\n"
	    
	    "Neutralizes the content of the revised layer to initially have no effect on "
	    "the background. For layers with alpha, that means the layer is emptied. "
	    "Layers without alpha are cleared to a neutral color, dependent on the layer "
	    "mode used, except for the modes where there is no neutral value; in those cases, "
	    "alpha channel is added and the layer is emptied.\n\n"
	    "Useful for incremental paint application and other incremental applications of all kinds.\n"
	    "Layer masks are preserved; this is useful for easily working on a restricted area.",

	    'David Gowers <00ai99@gmail.com>','2006','David Gowers <00ai99@gmail.com>',
	    'A_pply', 
	    '*',
	    PLUGIN, 
	    [(PF_INT, 'run_mode', "Run mode (not used)"), (PF_IMAGE, 'image', 'Image'), (PF_DRAWABLE, 'drawable', 'Drawable')],
	    [])

        gimp.menu_register('plug_in_apply_layer','<Image>/_Layer')
        gimp.menu_register('plug_in_apply_layer','<Layers>/')

    def plug_in_apply_layer(self, run_mode, image, drawable = None):
        from gettext import gettext as _
        if drawable == None: # being called from layers dialog, no layer provided(?)
	    drawable = image.active_layer
            
	# preserve parasites, vectorlayer info, positioning.. everything but pixels
	# operate on the parent layer if the layer mask is active
	if drawable.is_layer_mask:
	    drawable = filter(lambda v: v.mask == drawable, image.layers)[0]
	# not applicable to bottommost layer
	# 
	if image.layers[-1] == drawable:
	    gimp.message(_('"Apply layer" must be used on a layer above the bottom of the layer stack'))
	    return
	image.undo_group_start()
	fillcolor = neutral_color[drawable.mode]
	# create a new layer with the same positioning, mode, parasites... as the old layer.
	if fillcolor == -1:
	    # add alpha to layers that otherwise couldn't be neutralized.
	    newlayer = pdb.gimp_layer_copy (drawable, 1)
	else:
	    newlayer = pdb.gimp_layer_copy (drawable, 0)
	oldname = drawable.name
	# merge down, expand as needed. Expand as needed Chosen for the policy of always preserving
	# the maximum amount of useful data.
	image.merge_down (drawable, 0) 
	if newlayer.has_alpha:
	    newlayer.fill (TRANSPARENT_FILL)
	    # layer mask is preserved.
	else:
	    pdb.gimp_context_push()
	    gimp.set_foreground (fillcolor)
	    newlayer.fill (FOREGROUND_FILL)
	    pdb.gimp_context_pop()
	newlayer.name = oldname
	image.add_layer (newlayer) 
	image.undo_group_end()
	gimp.displays_flush()
    
    def start(self):
	gimpplugin.plugin.start(self)
	
if __name__ == '__main__':
    ApplyLayer().start()
