
# Edit window stuff.
#
# Copyright (c) 1993, 1994 Pacific Marketing and Consulting
# Author:  Wayne A. Christopher, wayne@pmac.com
#          Pacific Marketing and Consulting, Berkeley, California
#

set edit_move_defaults [list "edit_move_incr 1" \
			    "edit_move_number 1" \
			    "edit_move_do_trans 0" \
			    "edit_move_xoff 0" \
			    "edit_move_yoff 0" \
			    "edit_move_zoff 0" \
			    "edit_move_do_rot 0" \
			    "edit_move_axis 0" \
			    "edit_move_amount 90" \
			    "edit_move_amount_other 0" \
			    "edit_move_do_mirror 0" \
			    "edit_move_mirroraxis 2" \
			    "edit_move_mirrorhow centroid" \
			    "edit_move_do_scale 0" \
			    "edit_move_scale_copies 0" \
			    "edit_move_scale 1" \
			    "edit_move_do_group 0" \
			    "edit_move_all 0" \
			    "edit_move_group {}"]

proc edit_move_set_units {prefix} {
    set defl [unit_get_default_choice length]
    foreach p {x y z} {
	global ${prefix}_${p}off_units
	if ![info exists ${prefix}_${p}off_units] {
	    set ${prefix}_${p}off_units $defl
	}
    }
}

proc edit_move_object_or_group {copy} {
    global coll_current
    
    if ![info exists coll_current] return
    
    if {$coll_current != ""} {
	if $copy {
	    coll_copy group
	} else {
	    coll_move group
	}
    } else {
	edit_move $copy
    }
}

proc edit_move {copy {collection ""} {colltype ""}} {
    global current_object edit_move_objects edit_move_all type_name
    global copy_move_all_options copy_move_only_translate edit_move_defaults
    
    check_name_conflicts
    
    set edit_move_all 0
    set copy_move_all_options 1
    if {$collection == ""} {
	set edit_move_objects [tree_list_selected]
	if {$edit_move_objects == ""} {
	    user_error "No selected objects to [expr {$copy ? "copy" : "move"}]."
	    return
	}
	
	set pmess 0
	set not_material_objects ""
	foreach obj $edit_move_objects {
	    set type [$obj getval obtype]
	    
	    if [info exists copy_move_only_translate($type)] {
		set copy_move_all_options 0
	    }
	    
	    if {$type == "material"} {
		set new [db_copy $obj]
		mess "New material \"[$new getval name]\" created\n"
		continue
	    } else {
		lappend not_material_objects $obj
	    }
	    
	    if {!$pmess && [object_in_part $obj]} {
		mess "Warning: transformations will be relative to the local coordinate system of the assembly\n" red
		incr pmess
	    }
	}
	
	if {$not_material_objects == "" } { 
	    return 
	} else {
	    set edit_move_objects $not_material_objects
	}
	
	if {[llength $edit_move_objects] == 1} {
	    set base [lindex $edit_move_objects 0]
	    set name [$base getval name]
	    set typen $type
	    if [info exists type_name($typen)] { set typen $type_name($typen) }
	    set title1 "$typen $name"
	} else {
	    set title1 "[llength $edit_move_objects] objects"
	}
	
	if $copy {
	    set title "Copy $title1"; set word Copy
	} elseif {$type == "domain"} {
	    set title "Move all objects in model"; set word Move
	    set edit_move_all 1
	} else {
	    set title "Move $title1"; set word Move
	}
	if $copy { set form .copy } else { set form .move }
	set docoll 0
	
    } else {
	set name [$collection getval name]
	
	if $copy {
	    set title "Copy $colltype $name"; set word Copy
	} else {
	    set title "Move $colltype $name"; set word Move
	}
	set edit_move_objects [lsort -command creation_order_cmp \
				   [$collection list_contents]]
	if $copy { set form .copy_coll } else { set form .move_coll }
	set docoll 1
    }
    
    set buts [list "Apply \"edit_move_apply $form $copy\"" \
      	           "Done  \"form_done $form\""]
    
    foreach vv $edit_move_defaults {
	set name [lindex $vv 0]
	global $name
	#if ![info exists $name] { set $name [lindex $vv 1] }
	# always reset these values
	set $name [lindex $vv 1]
    }
    edit_move_set_units edit_move
    if !$copy_move_all_options {
	set edit_move_do_rot 0
	set edit_move_do_scale 0
	set edit_move_do_mirror 0
    }

    set d [form_init $form "$title" "model move" $buts]
    if {$d == ""} return
    
    form_frame $d.fr sunken 3 {0 0}; set d $d.fr
        
    if $copy {
	form_frame $d.f1 flat 0 {1 0 -cols - -fill none}
	
	form_label $d.f1.nl "Number of copies" {3 1}
	form_entry $d.f1.n edit_move_number int {3 2 -cols 2}

	form_checkbutton $d.f1.gr "Group name" edit_move_do_group {5 1}
	form_entry $d.f1.gre edit_move_group string {5 2 -cols 2} edit_move_do_group

        form_rowheight $d 0 2
    }
    
    form_frame $d.transform flat 0 {2 0 -cols - -fill none}
    make_transform_form $d.transform edit_move copy_move_all_options
    
    if $copy {
	form_colwidth $d 0 4 M
	form_checkbutton $d.scalecopies "Scale copies cumulatively" \
	    edit_move_scale_copies {6 2 -cols 3 -fill none} edit_move_do_scale
    }
    
    form_finish
}

