/* Gnome gxsm - Gnome X Scanning Microscopy
 * universal STM/AFM/SARLS/SPALEED/... controlling and
 * data analysis software
 * 
 * Gxsm Plugin Name: DSPMover.C
 * ========================================
 * 
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Authors: Percy Zahl <zahl@fkp.uni-hannover.de>
 * additional features: Andreas Klust <klust@fkp.uni-hannover.de>
 *
 * 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.
 */

/* Please do not change the Begin/End lines of this comment section!
 * this is a LaTeX style section used for auto generation of the PlugIn Manual 
 * Chapter. Add a complete PlugIn documentation inbetween the Begin/End marks!
 * All "% PlugInXXX" commentary tags are mandatory
 * All "% OptPlugInXXX" tags are optional
 * --------------------------------------------------------------------------------
% PlugInModuleIgnore

% BeginPlugInDocuSection
% PlugInDocuCaption: Mover/Slider and coarse approch control (OBSOLETE)
% PlugInName: DSPmover
% PlugInAuthor: Percy Zahl
% PlugInAuthorEmail: zahl@users.sf.net
% PlugInMenuPath: windows-sectionDSP Mover

% PlugInDescription
Gxsm or better the DSP software provides a set of commands for coarse
motion control. This includes a automatic approach and a general
purpose mover/slider control (X/Y/Z/...) control.

Supported are two principal types of coarse motion, which are to be
configured via the preferences dialog. According to the type the
presented control dialog will be different.

\begin{description}
\item[Slider] Are all type of coarse approach mechanism, which are
  driven by a external high voltage slider control box and from point
  of the Gxsm/DSP they are controlled by selecting a slider, setting a
  direction bit and putting out a clock/pulse signal to make a single
  step (width is controlled by some external settings on that box,
  Gxsm does not know about). Therefore the DSP's digital IO is used.
  Details of the wiring to IO-ports and control sequence are software
  coded in the DSP program, but can quit easy be changed/adapted, get
  in contact with the Gxsm team!  Out of the box supported types are
  the old and newer Omicron coarse motion slider controls. (used in
  Bremen and Essen, refer to the Gxsm user address list here:\\
  \GxsmWebLink{gxsm.sourceforge.net/index.php?content=gxsmusersfragment}
\item[Mover] Are all type of ``simple'' slip and stick motions
  generated by some hard metal sliding on some other hard stuff (like
  sapphire balls), used typically in Besocke and Beetle type SPM
  designs, where a set of XY tube piezos is used to drive a circular
  up- and down motion of a ramp.  Therefore a sawtooth like (here a
  special parabola shaped voltage curve is used) voltage is generated
  by the DSP and amplified by the same piezodrive which is used for
  scanning as well.
\end{description}

% PlugInUsage
The control dialog is opened via \GxsmMenu{windows-sectionDSP Mover} and
appears dependen on configurated mode:

\GxsmScreenShot{MoverControl-Rotation}{The Mover-Control window in Rotation settings.}
\GxsmScreenShot{MoverControl-Auto}{The Mover-Control window in Auto folder.}
\GxsmScreenShot{SliderControl}{The Slider-Control version.}


%% OptPlugInSubSection: replace this line by the subsection caption
%all following lines until next tag are going into this subsection


% OptPlugInConfig
Setup the control type:\\
\GxsmPref{User}{User/SliderControlType}: mover,slider\\
%
Set motion control parameters:\\
Motion Parameter Set Folders (AFM/Besocke mover type): Besocke X\&Y, PSD, Rotation, Lens, Auto\\
Settings: Amplitude, Speed, Steps\\


% OptPlugInKnownBugs
In case the mover did not stop (can happen with older DSP software
versions) press and release the center stop button or the same
direction button again. To be safe, limit the maximal number of steps!

% OptPlugInNotes
This PlugIn is just the user frontend to interact with the DSP's
mover/slider control commands.

% OptPlugInHints
Using the WRM Piezo drive, set Z gain to 15 and Z Offset to 6.0! Set
popper U, I and CI values! And set the Z-gain to 10 or 15 in the DSP
Control!

% EndPlugInDocuSection
 * -------------------------------------------------------------------------------- 
 */

