#   This is a component of LinuxCNC
#   Copyright 2015 Moses McKnight <moses@texband.net>
#       modified from a sample made by Chris Morley
#
#   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 os, sys, subprocess
import tempfile, atexit, shutil

import hal
import gtk
import pango
import linuxcnc
import hal_glib
from hal_glib import GStat
from gscreen import preferences
from gladevcp.hal_filechooser import FilterProgram, EMC_FileChooserDialog
import ccc_gcodeview
import aboutpage


_X = 0;_Y = 1;_Z = 2;_A = 3;_B = 4;_C = 5;_U = 6;_V = 7;_W = 8
_SPINDLE_INPUT = 1;_PERCENT_INPUT = 2;_VELOCITY_INPUT = 3;_DEGREE_INPUT = 4

PYNONE              = 0
PYZERO_X            = 1
PYZERO_Y            = 2
PYZERO_Z            = 3
PYZERO_A            = 4
PYZERO_B            = 5
PYHOME_X            = 6
PYHOME_Y            = 7
PYHOME_Z            = 8
PYHOME_A            = 9
PYHOME_B            = 10
PYTORCHDISABLE_TGL  = 11
PYTHCDISABLE_TGL    = 12
PYTHCPRESET_UP      = 13
PYTHCPRESET_DN      = 14
PYOUTPUT1           = 15
PYOUTPUT2           = 16
PYOUTPUT3           = 17
PYINTERLOCK_OUT1    = 18
PYINTERLOCK_OUT2    = 19
PYINTERLOCK_OUT3    = 20
PYMACRO1            = 21
PYMACRO2            = 22
PYMACRO3            = 23
PYFO_RESET          = 24
PYFO_PLUS           = 25
PYFO_MINUS          = 26
PYESTOP_TGL         = 27
PYRUN               = 28
PYFEEDHOLD          = 29
PYSTOP              = 30
PYSTEP_INC          = 31
PYSINGLESTEP_TGL    = 32
PYJOG_X_PLUS        = 33
PYJOG_X_MINUS       = 34
PYJOG_Y_PLUS        = 35
PYJOG_Y_MINUS       = 36
PYJOG_Z_PLUS        = 37
PYJOG_Z_MINUS       = 38
PYJOG_A_PLUS        = 39
PYJOG_A_MINUS       = 40
PYJOG_B_PLUS        = 41
PYJOG_B_MINUS       = 42
PYJOG_C_PLUS        = 43
PYJOG_C_MINUS       = 44
PYJOGPERCENT_100    = 45
PYJOGPERCENT_50     = 46
PYJOGPERCENT_25     = 47
PYJOGPERCENT_10     = 48