proc copy_move_close {} {
    catch {form_done .copy}
    catch {form_done .move}
}

proc make_transform_form {d prefix all_options_variable} {
    edit_move_set_units $prefix
    
    form_checkbutton $d.doscale Scale ${prefix}_do_scale {5 0 -cols 2} \
	$all_options_variable
    form_entry $d.s ${prefix}_scale string {5 2 -cols 3} \
	"${prefix}_do_scale $all_options_variable"
    
    form_checkbutton $d.domirror Mirror ${prefix}_do_mirror {10 0 -cols 2} \
	$all_options_variable
    form_label $d.fl "Plane" {11 1} "${prefix}_do_mirror $all_options_variable" 
    form_radiobutton $d.fz XY ${prefix}_mirroraxis 2 {11 2} \
	"${prefix}_do_mirror $all_options_variable"
    form_radiobutton $d.fx YZ ${prefix}_mirroraxis 0 {11 3} \
	"${prefix}_do_mirror $all_options_variable"
    form_radiobutton $d.fy XZ ${prefix}_mirroraxis 1 {11 4} \
	"${prefix}_do_mirror $all_options_variable"
    form_label $d.mal "About" {12 1} \
	"${prefix}_do_mirror $all_options_variable"
    form_radiobutton $d.mac Centroid ${prefix}_mirrorhow centroid \
	{12 2 -cols 3} "${prefix}_do_mirror $all_options_variable"
    form_radiobutton $d.mab "Low end" ${prefix}_mirrorhow lowend \
	{13 2 -cols 3} "${prefix}_do_mirror $all_options_variable"
    form_radiobutton $d.mat "High end" ${prefix}_mirrorhow highend \
	{14 2 -cols 3} "${prefix}_do_mirror $all_options_variable"
    
    form_checkbutton $d.dorot Rotate ${prefix}_do_rot {15 0 -cols 2} \
	$all_options_variable
    form_label $d.al "Axis" {16 1} "${prefix}_do_rot $all_options_variable"
    form_radiobutton $d.x X ${prefix}_axis 0 {16 2} \
	"${prefix}_do_rot $all_options_variable"
    form_radiobutton $d.y Y ${prefix}_axis 1 {16 3} \
	"${prefix}_do_rot $all_options_variable"
    form_radiobutton $d.z Z ${prefix}_axis 2 {16 4} \
	"${prefix}_do_rot $all_options_variable"
    form_label $d.aml "Degrees" {17 1} "${prefix}_do_rot $all_options_variable"
    form_radiobutton $d.d90  90  ${prefix}_amount 90  {17 2} \
	"${prefix}_do_rot $all_options_variable"
    form_radiobutton $d.d180 180 ${prefix}_amount 180 {17 3} \
	"${prefix}_do_rot $all_options_variable"
    form_radiobutton $d.d270 270 ${prefix}_amount 270 {17 4} \
	"${prefix}_do_rot $all_options_variable"
    if 0 {
	form_frame $d.doth flat 0 {18 2 -cols 3}
	form_radiobutton $d.doth.b Other ${prefix}_amount other {0 0} ${prefix}_do_rot
	form_entry $d.doth.r ${prefix}_amount_other float {0 1} \
	    [list ${prefix}_do_rot "${prefix}_amount other"]
    }
    
    form_checkbutton $d.dotrans Translate ${prefix}_do_trans {20 0 -cols 2}
    form_label $d.xil "X offset" {21 1} ${prefix}_do_trans
    form_label $d.yil "Y offset" {22 1} ${prefix}_do_trans
    form_label $d.zil "Z offset" {23 1} ${prefix}_do_trans
    model_unitentry $d.xi ${prefix}_xoff length {21 2 -cols 3} \
	${prefix}_do_trans
    model_unitentry $d.yi ${prefix}_yoff length {22 2 -cols 3} \
	${prefix}_do_trans
    model_unitentry $d.zi ${prefix}_zoff length {23 2 -cols 3} \
	${prefix}_do_trans
    
    form_colwidth $d 0 2 M
}