#include <gtk/gtk.h>
#include "config.h"
#include "plugin.h"

static void DSPMover_about( void );
static void DSPMover_query( void );
static void DSPMover_cleanup( void );

static void DSPMover_show_callback( GtkWidget*, void* );

GxsmPlugin DSPMover_pi = {
	NULL,
	NULL,
	0,
	NULL,
	"DSPMover",
	"+spmHARD +srangerspmHARD +Innovative_DSP:SPMHARD +SRanger:SPMHARD +LAN_RHK:SPMHARD +STM +AFM",
//  "+ALL",
	NULL,
	"Percy Zahl",
	"windows-section",
	N_("DSP Mover"),
	N_("open the DSP Mover controlwindow"),
	"DSP mover control",
	NULL,
	NULL,
	NULL,
	DSPMover_query,
	DSPMover_about,
	NULL,
	NULL,
	DSPMover_cleanup
};

static const char *about_text = N_("Gxsm DSPMover Plugin:\n"
                                   "This plugin runs a control window to send "
                                   "slider/mover motion commands to the DSP.\n"
                                   "There is a autoapproch control "
                                   "(STM/AFM: Slider and Besocke Type) "
                                   "an a general mover/slider motion control."
	);


GxsmPlugin *get_gxsm_plugin_info ( void ){ 
	DSPMover_pi.description = g_strdup_printf(N_("Gxsm DSPMover plugin %s"), VERSION);
	return &DSPMover_pi; 
}


#include "gxsm_app.h"

#include "unit.h"
#include "pcs.h"
#include "xsmtypes.h"
#include "glbvars.h"

#include "include/dsp-pci32/xsm/xsmcmd.h"



typedef union AmpIndex {
	struct { unsigned char ch, x, y, z; } s;
	unsigned long   l;
};

 
class DSPMoverControl : public AppBase{
public:
	DSPMoverControl(DSP_Param *Dsp, XSM_Hardware *Hard, int InWindow=TRUE);
	virtual ~DSPMoverControl();

	void update();
	void updateDSP(int sliderno=-1);
	static void ExecCmd(int cmd);
	static void ChangedNotify(Param_Control* pcs, gpointer data);
	static int CmdAction(GtkWidget *widget, DSPMoverControl *dspc);
	static int StopAction(GtkWidget *widget, DSPMoverControl *dspc);

private:
	void create_folder();

	UnitObj *Unity, *Volt, *Time, *Length;
	DSP_Param    *dsp;
	XSM_Hardware *hard;
};

DSPMoverControl *DSPMoverClass = NULL;

static void DSPMover_query(void)
{
	static GnomeUIInfo menuinfo[] = { 
		{ GNOME_APP_UI_ITEM, 
		  DSPMover_pi.menuentry, DSPMover_pi.help,
		  (gpointer) DSPMover_show_callback, NULL,
		  NULL, GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_BLANK, 
		  0, GDK_CONTROL_MASK, NULL },

		GNOMEUIINFO_END
	};

	gnome_app_insert_menus(GNOME_APP(DSPMover_pi.app->getApp()), DSPMover_pi.menupath, menuinfo);

	// new ...

	DSPMoverClass = new DSPMoverControl
		( & DSPMover_pi.app->xsm->data.hardpars, 
		  DSPMover_pi.app->xsm->hardware
			);
	DSPMoverClass->SetResName ("WindowMoverControl", "false", xsmres.geomsave);

	if(DSPMover_pi.status) g_free(DSPMover_pi.status); 
	DSPMover_pi.status = g_strconcat(N_("Plugin query has attached "),
					 DSPMover_pi.name, 
					 N_(": DSPMover is created."),
					 NULL);
}

static void DSPMover_about(void)
{
	const gchar *authors[] = { "Percy Zahl", NULL};
	gtk_show_about_dialog (NULL, "program-name",  DSPMover_pi.name,
					"version", VERSION,
					  "license", GTK_LICENSE_GPL_3_0,
					  "comments", about_text,
					  "authors", authors,
					  NULL
				));
}