# This is a handler file for using Gscreen's infrastructure
# to load a completely custom glade screen
# The only things that really matters is that it's saved as a GTK builder project,
# the toplevel window is called window1 (The default name) and you connect a destroy
# window signal else you can't close down linuxcnc
#
#
# The following functions in this class (if they exist) are called by Gscreen in the following order:
#
# initialize_preferences()
# initialize_keybindings()
# initialize_pins()
# initialize_manual_toolchange()
# connect_signals(handlers)
# initialize_widgets()
#
# Then a timer is started with timer_interrupt() as the callback function. The default
# implementation in gscreen calls a number of functions and then calls
# periodic() in this class if it exists
#
class HandlerClass:

    # This will be pretty standard to gain access to everything
    # emc is for control and status of linuxcnc
    # data is important data from gscreen and linuxcnc
    # widgets is all the widgets from the glade files
    # gscreen is for access to gscreens methods
    def __init__(self, halcomp,builder,useropts,gscreen):
        self.halcomp = halcomp
        self.builder = builder
        self.emc = gscreen.emc
        self.emc_cmd = linuxcnc.command()
        self.data = gscreen.data
        self.widgets = gscreen.widgets
        self.gscreen = gscreen
        self.gstat = GStat()
        self.inifile = gscreen.inifile
        self.pins_initialized = False
        self.dros = {}
        
        self.widgets.scrolledwindow1.remove(self.widgets.gcode_view)
        self.widgets.gcode_view = ccc_gcodeview.CCC_GcodeView(self.gstat)
        self.widgets.gcode_view.set_property("visible", True)
        self.widgets.gcode_view.set_property("can_focus", True)
        self.widgets.gcode_view.set_property("editable", False)
        self.widgets.gcode_view.set_property("left_margin", 2)
        self.widgets.gcode_view.set_property("right_margin", 2)
        self.widgets.gcode_view.set_property("show_line_numbers", True)
        self.widgets.gcode_view.set_property("show_line_marks", True)
        self.widgets.gcode_view.set_property("tab_width", 4)
        self.widgets.gcode_view.set_property("auto_indent", True)
        self.widgets.gcode_view.set_property("highlight_current_line", True)
        self.widgets.gcode_view.set_property("indent_on_tab", False)
        self.widgets.scrolledwindow1.add(self.widgets.gcode_view)
        self.do_goto_line = True
        
        self.machine_type = int(self.inifile.find("DISPLAY", "CCC_MACHINE_TYPE") or -1000)
        if (-1000 == self.machine_type):
            self.machine_type = int(self.inifile.find("DISPLAY", "PLASMA_TABLE") or 0)
        self.is_plasma = 0
        if (1 == self.machine_type):
            self.is_plasma = 1
        
        # get dthc axis and make sure it is a valid axis
        self.dthc_axis = self.inifile.find("DISPLAY", "CCC_DTHC_AXIS") or 'z'
        self.dthc_axis = self.dthc_axis.lower()
        if self.dthc_axis not in('z','a','b'):
            self.dthc_axis = 'z'
        self.simple_rfl = int(self.inifile.find("DISPLAY", "CCC_RFL_FUNCTION") or 1)
        self.no_home_required = int(self.inifile.find("TRAJ", "NO_FORCE_HOMING") or 0)
        self.editor = self.inifile.find("DISPLAY", "EDITOR") or "mousepad"
        self.simulator = int(self.inifile.find("DISPLAY", "SIMULATOR") or 0)
        
        self.tmp = None
        self.realfilename = ""
        self.gcodefolder = os.path.expanduser("~/linuxcnc/nc_files")
        #self.gcodefolder = inifile.find("DISPLAY", "PROGRAM_PREFIX")

        # Look for an optional preferece file path otherwise it uses ~/.gscreen_preferences
        # then initiate access to saved prefernces
        temp = self.inifile.find("DISPLAY", "PREFERENCE_FILE_PATH")
        #prefdir = os.path.dirname(temp)
        #if (prefdir != '' and not os.path.exists(prefdir)):
        #    os.makedirs(prefdir)
        #dbg("**** GSCREEN INFO: Preference file path: %s"%temp)
        self.prefs = preferences.preferences(temp)
        self.jog_percent = self.prefs.getpref('jog_percent', .20, float)
        
        self.widgets.loadmat_x.set_value(self.prefs.getpref('loadmaterial_x', 0.0, float))
        self.widgets.loadmat_y.set_value(self.prefs.getpref('loadmaterial_y', 0.0, float))
        self.widgets.loadmat_z.set_value(self.prefs.getpref('loadmaterial_z', 0.0, float))
        self.widgets.toffrate.set_value(self.prefs.getpref('touchoff_rate', 20, int))
        self.widgets.tofflimit.set_value(self.prefs.getpref('touchoff_limit', -0.75, float))
        self.prefs.putpref('desktop_notify', False)
        
        self.max_vel = []
        self.jog_rate = []
        self.pnjog_rate = []
        # isjogging is used for keyboard jogging state
        self.isjogging = [0,0,0,0,0,0,0,0,0]
        # ispnjogging is used for pendant jogging state - primarily so we don't improperly stop keyboard jogging
        self.ispnjogging = [0,0,0,0,0,0,0,0,0]
        self.cur_line_number = 0

        for i in range(9):
            maxvel = self.inifile.find("AXIS_%s"%i, "MAX_VELOCITY")
            if maxvel:
                maxvel = float(maxvel)
            else:
                maxvel = 5.0
            self.max_vel.append(maxvel)
            #print 'max_vel[%i] = %f' % (i, maxvel)
            self.jog_rate.append(maxvel * self.jog_percent)
            self.pnjog_rate.append(maxvel)
            #print 'jog_rate[%i] = %f' % (i, self.jog_rate[i])
            
        self.widget_nojog_list = ['GtkTreeView',
                                    'GtkEntry',
                                    'EMC_SourceView',
                                    'CCC_GcodeView',
                                    'GtkHScale',
                                    'GtkVScale',
                                    'VteTerminal',
                                    'GtkSocket',
                                    'x_dro_entry',
                                    'y_dro_entry',
                                    'z_dro_entry',
                                    'a_dro_entry',
                                    'b_dro_entry']
        try:
            s = subprocess.check_output(["dpkg-query", "-W", "-f=${Version}", "commandcnc"])
            self.version = s.strip()
        except subprocess.CalledProcessError:
            self.version = ""
        try:
            s = subprocess.check_output(["uname", "-r"])
            self.kernel_version = s.strip()
        except subprocess.CalledProcessError:
            self.kernel_version = ""
        
        #gobject.timeout_add(30, self.handle_pendant_functions)

  

    #def on_show_alarm_page(self,widget):
        #self.widgets.notebook_debug.set_show_tabs(self.widgets.menuitem5.get_active())
        #self.widgets.notebook_debug.set_current_page(0)

    # This connects signals without using glade's autoconnect method
    def connect_signals(self,handlers):
        signal_list = [ ["window1","destroy", "on_window1_destroy"],
                        #["restart","clicked", "launch_restart_dialog"],
                        #["metric_select","activate","on_metric_select_clicked"],
                        #["s_run","clicked","on_button_spindle_controls_clicked"],
                        #["spindle_control","clicked", "on_spindle_control_clicked"],
                        #["spindle_preset","clicked", "on_preset_spindle"],
                        #["spindle_increase","clicked", "on_spindle_speed_adjust"],
                        #["spindle_decrease","clicked", "on_spindle_speed_adjust"],
                        ["run_halshow","clicked", "on_halshow"],
                        ["run_calibration","clicked", "on_calibration"],
                        ["run_status","clicked", "on_status"],
                        ["run_halmeter","clicked", "on_halmeter"],
                        ["run_halscope","clicked", "on_halscope"],
                        ["run_ladder","clicked", "on_ladder"],
                    ]
        for i in signal_list:
            if len(i) == 3:
                self.widgets[i[0]].connect(i[1], self.gscreen[i[2]])
            elif len(i) == 4:
                self.widgets[i[0]].connect(i[1], self.gscreen[i[2]],i[3])
        
        self.widgets.window1.connect("notify::is-active", self.is_active_changed)
        self.widgets.menuitem_quit.connect("activate", self.on_quit)
        self.widgets.display_inch.connect("activate", lambda i:self.set_display_units(self.data._IMPERIAL))
        self.widgets.display_metric.connect("activate", lambda i:self.set_display_units(self.data._MM))
        self.widgets.clear_messages.connect("activate", self.clear_messages)
        self.widgets.clear_msgs.connect("clicked", self.clear_messages)
        self.widgets.gcode_view.gstat_connect()
        self.widgets.gcode_view.get_buffer().connect("mark-set", self.on_gcode_selection_changed)
        self.widgets.gcode_view.connect('gcodeview-file-loaded', self.after_gcodefile_load)
        self.widgets.gcode_view.connect('gcodeview-line-changed', self.after_gcodeline_change)
        self.widgets.estop_btn.connect("clicked", self.on_reset_toggle)
        self.widgets.run_btn.connect("clicked", self.on_run_btn_clicked)
        self.widgets.runfromline_btn.connect("clicked", self.on_runfromline_btn_clicked)
        self.widgets.feedhold_btn.connect("clicked", self.on_feedhold_clicked)
        self.widgets.stop_btn.connect("clicked", self.on_stop_clicked)
        #self.widgets.singlestep_btn.connect("clicked", self.on_single_step_clicked)
        self.widgets.scale_jog.connect("value_changed", self.on_scale_jog_value_changed)
        self.widgets.reset_jog.connect("clicked", lambda i:self.widgets.adjustment_jog.set_value(20))
        self.widgets.scale_fo.connect("value_changed", self.on_scale_fo_value_changed)
        self.widgets.reset_fo.connect("clicked", lambda i:self.widgets.adjustment_fo.set_value(100))
        self.widgets.scale_mv.connect("value_changed", self.on_scale_mv_value_changed)
        self.widgets.reset_mv.connect("clicked", lambda i:self.widgets.adjustment_mv.set_value(100))

        self.widgets.menuitem_about.connect("activate", self.show_aboutdialog)
        self.widgets.aboutdialog.connect("delete-event", self.on_aboutdialog_delete_event)
        self.widgets.aboutdialog.connect("response", lambda i,j:self.widgets.aboutdialog.hide())
        self.widgets.open_gcode_btn.connect("clicked", self.on_open_gcode)
        self.widgets.reload_gcode_btn.connect("clicked", self.on_reload_gcode)
        self.widgets.close_gcode_btn.connect("clicked", self.on_close_gcode)
        self.widgets.edit_gcode_btn.connect("clicked", self.on_edit_gcode)
        self.widgets.linenum.connect('value-changed', self.goto_line)

        #self.widgets.menuitem5.connect("activate",self.on_show_alarm_page)
        #self.widgets.show_dro.connect('toggled',self.on_gremlin_radiobutton_toggled)
        #self.widgets.show_offsets.connect('toggled',self.on_gremlin_radiobutton_toggled)
        #self.widgets.show_clean.connect('toggled',self.on_gremlin_radiobutton_toggled)

        self.widgets.gremlin.connect("gcode-error", self.on_gremlin_gcode_error)
        self.widgets.gremlin.connect("line-clicked", self.on_gremlin_line_clicked)
      #  self.widgets.gremlin.connect("gremlin-file-loaded", self.on_gremlin_file_opened)
        # Gremlin view buttons
        self.widgets.show_offsets.connect('toggled', self.on_gremlin_offsets_toggled)
        self.widgets.zoom_in.connect("clicked",lambda i:self.gscreen.zoom_in())
        self.widgets.zoom_out.connect("clicked",lambda i:self.gscreen.zoom_out())
        self.widgets.view_dimensions.connect('toggled', self.on_tbtn_view_dimension_toggled)
        self.widgets.view_tool.connect('toggled', self.on_tbtn_view_tool_toggled)
        self.widgets.view_clear.connect("clicked", lambda i:self.widgets.gremlin.clear_live_plotter())
        #self.widgets.view.connect('clicked', self.on_gremlin_view_toggled)
        
        for i in('x','y','z','a','b'):
            self.dros[i] = self.builder.get_object(i+'_dro')

        if (self.is_plasma):
            self.widgets.view_p.hide()
            self.widgets.view_x.hide()
            self.widgets.view_y.hide()
            self.widgets.view_z.hide()
            self.dros[self.dthc_axis].hide()
            self.dros[self.dthc_axis] = self.builder.get_object(self.dthc_axis+'_dro1')
            self.dros[self.dthc_axis].show()
            self.widgets.manbtn_touchoff.set_label("%s Touch-off" % self.dthc_axis.upper())
        else:
            self.widgets.view_p.connect('toggled', self.on_rbt_view_p_toggled)
            self.widgets.view_x.connect('toggled', self.on_rbt_view_x_toggled)
            self.widgets.view_y.connect('toggled', self.on_rbt_view_y_toggled)
            self.widgets.view_z.connect('toggled', self.on_rbt_view_z_toggled)
        
        self.widgets.jog_speed.connect("changed",self.on_jog_speed_changed)
        #self.widgets.sneg.connect("clicked",lambda i:self.gscreen.spindle_adjustment(False,True))
        #self.widgets.spos.connect("clicked",lambda i:self.gscreen.spindle_adjustment(True,True))
        for i in('x','y','z','a','b'):
            self.dros[i].connect('button-press-event', self.on_dro_clicked, i)
            self.widgets[i+'_dro_entry'].connect('insert-text', self.on_dro_insert_text, i)
            self.widgets[i+'_dro_entry'].connect('focus-out-event', self.on_dro_focus_out, i)
            self.widgets[i+'_dro_entry'].connect('activate', self.on_dro_activate, i)
            #self.widgets[i+'_dro_entry'].connect('key-press-event', self.on_dro_key_press_event, i)
            #self.widgets['home_'+i].connect("clicked", self.home_axis, i)
            self.widgets['zero_'+i].connect("clicked", self.zero_axis, i)
        #for i in('x','y','z',):
            #self.widgets['touch_off'+i].connect("clicked", self.on_touch_off_clicked, i)
            #self.widgets[i+'neg'].connect("pressed", self['jog_'+i],0,True)
            #self.widgets[i+'neg'].connect("released", self['jog_'+i],0,False)
            #self.widgets[i+'pos'].connect("pressed", self['jog_'+i],1,True)
            #self.widgets[i+'pos'].connect("released", self['jog_'+i],1,False)
            
        #self.widgets.z_dro.connect('button-press-event', self.on_dro_clicked, 'z')
            
        self.widgets.spindletorch.connect('clicked', self.spindle_torch_clicked)
        self.widgets.output1.connect('clicked', self.output1_clicked)
        self.widgets.output2.connect('clicked', self.output2_clicked)
        self.widgets.ignore_limits.connect('clicked', self.ignore_limits_clicked)
        
        self.widgets.loadmat_x.connect('value-changed', self.loadmaterial_x_changed)
        self.widgets.loadmat_y.connect('value-changed', self.loadmaterial_y_changed)
        self.widgets.loadmat_z.connect('value-changed', self.loadmaterial_z_changed)
        self.widgets.toffrate.connect('value-changed', self.touchoff_rate_changed)
        self.widgets.tofflimit.connect('value-changed', self.touchoff_limit_changed)
        self.widgets.manbtn_loadmaterial.connect('clicked', self.handle_subroutine_button)
        self.widgets.manbtn_touchoff.connect('clicked', self.handle_subroutine_button)
        self.widgets.optionalstop.connect('toggled', self._on_opstop_toggled)
        self.widgets.clear_mdi_btn.connect('clicked', self.clear_mdi_history)

        self.gstat.connect('state-on', self._on_state_on)
        self.gstat.connect('state-off', self._on_state_off)
        self.gstat.connect('state-estop', self._on_state_estop)
        #self.gstat.connect('state-estop-reset', self._on_state_estop_reset)
        self.gstat.connect('interp-idle', self._on_interp_idle)
        self.gstat.connect('interp-run', self._on_interp_run)
        #self.gstat.connect('interp-paused', self._on_interp_paused)
        self.gstat.connect('file-loaded', self._on_hal_status_file_loaded)
        #self.gstat.connect('line-changed', self._on_state_line_changed)
        
        #hal pin signals
        self.zsync_ready_in.connect('value-changed', self._on_zsync_ready_change)


    # We don't want Gscreen to initialize it's regular widgets because this custom
    # screen doesn't have most of them. So we add this function call.
    # Since this custom screen uses gladeVCP magic for its interaction with linuxcnc
    # We don't add much to this function, but we do want to be able to change the theme so:
    # We change the GTK theme to what's in gscreen's preference file.
    # gscreen.change_theme() is a method in gscreen that changes the GTK theme of window1
    # gscreen.data.theme_name is the name of the theme from the preference file 
    # To truely be friendly, we should add a way to change the theme directly in the custom screen.
    # we also set up the statusbar and add a ready-to-home message
    def initialize_widgets(self):
        self.gscreen.init_show_windows()
        self.gscreen.init_dynamic_tabs()
        self.init_screen1_geometry()
        #self.gscreen.init_embeded_terminal()
        self.data.theme_name = "CommandCNC"
        self.gscreen.change_theme(self.data.theme_name)
        self.gscreen.statusbar_id = self.widgets.statusbar1.get_context_id("Statusbar1")
        self.gscreen.homed_status_message = self.widgets.statusbar1.push(1,"Ready For Homing")

        machinename = self.inifile.find("EMC", "MACHINE")
        if not machinename:
            print "No MACHINE ini entry found - using defaults"
            if self.is_plasma:
                machinename = "Plasma"
            else:
                machinename = "Router"
        self.widgets.window1.set_title("CommandCNC - " + machinename)
        if not self.is_plasma:
            self.widgets.spindletorch.show()
            self.widgets.notebook_2.remove_page(1)
            
        if self.simulator:
            self.widgets.blockdelete.set_active(True)
        self.delstate = self.widgets.blockdelete.get_active()
        self.widgets.optionalstop.set_active(self.prefs.getpref('opstop', True))
        
        self.a_axis_type = self.inifile.find("AXIS_3", "TYPE")
        if (self.a_axis_type):
            self.widgets.hbox_a_dro.set_visible(True)
        self.b_axis_type = self.inifile.find("AXIS_4", "TYPE")
        if (self.b_axis_type):
            self.widgets.hbox_b_dro.set_visible(True)

        self.continuousjog_index = 0
        for num,i in enumerate(self.data.jog_increments):
            #print i
            self.widgets.jog_speed.append_text(i)
            if i == "continuous":
                self.continuousjog_index = num
                self.data.current_jogincr_index = num
                self.widgets.jog_speed.set_active(num)
        self.gscreen.set_dro_units(self.data.dro_units)
        self.widgets.adjustment_mv.set_value(self.data.maxvelocity*100)
        self.widgets.adjustment_jog.set_value(self.jog_percent*100)
        self.widgets.adjustment_fo.set_upper(self.data.feed_override_max*100)
        self.widgets.adjustment_fo.set_value(self.data.feed_override*100)
        #self.widgets.notebook_debug.set_show_tabs(False)
        #self.gscreen.keylookup.add_conversion('F4','TEST2','on_keycall_POWER')
        
        about = aboutpage.about()
        if (about.valid == True):
            self.widgets.notebook_2.append_page(about, gtk.Label("About"))
            if (about.tabimage != None):
                self.widgets.notebook_2.set_tab_label(about, about.tabimage)
        
        self.gremlin_setup()
        #self.widgets.show_dro.set_active(True)
        
        self.gscreen.keylookup.add_binding('bracketright', self.prefs.getpref('Key_bracketright', 'APOS', str,"KEYCODES"))
        self.gscreen.keylookup.add_binding('bracketleft', self.prefs.getpref('Key_bracketleft', 'ANEG', str,"KEYCODES"))
        self.gscreen.keylookup.add_binding('braceright', self.prefs.getpref('Key_braceright', 'APOS', str,"KEYCODES"))
        self.gscreen.keylookup.add_binding('braceleft', self.prefs.getpref('Key_braceleft', 'ANEG', str,"KEYCODES"))

        self.gscreen.keylookup.add_binding('period', self.prefs.getpref('Key_period', 'BPOS', str, 'KEYCODES'))
        self.gscreen.keylookup.add_binding('greater', self.prefs.getpref('Key_greater', 'BPOS', str, 'KEYCODES'))
        self.gscreen.keylookup.add_binding('comma', self.prefs.getpref('Key_comma', 'BNEG', str, 'KEYCODES'))
        self.gscreen.keylookup.add_binding('less', self.prefs.getpref('Key_less', 'BNEG', str, 'KEYCODES'))
        self.gscreen.keylookup.add_binding('space', self.prefs.getpref('Key_space', 'FEEDHOLD', str, 'KEYCODES'))
        self.gscreen.keylookup.add_binding('F11', self.prefs.getpref('Key_F11', 'FULLSCREEN', str, 'KEYCODES'))
        self.gscreen.keylookup.add_binding('u', 'OXYUP')
        self.gscreen.keylookup.add_binding('d', 'OXYDOWN')
        #self.gscreen.keylookup.add_binding('r', 'RUN')
        #self.gscreen.keylookup.add_binding('R', 'RUN')

        self.gscreen.keylookup.add_call('BPOS', 'on_keycall_BPOS')
        self.gscreen.keylookup.add_call('BNEG', 'on_keycall_BNEG')
        self.gscreen.keylookup.add_call('FEEDHOLD', 'on_keycall_FEEDHOLD')
        self.gscreen.keylookup.add_call('FULLSCREEN', 'on_keycall_FULLSCREEN')
        self.gscreen.keylookup.add_call('OXYUP', 'on_keycall_OXYUP')
        self.gscreen.keylookup.add_call('OXYDOWN', 'on_keycall_OXYDOWN')
        #self.gscreen.keylookup.add_call('RUN', 'on_keycall_RUN')
        
        #stylepath = [os.getcwd() + "/resources/",]
        stylepath = ["/usr/share/gscreen/skins/commandcnc/resources/",]
        self.widgets.gcode_view.set_language('gcode')

        colorscheme = int(self.inifile.find("DISPLAY", "COLOR_SCHEME") or 0)
        if (colorscheme):
            self.widgets.gcode_view.set_style_scheme('codeview_light', stylepath)
            self.widgets.gcode_view.set_mark_category_background('motion', gtk.gdk.Color('#00ff40'))
        else:
            self.widgets.gcode_view.set_style_scheme('codeview_dark', stylepath)
            self.widgets.gcode_view.set_mark_category_background('motion', gtk.gdk.Color('#6300b8'))
        #self.widgets._terminal.set_background_tint_color(gtk.gdk.color_parse('white'))
        
        self.widgets.feedrate.modify_font(pango.FontDescription("Droid Sans 16"))
        self.widgets.x_dro.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.y_dro.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.z_dro.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.z_dro1.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.a_dro.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.a_dro1.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.b_dro.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.b_dro1.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.x_dro_entry.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.y_dro_entry.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.z_dro_entry.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.a_dro_entry.modify_font(pango.FontDescription("Droid Sans 20"))
        self.widgets.b_dro_entry.modify_font(pango.FontDescription("Droid Sans 20"))
        
        if (self.data.dro_units == self.data._IMPERIAL):
            self.widgets.display_inch.set_active(True)
        else:
            self.widgets.display_metric.set_active(True)

        self._on_state_estop(self)


    def init_screen1_geometry(self):
        # maximize window or set geometry and optionally maximize 
        if "max" in self.data.window_geometry:
            self.widgets.window1.maximize()
        elif self.data.window_geometry == "default":
            self.gscreen.show_try_errors()
        else:
            good = self.widgets.window1.parse_geometry(self.data.window_geometry)
            if self.data.window_max:
               self.widgets.window1.maximize()
            if not good:
                print _("**** WARNING GSCREEN: could not understand the window geometry info in preference file")
        if self.data.fullscreen1 == True:
            self.widgets.window1.fullscreen()
        else:
            self.widgets.window1.unfullscreen()

    # If we need extra HAL pins here is where we do it.
    # Note you must import hal at the top of this script to build the pins here. or:
    # For gaxis there is only jog pins so we call gscreen to
    # add it's default jog pins
    def initialize_pins(self):
        self.gscreen.init_jog_pins()
        self.zsync_ready_in     = hal_glib.GPin(self.halcomp.newpin('zsync-ready-in', hal.HAL_BIT, hal.HAL_IN))
        #self.zsync_ready_out    = hal_glib.GPin(self.halcomp.newpin('zsync-ready-out', hal.HAL_BIT, hal.HAL_OUT))
        self.zpos_final_in      = hal_glib.GPin(self.halcomp.newpin('zpos-final-in', hal.HAL_FLOAT, hal.HAL_IN))
        self.zpos               = hal_glib.GPin(self.halcomp.newpin('zpos', hal.HAL_FLOAT, hal.HAL_IN))
        self.customer_id        = hal_glib.GPin(self.halcomp.newpin('customer-id', hal.HAL_U32, hal.HAL_IN))
        self.z_jog_rate         = hal_glib.GPin(self.halcomp.newpin('z-jog-rate', hal.HAL_FLOAT, hal.HAL_OUT))
        self.oxyup              = hal_glib.GPin(self.halcomp.newpin('oxy-up', hal.HAL_BIT, hal.HAL_OUT))
        self.oxydown            = hal_glib.GPin(self.halcomp.newpin('oxy-down', hal.HAL_BIT, hal.HAL_OUT))
        self.pn200function      = hal_glib.GPin(self.halcomp.newpin('pn200func', hal.HAL_U32, hal.HAL_IO))
        self.step_jog           = hal_glib.GPin(self.halcomp.newpin('step-jog', hal.HAL_BIT, hal.HAL_OUT))


    def on_quit(self, widget, data=None):
        self.widgets.window1.destroy()
        #self.gscreen.on_window1_destroy(self.widgets.window1)
        
    def set_display_units(self, units):
        self.gscreen.set_dro_units(units, True)
        if units == self.data._IMPERIAL:
            for i in('x','y','z','a','b'):
                self.builder.get_object(i+'_dro').set_to_inch()
        else:
            for i in('x','y','z','a','b'):
                self.builder.get_object(i+'_dro').set_to_mm()
        
    def show_aboutdialog(self, i):
        self.widgets.aboutdialog.set_program_name("CommandCNC")
        self.widgets.aboutdialog.set_version(self.version)
        self.widgets.aboutdialog.set_website_label("Customer ID: "+str(self.customer_id.get()))
        self.widgets.aboutdialog.set_copyright("Linux kernel version: %s" % self.kernel_version)
        self.widgets.aboutdialog.show()
        
    def on_aboutdialog_delete_event(self, i,j):
        self.widgets.aboutdialog.hide()
        return True
        
    def is_active_changed(self, w, param):
        if False == self.widgets.window1.props.is_active:
            for i in range(9):
                if self.isjogging[i]:
                    self.do_key_jog(i, 0, 0, 0, False)

        
    # checks the current operating mode according to the UI
    def check_mode(self):
        string = []
        #if not key_state:  # if the key was released, we always want to stop the current continuous jog
        #    string.append(self.data._JOG)
        if (self.data.mode_order[0] == self.data._MAN):
            if (self.widgets.window1.get_focus() == None):  #check this first because get_focus().get_name() sets focus to a widget
                string.append(self.data._MAN)
                string.append(self.data._JOG)
            elif (self.widgets.window1.get_focus().get_name() not in self.widget_nojog_list):
                string.append(self.data._MAN)
                string.append(self.data._JOG)
        #else:
            #print "Jogging: %s" % self.widgets.window1.get_focus().class_path()
        return string

    def mktemp(self):
        if self.tmp:
            return
        self.tmp = tempfile.mkdtemp(prefix='emcflt-', suffix='.d')
        atexit.register(lambda: shutil.rmtree(self.tmp))

    def on_open_gcode(self, widget):
        dialog = EMC_FileChooserDialog(title="Open File",action=gtk.FILE_CHOOSER_ACTION_OPEN, 
                buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
        dialog.set_current_folder(self.gcodefolder)
        dialog.show()
        r = dialog.run()
        fn = dialog.get_filename()
        dialog.hide()
        if r == gtk.RESPONSE_OK:
            self.delstate = self.widgets.blockdelete.get_active()
            self.widgets.blockdelete.set_active(True)
            dialog.load_file(fn)
            self.realfilename = fn
            self.gcodefolder = os.path.dirname(fn)
        dialog.destroy()
        
    def on_reload_gcode(self, widget):
        if ("" == self.realfilename):
            return
        flt = None
        ext = os.path.splitext(self.realfilename)[1]
        if ext:
            flt = self.inifile.find("FILTER", ext[1:])
        if flt:
            if not self.tmp:
                self.mktemp()
            tmp = os.path.join(self.tmp, os.path.basename(self.realfilename))
            flt = FilterProgram(flt, self.realfilename, tmp)
        else:
            tmp = self.realfilename
        self.delstate = self.widgets.blockdelete.get_active()
        self.widgets.blockdelete.set_active(True)
        self.emc_cmd.mode(linuxcnc.MODE_AUTO)
        self.emc_cmd.program_open(tmp)
        # I think we emit this signal here because it is not emitted elsewhere on file reload
        self.gstat.emit('file-loaded', tmp)

    def on_close_gcode(self, widget):
        self.emc_cmd.mode(linuxcnc.MODE_AUTO)
        self.emc_cmd.program_open("blank.ngc")
        self.realfilename = ""
    
    def on_edit_gcode(self, widget):
        if ("" == self.realfilename):
            return
        subprocess.call([self.editor, self.realfilename])
        self.on_reload_gcode(None)
    
    def goto_line(self, widget):
        if (True == self.do_goto_line):
            self.cur_line_number = int(self.widgets.linenum.get_value())
            print "goto_line: %d" % self.cur_line_number
            self.widgets.gcode_view.set_line_number(self.cur_line_number)
        self.do_goto_line = True

    def on_reset_toggle(self, widget, user):
        if not self.data.machine_on:
            self.emc.estop_reset(1)
            self.emc_cmd.wait_complete()
            self.emc.machine_on(1)
            self.gscreen.add_alarm_entry(_("Machine powered on"))
        else:
            self.emc.machine_off(1)
            self.emc.estop(1)
            self.gscreen.add_alarm_entry(_("Machine Estopped!"))

    # when run is pressed, destroy the restart dialog if it's showing
    def on_run_btn_clicked(self, widget, junk):
        self.widgets.run_btn.set_active(False)  #don't need a toggle button here so just reset it
        if not self.data.restart_dialog == None:
            self.data.restart_dialog.destroy()
            self.data.restart_dialog = None
        self.widgets.gcode_view.set_line_number(1)
        self.emc_cmd.mode(linuxcnc.MODE_AUTO)
        self.emc_cmd.wait_complete()
        self.emc_cmd.auto(linuxcnc.AUTO_RUN, 0)
        #self.program_start_line = self.reset_line
    
    def on_feedhold_clicked(self, widget, junk):
        self.gstat.stat.poll()
        if self.gstat.stat.task_mode not in (linuxcnc.MODE_AUTO, linuxcnc.MODE_MDI):
            return
        if self.gstat.stat.paused:
            self.emc_cmd.auto(linuxcnc.AUTO_RESUME)
        elif self.gstat.stat.interp_state != linuxcnc.INTERP_IDLE:
            self.emc_cmd.auto(linuxcnc.AUTO_PAUSE)

    def on_stop_clicked(self, widget, junk):
        #self.gstat.stat.poll()
        #self.cur_line_number = self.gstat.stat.motion_line
        #print "motion_line: %d" % self.gstat.stat.motion_line
        self.emc_cmd.abort()
        self.emc_cmd.wait_complete()
        #self.widgets.gcode_view.set_line_number(self.cur_line_number)

    def on_runfromline_btn_clicked(self, widget):
        #self.widgets.runfromline_btn.set_active(False)
        if not self.data.restart_dialog == None:
            self.data.restart_dialog.destroy()
            self.data.restart_dialog = None
        self.cur_line_number = self.widgets.gcode_view.get_line_number()
        self.emc_cmd.mode(linuxcnc.MODE_AUTO)
        self.emc_cmd.wait_complete()
        if (self.simple_rfl):
            self.emc_cmd.srfl_auto(linuxcnc.AUTO_RUN, self.cur_line_number)
        else:
            self.emc_cmd.auto(linuxcnc.AUTO_RUN, self.cur_line_number)
        
    def on_single_step_clicked(self, widget):
        pass
    
    def spindle_torch_clicked(self, widget, user):
        if self.widgets.spindletorch.get_active():
            self.emc.spindle_forward(1)
        else:
            self.emc.spindle_off(0)
    def output1_clicked(self, widget, user):
        if self.widgets.output1.get_active():
            self.emc.mist_on(1)
        else:
            self.emc.mist_off(0)
    def output2_clicked(self, widget, user):
        if self.widgets.output2.get_active():
            self.emc.flood_on(1)
        else:
            self.emc.flood_off(0)
    
    def ignore_limits_clicked(self, widget, user):
        self.emc.override_limits(1)
    
    def loadmaterial_x_changed(self, data):
        val = self.widgets.loadmat_x.get_value()
        self.prefs.putpref('loadmaterial_x', val, float)
    def loadmaterial_y_changed(self, data):
        val = self.widgets.loadmat_y.get_value()
        self.prefs.putpref('loadmaterial_y', val, float)
    def loadmaterial_z_changed(self, data):
        val = self.widgets.loadmat_z.get_value()
        self.prefs.putpref('loadmaterial_z', val, float)
    def touchoff_rate_changed(self, data):
        self.prefs.putpref('touchoff_rate', self.widgets.toffrate.get_value_as_int(), int)
    def touchoff_limit_changed(self, data):
        self.prefs.putpref('touchoff_limit', self.widgets.tofflimit.get_value(), float)
    
    def handle_subroutine_button(self, widget, sub=None):
        if widget == None:
            name = sub
        else:
            name = widget.get_name().split('_')[1]
        if self.dthc_axis != 'z' and name == "touchoff":
            name = "%s_%s" % (self.dthc_axis, name)
        #print name
        cmd = "o<%s> call" % name
        #print cmd
        self.gstat.stat.poll()
        if self.gstat.stat.task_mode != linuxcnc.MODE_MDI:
            self.emc_cmd.mode(linuxcnc.MODE_MDI)
            self.emc_cmd.wait_complete()
        self.gscreen.mdi_control.user_command(cmd)

    def _on_opstop_toggled(self, userdata):
        self.prefs.putpref( "opstop", self.widgets.optionalstop.get_active(), bool )
    
    def clear_mdi_history(self, widget):
        try:
            f = open(self.widgets.mdihistory.filename, "w")
            f.close()
        except IOError:
            print >>sys.stderr, "Can't open MDI history file [%s] for writing" % file_name
        self.widgets.mdihistory.reload()
        
    def clear_messages(self, widget):
        self.widgets.alarm_history.get_buffer().set_text('')
        self.widgets.statusbar1.remove_all(self.gscreen.statusbar_id)

    #def load_material(self, widget):
        #cmd = "o<loadmaterial> call [%f] [%f] [%f]" % (
        #    self.widgets.loadmat_x.get_value(),
        #    self.widgets.loadmat_y.get_value(),
        #    self.widgets.loadmat_z.get_value() )
        #cmd = "G0 Z%f" % 
        
        #cmd = "o<loadmaterial> call"
        #self.gscreen.mdi_control.user_command(cmd)
        
        #while self.gscreen.mdi_control.mdi_is_reading():
        #    pass
        #self.emc_cmd.wait_complete()
        #cmd = "G0 X%f Y%f" % ()
        #self.gscreen.mdi_control.user_command(cmd)

    def jog_x(self,widget,direction,state):
        self.do_key_jog(_X,direction,state)
    def jog_y(self,widget,direction,state):
        self.do_key_jog(_Y,direction,state)
    def jog_z(self,widget,direction,state):
        self.do_key_jog(_Z,direction,state)

    def on_scale_jog_value_changed(self,widget):
        self.jog_percent = widget.get_value()/100
        self.prefs.putpref('jog_percent', self.jog_percent, float)
        for i in range(9):
            self.jog_rate[i] = self.max_vel[i] * self.jog_percent
            #print 'jog_rate[%i] = %f' % (i, self.jog_rate[i])
        self.z_jog_rate.set(self.jog_rate[2])
        
    def on_scale_fo_value_changed(self,widget):
        self.gscreen.set_feed_override((widget.get_value()/100),True)

    def on_scale_mv_value_changed(self,widget):
        self.gscreen.set_velocity_override((widget.get_value()/100),True)

    def on_jog_speed_changed(self,widget):
        self.data.current_jogincr_index = widget.get_active()
        self.gscreen.set_jog_increments(vector = self.data.current_jogincr_index)
    
    
    # keybinding calls
    
    # I can change this to remove focus AND abort if code is running easily - so that only 
    # one press of Esc is ever needed to abort, but then I need to figure out how to handle
    # cases such as the oxy-fuel Z control keys which may need to have focus removed without
    # aborting.  One idea is to use a key such as Tab for focus removal, another is to have a
    # button on screen that indicates if jogging is allowed and clicking it will allow jogging.
    def on_keycall_ABORT(self,state,SHIFT,CNTRL,ALT):
        if state: # only activate when pushed not when released
            #self.widgets.window1.set_focus(None)
            #if (self.gstat.stat.interp_state != linuxcnc.INTERP_IDLE):
            if self.widgets.window1.get_focus() == None:
                on_stop_clicked(0,0)
            else:
                self.widgets.window1.set_focus(None)
            return True
    def on_keycall_ESTOP(self,state,SHIFT,CNTRL,ALT):
        if state: # only activate when pushed not when released
            self.on_reset_toggle(None, None)
            return True
    def on_keycall_POWER(self,state,SHIFT,CNTRL,ALT):
        #if state:
            #self.widgets.emc_toggle_power.emit('activate')
            #return True
        return False
    def on_keycall_INCREMENTS(self,state,SHIFT,CNTRL,ALT):
        if state and self.data._JOG in self.check_mode(): # manual mode required
            #print 'hi'
            if SHIFT:
                self.gscreen.set_jog_increments(index_dir = -1)
            else:
                self.gscreen.set_jog_increments(index_dir = 1)
            # update the combo box
            self.widgets.jog_speed.set_active(self.data.current_jogincr_index)
            return True
    def on_keycall_XPOS(self,state,SHIFT,CNTRL,ALT):
        axis = _X
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        return False
    def on_keycall_XNEG(self,state,SHIFT,CNTRL,ALT):
        axis = _X
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        return False
    def on_keycall_YPOS(self,state,SHIFT,CNTRL,ALT):
        axis = _Y
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        return False
    def on_keycall_YNEG(self,state,SHIFT,CNTRL,ALT):
        axis = _Y
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        return False
    def on_keycall_ZPOS(self,state,SHIFT,CNTRL,ALT):
        axis = _Z
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        return False
    def on_keycall_ZNEG(self,state,SHIFT,CNTRL,ALT):
        axis = _Z
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        return False
    def on_keycall_APOS(self,state,SHIFT,CNTRL,ALT):
        axis = _A
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        return False
    def on_keycall_ANEG(self,state,SHIFT,CNTRL,ALT):
        axis = _A
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        return False
    def on_keycall_BPOS(self,state,SHIFT,CNTRL,ALT):
        axis = _B
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 1, state, SHIFT, False)
            return True
        return False
    def on_keycall_BNEG(self,state,SHIFT,CNTRL,ALT):
        axis = _B
        if self.isjogging[axis] and not state:  # if this axis is jogging and the key was released...
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.do_key_jog(axis, 0, state, SHIFT, False)
            return True
        return False
    def on_keycall_FEEDHOLD(self, state, SHIFT, CNTRL, ALT):
        #if key is pressed and we are running gcode, then do a feedhold
        if (state and (self.gstat.stat.interp_state != linuxcnc.INTERP_IDLE)):
            self.on_feedhold_clicked(None, None)
            return True
        elif (self.widgets.window1.get_focus().get_name() in self.widget_nojog_list):
            #return False to allow the widget with focus to handle the space bar
            return False
        #don't allow any widgets other than the ones in the widget_nojog_list to respond to space bar
        return True
    def on_keycall_FULLSCREEN(self,state,SHIFT,CNTRL,ALT):
        if (state):
            self.gscreen.set_fullscreen1(not self.data.fullscreen1)
            return True
        return False
    def on_keycall_OXYUP(self,state,SHIFT,CNTRL,ALT):
        if (state and (self.widgets.window1.get_focus().get_name() not in self.widget_nojog_list)):
            self.oxyup.set(1)
            return True
        else:
            self.oxyup.set(0)
        return False
    def on_keycall_OXYDOWN(self,state,SHIFT,CNTRL,ALT):
        if (state and (self.widgets.window1.get_focus().get_name() not in self.widget_nojog_list)):
            self.oxydown.set(1)
            return True
        else:
            self.oxydown.set(0)
        return False