proc apply_transformation {obj prefix} {
    foreach shn [$obj getval section_list] {
	apply_transformation_shape [$obj getval ${shn}_shape] $prefix
    }
}

proc apply_transformation_shape {shape prefix} {
    foreach cmd [get_transformation_cmds $prefix] {
	eval $shape $cmd
    }
}

proc get_transformation_cmds {prefix} {
    set cmds ""
    foreach i {
	xoff yoff zoff axis amount amount_other mirroraxis scale
	do_trans do_rot do_scale do_mirror mirrorhow 
	xoff_units yoff_units zoff_units
    } {
	global ${prefix}_$i
	set $i [set ${prefix}_$i]
    }
    global ${prefix}_centroid
    if [info exists ${prefix}_centroid] {
	set cent [set ${prefix}_centroid]
    } else {
	set cent ""
    }
    foreach i {x y z} {
	set ${i}off1 [unit_convert [set ${i}off] [set ${i}off_units] 0]
    }
    
    if $do_trans  { lappend cmds [list translate "$xoff1 $yoff1 $zoff1"] }
    if $do_rot    {
	if {$amount == "other"} {
	    lappend cmds [list rotate $axis $amount_other $cent]
	} else {
	    lappend cmds [list rotate $axis $amount $cent]
	}
    }
    if $do_scale  {
	if {$scale <= 0} {
	    user_error "The scale must be a positive number"
	    set scale 1
	}
	lappend cmds [list scale $scale $cent]
    }
    if $do_mirror {
	switch [set ${prefix}_mirrorhow] {
	    centroid {
		lappend cmds [list mirror $mirroraxis $cent]
	    }
	    lowend {
		lappend cmds [list mirror $mirroraxis $cent]
	    }
	    highend {
		lappend cmds [list mirror $mirroraxis $cent]
	    }
	}
    }
    return $cmds
}