static void DSPMover_cleanup( void ){
	PI_DEBUG (DBG_L2, "DSPMover Plugin Cleanup" );
	gchar *mp = g_strconcat(DSPMover_pi.menupath, DSPM
	xrm.Get("hardpars.MOV_Steps", &hardpars.MOV_Steps, "100.0");
	xrm.Get("hardpars.TIP_nSteps", &hardpars.TIP_nSteps, "10");
	xrm.Get("hardpars.TIP_Delay", &hardpars.TIP_Delay, "0.1");
	xrm.Get("hardpars.TIP_DUz", &hardpars.TIP_DUz, "5");
	xrm.Get("hardpars.TIP_DUzRev", &hardpars.TIP_DUzRev, "5");
	xrm.Get("hardpars.AFM_Amp", &hardpars.AFM_Amp);
	xrm.Get("hardpars.AFM_Speed", &hardpars.AFM_Speed);
	xrm.Get("hardpars.AFM_Steps", &hardpars.AFM_Steps);

  	// defaults for AFM Mover (hardware SICAF)
	// [0] Besocke XY
	// [1] Besocke rotation
	// [2] Besocke PSD
	// [3] Besocke lens
  
	xrm.Get("hardpars.AFM_usrAmp0", &hardpars.AFM_usrAmp[0], "3.0");
	xrm.Get("hardpars.AFM_usrAmp1", &hardpars.AFM_usrAmp[1], "3.5");
	xrm.Get("hardpars.AFM_usrAmp2", &hardpars.AFM_usrAmp[2], "8.0");
	xrm.Get("hardpars.AFM_usrAmp3", &hardpars.AFM_usrAmp[3], "8.0");
	xrm.Get("hardpars.AFM_usrSpeed0", &hardpars.AFM_usrSpeed[0], "3");
	xrm.Get("hardpars.AFM_usrSpeed1", &hardpars.AFM_usrSpeed[1], "3");
	xrm.Get("hardpars.AFM_usrSpeed2", &hardpars.AFM_usrSpeed[2], "5");
	xrm.Get("hardpars.AFM_usrSpeed3", &hardpars.AFM_usrSpeed[3], "5");
	xrm.Get("hardpars.AFM_usrSteps0", &hardpars.AFM_usrSteps[0], "10");
	xrm.Get("hardpars.AFM_usrSteps1", &hardpars.AFM_usrSteps[1], "2");
	xrm.Get("hardpars.AFM_usrSteps2", &hardpars.AFM_usrSteps[2], "100");
	xrm.Get("hardpars.AFM_usrSteps3", &hardpars.AFM_usrSteps[3], "100");




#define ARROW_SIZE 40

static gboolean create_window_key_press_event_lcb(GtkWidget *widget, GdkEventKey *event,GtkWidget *win)
{
	if (event->keyval == GDK_Escape) {
		PI_DEBUG (DBG_L2, "Got escape\n" );
		return TRUE;
	}
	return FALSE;
}
			

void DSPMoverControl::create_folder (){
	GSList *EC_list=NULL;

	Gtk_EntryControl *ec;

	GtkWidget *box, *vbox_param, *hbox_param, *hbox, *vbox_param2, *frame_param;
	GtkWidget *input;
	GtkWidget *notebook;
	GtkWidget *MoverCrtl;
	GtkWidget *tab, *button;
	GtkAccelGroup *accel_group=NULL;

	if( IS_MOVER_CTRL ){
		accel_group = gtk_accel_group_new ();
		AppWindowInit(MOV_MOVER_TITLE);
	}
	else
		AppWindowInit(MOV_SLIDER_TITLE);

	box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_widget_show (box);
	gtk_box_pack_start (GTK_BOX (vbox), box, TRUE, TRUE, 0);

	// ========================================
	notebook = gtk_notebook_new ();
	gtk_widget_show (notebook);
	gtk_box_pack_start(GTK_BOX(box), notebook, TRUE, TRUE, GXSM_WIDGET_PAD);

	const char *MoverNames[] = { "Besocke X&Y", "Rotation", "PSD", "Lens", "Auto", NULL};

	Gtk_EntryControl *Ampl, *Spd, *Stp;
	int i,itab;
	for(itab=i=0; MoverNames[i]; ++i){
		if( IS_SLIDER_CTRL && i != 4 ) continue;
		PI_DEBUG (DBG_L2, "DSPMoverControl::SiCaf - Mover:" << MoverNames[i]);

		vbox_param = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
		gtk_widget_show (vbox_param);
		MoverCrtl = gtk_label_new (MoverNames[i]);
		gtk_widget_show (MoverCrtl);
		itab++;
		gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox_param, MoverCrtl);

		if( i<4 ){
			input = mygtk_create_input  ("Amplitude", vbox_param, hbox_param);
			g_object_set_data( G_OBJECT (input), "MoverNo", (gpointer)i);
			Ampl = new Gtk_EntryControl (Volt, MLD_WERT_NICHT_OK, &dsp->AFM_usrAmp[i], 0., 20., "5.2f", input);
			Ampl->Set_ChangeNoticeFkt   (DSPMoverControl::ChangedNotify, this);
			EC_list = g_slist_prepend   ( EC_list, Ampl);
      
			input = mygtk_create_input  ("Speed", vbox_param, hbox_param);
			g_object_set_data( G_OBJECT (input), "MoverNo", (gpointer)i);
			Spd = new Gtk_EntryControl  (Unity, MLD_WERT_NICHT_OK, &dsp->AFM_usrSpeed[i], 1., 20., "4.0f", input);
			Spd->Set_ChangeNoticeFkt    (DSPMoverControl::ChangedNotify, this);
			EC_list = g_slist_prepend   ( EC_list, Spd);
      
			input = mygtk_create_input  ("Steps", vbox_param, hbox_param);
			g_object_set_data( G_OBJECT (input), "MoverNo", (gpointer)i);
			Stp = new Gtk_EntryControl  (Unity, MLD_WERT_NICHT_OK, &dsp->AFM_usrSteps[i], 1., 5000., "4.0f", input);
			Stp->Set_ChangeNoticeFkt    (DSPMoverControl::ChangedNotify, this);
			EC_list = g_slist_prepend   ( EC_list, Stp);
		}
		if(i==4){
			if( IS_MOVER_CTRL ){
				frame_param = gtk_frame_new ("Mover Timing");
				gtk_widget_show (frame_param);
				gtk_container_add (GTK_CONTAINER (vbox_param), frame_param);
        
				vbox_param2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
				gtk_widget_show (vbox_param2);
				gtk_container_add (GTK_CONTAINER (frame_param), vbox_param2);
      
				input = mygtk_create_input("Ampl.", vbox_param2, hbox_param);
				ec = new Gtk_EntryControl (Volt, MLD_WERT_NICHT_OK, &dsp->MOV_Ampl, 0., 20., "5.2f", input);
				ec->Set_ChangeNoticeFkt(DSPMoverControl::ChangedNotify, this);
				EC_list = g_slist_prepend( EC_list, ec);
        
				input = mygtk_create_input("Speed", vbox_param2, hbox_param);
				ec = new Gtk_EntryControl (Unity, MLD_WERT_NICHT_OK, &dsp->MOV_Speed, 1., 20., "4.0f", input);
				ec->Set_ChangeNoticeFkt(DSPMoverControl::ChangedNotify, this);
				EC_list = g_slist_prepend( EC_list, ec);
        
				input = mygtk_create_input("Steps", vbox_param2, hbox_param);
				ec = new Gtk_EntryControl (Unity, MLD_WERT_NICHT_OK, &dsp->MOV_Steps, 1., 5000., "4.0f", input);
				ec->Set_ChangeNoticeFkt(DSPMoverControl::ChangedNotify, this);
				EC_list = g_slist_prepend( EC_list, ec);
			}
      
			// ========================================
      
			gchar *labtmp;
			if(IS_MOVER_CTRL || IS_SLIDER_CTRL )
				labtmp=g_strconcat("Auto Approch Timing [", xsmres.SliderControlType, "]",NULL);
			else
				labtmp=g_strconcat("Auto Approch Timing [", xsmres.SliderControlType, "]<=ERROR ",NULL);
			frame_param = gtk_frame_new (labtmp);
			g_free(labtmp);
			gtk_widget_show (frame_param);
			gtk_container_add (GTK_CONTAINER (vbox_param), frame_param);

			vbox_param2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
			gtk_widget_show (vbox_param2);
			gtk_container_add (GTK_CONTAINER (frame_param), vbox_param2);

			if(! IS_MOVER_CTRL ){
				input = mygtk_create_input("Steps", vbox_param2, hbox_param);
				ec = new Gtk_EntryControl (Unity, MLD_WERT_NICHT_OK, &dsp->TIP_nSteps, 0., 100., "4.0f", input);
				ec->Set_ChangeNoticeFkt(DSPMoverControl::ChangedNotify, this);
				EC_list = g_slist_prepend( EC_list, ec);
        
				input = mygtk_create_input("Delay", vbox_param2, hbox_param);
				ec = new Gtk_EntryControl (Time, MLD_WERT_NICHT_OK, &dsp->TIP_Delay, 0.0005, 1., "6.5f", input);
				ec->Set_ChangeNoticeFkt(DSPMoverControl::ChangedNotify, this);
				EC_list = g_slist_prepend( EC_list, ec);
			}

			input = mygtk_create_input("Z++", vbox_param2, hbox_param);
			ec = new Gtk_EntryControl (Unity, MLD_WERT_NICHT_OK, &dsp->TIP_DUz, 1., 32000., "5.0f", input);
			ec->Set_ChangeNoticeFkt(DSPMoverControl::ChangedNotify, this);
			EC_list = g_slist_prepend( EC_list, ec);
      
			input = mygtk_create_input("Z--", vbox_param2, hbox_param);
			ec = new Gtk_EntryControl (Unity, MLD_WERT_NICHT_OK, &dsp->TIP_DUzRev, 1., 32000., "5.0f", input);
			ec->Set_ChangeNoticeFkt(DSPMoverControl::ChangedNotify, this);
			EC_list = g_slist_prepend( EC_list, ec);

		}
		// ========================================
    
		tab = gtk_table_new (5, 3, FALSE);
		gtk_widget_show (tab);
		gtk_container_add (GTK_CONTAINER (vbox_param), tab);
    
		// ========================================
		// Direction Buttons

		if( IS_MOVER_CTRL ){

			// STOP
			button = gtk_button_new();
			gtk_widget_set_size_request (button, ARROW_SIZE, ARROW_SIZE);
			gtk_widget_show (button);
			gtk_table_attach (GTK_TABLE (tab), button, 2, 3, 1, 2,
					  (GtkAttachOptions)(GTK_FILL),
					  (GtkAttachOptions)(GTK_FILL),
					  FALSE, FALSE);
			g_signal_connect (G_OBJECT (button), "pressed",
					    G_CALLBACK (DSPMoverControl::StopAction),
					    this);
			// UP
			button = create_arrow_button(GTK_ARROW_UP, GTK_SHADOW_OUT);
			gtk_widget_set_size_request (button, ARROW_SIZE, ARROW_SIZE);
			gtk_widget_show (button);
			gtk_table_attach (GTK_TABLE (tab), button, 2, 3, 0, 1,
					  (GtkAttachOptions)(GTK_FILL),
					  (GtkAttachOptions)(GTK_FILL),
					  FALSE, FALSE);
			g_object_set_data( G_OBJECT (button), "DSP_cmd", (gpointer)DSP_CMD_AFM_MOV_YP);
			g_object_set_data( G_OBJECT (button), "MoverNo", (gpointer)i);
			g_signal_connect (G_OBJECT (button), "pressed",
					    G_CALLBACK (DSPMoverControl::CmdAction),
					    this);
			g_signal_connect (G_OBJECT (button), "released",
					    G_CALLBACK (DSPMoverControl::StopAction),
					    this);
			g_signal_connect(G_OBJECT(box), "key_press_event", 
					   G_CALLBACK(create_window_key_press_event_lcb), this);

//      gtk_widget_add_accelerator (button, "pressed", accel_group,
//                                  GDK_F3+4*i, (GdkModifierType)0,
//                                  GTK_ACCEL_VISIBLE);
      
			// LEFT
			button = create_arrow_button(GTK_ARROW_LEFT, GTK_SHADOW_OUT);
			gtk_widget_set_size_request (button, ARROW_SIZE, ARROW_SIZE);
			gtk_widget_show (button);
			gtk_table_attach (GTK_TABLE (tab), button, 1, 2, 1, 2,
					  (GtkAttachOptions)(GTK_FILL),
					  (GtkAttachOptions)(GTK_FILL),
					  FALSE, FALSE);
			g_object_set_data( G_OBJECT (button), "DSP_cmd", (gpointer)DSP_CMD_AFM_MOV_XM);
			g_object_set_data( G_OBJECT (button), "MoverNo", (gpointer)i);
			g_signal_connect (G_OBJECT (button), "pressed",
					    G_CALLBACK (DSPMoverControl::CmdAction),
					    this);
			g_signal_connect (G_OBJECT (button), "released",
					    G_CALLBACK (DSPMoverControl::StopAction),
					    this);
//      gtk_widget_add_accelerator (button, "pressed", accel_group,
//                                  GDK_F1+4*i, (GdkModifierType)0,
//                                  GTK_ACCEL_VISIBLE);

			// RIGHT
			button = create_arrow_button(GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
			gtk_widget_set_size_request (button, ARROW_SIZE, ARROW_SIZE);
			gtk_widget_show (button);
			gtk_table_attach (GTK_TABLE (tab), button, 3, 4, 1, 2,
					  (GtkAttachOptions)(GTK_FILL),
					  (GtkAttachOptions)(GTK_FILL),
					  FALSE, FALSE);
			g_object_set_data( G_OBJECT (button), "DSP_cmd", (gpointer)DSP_CMD_AFM_MOV_XP);
			g_object_set_data( G_OBJECT (button), "MoverNo", (gpointer)i);
			g_signal_connect (G_OBJECT (button), "pressed",
					    G_CALLBACK (DSPMoverControl::CmdAction),
					    this);
			g_signal_connect (G_OBJECT (button), "released",
					    G_CALLBACK (DSPMoverControl::StopAction),
					    this);

// gtk_widget_get_toplevel()
//      gtk_widget_add_accelerator (button, "pressed", accel_group,
//                                  GDK_F1+4*i, (GdkModifierType)0,
//                                  GTK_ACCEL_VISIBLE);

			// DOWN
			button = create_arrow_button(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
			gtk_widget_set_size_request (button, ARROW_SIZE, ARROW_SIZE);
			gtk_widget_show (button);
			gtk_table_attach (GTK_TABLE (tab), button, 2, 3, 2, 3,
					  (GtkAttachOptions)(GTK_FILL),
					  (GtkAttachOptions)(GTK_FILL),
					  FALSE, FALSE);
			g_object_set_data( G_OBJECT (button), "DSP_cmd", (gpointer)DSP_CMD_AFM_MOV_YM);
			g_object_set_data( G_OBJECT (button), "MoverNo", (gpointer)i);
			g_signal_connect (G_OBJECT (button), "pressed",
					    G_CALLBACK (DSPMoverControl::CmdAction),
					    this);
			g_signal_connect (G_OBJECT (button), "released",
					    G_CALLBACK (DSPMoverControl::StopAction),
					    this);
//      gtk_widget_add_accelerator (button, "pressed", accel_group,
//                                  GDK_F2+4*i, (GdkModifierType)0,
//                                  GTK_ACCEL_VISIBLE);

    
		}
		// ========================================
		// used to auto-center ...
		hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
		gtk_widget_show (hbox);
		gtk_table_attach (GTK_TABLE (tab), hbox, 0, 1, 1, 2,
				  (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
				  (GtkAttachOptions)(GTK_FILL),
				  FALSE, FALSE);
    
		hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
		gtk_widget_show (hbox);
		gtk_table_attach (GTK_TABLE (tab), hbox, 4, 5, 1, 2,
				  (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
				  (GtkAttachOptions)(GTK_FILL),
				  FALSE, FALSE);
    
		if(i==4){
			// ========================================
			// Auto App. Control Buttons
			button = gtk_button_new_with_label("Start");
			gtk_widget_show (button);
			gtk_table_attach (GTK_TABLE (tab), button, 4, 5, 0, 1,
					  (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
					  (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
					  FALSE, FALSE);
			if(IS_MOVER_CTRL)
				g_object_set_data( G_OBJECT (button), "DSP_cmd", (gpointer)DSP_CMD_APPROCH_MOV_XP);
			else
				g_object_set_data( G_OBJECT (button), "DSP_cmd", (gpointer)DSP_CMD_APPROCH);

			g_object_set_data( G_OBJECT (button), "MoverNo", (gpointer)i);
			g_signal_connect (G_OBJECT (button), "pressed",
					    G_CALLBACK (DSPMoverControl::CmdAction),
					    this);
      
			button = gtk_button_new_with_label("Stop");
			gtk_widget_show (button);
			gtk_table_attach (GTK_TABLE (tab), button, 4, 5, 2, 3,
					  (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
					  (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
					  FALSE, FALSE);
			g_object_set_data( G_OBJECT (button), "DSP_cmd", (gpointer)DSP_CMD_CLR_PA);
			g_object_set_data( G_OBJECT (button), "MoverNo", (gpointer)i);
			g_signal_connect (G_OBJECT (button), "pressed",
					    G_CALLBACK (DSPMoverControl::CmdAction),
					    this);
		}
    
	}
  
	if(accel_group)
		gtk_window_add_accel_group (GTK_WINDOW (GetWidget()), accel_group);
	// ============================================================
	// save List away...
	g_object_set_data( G_OBJECT (widget), "MOVER_EC_list", EC_list);


}

void DSPMoverControl::update(){
	g_slist_foreach
		( (GSList*) g_object_get_data( G_OBJECT (widget), "MOVER_EC_list"),
		  (GFunc) App::update_ec, NULL
			);
}

void DSPMoverControl::updateDSP(int sliderno){
	PI_DEBUG (DBG_L2, "Hallo DSP ! Mover No:" << sliderno );
	if(sliderno >= 0 && sliderno < 4){
		dsp->AFM_Amp   = dsp->AFM_usrAmp  [sliderno];
		dsp->AFM_Speed = dsp->AFM_usrSpeed[sliderno];
		dsp->AFM_Steps = dsp->AFM_usrSteps[sliderno];
	}else{
		dsp->AFM_Amp   = dsp->MOV_Ampl;
		dsp->AFM_Speed = dsp->MOV_Speed;
		dsp->AFM_Steps = dsp->MOV_Steps;
	}
	DSPMover_pi.app->xsm->hardware->PutParameter(dsp);
}

int DSPMoverControl::CmdAction(GtkWidget *widget, DSPMoverControl *dspc){
	int idx=-1;
	int cmd;
	PI_DEBUG (DBG_L2, "MoverCrtl::CmdAction " );
	if(IS_MOVER_CTRL)
		idx = (int)g_object_get_data( G_OBJECT (widget), "MoverNo");
	dspc->updateDSP(idx);
	cmd = (int)g_object_get_data( G_OBJECT (widget), "DSP_cmd");
	if(cmd>0)
		dspc->ExecCmd(cmd);
	PI_DEBUG (DBG_L2, "cmd=" << cmd << " Mover=" << idx );
	return 0;
}

int DSPMoverControl::StopAction(GtkWidget *widget, DSPMoverControl *dspc){
	PI_DEBUG (DBG_L2, "DSPMoverControl::StopAction" );
	dspc->ExecCmd(DSP_CMD_CLR_PA);
	return 0;
}

void DSPMoverControl::ChangedNotify(Param_Control* pcs, gpointer dspc){
	int idx=-1;

	gchar *us=pcs->Get_UsrString();
	PI_DEBUG (DBG_L2, "MoverCrtl:: Param Changed: " << us );
	g_free(us);

	if(IS_MOVER_CTRL)
		idx = (int)pcs->GetEntryData("MoverNo");
	((DSPMoverControl*)dspc)->updateDSP(idx);
}

void DSPMoverControl::ExecCmd(int cmd){
	DSPMover_pi.app->xsm->hardware->ExecCmd(cmd);
}