#    def on_keycall_RUN(self,state,SHIFT,CNTRL,ALT):
#        if (state and (self.widgets.window1.get_focus().get_name() not in self.widget_nojog_list)):
#        return False

    def do_key_jog(self, axis, direction, action, SHIFT, pendant=False):
        if self.data.estopped: return
        if self.emc.masked: return
        #if self.data._JOG in self.check_mode(): # jog mode active:
        if not action: cmd = 0
        elif direction: cmd = 1
        else: cmd = -1
        #self.emc.jogging(1)
        self.emc_cmd.mode(linuxcnc.MODE_MANUAL)
        #print self.data.jog_increments[self.data.current_jogincr_index]
        if self.data.jog_increments[self.data.current_jogincr_index] == ("continuous"): # continuous jog
            #print "active axis jog:",axis
            if cmd == 0:  # Don't check if jog allowed here, because we always want to stop jogging if key is released
                #print "direction:", cmd
                self.isjogging[axis] = 0
                self.ispnjogging[axis] = 0
                self.emc_cmd.jog(linuxcnc.JOG_STOP, axis)
            else:  # if self.data._JOG in self.check_mode(): #check if jogging is allowed
                if True == pendant:
                    rate = self.pnjog_rate[axis]
                elif SHIFT:
                    rate = self.max_vel[axis]
                else:
                    rate = self.jog_rate[axis]
                #print "rate:", rate
                if True == pendant:
                    self.ispnjogging[axis] = cmd
                else:
                    self.isjogging[axis] = cmd
                self.emc_cmd.jog(linuxcnc.JOG_CONTINUOUS, axis, cmd * rate)
        else:
            #print "jog incremental"
            if cmd == 0: return # don't want release of button to stop jog
            #if self.data._JOG in self.check_mode():  # check if jogging is allowed
            self.gscreen.mdi_control.mdi.emcstat.poll()
            if self.gscreen.mdi_control.mdi.emcstat.state != 1: return
            jogincr = self.data.jog_increments[self.data.current_jogincr_index]
            distance = self.gscreen.parse_increment(jogincr)
            rate = self.jog_rate[axis]
            self.emc_cmd.jog(linuxcnc.JOG_INCREMENT, axis, cmd * rate, distance)

    def on_touch_off_clicked(self,widget,axis):
        self.gscreen.launch_numerical_input("on_offset_origin_entry_return",axis,None,"Touch off %s"% axis.upper())

    def on_offset_origin_entry_return(self,widget,result,calc,axis,userdata2):
        value = calc.get_value()
        if result == gtk.RESPONSE_ACCEPT:
            if value == None:
                return
            if not axis == "s":
                if axis in('a','b','c'):
                    pos = self.gscreen.get_qualified_input(value,switch=_DEGREE_INPUT)
                else:
                    pos = self.gscreen.get_qualified_input(value)
                self.gscreen.mdi_control.set_axis(axis,pos)
                self.gscreen.reload_plot()
        widget.destroy()
        self.data.entry_dialog = None
        
    # DRO functions -----------------------------------------------------------
    def on_dro_clicked(self, widget, event, axis):
        if (event.type == gtk.gdk._2BUTTON_PRESS):
            #print axis+"_dro double-clicked"
            self.dros[axis].hide()
            self.widgets[axis+'_dro_entry'].set_text(self.dros[axis].get_text())
            self.widgets[axis+'_dro_entry'].show()
            self.widgets[axis+'_dro_entry'].grab_focus()
            return True
        return False
    
    def on_dro_insert_text(self, widget, new_text, length, pos, axis):
        #print axis+"_dro_insert_text"
        try:
            float(new_text)
            return
        except ValueError:
            if new_text in('0','1','2','3','4','5','6','7','8','9','.','-'):
                return
            else:
                widget.stop_emission('insert-text')
        
    def on_dro_focus_out(self, widget, event, axis):
        #print axis+"_dro_focus_out"
        self.widgets[axis+'_dro_entry'].hide()
        #self.widgets[axis+'_dro'].set_text(self.widgets[axis+'_dro_entry'].get_text())
        self.dros[axis].show()
        
    def on_dro_activate(self, widget, axis):
        #print axis+"_dro_activate"
        self.widgets[axis+'_dro_entry'].hide()
        try:
            value = float(self.widgets[axis+'_dro_entry'].get_text())
            if value == None:
                return
            if not axis == "s":
                if axis in('a','b','c'):
                    #if (axis == 'a' and self.a_axis_type == "ANGULAR") or (axis == 'b' and self.b_axis_type == "ANGULAR"):
                    pos = self.gscreen.get_qualified_input(value,switch=_DEGREE_INPUT)
                else:
                    pos = self.gscreen.get_qualified_input(value)
                if (axis == self.dthc_axis):
                    self.gscreen.mdi_control.user_command('G92 %s%f' % (self.dthc_axis, pos))
                else:
                    self.gscreen.mdi_control.set_axis(axis,pos)
                self.gscreen.reload_plot()
        except ValueError:
            print "Not a valid number"
        self.dros[axis].show()
        
    #def on_dro_key_press_event(self, widget, event, axis):
        #print axis+"_dro_key_press_event"
        #if (gtk.gdk.keyval_name(event.keyval) == 'Escape'):
            #print "    ESC key pressed"
    #--------------------------------------------------------------------------
    
    def home_axis(self, widget, axis):
        axmap = { 'x':0, 'y':1, 'z':2, 'a':3, 'b':4, 'c':5 }
        self.emc_cmd.mode(linuxcnc.MODE_MANUAL)
        self.emc_cmd.wait_complete()
        self.emc_cmd.home(axmap[axis])
        #self.emc_cmd.wait_complete()
        #self.gscreen.mdi_control.set_axis(axis, 0.0)
        #pass
        
    def zero_axis(self, widget, axis):
        if (axis == self.dthc_axis):
            self.gscreen.mdi_control.user_command("G92 %s0.0" % self.dthc_axis)
        else:
            self.gscreen.mdi_control.set_axis(axis, 0.0)
        self.gscreen.reload_plot()


    # gremlin relevant functions ----------------------------------------------
    def gremlin_setup(self):
        offsets = False
        dro = False
        state = True
        self.widgets.gremlin.set_property('enable_dro', dro)
        self.widgets.gremlin.set_property('show_program', state)
        self.widgets.gremlin.set_property('show_limits', state)
        self.widgets.gremlin.set_property('show_extents_option', state)
        self.widgets.gremlin.set_property('show_live_plot', state)
        self.widgets.gremlin.set_property('show_tool', state)
        if (self.is_plasma):
            self.widgets.gremlin.set_property('mouse_btn_mode', 6)
        else:
            self.widgets.gremlin.set_property('mouse_btn_mode', 4)
        self.widgets.gremlin.show_offsets = offsets
        self.widgets.view_dimensions.set_active(True)
        self.widgets.view_tool.set_active(True)
        #self.widgets.gremlin.colors['backplottraverse'] = (0.00, 0.90, 1.00)
        #self.widgets.gremlin.colors['backplotfeed'] = (0.5, 0.25, 0.25)
    
    def on_gremlin_offsets_toggled(self,widget):
        data = widget.get_active()
        if data:
            dro = offsets = True
            state = dim = tool = False
        else:
            offsets = dro = False
            state = True
            dim = self.widgets.view_dimensions.get_active()
            tool = self.widgets.view_tool.get_active()
        self.widgets.gremlin.set_property('enable_dro', dro)
        self.widgets.gremlin.set_property('show_program', state)
        self.widgets.gremlin.set_property('show_limits', state)
        self.widgets.gremlin.set_property('show_extents_option', dim)
        self.widgets.gremlin.set_property('show_live_plot', state)
        self.widgets.gremlin.set_property('show_tool', tool)
        self.widgets.gremlin.show_offsets = offsets

    #def on_gremlin_view_toggled(self,widget):
        #self.gscreen.toggle_view()
        #label = self.data.plot_view[0].upper()
        #widget.set_label('View %s'% label)

    def on_rbt_view_p_toggled( self, widget, data = None ):
        if self.widgets.view_p.get_active():
            self.widgets.gremlin.set_property( "view", "p" )
        #self.prefs.putpref( "gremlin_view", "rbt_view_p", str )

    def on_rbt_view_x_toggled( self, widget, data = None ):
        if self.widgets.view_x.get_active():
            self.widgets.gremlin.set_property( "view", "x" )
        #self.prefs.putpref( "gremlin_view", "rbt_view_x", str )

    def on_rbt_view_y_toggled( self, widget, data = None ):
        if self.widgets.view_y.get_active():
            self.widgets.gremlin.set_property( "view", "y" )
        #self.prefs.putpref( "gremlin_view", "rbt_view_y", str )

    def on_rbt_view_z_toggled( self, widget, data = None ):
        if self.widgets.view_z.get_active():
            self.widgets.gremlin.set_property( "view", "z" )
        #self.prefs.putpref( "gremlin_view", "rbt_view_z", str )

    #def on_rbt_view_y2_toggled( self, widget, data = None ):
        #if self.log: self._add_alarm_entry( "rbt_view_y2_toggled" )
        #if self.widgets.view_y2.get_active():
            #self.widgets.gremlin.set_property( "view", "y2" )
        #self.prefs.putpref( "gremlin_view", "rbt_view_y2", str )

    #def on_btn_zoom_in_clicked( self, widget, data = None ):
        #if self.log: self._add_alarm_entry( "btn_zoom_in_clicked" )
        #self.widgets.gremlin.zoom_in()

    #def on_btn_zoom_out_clicked( self, widget, data = None ):
        #if self.log: self._add_alarm_entry( "btn_zoom_out_clicked" )
        #self.widgets.gremlin.zoom_out()

    #def on_btn_delete_view_clicked( self, widget, data = None ):
        #if self.log: self._add_alarm_entry( "btn_delete_view_clicked" )
        #self.widgets.gremlin.clear_live_plotter()

    def on_tbtn_view_dimension_toggled( self, widget, data = None ):
        #if self.log: self._add_alarm_entry( "tbtn_view_dimensions_toggled" )
        self.widgets.gremlin.set_property( "show_extents_option", widget.get_active() )
        #self.prefs.putpref( "view_dimension", self.widgets.tbtn_view_dimension.get_active(), bool )

    def on_tbtn_view_tool_path_toggled( self, widget, data = None ):
        #if self.log: self._add_alarm_entry( "btn_view_tool_path_clicked" )
        self.widgets.gremlin.set_property( "show_live_plot", widget.get_active() )
        #self.prefs.putpref( "view_tool_path", self.widgets.tbtn_view_tool_path.get_active(), bool )
    
    def on_tbtn_view_tool_toggled( self, widget, data = None ):
        self.widgets.gremlin.set_property('show_tool', widget.get_active())

    def on_gcode_selection_changed(self, txtbuf, txtiter, txtmark):
        if (txtmark.get_name() == "insert"):
            self.cur_line_number = txtiter.get_line() + 1
            self.widgets.gcode_view.set_line_number(self.cur_line_number)
            self.widgets.gremlin.set_highlight_line(self.cur_line_number)
            self.do_goto_line = False
            self.widgets.linenum.set_value(self.cur_line_number)
            #print self.cur_line_number
    
    def on_gremlin_gcode_error(self, widget, error):
        self.gscreen.add_alarm_entry(error)

    def on_gremlin_line_clicked(self, widget, line):
        if (line != self.cur_line_number):
            self.cur_line_number = line
            self.widgets.gcode_view.set_line_number(self.cur_line_number)
            self.do_goto_line = False
            self.widgets.linenum.set_value(self.cur_line_number)
        #print self.cur_line_number
        
    def on_gremlin_file_opened(self, widget):
        self.widgets.blockdelete.set_active(self.delstate)
        
    #def _on_state_line_changed(self, widget, line):
        #pass
        #print line
        #self.cur_line_number = line

    def _on_zsync_ready_change(self, hal_pin, data=None):
        #if (hal_pin.get() == True):
            #cmd = "G92 Z%s" % self.zpos_final_in.get()
            #print cmd
            #self.gstat.stat.poll()
            #if self.gstat.stat.task_mode != linuxcnc.MODE_MDI:
                #self.emc_cmd.mode(linuxcnc.MODE_MDI)
                #self.emc_cmd.wait_complete()
            #self.gscreen.mdi_control.user_command(cmd)
        pass

    # I don't call self.gstat.stat.poll() here because it is or should be called just before this function
    def is_file_loaded(self):
        if self.gstat.stat.file:
            return True
        else:
            return False

    def _on_state_on(self, widget):
        self.gstat.stat.poll()
        self.data.machine_on = self.gstat.stat.task_state == linuxcnc.STATE_ON
        self.widgets.estop_btn.set_light_on(False)
        #Home and Zero buttons get set in _on_interp_idle()
        self.widgets.spindletorch.set_sensitive(True)
        self.widgets.output1.set_sensitive(True)
        self.widgets.output2.set_sensitive(True)
        
    def _on_state_off(self, widget):
        self.widgets.estop_btn.set_light_on(True)
        self.widgets.run_btn.set_sensitive(False)
        self.widgets.runfromline_btn.set_sensitive(False)
        self.widgets.feedhold_btn.set_sensitive(False)
        self.widgets.zero_x.set_sensitive(False)
        self.widgets.zero_y.set_sensitive(False)
        self.widgets.zero_z.set_sensitive(False)
        self.widgets.zero_a.set_sensitive(False)
        self.widgets.zero_b.set_sensitive(False)
        self.widgets.spindletorch.set_sensitive(False)
        self.widgets.output1.set_sensitive(False)
        self.widgets.output2.set_sensitive(False)
        
    def _on_state_estop(self, widget):
        self.widgets.estop_btn.set_light_on(True)
        self.widgets.run_btn.set_sensitive(False)
        self.widgets.runfromline_btn.set_sensitive(False)
        self.widgets.feedhold_btn.set_sensitive(False)
        self.widgets.zero_x.set_sensitive(False)
        self.widgets.zero_y.set_sensitive(False)
        self.widgets.zero_z.set_sensitive(False)
        self.widgets.zero_a.set_sensitive(False)
        self.widgets.zero_b.set_sensitive(False)
        self.widgets.spindletorch.set_sensitive(False)
        self.widgets.output1.set_sensitive(False)
        self.widgets.output2.set_sensitive(False)
        
    def _on_interp_idle(self, widget):
        self.gstat.stat.poll()
        self.widgets.run_btn.set_sensitive(self.data.machine_on and self.is_file_loaded() and (self.data.all_homed or self.no_home_required))
        self.widgets.runfromline_btn.set_sensitive(self.data.machine_on and self.is_file_loaded() and (self.data.all_homed or self.no_home_required))
        self.widgets.feedhold_btn.set_sensitive(False)
        self.widgets.linenum.set_sensitive(True)  #can change line numbers when idle
        self.widgets.zero_x.set_sensitive(self.data.machine_on)
        self.widgets.zero_y.set_sensitive(self.data.machine_on)
        self.widgets.zero_z.set_sensitive(self.data.machine_on)
        self.widgets.zero_a.set_sensitive(self.data.machine_on)
        self.widgets.zero_b.set_sensitive(self.data.machine_on)

    def _on_interp_run(self, widget):
        self.widgets.run_btn.set_sensitive(False)
        self.widgets.runfromline_btn.set_sensitive(False)
        self.widgets.feedhold_btn.set_sensitive(self.data.machine_on)
        self.widgets.linenum.set_sensitive(False)  #can NOT change line numbers when running
        self.widgets.zero_x.set_sensitive(False)
        self.widgets.zero_y.set_sensitive(False)
        self.widgets.zero_z.set_sensitive(False)
        self.widgets.zero_a.set_sensitive(False)
        self.widgets.zero_b.set_sensitive(False)

    #def _on_interp_paused(self, widget):
       #pass

    #def _on_interp_waiting(self, widget):
        #pass
    
    # erase the ready-to-home message on statusbar
    def on_hal_status_all_homed(self,widget):
        self.gstat.stat.poll()
        self.data.all_homed = True
        self.widgets.statusbar1.remove_message(self.gscreen.statusbar_id,self.gscreen.homed_status_message)
        self.widgets.run_btn.set_sensitive(self.data.machine_on and self.is_file_loaded())
        self.widgets.runfromline_btn.set_sensitive(self.data.machine_on and self.is_file_loaded())
        
    def _on_hal_status_file_loaded(self, widget, filename):
        self.widgets.run_btn.set_sensitive(self.data.machine_on and (self.data.all_homed or self.no_home_required))
        self.widgets.runfromline_btn.set_sensitive(self.data.machine_on and (self.data.all_homed or self.no_home_required))
        self.widgets.gremlin.clear_live_plotter()
        #self.gscreen.add_alarm_entry(_("Program loaded: %s"%filename))
        
    def after_gcodefile_load(self, widget):
        self.widgets.linenum_adj.set_upper(self.widgets.gcode_view.program_length)
        
    def after_gcodeline_change(self, widget):
        self.do_goto_line = False
        self.widgets.linenum.set_value(self.widgets.gcode_view.offset)

    # check and handle pendant commands
    def handle_pendant_functions(self):
        pnfunc = self.pn200function.get()
        self.pn200function.set(PYNONE)
        axlist = ['x','y','z','a','b']
        needsjogging = [0,0,0,0,0,0,0,0,0]
        if (PYNONE == pnfunc):
            pass
        elif (PYZERO_X <= pnfunc <= PYZERO_B):
            self.zero_axis(None, axlist[pnfunc - 1])
        elif (PYHOME_X <= pnfunc <= PYHOME_B):
            self.home_axis(None, axlist[pnfunc - 6])
        elif (PYTORCHDISABLE_TGL == pnfunc):
            pass
        elif (PYTHCDISABLE_TGL == pnfunc):
            pass
        elif (PYTHCPRESET_UP == pnfunc):
            pass
        elif (PYTHCPRESET_DN == pnfunc):
            pass
        elif (PYOUTPUT1 == pnfunc):
            # interlock key is NOT pressed, only turn outputs off.
            self.widgets.spindletorch.set_active(False)
            self.spindle_torch_clicked(None, None)
        elif (PYOUTPUT2 == pnfunc):
            self.widgets.output1.set_active(False)
            self.output1_clicked(None, None)
        elif (PYOUTPUT3 == pnfunc):
            self.widgets.output2.set_active(False)
            self.output2_clicked(None, None)
        elif (PYINTERLOCK_OUT1 == pnfunc):
            # interlock key is pressed, toggle output off and on
            self.widgets.spindletorch.set_active(not self.widgets.spindletorch.get_active())
            self.spindle_torch_clicked(None, None)
        elif (PYINTERLOCK_OUT2 == pnfunc):
            self.widgets.output1.set_active(not self.widgets.output1.get_active())
            self.output1_clicked(None, None)
        elif (PYINTERLOCK_OUT3 == pnfunc):
            self.widgets.output2.set_active(not self.widgets.output2.get_active())
            self.output2_clicked(None, None)
        elif (PYMACRO1 == pnfunc):
            self.handle_subroutine_button(widget=None, sub="touchoff")
        elif (PYMACRO2 == pnfunc):
            self.handle_subroutine_button(widget=None, sub="loadmaterial")
        elif (PYMACRO3 == pnfunc):
            pass
        elif (PYFO_RESET == pnfunc):
            self.widgets.adjustment_fo.set_value(100)
        elif (PYFO_PLUS == pnfunc):
            val = self.widgets.scale_fo.get_value() + 5
            self.widgets.adjustment_fo.set_value(val)
        elif (PYFO_MINUS == pnfunc):
            val = self.widgets.scale_fo.get_value() - 5
            self.widgets.adjustment_fo.set_value(val)
        elif (PYESTOP_TGL == pnfunc):
            self.on_reset_toggle(None, None)
        elif (PYRUN == pnfunc):
            self.on_run_btn_clicked(None, None)
        elif (PYFEEDHOLD == pnfunc):
            self.on_feedhold_clicked(None, None)
        elif (PYSTOP == pnfunc):
            self.on_stop_clicked(None, None)
        elif (PYSTEP_INC == pnfunc):
            end = len(self.data.jog_increments)-1
            self.data.current_jogincr_index -= 1
            if self.data.current_jogincr_index < 0:
                self.data.current_jogincr_index = end
            self.gscreen.set_jog_increments(vector = self.data.current_jogincr_index)
            self.widgets.jog_speed.set_active(self.data.current_jogincr_index)
        elif (PYSINGLESTEP_TGL == pnfunc):
            end = len(self.data.jog_increments)-1
            if self.data.current_jogincr_index == end:
                self.data.current_jogincr_index -= 1
            else:
                self.data.current_jogincr_index = end
            self.gscreen.set_jog_increments(vector = self.data.current_jogincr_index)
            self.widgets.jog_speed.set_active(self.data.current_jogincr_index)
        elif (PYJOG_X_PLUS == pnfunc):
            needsjogging[_X] = 1
        elif (PYJOG_X_MINUS == pnfunc):
            needsjogging[_X] = -1
        elif (PYJOG_Y_PLUS == pnfunc):
            needsjogging[_Y] = 1
        elif (PYJOG_Y_MINUS == pnfunc):
            needsjogging[_Y] = -1
        elif (PYJOG_Z_PLUS == pnfunc):
            needsjogging[_Z] = 1
        elif (PYJOG_Z_MINUS == pnfunc):
            needsjogging[_Z] = -1
        elif (PYJOG_A_PLUS == pnfunc):
            needsjogging[_A] = 1
        elif (PYJOG_A_MINUS == pnfunc):
            needsjogging[_A] = -1
        elif (PYJOG_B_PLUS == pnfunc):
            needsjogging[_B] = 1
        elif (PYJOG_B_MINUS == pnfunc):
            needsjogging[_B] = -1
        elif (PYJOG_C_PLUS == pnfunc):
            needsjogging[_C] = 1
        elif (PYJOG_C_MINUS == pnfunc):
            needsjogging[_C] = -1
        elif (PYJOGPERCENT_100 == pnfunc):
            for i in range(9):
                self.pnjog_rate[i] = self.max_vel[i]
        elif (PYJOGPERCENT_50 == pnfunc):
            for i in range(9):
                self.pnjog_rate[i] = self.max_vel[i] * 0.5
        elif (PYJOGPERCENT_25 == pnfunc):
            for i in range(9):
                self.pnjog_rate[i] = self.max_vel[i] * 0.25
        elif (PYJOGPERCENT_10 == pnfunc):
            for i in range(9):
                self.pnjog_rate[i] = self.max_vel[i] * 0.1
        for i in range(len(needsjogging)):
            # only stop jogging here if we were doing pendant jogging
            if self.ispnjogging[i] and not needsjogging[i]:
                self.do_key_jog(i, 0, 0, 0)
            elif needsjogging[i]:
                if needsjogging[i] < 0:
                    jdir = 0
                else:
                    jdir = 1
                self.do_key_jog(i, jdir, 1, 0, True)


    # every 100 milli seconds this gets called
    # add pass so gscreen doesn't try to update it's regular widgets or
    # add the individual function names that you would like to call.
    def periodic(self):
        if False == self.pins_initialized:
            self.z_jog_rate.set(self.jog_rate[2])
            self.oxyup.set(0)
            self.oxydown.set(0)
            self.pins_initialized = True
        
        if self.data.jog_increments[self.data.current_jogincr_index] != ("continuous"):
            self.step_jog.set(True)
        else:
            self.step_jog.set(False)
            
        if (self.is_plasma):
            zp = self.zpos.get()
            zp = self.gscreen.status.convert_units(zp)
            if self.data.dro_units == self.data._MM:
                text = "%10.3f"% (zp)
            else:
                text = "%9.4f"% (zp)
            self.dros[self.dthc_axis].set_label(text)
        
        fr = self.data.velocity
        if self.data.dro_units == self.data._MM:
            text = "%.2f"% (fr)
        else:
            text = "%.3f"% (fr)
        self.widgets.feedrate.set_label(text)
        
        #See the gscreen.update_position() function for various functions that can be called here.
        #self.gscreen.update_position()

        #self.gscreen.update_jog_rate_label()
        #self.gscreen.update_mode_label()
        #self.gscreen.update_units_button_label()
        #self.gscreen.update_active_gcodes()
        #self.widgets.activegcodes.set_label(" ".join(self.gscreen.data.active_gcodes))
        
        self.widgets.ignore_limits.set_light_on(self.data.or_limits)
        #self.widgets.gcode_view.on_line_changed(0, self.gstat.stat.id or self.gstat.stat.motion_line)
        
        # call the function to handle pendant commands.  If 100 ms is too slow,
        # I'll make a timer in the init() function to call it faster.
        self.handle_pendant_functions()



    def __getitem__(self, item):
        return getattr(self, item)
    def __setitem__(self, item, value):
        return setattr(self, item, value)

# standard handler call
def get_handlers(halcomp,builder,useropts,gscreen):
     return [HandlerClass(halcomp,builder,useropts,gscreen)]