proc edit_move_apply {form copy {settings ""}} {
    global edit_move_objects edit_move_number edit_move_incr
    global edit_move_xoff edit_move_yoff edit_move_zoff
    global edit_move_xoff_units edit_move_yoff_units edit_move_zoff_units
    global edit_move_axis edit_move_amount edit_move_amount_other
    global edit_move_mirroraxis dont_move_sections_individually
    global edit_move_scale edit_move_scale_copies edit_move_mirrorhow
    global edit_move_group edit_do_group
    global edit_move_do_trans edit_move_do_rot edit_move_do_scale
    global edit_move_do_mirror domain edit_move_all edit_move_do_group
    global current_object edit_move_defaults app_has_transform
    
    check_name_conflicts
    
    edit_move_set_units edit_move
    
    if {$settings != ""} {
	foreach vv $edit_move_defaults {
	    set name [lindex $vv 0]
	    set [lindex $vv 0] [lindex $vv 1]
	}
	foreach {n v} $settings { set edit_move_$n $v }
    }
    
    if {$edit_move_objects == ""} return
    
    if {$form != ""} {
	if ![form_check_values $form] return
	form_widgets_to_variables $form
    }
    
    foreach i {x y z} {
	set ${i}off1 [unit_convert [set edit_move_${i}off] \
			  [set edit_move_${i}off_units] 0]
    }
    
    foreach i {x y z} {set $i [set ${i}off1]}
    set scale $edit_move_scale
    
    undo_group 1

    set lastone ""
    
    busy 1
    if $copy {
	# First figure out the centroid of the objects.  Then each time
	# we make a new copy, update the centroid.
	set cent [centroid_of_objects $edit_move_objects]
	set bbox [bbox_of_objects $edit_move_objects]

	switch $edit_move_mirrorhow {
	    centroid { set mcent $cent }
	    lowend   { set mcent [lindex $bbox 0] }
	    highend  { set mcent [lindex $bbox 1] }
	}
	set ntot [expr $edit_move_number * [llength $edit_move_objects]]
	if {$ntot > 32} {
	    interrupt begin "Copying objects" "" $ntot
	    set interr 1
	} else {
	    set interr 0
	}
	set count 0
	
	# A part and an object contained within that part might both
	# be in one group.  If that happens filter out the sub-objects so they
	# don't get copied twice.
	foreach old $edit_move_objects {
	    if {[$old getval obtype] == "part"} {
		set have_part($old) 1
	    }
	}
	set move_these_objects ""
	foreach old $edit_move_objects {
	    set ok 1
	    for {set par [$old get -model_container]} {$par != ""} \
		{set par [$par get -model_container]} {
		    if [info exists have_part($par)] { set ok 0; break }
		}
	    if $ok { lappend move_these_objects $old }
	}
	
	for {set i 0} {$i < $edit_move_number} {incr i} {
	    check_name_conflicts
    
	    if {$interr < 0} break
	    if $edit_move_do_trans {
 		set cent [list [expr {[lindex $cent 0] + $xoff1}] \
			      [expr {[lindex $cent 1] + $yoff1}] \
			      [expr {[lindex $cent 2] + $zoff1}]]
 		set mcent [list [expr {[lindex $mcent 0] + $xoff1}] \
			       [expr {[lindex $mcent 1] + $yoff1}] \
			       [expr {[lindex $mcent 2] + $zoff1}]]
	    }
	    foreach old $move_these_objects {
		incr count
		if {$interr && [interrupt update $count "Copying objects"]} {
		    set interr -1; break
		}
		
		set type [$old getval obtype]
		set is_part [expr {$type == "part"}]
		
		if {$type == "domain"} {
		    mess "Warning: not making a copy of the cabinet.\n"
		    continue
		}
		if $is_part {
		    set new [db_copy_part $old]
		} else {
		    set new [db_copy $old "" "" 0]
		}
		set lastone $new
		
		if [info exists app_has_transform($type)] {
		    set appfunc app_transform_$type
		} else {
		    set appfunc ""
		}
		
		set moved 0
		if {$appfunc == ""} {
		    set shapes [db_shapes $new]
		} else {
		    set shapes [db_shapes $new 0]
		}
		foreach sh $shapes {
		    set cmds ""
		    if $edit_move_do_trans {
			if {$appfunc != ""} {
			    $appfunc $sh translate "$x $y $z"
			} else {
			    $sh translate "$x $y $z"
			}
			edit_move_exprs $sh translate $x $y $z
			lappend cmds [list translate "$x $y $z"]
			set moved 1
		    }
		    if $edit_move_do_rot {
			if $is_part {
			    user_error "Sorry, can't rotate parts yet"
			    busy 0
			    return
			}
			if {$edit_move_amount == "other"} {
			    $sh rotate $edit_move_axis \
				$edit_move_amount_other $cent
			    lappend cmds [list rotate $edit_move_axis \
					      $edit_move_amount_other $cent]
			} else {
			    if {$appfunc != ""} {
				$appfunc $sh rotate $edit_move_axis \
				    $edit_move_amount $cent
			    } else {
				$sh rotate $edit_move_axis \
				    $edit_move_amount $cent
				
			    }
			    lappend cmds [list rotate $edit_move_axis \
					      $edit_move_amount $cent]
			}
			set moved 1
		    }
		    if $edit_move_do_scale {
			if {$scale <= 0} {
			    user_error "The scale must be a positive number"
			    set scale 1
			}
			if {$appfunc != ""} {
			    $appfunc $sh scale $scale $cent
			} else {
			    $sh scale $scale $cent
			}
			lappend cmds [list scale $scale $cent]
			    
			edit_move_exprs $sh scale $scale
			set moved 1
		    }
		    if $edit_move_do_mirror {
			if $is_part {
			    user_error "Sorry, can't mirror parts yet" 
			    busy 0
			    return
			}
			if {$appfunc != ""} {
			    $appfunc $sh mirror $edit_move_mirroraxis $mcent
			} else {
			    $sh mirror $edit_move_mirroraxis $mcent
			}
			lappend cmds [list mirror $edit_move_mirroraxis $mcent]
			set moved 1
		    }
		    if $moved { update_decorations $sh }
		}
		if {$shapes != ""} {
		    #named_point_copymove_object $new $old $cmds
		    profile_copymove_object $new $cmds
		}
		
		do_object_transformation $new edit_move_
		if {$edit_move_do_group && ($edit_move_group != "")} {
		    set grs [$new getval groups]
		    lappend grs $edit_move_group
		    $new setval groups $grs
		    coll_add group $edit_move_group $new
		}
		
		check_name_conflicts
    
		app_clean_up_$type $new
		undo_record_create_full $new
		switch [edit_check_outside $new 0] {
		    1 {
			# The user hit cancel
			edit_delete 0 $new
			set i $edit_move_number
			set lastone ""
			break
		    }
		    2 {
			# The user hit move or resize
			set i $edit_move_number
			break
		    }
		}
	    }
	    # update idle
	    set x [expr {$x + $xoff1}]
	    set y [expr {$y + $yoff1}]
	    set z [expr {$z + $zoff1}]
	    if $edit_move_scale_copies {
		set ns ""
		foreach s $scale ms $edit_move_scale {
		    lappend ns [expr {$ms * $s}]
		}
		set scale $ns
	    }
	    
	    if {!(($i + 1) % 10)} { update idle }
	}
	interrupt finish
	
	check_name_conflicts
    
	tree_sort_all
	
    } elseif {$edit_move_objects == $domain} {
	set bbox [[$domain getval body_shape] get_bbox]
	set cent [[$domain getval body_shape] get_centroid]
	switch $edit_move_mirrorhow {
	    centroid { set mcent $cent }
	    lowend   {
		set mcent 0
		foreach i {0 1 2} {
		    lappend mcent [lindex [lindex $bbox 0] $i]
		}
	    }
	    highend   {
		set mcent 0
		foreach i {0 1 2} {
		    lappend mcent [lindex [lindex $bbox 1] $i]
		}
	    }
	}
	
	set allobjs ""
	set partobjs ""
	foreach obj [db_list_objects_recursive] {
	    if {[$obj getval obtype] != "part"} {
		lappend allobjs $obj
	    } else {
		lappend partobjs $obj
	    }
	}
	
	# In some cases you can get multiple instances of the objects and
	# clearly that is bad...
	set allobjs [luniq $allobjs]
	set partobjs [luniq $partobjs]
	
	foreach obj $allobjs {
	    set type [$obj getval obtype]
	    
	    if [info exists app_has_transform($type)] {
		set appfunc app_transform_$type
	    } else {
		set appfunc ""
	    }
	    
	    if {$appfunc == ""} {
		set shapes [db_shapes $obj]
	    } else {
		set shapes [db_shapes $obj 0]
	    }
	    
	    foreach sh $shapes {
		catch {unset var1}
		foreach v [$sh list_values nooffset] {
		    set var1([lindex $v 0]) [lindex $v 1]
		}
		
		set cmds ""
		if $edit_move_do_trans {
		    if {$appfunc == ""} {
			$sh translate "$x $y $z"
		    }
		    edit_move_exprs $sh translate $x $y $z
		    lappend cmds [list translate "$x $y $z"]
		}
		if $edit_move_do_rot {
		    if {$edit_move_amount == "other"} {
			if {$appfunc == ""} {
			    $sh rotate $edit_move_axis \
				$edit_move_amount_other $cent
			}
			lappend cmds [list rotate $edit_move_axis \
					  $edit_move_amount_other $cent]
		    } else {
			if {$appfunc == ""} {
			    $sh rotate $edit_move_axis $edit_move_amount $cent
			}
			lappend cmds [list rotate $edit_move_axis \
					  $edit_move_amount $cent]
		    }
		}
		if $edit_move_do_scale {
		    if {$edit_move_scale <= 0} {
			user_error "The scale must be a positive number"
			set edit_move_scale 1
		    }
		    if {$appfunc == ""} {
			$sh scale $edit_move_scale $cent
		    }
		    edit_move_exprs $sh scale $edit_move_scale
		    lappend cmds [list scale $edit_move_scale $cent]
		}
		if $edit_move_do_mirror {
		    if {$appfunc == ""} {
			$sh mirror $edit_move_mirroraxis $mcent
		    }
		    lappend cmds [list mirror $edit_move_mirroraxis $mcent]
		}
		
		undo_record_move_shape $sh $cmds
		
		if [info exists app_has_transform($type)] {
		    set appfunc app_transform_$type
		} else {
		    set appfunc ""
		}
		
		if {$appfunc != ""} {
		    foreach c $cmds {
			eval $appfunc $sh $c
		    }
		    app_clean_up_$type $obj
		}
		update_decorations $sh
		
		if 0 {
		    set diff ""
		    foreach v [$sh list_values nooffset] {
			set ov $var1([lindex $v 0])
			if [string compare $ov [lindex $v 1]] {
			    lappend diff [list [lindex $v 0] $ov [lindex $v 1]]
			}
		    }
		    if {$diff != ""} {	    
			undo_record_change_shape $sh $diff
		    }
		}
	    }
	    do_object_transformation $obj edit_move_
	    if {$shapes != ""} {
		named_point_copymove_object $obj "" $cmds
		profile_copymove_object $obj $cmds
	    }
	}
	
	foreach obj $partobjs {
	    do_object_transformation $obj edit_move_
	}
	
	edit_reset_params
    } else {
	# First figure out the centroid of the objects.
	set cent [centroid_of_objects $edit_move_objects]
	set bbox [bbox_of_objects $edit_move_objects]
	
	switch $edit_move_mirrorhow {
	    centroid { set mcent $cent }
	    lowend   { set mcent [lindex $bbox 0] }
	    highend  { set mcent [lindex $bbox 1] }
	}
	
	set allobjs ""
	set partobjs ""
	foreach obj $edit_move_objects {
	    if {[$obj getval obtype] == "part"} {
		foreach sub [db_list_objects_recursive "" $obj] {
		    if {[$sub getval obtype] != "part"} {
			lappend allobjs $sub
		    }
		}
		lappend partobjs $obj
	    } else {
		lappend allobjs $obj
	    }
	}
	
	# In some cases you can get multiple instances of the objects and
	# clearly that is bad...
	set allobjs [luniq $allobjs]
	set partobjs [luniq $partobjs]
	
	foreach obj $allobjs {
	    set type [$obj getval obtype]
	    
	    set one_sh [db_shapes $obj 0]
	    if {$one_sh == ""} continue
	    
	    set this_cent [$one_sh transform_coords_inverse $cent]
	    set this_mcent [$one_sh transform_coords_inverse $mcent]
	    
	    set cmds ""
	    if $edit_move_do_trans {
		lappend cmds [list translate "$x $y $z"]
	    }
	    if $edit_move_do_rot {
		if {$edit_move_amount == "other"} {
		    lappend cmds [list rotate $edit_move_axis \
				      $edit_move_amount_other $this_cent]
		} else {
		    lappend cmds [list rotate $edit_move_axis \
				      $edit_move_amount $this_cent]
		}
	    }
	    if $edit_move_do_scale {
		if {$edit_move_scale <= 0} {
		    user_error "The scale must be a positive number"
		    set edit_move_scale 1
		}
		lappend cmds [list scale $edit_move_scale $this_cent]
	    }
	    if $edit_move_do_mirror {
		lappend cmds [list mirror $edit_move_mirroraxis $this_mcent]
	    }
	    if [info exists app_has_transform($type)] {
		set appfunc app_transform_$type
	    } else {
		set appfunc ""
	    }
	    if [info exists dont_move_sections_individually($type)] {
		if {$appfunc != ""} {
		    foreach c $cmds {
			eval $appfunc [db_shapes $obj 0] $c
		    }
		    app_clean_up_$type $obj
		}
		set shapes [db_shapes $obj]
		foreach ss $shapes { update_decorations $ss }
		undo_record_move_shape [db_shapes $obj 0] $cmds
		
	    } else {
		if {$appfunc == ""} {
		    set shapes [db_shapes $obj]
		} else {
		    set shapes [db_shapes $obj 0]
		}
		foreach sh $shapes {
		    catch {unset var1}
		    foreach v [$sh list_values nooffset] {
			set var1([lindex $v 0]) [lindex $v 1]
		    }
		    
		    if $edit_move_do_trans {
			if {$appfunc == ""} {
			    $sh translate "$x $y $z"
			}
			edit_move_exprs $sh translate $x $y $z
		    }
		    if $edit_move_do_rot {
			if {$appfunc == ""} {
			    if {$edit_move_amount == "other"} {
				$sh rotate $edit_move_axis \
				    $edit_move_amount_other $this_cent
			    } else {
				$sh rotate $edit_move_axis $edit_move_amount $this_cent
			    }
			}
		    }
		    if $edit_move_do_scale {
			if {$edit_move_scale <= 0} {
			    user_error "The scale must be a positive number"
			    set edit_move_scale 1
			}
			if {$appfunc == ""} {
			    $sh scale $edit_move_scale $this_cent
			}
			edit_move_exprs $sh scale $edit_move_scale
		    }
		    if $edit_move_do_mirror {
			if {$appfunc == ""} {
			    $sh mirror $edit_move_mirroraxis $this_mcent
			}
		    }
		    if {$appfunc != ""} {
			foreach c $cmds {
			    eval $appfunc $sh $c
			}
			app_clean_up_$type $obj
		    }
		    
		    update_decorations $sh
		    
		    undo_record_move_shape $sh $cmds
		}
	    }
	    do_object_transformation $obj edit_move_
	    if {$shapes != ""} {
		named_point_copymove_object $obj "" $cmds
		profile_copymove_object $obj $cmds
	    }
	    if {$obj == $current_object} edit_reset_params
	    
	    if {$form != ""} {
		if [edit_check_outside $obj 1] {
		    break
		}
	    }
	}
	
	foreach obj $partobjs {
	    do_object_transformation $obj edit_move_
	}
	edit_reset_params
    }
    
    busy 0
    
    check_name_conflicts
    
    undo_group 0
    
    db_dirty mesh
    
    if {$lastone != ""} { model_set_current $lastone }
    
    check_name_conflicts
}

proc edit_move_exprs {sh how args} {
    switch $how {
	translate {
	    set only_match ?
	    # This is the lazy way to do it
	    catch { if {[$sh getval diff_flag] == 1} { set only_match s } }
	    foreach dir {x y z} incr $args {
		if !$incr continue
		foreach {p v} [$sh get_all_exprs] {
		    if {[string match ${dir}${only_match} $p]} {
			set ll [split $v +]
			set lv [lindex $ll end]
			set lr [lrange $ll 0 end-1]
			set un [$sh getunits $p]
			set incru [unit_convert $incr $un 1]
			if {[scan $lv %g lv1] == 1} {
			    set lv2 [expr $lv1 + $incru]
			    set v [join [concat $lr $lv2] +]
			} else {
			    set v "$v+$incru"
			}
			$sh setexpr $p $v
		    }
		}
	    }
	}
	scale {
	    set sc [lindex $args 0]
	    if {![catch {$sh getval diff_flag} res] && ($res == 1)} {
		foreach {p v} [$sh get_all_exprs] {
		    if {[string match ?d $p]} {
			set ll [split $v *]
			set lv [lindex $ll end]
			set lr [lrange $ll 0 end-1]
			if {[scan $lv %g lv1] == 1} {
			    set lv2 [expr $lv1 * $sc]
			    set v [join [concat $lr $lv2] *]
			} else {
			    set v "$v*$sc"
			}
			$sh setexpr $p $v
		    }
		}
	    }
	}
    }
}

proc edit_move_object_location_exprs {obj how args} {
    switch $how {
	translate {
	    foreach dir {x y z} incr $args {
		if !$incr continue
		foreach se {s e c} {
		    if ![$obj checkval $dir$se] continue
		    set v [$obj si_expr $dir$se]
		    if [string match *\$* $v] {
			set ll [split $v +]
			set lv [lindex $ll end]
			set lr [lrange $ll 0 end-1]
			if {[scan $lv %g lv1] == 1} {
			    set lv2 [expr $lv1 + $incr]
			    set v [join [concat $lr $lv2] +]
			} else {
			    set v "$v+$incr"
			}
		    } else {
			set v [expr $v + $incr]
		    }
		    $obj setval $dir$se $v
		}
	    }
	}
    }
}

proc do_object_transformation {obj prefix} {
    global app_has_apply_transform
    set type [$obj getval obtype]
    if [info exists app_has_apply_transform($type)] {
	app_apply_transform_$type $obj $prefix
    }
}

proc move_whole_object {obj off} {
    global app_has_transform
    set type [$obj getval obtype]
    if [info exists app_has_transform($type)] {
	app_transform_$type [db_shapes $obj 0] translate $off
    } else {
	foreach sh [db_shapes $obj] {
	    $sh translate $off
	}
    }
    app_clean_up_$type $obj
    
    foreach sh [db_shapes $obj] {
	update_decorations $sh 1 1
    }
}

# This is called from app_transform_XXX

proc transform_object_coords {obj pref suff info} {
    set cc ""
    
    foreach i {0 1 2} {
	set x [lindex {x y z} $i]
	set vv ${pref}$x$suff
	if ![$obj checkval $vv] {
	    # it won't matter
	    lappend cc 0
	} else {
	    lappend cc [$obj si_value $vv]
	}
    }
    
    set cc1 [eval transform_coords \"$cc\" $info]
    
    foreach i {0 1 2} {
	set x [lindex {x y z} $i]
	set vv ${pref}$x$suff
	if [$obj checkval $vv] {
	    $obj setval $vv [lindex $cc1 $i]
	}
    }
}

proc transform_side_name {name sh how arglist} {
    set num [db_side_number_from_name $sh $name]
    set dir [db_direction_from_side_number $sh $num]
    switch $how {
	rotate {
	    set dir [eval transform_coords \"$dir\" nt_rotate $arglist]
	}
	mirror {
	    set dir [eval transform_coords \"$dir\" mirror $arglist]
	}
    }
    return [db_side_number_from_direction $sh $dir]
}					
