# This is a handler file for using Gscreen's infrastructure
# to load a completely custom glade screen.
# The only thing 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 otherwise you can't close down linuxcnc

import gi
from gi.repository import Gtk as gtk
from gi.repository import Gdk as gdk
from gi.repository import GObject as gobject
from gi.repository import Pango as pango
import hal
import hal_glib
import linuxcnc
from time import strftime

# constants
_RELEASE = " 1.1"
_INCH = 0
_MM = 1
# Mode Notebook Tabs
_MODE_MANUAL = 1
_MODE_MDI = 0
_MODE_AUTO = 1
# Main Notebook Tabs
_NB_PREVIEW = 0
_NB_OFFSET = 1
_NB_TOOL = 2
_NB_ALARMS = 3
_NB_SETUP = 4

NOTIFY_AVAILABLE = False
ALERT_ICON = "/home/jim/linuxcnc/configs/silverdragon/images/applet-critical.png"
INFO_ICON = "/home/jim/linuxcnc/configs/silverdragon/images/std_info.gif"

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

class HandlerClass:

    # This will be pretty standard to gain access to everything
    # command 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.command = linuxcnc.command()
            self.data = gscreen.data
            self.widgets = gscreen.widgets
            self.gscreen = gscreen
            self.stat = linuxcnc.stat()
            self.halcomp = self.gscreen.halcomp
            self.error_channel = linuxcnc.error_channel()

# global variables
            self.initialized = False
            self.factor = 1.0
            self.slow_jog = 0
            self.fast_jog = 0
            self.slow_jog_factor = 10
            self.metric_units = True
            self.start_system = "G54"
            self.system_list = ("0", "G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3")
            self.units = ["", "IN", "MM", "CM"]
            self.must_be_auto = "Must be in AUTO mode"
            self.program_length = 0
            self.program_progress = 0.0
            self.current_line = 1
            self.distance = 0
            self.jog_increments = []
            self.tool_diameter = 0.0
            self.load_tool = False
            self.tool_change = False
            self.use_auto_zref = False
            self.tool_sensor = False
            self.tool_sensor_x = 0.0
            self.tool_sensor_y = 0.0
            self.tool_sensor_z = 0.0
            self.laser_x = 0.0
            self.laser_y = 0.0
            self.home_x = 0.0
            self.home_y = 0.0
            self.home_z = 0.0
            self.axis_to_ref = []
            self.xpos = 0
            self.ypos = 0
            self.width = 0
            self.height = 0
            self.file_to_load = ""
            gobject.timeout_add(1000, self.clock)
            self.default_theme = gtk.Settings.get_default().get_property("gtk-theme-name")
            self.label_home_x = self.widgets.btn_home_x.get_children()[0]
            self.label_home_y = self.widgets.btn_home_y.get_children()[0]
            self.label_home_z = self.widgets.btn_home_z.get_children()[0]
            self.init_file_to_load()

    def __getitem__(self, item):
        return getattr(self, item)

    # This connects signals without using glade's autoconnect method
    # in this case to destroy the window
    # it calls the method in gscreen: gscreen.on_window_destroy()
    # Signals in this list are connected to gscreen methods
    def connect_signals(self,handlers):
        signal_list = [ ["window1","destroy", "on_window1_destroy"],
                        ["btn_show_hal", "clicked", "on_halshow"],
                        ["btn_hal_meter", "clicked", "on_halmeter"],
                        ["btn_hal_scope", "clicked", "on_halscope"],
                        ["btn_classicladder", "clicked", "on_ladder"],
                        ["btn_status", "clicked", "on_status"],
                        ["btn_calibration", "clicked", "on_calibration"],
                        ["desktop_notify", "toggled", "on_desktop_notify_toggled"],
                        ["theme_choice", "changed", "on_theme_choice_changed"],
                        ["btn_pop_statusbar", "clicked", "on_pop_statusbar_clicked"]]
        for i in signal_list:
            if len(i) == 3:
                self.gscreen.widgets[i[0]].connect(i[1], self.gscreen[i[2]])
            elif len(i) == 4:
                self.gscreen.widgets[i[0]].connect(i[1], self.gscreen[i[2]],i[3])
        for i in('x','y','z'):
            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.jog_speed.connect("value_changed",self.jog_speed_changed)
        self.widgets.full_window.connect("pressed", self.on_fullscreen_pressed)
        self.widgets.max_window.connect("pressed", self.on_max_window_pressed)
        self.widgets.default_window.connect("pressed", self.on_default_window_pressed)

    # 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 the window to display.
    # init_show_window will do this
    def initialize_widgets(self):
        self.gscreen.init_show_windows()
        self.gscreen.init_gremlin()
        self.gscreen.init_audio()
        self.gscreen.init_desktop_notify()
        self.gscreen.init_statusbar()
        self.gscreen.init_tooleditor()
        self.gscreen.init_themes()
        self.init_home()
        self.init_jog_increments()
        self.init_tool_measurement()
        self.init_button_colors()
        self.init_offsetpage()
        self.init_sensitive_on_off()
        self.init_sensitive_run_idle()
        self.init_sensitive_all_homed()
        self.init_unit_labels()
        self.widgets.chk_reload_tool.set_active(self.gscreen.prefs.getpref("reload_tool", True, bool))
        self.widgets.rbt_abs.emit("toggled")
        self.widgets.rbt_rel.set_label(self.start_system)
        opt_blocks = self.gscreen.prefs.getpref("blockdel", False, bool)
        self.widgets.tbtn_optional_blocks.set_active(opt_blocks)
        self.command.set_block_delete(opt_blocks)
        optional_stops = self.gscreen.prefs.getpref( "opstop", False, bool )
        self.widgets.tbtn_optional_stops.set_active( optional_stops )
        self.command.set_optional_stop( optional_stops )
        self.widgets.chk_show_dro.set_active(self.gscreen.prefs.getpref("enable_dro", False, bool))
        self.widgets.show_offsets.set_active(self.gscreen.prefs.getpref("show_offsets", False, bool))
        self.widgets.chk_show_dtg.set_active(self.gscreen.prefs.getpref("show_dtg", False, bool))
        self.widgets.show_offsets.set_sensitive(self.widgets.chk_show_dro.get_active())
        self.widgets.chk_show_dtg.set_sensitive(self.widgets.chk_show_dro.get_active())
        self.widgets.cmb_mouse_button_mode.set_active(self.gscreen.prefs.getpref("mouse_btn_mode", 4, int))
        self.widgets.tbtn_view_tool_path.set_active(self.gscreen.prefs.getpref("view_tool_path", True, bool))
        self.widgets.tbtn_view_dimension.set_active(self.gscreen.prefs.getpref("view_dimension", True, bool))
        self.widgets.rbt_view_p.emit("toggled")
        self.widgets.tbtn_units.set_label("MM")

        if self.gscreen.prefs.getpref("run_from_line", "no_run", str) == "no_run":
            self.widgets.chk_run_from_line.set_active(False)
            self.widgets.btn_from_line.set_sensitive(False)
        else:
            self.widgets.chk_run_from_line.set_active(True)
            self.widgets.btn_from_line.set_sensitive(True)
        if self.gscreen.prefs.getpref("show_preview_on_offset", False, bool):
            self.widgets.rbtn_show_preview.set_active(True)
        else:
            self.widgets.rbtn_show_offsets.set_active(True)
        self.widgets.chk_use_kb_shortcuts.set_active(self.gscreen.prefs.getpref("use_keyboard_shortcuts", False, bool))
        self.widgets.adj_start_spindle_RPM.set_value(self.spindle_start_rpm)
        self.widgets.gcode_view.set_sensitive(False)
        self.widgets.ntb_main.set_current_page(_NB_PREVIEW)
        self.gscreen.sensitize_widgets(self.data.sensitive_on_off, False)
        self.command.mode(linuxcnc.MODE_MANUAL)
        self.command.wait_complete()

    def initialize_preferences(self):
        self.gscreen.init_general_pref()
        self.gscreen.init_theme_pref()
        self.gscreen.init_window_geometry_pref()
        self.no_force_homing = self.gscreen.inifile.find("TRAJ", "NO_FORCE_HOMING")
        if self.no_force_homing:
            self.widgets.chk_reload_tool.set_sensitive(False)
            self.widgets.chk_reload_tool.set_active(False)
            self.widgets.lbl_reload_tool.set_visible(True)
        default_spindle_speed = self.gscreen.inifile.find("DISPLAY", "DEFAULT_SPINDLE_SPEED")
        self.spindle_start_rpm = self.gscreen.prefs.getpref( 'spindle_start_rpm', default_spindle_speed, float )
        # get the values for the sliders
        default_jog_vel = float(self.gscreen.inifile.find("TRAJ", "DEFAULT_LINEAR_VELOCITY"))
        self.fast_jog = default_jog_vel
        self.slow_jog = default_jog_vel / self.slow_jog_factor
        self.jog_rate_max = self.gscreen.inifile.find("TRAJ", "MAX_LINEAR_VELOCITY")
        self.data.spindle_override_max = self.gscreen.inifile.find("DISPLAY", "MAX_SPINDLE_OVERRIDE")
        self.data.spindle_override_min = self.gscreen.inifile.find("DISPLAY", "MIN_SPINDLE_OVERRIDE")
        self.data.feed_override_max = self.gscreen.inifile.find("DISPLAY", "MAX_FEED_OVERRIDE")
        self.data.dro_actual = self.gscreen.inifile.find("DISPLAY", "POSITION_FEEDBACK")
        # set the slider limits
        self.widgets.jog_speed.set_range(100, float(self.jog_rate_max) * 60)
        self.widgets.jog_speed.set_value(default_jog_vel * 60)
        self.widgets.jog_speed.set_digits(0)
        self.widgets.spc_spindle.set_range(float(self.data.spindle_override_min) * 100, float(self.data.spindle_override_max) * 100)
        self.widgets.spc_spindle.set_value(100)
        self.widgets.spc_rapid.set_range(1, 100)
        self.widgets.spc_rapid.set_value(100)
        self.widgets.spc_feed.set_range(1, float(self.data.feed_override_max) * 100)
        self.widgets.spc_feed.set_value(100)
        self.max_velocity = self.stat.max_velocity

    def initialize_keybindings(self):
        self.widgets.window1.connect("key_press_event", self.gscreen.on_key_event, 1)
        self.widgets.window1.connect("key_release_event", self.gscreen.on_key_event, 0)

    def init_home(self):
        self.home_x = self.gscreen.inifile.find("JOINT_0", "HOME")
        self.home_y = self.gscreen.inifile.find("JOINT_1", "HOME")
        self.home_z = self.gscreen.inifile.find("JOINT_2", "HOME")

    def init_tool_measurement(self):
        # set up auto zref
        xpos = self.gscreen.inifile.find("TOOLSENSOR", "X")
        ypos = self.gscreen.inifile.find("TOOLSENSOR", "Y")
        zpos = self.gscreen.inifile.find("TOOLSENSOR", "Z")
        sensor_height = self.gscreen.inifile.find("TOOLSENSOR", "SENSOR_HEIGHT")
        maxprobe = self.gscreen.inifile.find("TOOLSENSOR", "MAXPROBE")
        search_vel = self.gscreen.inifile.find("TOOLSENSOR", "SEARCH_VEL")
        probe_vel = self.gscreen.inifile.find("TOOLSENSOR", "PROBE_VEL")
        self.halcomp["probe_vel"] = probe_vel
        self.halcomp["search_vel"] = search_vel
        self.halcomp["sensor_height"] = sensor_height
        self.halcomp["maxprobe"] = maxprobe
        if not xpos or not ypos or not zpos or not maxprobe:
            self.widgets.chk_use_auto_zref.set_active(False)
            self.widgets.chk_use_auto_zref.set_sensitive(False)
            self.widgets.btn_g30.set_sensitive(False)
            self.widgets.btn_blockheight.set_sensitive(False)
            self.widgets.lbl_blockheight.hide()
            self.widgets.chk_use_auto_zref.set_sensitive(False)
            print(_("No valid tool sensor location"))
            print(_("Block Height - Disabled"))
        else:
            self.widgets.lbl_tool_measurement.hide()
            self.widgets.lbl_xpos.set_label(str(xpos))
            self.widgets.lbl_ypos.set_label(str(ypos))
            self.widgets.lbl_zpos.set_label(str(zpos))
            self.widgets.lbl_max_probe.set_label(str(maxprobe))
            self.widgets.lbl_sensor_height.set_label(str(sensor_height))
            self.widgets.lbl_search_vel.set_label(str(search_vel))
            self.widgets.lbl_probe_vel.set_label(str(probe_vel))
            self.use_auto_zref = self.gscreen.prefs.getpref("use_autozref", False, bool)
            self.tool_sensor = True
            self.tool_sensor_x = xpos
            self.tool_sensor_y = ypos
            self.tool_sensor_z = zpos
            print(_("Block Height - Enabled"))
        self.widgets.chk_use_auto_zref.emit("toggled")
        # set up laser crosshair offsets
        xpos = self.gscreen.inifile.find("LASER", "X")
        ypos = self.gscreen.inifile.find("LASER", "Y")
        if not xpos or not ypos:
            self.widgets.btn_laser_zero.set_sensitive(False)
            self.widgets.tbtn_laser.set_sensitive(False)
        else:
            self.laser_x = xpos
            self.laser_y = ypos

    def init_button_colors(self):
        # set the button background colors and digits of the DRO
        self.homed_textcolor = self.gscreen.prefs.getpref("homed_textcolor", "#00FF00", str)     # default green
        self.unhomed_textcolor = self.gscreen.prefs.getpref("unhomed_textcolor", "#FF0000", str) # default red
        self.homed_color = gdk.RGBA()
        self.homed_color.parse(self.homed_textcolor)
        self.widgets.homed_colorbtn.set_rgba(self.homed_color)
        self.unhomed_color = gdk.RGBA()
        self.unhomed_color.parse(self.unhomed_textcolor)
        self.widgets.unhomed_colorbtn.set_rgba(self.unhomed_color)
        self.widgets.tbtn_estop.modify_bg(gtk.StateFlags.NORMAL, gdk.color_parse("#00FF00"))
        self.widgets.tbtn_estop.modify_bg(gtk.StateFlags.ACTIVE, gdk.color_parse("#FF0000"))
        self.widgets.tbtn_on.modify_bg(gtk.StateFlags.NORMAL, gdk.color_parse("#FF0000"))
        self.widgets.tbtn_on.modify_bg(gtk.StateFlags.ACTIVE, gdk.color_parse("#00FF00"))
        self.label_home_x.modify_fg(gtk.StateFlags.NORMAL, gdk.color_parse("#FF0000"))
        self.label_home_y.modify_fg(gtk.StateFlags.NORMAL, gdk.color_parse("#FF0000"))
        self.label_home_z.modify_fg(gtk.StateFlags.NORMAL, gdk.color_parse("#FF0000"))
        # set the active colours of togglebuttons and radiobuttons
        blue_list = ["tbtn_mist", "tbtn_flood", "tbtn_laser", "tbtn_spare",
                     "tbtn_units", "tbtn_pause",
                     "tbtn_optional_stops", "tbtn_optional_blocks",
                     "rbt_abs", "rbt_rel", "rbt_dtg",
                     "rbt_reverse", "rbt_stop", "rbt_forward"]
        green_list = ["rbt_manual", "rbt_mdi", "rbt_auto"]
        other_list = ["rbt_view_p", "rbt_view_x", "rbt_view_y", "rbt_view_z",
                      "tbtn_view_dimension", "tbtn_view_tool_path"]
        for btn in blue_list:
            self.widgets["{0}".format(btn)].modify_bg(gtk.StateFlags.ACTIVE, gdk.color_parse("#44A2CF"))
        for btn in green_list:
            self.widgets["{0}".format(btn)].modify_bg(gtk.StateFlags.ACTIVE, gdk.color_parse("#A2E592"))
        for btn in other_list:
            self.widgets["{0}".format(btn)].modify_bg(gtk.StateFlags.ACTIVE, gdk.color_parse("#BB81B5"))

    def init_sensitive_on_off(self):
        self.data.sensitive_on_off = ["table_run", "tbl_dro", "vbox_overrides",
                                      "vbox_tool", "hbox_spindle_ctl"]

    def init_sensitive_run_idle(self):
        self.data.sensitive_run_idle = ["rbt_manual", "rbt_mdi", "rbt_auto",
                                        "tbtn_optional_blocks", "tbtn_optional_stops",
                                        "btn_run", "rbt_reverse", "rbt_stop", "rbt_forward"]

    def init_sensitive_all_homed(self):
        self.data.sensitive_all_homed = ["btn_zero_x", "btn_zero_y", "btn_zero_z",
                                         "btn_ref_x", "btn_ref_y", "btn_ref_z"]

    def initialize_pins(self):
        # pins for block height
        self.halcomp.newpin("sensor_height", hal.HAL_FLOAT, hal.HAL_OUT)
        pin = self.halcomp.newpin("block_height", hal.HAL_FLOAT, hal.HAL_OUT)
        hal_glib.GPin(pin).connect("value_changed", self.on_blockheight_changed)
        self.halcomp["block_height"] = self.gscreen.prefs.getpref("blockheight", 0.0, float)
        # pins for probing
        self.halcomp.newpin("search_vel", hal.HAL_FLOAT, hal.HAL_OUT)
        self.halcomp.newpin("probe_vel", hal.HAL_FLOAT, hal.HAL_OUT)
        self.halcomp.newpin("maxprobe", hal.HAL_FLOAT, hal.HAL_OUT)
        # pins for clear_mdi button
        self.halcomp.newpin("clear_mdi", hal.HAL_BIT, hal.HAL_OUT)
        pin = self.halcomp.newpin("clear_mdi-waiting", hal.HAL_BIT, hal.HAL_OUT)
        pin = self.halcomp.newpin("clear_mdi-response", hal.HAL_BIT, hal.HAL_OUT)
        hal_glib.GPin(pin).connect("value_changed", self.on_clear_mdi_changed)

    # 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):
        # put the poll command in a try, so if the linuxcnc pid is killed
        # from an external command, also quit the GUI
        try:
            self.stat.poll()
        except:
            raise SystemExit("SilverDragon cannot poll linuxcnc status any more")
        error = self.error_channel.poll()
        if error:
            self.gscreen.notify(_("ERROR"), _(error), ALERT_ICON)
        if self.data.active_gcodes != self.stat.gcodes:
            self.gscreen.update_active_gcodes()
        if self.data.active_mcodes != self.stat.mcodes:
            self.gscreen.update_active_mcodes()
        if self.stat.task_mode == linuxcnc.MODE_MANUAL \
            and self.stat.interp_state == linuxcnc.INTERP_IDLE \
            and self.widgets.ntb_main.get_current_page() == _NB_PREVIEW:
            self.widgets.hal_led_jog.set_active(True)
        else:
            self.widgets.hal_led_jog.set_active(False)
        self._update_vel()
        self._update_coolant()
        self._update_spindle()
        self._update_vc()
        return True

    def clock(self):
        self.update_progress()
        self.update_dro()
        self.widgets.rbt_rel.set_label(self.system_list[self.stat.g5x_index])
        self.widgets.entry_clock.set_text(strftime("%H:%M:%S"))
        return True

# ========================
# Start of widget handlers
# ========================
    def on_window1_show(self, widget, data=None):
        self.stat.poll()
        if self.stat.task_state == linuxcnc.STATE_ESTOP:
            self.widgets.tbtn_estop.set_active(True)
            self.widgets.tbtn_on.set_sensitive(False)
        else:
            self.widgets.tbtn_estop.set_active(False)
            self.widgets.tbtn_on.set_sensitive(True)

        if self.file_to_load != "":
            self.widgets.hal_action_open.load_file(self.file_to_load)
        start_as = self.gscreen.prefs.getpref("window_geometry", "default", str)
        if start_as == "fullscreen":
            self.widgets.window1.fullscreen()
        elif start_as == "max":
            self.widgets.window1.maximize()
        else:
            self.xpos = int(self.gscreen.prefs.getpref("x_pos", 10, float))
            self.ypos = int(self.gscreen.prefs.getpref("y_pos", 10, float))
            self.width = int(self.gscreen.prefs.getpref("width", 1440, float))
            self.height = int(self.gscreen.prefs.getpref("height", 900, float))
            # set the adjustments according to Window position and size
            self.widgets.adj_x_pos.set_value(self.xpos)
            self.widgets.adj_y_pos.set_value(self.ypos)
            self.widgets.adj_width.set_value(self.width)
            self.widgets.adj_height.set_value(self.height)
            # move and resize the window
            self.widgets.window1.move(self.xpos, self.ypos)
            self.widgets.window1.resize(self.width, self.height)
        self.initialized = True

    # estop machine before closing
    def on_window1_destroy(self, widget, data=None):
        print("estopping / killing silverdragon")
        self.command.state(linuxcnc.STATE_OFF)
        self.command.state(linuxcnc.STATE_ESTOP)
        gtk.main_quit()

    def on_btn_exit_clicked(self, widget, data=None):
        self.widgets.tbtn_estop.emit("toggled")
        self.widgets.window1.destroy()
        return

    def on_rbt_manual_clicked(self, widget):
        if self.widgets.ntb_main.get_current_page() == _NB_SETUP:
            return
        self.command.mode(linuxcnc.MODE_MANUAL)
        self.command.wait_complete()

    def on_rbt_mdi_clicked(self, widget):
        if self.widgets.ntb_main.get_current_page() == _NB_SETUP:
            self.widgets.rbt_manual.set_active(True)
            self.widgets.rbt_mdi.set_active(False)
        else:
            self.command.mode(linuxcnc.MODE_MDI)
            self.command.wait_complete()

    def on_rbt_auto_clicked(self, widget):
        if self.widgets.ntb_main.get_current_page() == _NB_SETUP:
            self.widgets.rbt_manual.set_active(True)
            self.widgets.rbt_auto.set_active(False)
        else:
            self.command.mode(linuxcnc.MODE_AUTO)
            self.command.wait_complete()

    def on_ntb_main_switch_page(self, widget, tab, index):
        if self.stat.task_mode == linuxcnc.MODE_AUTO:
            return
        if index == _NB_OFFSET:
            names = self.widgets.offsetpage1.get_names()
            for system, name in names:
                system_name = "system_name_{0}".format(system)
                self.gscreen.prefs.putpref(system_name, name, str)
            self.widgets.offsetpage1.mark_active((self.system_list[self.stat.g5x_index]).lower())
        elif index == _NB_TOOL:
            self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle)
        elif index == _NB_ALARMS:
            return
        elif index == _NB_SETUP:
            self.widgets.rbt_manual.emit("clicked")
        else:
            return

    def on_rbt_forward_clicked(self, widget, data=None):
        self._set_spindle("forward")

    def on_rbt_reverse_clicked(self, widget, data=None):
        self._set_spindle("reverse")

    def on_rbt_stop_clicked(self, widget, data=None):
        self._set_spindle("stop")

    def on_tbtn_units_toggled(self, widget, data=None):
        if widget.get_active():
            self.metric_units = False
            widget.set_label("IN")
        else:
            self.metric_units = True
            widget.set_label("MM")
        for axis in self.data.axis_list:
            self.widgets["hal_dro_{0}".format(axis)].set_property("display_units_mm", self.metric_units)
        self.gscreen.set_dro_units(not widget.get_active(), 1)

    def on_rbt_abs_toggled(self, widget, data=None):
        for axis in self.data.axis_list:
            self.widgets["hal_dro_{0}".format(axis)].set_property("reference_type", 0)

    def on_rbt_rel_toggled(self, widget, data=None):
        for axis in self.data.axis_list:
            self.widgets["hal_dro_{0}".format(axis)].set_property("reference_type", 1)

    def on_rbt_dtg_toggled(self, widget, data=None):
        for axis in self.data.axis_list:
            self.widgets["hal_dro_{0}".format(axis)].set_property("reference_type", 2)

    def on_zero_axis_clicked(self, widget):
        if not self.data.all_homed:
            self.gscreen.notify(_("INFO"), _("Must be all homed to perform this operation"), INFO_ICON)
            return
        if self.stat.interp_state != linuxcnc.INTERP_IDLE:
            self.gscreen.notify(_("INFO"), _("Interpreter is not IDLE"), INFO_ICON)
            return
        if widget == self.widgets.btn_zero_x:
            self.axis_to_ref = "x"
        elif widget == self.widgets.btn_zero_y:
            self.axis_to_ref = "y"
        elif widget == self.widgets.btn_zero_z:
            self.axis_to_ref = "z"
        else:
            self.gscreen.notify(_("ERROR"), _("Unknown axis selected"), ALERT_ICON)
            return
        self.command.mode(linuxcnc.MODE_MDI)
        self.command.wait_complete()
        command = "G10 L20 P0 {0}0".format(self.axis_to_ref)
        self.command.mdi(command)
        self.command.wait_complete()
        self.gscreen.reload_plot()
        self.command.mode(linuxcnc.MODE_MDI)
        self.command.wait_complete()
        self.gscreen.prefs.putpref("offset_axis_{0}".format(self.axis_to_ref), 0, float)

    def on_ref_axis_clicked(self, widget):
        if not self.data.all_homed:
            self.gscreen.notify(_("INFO"), _("Must be all homed to perform this operation"), INFO_ICON)
            return
        if self.stat.interp_state != linuxcnc.INTERP_IDLE:
            self.gscreen.notify(_("INFO"), _("Interpreter is not IDLE"), INFO_ICON)
            return
        if widget == self.widgets.btn_ref_x:
            self.axis_to_ref = "x"
        elif widget == self.widgets.btn_ref_y:
            self.axis_to_ref = "y"
        elif widget == self.widgets.btn_ref_z:
            self.axis_to_ref = "z"
        else:
            self.gscreen.notify(_("ERROR"), _("Unknown axis selected"), ALERT_ICON)
            return
        callback = "on_offset_axis_return"
        title = "Enter Offset for Axis %s"%self.axis_to_ref
        self.gscreen.launch_numerical_input(callback, 0, 0, title)
        self.command.mode(linuxcnc.MODE_MDI)
        self.command.wait_complete()

    def on_tbtn_flood_toggled(self, widget, data=None):
        if self.stat.flood == widget.get_active():
            return
        if widget.get_active():
            self.command.flood(linuxcnc.FLOOD_ON)
        else:
            self.command.flood(linuxcnc.FLOOD_OFF)

    def on_tbtn_mist_toggled(self, widget, data=None):
        if self.stat.mist == widget.get_active():
            return
        if widget.get_active():
            self.command.mist(linuxcnc.MIST_ON)
        else:
            self.command.mist(linuxcnc.MIST_OFF)

    def on_tbtn_optional_blocks_toggled(self, widget, data=None):
        opt_blocks = widget.get_active()
        self.command.set_block_delete(opt_blocks)
        self.gscreen.prefs.putpref("blockdel", opt_blocks)

    def on_tbtn_optional_stops_toggled(self, widget, data=None):
        opt_stops = widget.get_active()
        self.command.set_optional_stop(opt_stops)
        self.gscreen.prefs.putpref("opstop", opt_stops)

    def on_tbtn_pause_toggled(self, widget, data=None):
        widgetlist = ["rbt_forward", "rbt_reverse", "rbt_stop"]
        paused = widget.get_active()
        self.gscreen.sensitize_widgets(widgetlist, paused)

    def on_btn_stop_clicked(self, widget, data=None):
        self.command.abort()
        self.set_restart_line(1)
        self.widgets.gcode_view.set_line_number(1)

    def on_btn_run_clicked(self, widget, data=None):
        if self.stat.task_mode != linuxcnc.MODE_AUTO:
            self.gscreen.notify(_("INFO"), self.must_be_auto, INFO_ICON)
            return
        self.widgets.ntb_main.set_current_page(_NB_PREVIEW)
        self.program_progress = 0.0
        self.gscreen.notify(_("INFO"), _("Cycle start pressed in AUTO mode"), INFO_ICON)
        self.widgets.hal_toggleaction_run.emit('activate')

    def on_btn_from_line_clicked(self, widget, data=None):
        if self.stat.task_mode != linuxcnc.MODE_AUTO:
            self.gscreen.notify(_("INFO"), self.must_be_auto, INFO_ICON)
            return
        self.gscreen.launch_restart_dialog(self)

    def on_spc_spindle_value_changed(self, widget, data=None):
        if not self.initialized:
            return
        # this is in a try except, because on initializing the window,
        # the values are still zero, so we would get a divide by zero error
        value = widget.get_value()
        try:
            if not abs(self.stat.settings[2]):
                if self.widgets.rbt_forward.get_active() or self.widgets.rbt_reverse.get_active():
                    speed = self.stat.spindle[0]['speed']
                else:
                    speed = 0
            else:
                speed = abs(self.stat.spindle[0]['speed'])
            spindle_override = value / 100
            self.command.spindleoverride(spindle_override)
        except:
            pass

    def on_spc_feed_value_changed(self, widget, data=None):
        if not self.initialized:
            return
        value = widget.get_value() / 100
        self.data.feed_override = value
        self.command.feedrate(value)

    def on_spc_rapid_value_changed(self, widget, data=None):
        if not self.initialized:
            return
        value = widget.get_value() / 100
        self.data.rapid_override = value
        self.command.rapidrate(value)

    def on_btn_home_clicked(self, widget, data=None):
        command = "G90 G53 G0 Z{}".format(self.home_z)
        self.command.mdi(command)
        self.command.wait_complete()
        command = "G53 G0 X{} Y{}".format(self.home_x, self.home_y)
        self.command.mdi(command)
        self.command.wait_complete()

    def on_btn_laser_zero_clicked(self, widget, data=None):
        if self.stat.task_mode != linuxcnc.MODE_MDI:
            self.gscreen.notify(_("INFO"), _("Must be in MDI mode"), INFO_ICON)
            return
        command = "G10 L20 P0 X{} Y{}".format(self.laser_x, self.laser_y)
        self.command.mdi(command)
        self.command.wait_complete()
        self.gscreen.reload_plot()

    def on_btn_M6_clicked(self, widget, data=None):
        tool = str(self.widgets.enter_tool.get_text())
        if not tool.isdigit():
            self.gscreen.notify(_("ERROR"), _("Invalid tool specified"), ALERT_ICON)
            self.widgets.enter_tool.set_text(str(self.stat.tool_in_spindle))
        else:
            command = "T{} M6".format(tool)
            self.command.mdi(command)
            self.command.wait_complete()

    def on_btn_g30_clicked(self, widget, data=None):
        if not self.data.all_homed:
            self.gscreen.notify(_("ERROR"), _("Machine is not homed"), ALERT_ICON)
            return
        self.command.mdi("G53 G0 Z0")
        self.command.wait_complete()
        command = "G53 G0 X{} Y{}".format(self.tool_sensor_x, self.tool_sensor_y)
        self.command.mdi(command)
        self.command.wait_complete()

    def on_btn_blockheight_clicked(self, widget, data=None):
        title = "Enter Block Height"
        callback = "on_blockheight_return"
        self.gscreen.launch_numerical_input(callback, 0, 0, title)

    def on_btn_touchoff_clicked(self, widget, data=None):
        if self.stat.tool_in_spindle == 0:
            self.gscreen.notify(_("INFO"), _("Cannot probe with no tool loaded"), INFO_ICON)
            return
        self.widgets.gremlin.clear_live_plotter()
        if self.use_auto_zref is True:
            command = "o< tool_sensor > call"
        else:
            command = "o< touch_plate > call"
        self.command.mdi(command)

    def on_btn_rapid_100_clicked(self, widget, data=None):
        self.widgets.spc_rapid.set_value(100)

    def on_btn_feed_100_clicked(self, widget, data=None):
        self.widgets.spc_feed.set_value(100)

    def on_btn_spindle_100_clicked(self, widget, data=None):
        self.widgets.spc_spindle.set_value(100)

    def jog_x(self,widget,direction,state):
        self.gscreen.do_key_jog(0,direction,state)

    def jog_y(self,widget,direction,state):
        self.gscreen.do_key_jog(1,direction,state)

    def jog_z(self,widget,direction,state):
        self.gscreen.do_key_jog(2,direction,state)

    def jog_speed_changed(self,widget):
        value = widget.get_value()
        self.gscreen.set_jog_rate(absolute = value)

    def on_cmb_increments_changed(self, widget, data=None):
        index = widget.get_active()
        if index == 0:
            self.data.current_jogincr_index = len(self.data.jog_increments) - 1
        else:
            self.data.current_jogincr_index = index - 1

    def on_rbt_view_p_toggled(self, widget, data=None):
        if self.widgets.rbt_view_p.get_active():
            self.widgets.gremlin.set_property("view", "p")

    def on_rbt_view_x_toggled(self, widget, data=None):
        if self.widgets.rbt_view_x.get_active():
            self.widgets.gremlin.set_property("view", "x")

    def on_rbt_view_y_toggled(self, widget, data=None):
        if self.widgets.rbt_view_y.get_active():
            self.widgets.gremlin.set_property("view", "y")

    def on_rbt_view_z_toggled(self, widget, data=None):
        if self.widgets.rbt_view_z.get_active():
            self.widgets.gremlin.set_property("view", "z")

    def on_btn_zoom_in_clicked(self, widget, data=None):
        self.widgets.gremlin.zoom_in()

    def on_btn_zoom_out_clicked(self, widget, data=None):
        self.widgets.gremlin.zoom_out()

    def on_btn_delete_view_clicked(self, widget, data=None):
        self.widgets.gremlin.clear_live_plotter()

    def on_tbtn_view_dimension_toggled(self, widget, data=None):
        self.widgets.gremlin.set_property("show_extents_option", widget.get_active())
        self.gscreen.prefs.putpref("view_dimension", self.widgets.tbtn_view_dimension.get_active())

    def on_tbtn_view_tool_path_toggled(self, widget, data=None):
        self.widgets.gremlin.set_property("show_live_plot", widget.get_active())
        self.gscreen.prefs.putpref("view_tool_path", self.widgets.tbtn_view_tool_path.get_active())

    def on_tbtn_jog_speed_toggled(self, widget, data=None):
        # due to imperial and metric options we have to get first the values of the widget
        max = self.widgets.adj_scale_jog_vel.get_upper()
        min = self.widgets.adj_scale_jog_vel.get_lower()
        value = self.widgets.jog_speed.get_value()
        if widget.get_active():
            self.fast_jog = value
            widget.set_label("SLOW")
            self.widgets.jog_speed.set_range(min / self.slow_jog_factor, max / self.slow_jog_factor)
            self.widgets.jog_speed.set_value(self.fast_jog / self.slow_jog_factor)
        else:
            self.slow_jog = value
            widget.set_label("FAST")
            self.widgets.jog_speed.set_range(min * self.slow_jog_factor, max * self.slow_jog_factor)
            self.widgets.jog_speed.set_value(self.fast_jog)

    def on_rbtn_show_preview_toggled(self, widget, data=None):
        self.gscreen.prefs.putpref("show_preview_on_offset", widget.get_active())

    def on_fullscreen_pressed(self, widget):
        self.gscreen.prefs.putpref("window_geometry", "fullscreen", str)
        self.widgets.window1.fullscreen()

    def on_max_window_pressed(self, widget):
        self.gscreen.prefs.putpref("window_geometry", "max", str)
        self.widgets.window1.unfullscreen()
        self.widgets.window1.maximize()

    def on_default_window_pressed(self, widget):
        self.gscreen.prefs.putpref("window_geometry", "default", str)
        self.widgets.window1.unfullscreen()
        self.widgets.window1.unmaximize()
        self.widgets.spbtn_x_pos.set_sensitive(True)
        self.widgets.spbtn_y_pos.set_sensitive(True)
        self.widgets.spbtn_width.set_sensitive(True)
        self.widgets.spbtn_height.set_sensitive(True)
        self.widgets.window1.move(self.xpos, self.ypos)
        self.widgets.window1.resize(self.width, self.height)

    def on_btn_zero_g92_clicked(self, widget, data=None):
        self.command.mode(linuxcnc.MODE_MDI)
        self.command.wait_complete()
        self.command.mdi("G92.1")
        self.command.mode(linuxcnc.MODE_MANUAL)
        self.command.wait_complete()
        self.widgets.rbt_offset.emit("toggled")

    def on_tbtn_edit_offsets_toggled(self, widget, data=None):
        state = widget.get_active()
        self.widgets.offsetpage.edit_button.set_active(state)
        widgetlist = ["btn_set_selected", "btn_zero_g92"]
        self.gscreen.sensitize_widgets(widgetlist, not state)
        if not state:  # must switch back to manual mode, otherwise jogging is not possible
            self.command.mode(linuxcnc.MODE_MANUAL)
            self.command.wait_complete()

    def on_chk_ignore_limits_toggled(self, widget, data=None):
        if self.widgets.chk_ignore_limits.get_active():
            if not self._check_limits():
                self.gscreen.notify(_("ERROR"), _("No limit switch is active, ignore limits will not be set."), ALERT_ICON)
                return
            self.command.override_limits()

    def on_chk_use_auto_zref_toggled(self, widget, data=None):
        state = widget.get_active()
        self.gscreen.prefs.putpref("use_autozref", state, bool)
        self.widgets.btn_blockheight.set_sensitive(state)
        self.widgets.frm_probe_pos.set_sensitive(state)
        self.use_auto_zref = state
        if state is True:
            self.widgets.lbl_blockheight.show()
        else:
            self.widgets.lbl_blockheight.hide()

    def on_chk_reload_tool_toggled(self, widget, data=None):
        state = widget.get_active()
        self.reload_tool_enabled = state
        self.gscreen.prefs.putpref("reload_tool", state)

    def on_chk_run_from_line_toggled(self, widget, data=None):
        if widget.get_active():
            self.gscreen.prefs.putpref("run_from_line", "run")
            self.widgets.btn_from_line.set_sensitive(True)
        else:
            self.gscreen.prefs.putpref("run_from_line", "no_run")
            self.widgets.btn_from_line.set_sensitive(False)

    def on_chk_use_kb_shortcuts_toggled(self, widget, data=None):
        self.gscreen.prefs.putpref("use_keyboard_shortcuts", widget.get_active())

    def on_chk_show_dro_toggled(self, widget, data=None):
        self.widgets.gremlin.set_property("enable_dro", widget.get_active())
        self.gscreen.prefs.putpref("enable_dro", widget.get_active())
        self.widgets.show_offsets.set_sensitive(widget.get_active())
        self.widgets.chk_show_dtg.set_sensitive(widget.get_active())

    def on_chk_show_dtg_toggled(self, widget, data=None):
        self.widgets.gremlin.set_property("show_dtg", widget.get_active())
        self.gscreen.prefs.putpref("show_dtg", widget.get_active())

    def on_show_offsets_toggled(self, widget, data=None):
        self.widgets.gremlin.show_offsets = widget.get_active()
        self.gscreen.prefs.putpref("show_offsets", widget.get_active())

    def on_btn_delete_clicked(self, widget, data=None):
        message = _("Do you really want to delete the MDI history?\n")
        message += _("This will not delete the MDI History file, but will\n")
        message += _("delete the listbox entries for this session")
        self.halcomp["clear_mdi-waiting"] = True
        self.gscreen.warning_dialog(message, False, None, "clear_mdi")

    def on_change_sound(self, widget, sound=None):
        file = widget.get_filename()
        if file:
            if widget == self.widgets.audio_error_chooser:
                self.error_sound = file
                self.gscreen.prefs.putpref("audio_error", file)
            else:
                self.alert_sound = file
                self.gscreen.prefs.putpref("audio_alert", file)

    def on_homed_colorbtn_color_set(self, widget):
        self.gscreen.prefs.putpref('homed_textcolor', gdk.RGBA.to_string(widget.get_rgba()), str)

    def on_unhomed_colorbtn_color_set(self, widget):
        self.gscreen.prefs.putpref('unhomed_textcolor', gdk.RGBA.to_string(widget.get_rgba()), str)

    def _from_internal_linear_unit(self, v, unit=None):
        if unit is None:
            unit = self.stat.linear_units
        lu = (unit or 1) * 25.4
        return v * lu

    def on_adj_x_pos_value_changed(self, widget, data=None):
        if not self.initialized:
            return
        value = int(widget.get_value())
        self.gscreen.prefs.putpref("x_pos", value, float)
        self.xpos = value
        self.widgets.window1.move(value, self.ypos)

    def on_adj_y_pos_value_changed(self, widget, data=None):
        if not self.initialized:
            return
        value = int(widget.get_value())
        self.gscreen.prefs.putpref("y_pos", value, float)
        self.ypos = value
        self.widgets.window1.move(self.xpos, value)

    def on_adj_width_value_changed(self, widget, data=None):
        if not self.initialized:
            return
        value = int(widget.get_value())
        self.gscreen.prefs.putpref("width", value, float)
        self.width = value
        self.widgets.window1.resize(value, self.height)

    def on_adj_start_spindle_RPM_value_changed(self, widget, data=None):
        self.spindle_start_rpm = widget.get_value()
        self.gscreen.prefs.putpref("spindle_start_rpm", self.spindle_start_rpm, float)

    def on_adj_scale_jog_vel_value_changed(self, widget, data=None):
        self.gscreen.prefs.putpref("scale_jog_vel", widget.get_value(), float)
        self.scale_jog_vel = widget.get_value()

    def on_adj_scale_feed_override_value_changed(self, widget, data=None):
        self.gscreen.prefs.putpref("scale_feed_override", widget.get_value(), float)
        self.scale_feed_override = widget.get_value()

    def on_adj_scale_rapid_override_value_changed(self, widget, data=None):
        self.gscreen.prefs.putpref("scale_rapid_override", widget.get_value(), float)
        self.scale_rapid_override = widget.get_value()

    def on_adj_scale_spindle_override_value_changed(self, widget, data=None):
        self.gscreen.prefs.putpref("scale_spindle_override", widget.get_value(), float)
        self.scale_spindle_override = widget.get_value()

    def on_adj_height_value_changed(self, widget, data=None):
        if not self.initialized:
            return
        value = int(widget.get_value())
        self.gscreen.prefs.putpref("height", value, float)
        self.height = value
        self.widgets.window1.resize(self.width, value)

# ==========
# HAL status
# ==========
    def on_hal_status_all_homed(self, widget):
        self.data.all_homed = True
        self.widgets.ntb_main.set_current_page(_NB_PREVIEW)
        self.command.mode(linuxcnc.MODE_MANUAL)
        self.command.wait_complete()
        self.gscreen.sensitize_widgets(self.data.sensitive_all_homed, True)
        self.set_motion_mode(1)
        self.widgets.statusbar1.remove(self.gscreen.statusbar_id, self.gscreen.homed_status_message)
        self.gscreen.notify(_("INFO"), _("All axes have been homed"), INFO_ICON)
        if self.widgets.chk_reload_tool.get_active():
            if self.stat.tool_in_spindle == 0:
                self.reload_tool()
            self.command.mode(linuxcnc.MODE_MANUAL)

    def on_hal_status_not_all_homed(self, widget, joints):
        self.data.all_homed = False
        if self.no_force_homing:
            return
        self.gscreen.sensitize_widgets(self.data.sensitive_all_homed, False)
        self.set_motion_mode(0)

    def on_hal_status_file_loaded(self, widget, filename):
        if filename:
            fileobject = file(filename, 'r')
            lines = fileobject.readlines()
            fileobject.close()
            self.program_length = len(lines)
            if len(filename) > 70:
                filename = filename[0:10] + "..." + filename[len(filename) - 50:len(filename)]
            self.widgets.gcode_tab.set_text(filename)
            self.gscreen.notify(_("INFO"), _("Program loaded: %s"%filename), INFO_ICON)
        else:
            self.program_length = 0
            self.widgets.gcode_tab.set_text("No program loaded")

    def on_hal_status_line_changed(self, widget, line):
        self.current_line = line

    def on_hal_status_interp_idle(self, widget):
        print("IDLE")
        if self.load_tool:
            return
        if self.stat.task_state == linuxcnc.STATE_ESTOP or self.stat.task_state == linuxcnc.STATE_OFF:
            self.gscreen.sensitize_widgets(self.data.sensitive_run_idle, False)
        else:
            self.gscreen.sensitize_widgets(self.data.sensitive_run_idle, True)
        if self.tool_change:
            self.command.mode(linuxcnc.MODE_MANUAL)
            self.command.wait_complete()
            self.tool_change = False
        self.current_line = 0

    def on_hal_status_interp_run(self, widget):
        print("RUN")
        self.gscreen.sensitize_widgets(self.data.sensitive_run_idle, False)

    def on_hal_status_tool_in_spindle_changed(self, object, new_tool_no):
        self.gscreen.prefs.putpref("tool_in_spindle", new_tool_no, int)
        self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle)
        self.update_toolinfo(new_tool_no)

    def on_hal_status_state_estop(self, widget=None):
        self.widgets.tbtn_estop.set_active(True)
        self.widgets.tbtn_on.set_sensitive(False)
        self.widgets.tbtn_on.set_active(False)
        self.widgets.tbtn_estop.set_label("ESTOP")
        self.widgets.chk_ignore_limits.set_sensitive(False)
        self.command.mode(linuxcnc.MODE_MANUAL)
        self.command.wait_complete()

    def on_hal_status_state_estop_reset(self, widget=None):
        self.widgets.tbtn_estop.set_active(False)
        self.widgets.tbtn_on.set_sensitive(True)
        self.widgets.tbtn_estop.set_label("RESET")
        self.widgets.chk_ignore_limits.set_sensitive(True)
        self._check_limits()

    def on_hal_status_state_off(self, widget):
        if self.widgets.tbtn_on.get_active():
            self.widgets.tbtn_on.set_active(False)
        self.widgets.chk_ignore_limits.set_sensitive(True)
        self.widgets.tbtn_on.set_label("OFF")
        self.gscreen.sensitize_widgets(self.data.sensitive_on_off, False)

    def on_hal_status_state_on(self, widget):
        if not self.widgets.tbtn_on.get_active():
            self.widgets.tbtn_on.set_active(True)
        self.widgets.chk_ignore_limits.set_sensitive(False)
        self.widgets.tbtn_on.set_label("ON")
        self.gscreen.sensitize_widgets(self.data.sensitive_on_off, True)
        self.widgets.rbt_manual.set_active(True)
        self.widgets.ntb_mode.set_current_page(_MODE_MANUAL)
        self.command.mode(linuxcnc.MODE_MANUAL)
        self.command.wait_complete()

    def on_hal_status_mode_manual(self, widget):
        print("MANUAL Mode")
        if self.widgets.ntb_main.get_current_page() == _NB_SETUP:
            return
        self.widgets.rbt_manual.set_active(True)
        self.widgets.ntb_main.set_current_page(_NB_PREVIEW)
        self.widgets.ntb_mode.set_current_page(_MODE_MANUAL)
        self._check_limits()
        self.last_key_event = None, 0

    def on_hal_status_mode_mdi(self, widget):
        if self.tool_change:
            self.widgets.ntb_mode.set_current_page(_MODE_MDI)
            return
        if not self.widgets.rbt_mdi.get_sensitive():
            self.command.abort()
            self.command.mode(linuxcnc.MODE_MANUAL)
            self.command.wait_complete()
            self.gscreen.notify(_("INFO"), _("It is not possible to change to MDI Mode at the moment"), INFO_ICON)
            return
        else:
            print("MDI Mode")
            self.widgets.hal_mdihistory.entry.grab_focus()
            self.widgets.ntb_mode.set_current_page(_MODE_MDI)
            self.widgets.rbt_mdi.set_active(True)
            self.last_key_event = None, 0

    def on_hal_status_mode_auto(self, widget):
        if not self.widgets.rbt_auto.get_sensitive():
            self.command.abort()
            self.command.mode(linuxcnc.MODE_MANUAL)
            self.command.wait_complete()
            self.gscreen.notify(_("INFO"), _("It is not possible to change to AUTO Mode at the moment"), INFO_ICON)
        else:
            print("AUTO Mode")
            self.widgets.ntb_mode.set_current_page(_MODE_AUTO)
            self.widgets.rbt_auto.set_active(True)
            self.last_key_event = None, 0

    def on_hal_dro_system_changed(self, widget, system):
        self.widgets.btn_rel.set_label(system)
        self.widgets.gremlin.set_property("metric_units", metric_units)
        widgetlist = ["jog_speed"]
        # self.stat.linear_units will return 1.0 for metric and 1/25,4 for imperial
        # display units not equal machine units
        if metric_units != int(self.stat.linear_units):
            if self.stat.linear_units == _MM:
                self.factor = (1.0 / 25.4)
            else:
                self.factor = 25.4
            self._update_slider(widgetlist)
        else:
            # display units equal machine units would be factor = 1,
            # but if factor not equal 1.0 than we have to reconvert from previous first
            if self.factor != 1.0:
                self.factor = 1 / self.factor
                self._update_slider(widgetlist)
                self.factor = 1.0
                self._update_slider(widgetlist)

    def on_hal_status_motion_mode_changed(self, widget, new_mode):
        if self.widgets.ntb_main.get_current_page() == _NB_SETUP:
            return
        if new_mode == 1 and self.stat.kinematics_type != linuxcnc.KINEMATICS_IDENTITY:
            self.widgets.gremlin.set_property("enable_dro", True)
            self.widgets.gremlin.use_joints_mode = True
        else:
            self.widgets.gremlin.use_joints_mode = False

# ================
# Helper functions
# ================
    def init_unit_labels(self):
        if self.stat.linear_units == 1:
            self.widgets.lbl_feed_units.set_text("MM/MIN")
            self.widgets.lbl_vc_units.set_text("M/MIN")
        else:
            self.widgets.lbl_feed_units.set_text("IN/MIN")
            self.widgets.lbl_vc_units.set_text("FT/MIN")

    def _check_limits(self):
        for axis in self.data.axis_list:
            axisnumber = "xyzabcuvw".index(axis)
            if self.stat.limit[axisnumber] != 0:
                return True
        if self.widgets.chk_ignore_limits.get_active():
            self.widgets.chk_ignore_limits.set_active(False)
        return False

    def on_blockheight_changed(self, pin):
        self.widgets.lbl_blockheight.set_text("{0:.3f}".format(pin.get()))

    def on_clear_mdi_changed(self, pin):
        if self.halcomp["clear_mdi-waiting"] is False:
            if self.halcomp["clear_mdi-response"] is True:
                self.widgets.hal_mdihistory.model.clear()

    def on_tool_change(self,widget):
        change = self.halcomp['change-tool']
        tool_no = self.halcomp['tool-number']
        if change:
            if tool_no == 0:
                message = _("Please remove the mounted tool")
                secondary = ""
            else:
                message = _("Please change to tool # %s"%tool_no)
                secondary = self.widgets.tooledit1.get_toolinfo(tool_no)[16]
            self.data.tool_message = self.gscreen.notify(_("INFO:"),message,None)
            self.gscreen.warning_dialog(message, True, secondary, pinname="TOOLCHANGE")
        else:
            self.halcomp['tool-changed'] = False

    def notify(self, alert, message):
        msg_text = alert + " " + message
        self.widgets.lbl_notify.set_text(msg_text)

    def check_mode(self):
        string=[]
        if self.stat.task_mode == linuxcnc.MODE_MANUAL and self.widgets.ntb_main.get_current_page() == _NB_PREVIEW:
            string.append( self.data._MAN)
            if self.stat.interp_state == linuxcnc.INTERP_IDLE:
                string.append(self.data._JOG)
        return string

# ==========================
# override gscreen key calls
# ==========================
    def on_keycall_ESTOP(self,state,SHIFT,CNTRL,ALT):
        if state:
            self.command.state(linuxcnc.STATE_ESTOP)
            return True

    def on_keycall_POWER(self,state,SHIFT,CNTRL,ALT):
        if state:
            if self.widgets.tbtn_estop.get_active():
                return True
            self.widgets.tbtn_on.emit("clicked")
            return True

    def on_keycall_ABORT(self,state,SHIFT,CNTRL,ALT):
        if state:
            self.command.abort()
            return True

    def on_keycall_XPOS(self,state,SHIFT,CNTRL,ALT):
        if not self.check_kb_shortcuts(): return
        self.gscreen.do_key_jog(0, 1, state)

    def on_keycall_XNEG(self,state,SHIFT,CNTRL,ALT):
        if not self.check_kb_shortcuts(): return
        self.gscreen.do_key_jog(0, 0, state)

    def on_keycall_YPOS(self,state,SHIFT,CNTRL,ALT):
        if not self.check_kb_shortcuts(): return
        self.gscreen.do_key_jog(1, 1, state)

    def on_keycall_YNEG(self,state,SHIFT,CNTRL,ALT):
        if not self.check_kb_shortcuts(): return
        self.gscreen.do_key_jog(1, 0, state)

    def on_keycall_ZPOS(self,state,SHIFT,CNTRL,ALT):
        if not self.check_kb_shortcuts(): return
        self.gscreen.do_key_jog(2, 1, state)

    def on_keycall_ZNEG(self,state,SHIFT,CNTRL,ALT):
        if not self.check_kb_shortcuts(): return
        self.gscreen.do_key_jog(2, 0, state)

    def on_keycall_INCREMENTS(self,state,SHIFT,CNTRL,ALT):
        if not self.check_kb_shortcuts(): return
        widget = self.widgets.cmb_increments
        if state:
            index = widget.get_active()
            length = len(self.data.jog_increments)
            if SHIFT: # increment index
                if index < length - 1:
                    index = index + 1
            else: # decrement index
                if index > 0:
                    index = index - 1
            widget.set_active(index)
            self.on_cmb_increments_changed(widget)
        return(True)

    def check_kb_shortcuts(self):
        use_shortcuts = self.widgets.chk_use_kb_shortcuts.get_active()
        if not use_shortcuts:
            self.gscreen.notify(_("INFO"), _("Keyboard shortcuts are disabled"), INFO_ICON)
        return (use_shortcuts)

    def _update_vel(self):
        real_feed = float(self.stat.settings[1] * self.stat.feedrate)
        if self.stat.linear_units == _MM: # metric
            self.widgets.lbl_velocity.set_text("{0:d}".format(int(self.stat.current_vel * 60.0 * self.factor)))
            if "G95" in self.data.active_gcodes:
                feed_str = "{0:d}".format(int(self.stat.settings[1]))
                real_feed_str = "{0:.2f}".format(real_feed)
            else:
                feed_str = "{0:d}".format(int(self.stat.settings[1]))
                real_feed_str = "{0:d}".format(int(real_feed))
        else: # imperial
            self.widgets.lbl_velocity.set_text("{0:.2f}".format(self.stat.current_vel * 60.0 * self.factor))
            if "G95" in self.data.active_gcodes:
                feed_str = "{0:.4f}".format(self.stat.settings[1])
                real_feed_str = "{0:.4f}".format(real_feed)
            else:
                feed_str = "{0:.3f}".format(self.stat.settings[1])
                real_feed_str = "{0:.3f}".format(real_feed)

    def set_motion_mode(self, state):
        # 1:teleop, 0: joint
        self.command.teleop_enable(state)
        self.command.wait_complete()

    def reload_tool(self):
        tool_to_load = self.gscreen.prefs.getpref("tool_in_spindle", 0, int)
        if tool_to_load == 0:
            return
        self.load_tool = True
        self.tool_change = True
        self.command.mode(linuxcnc.MODE_MDI)
        self.command.wait_complete()
        command = "M61 Q {0} G43".format(tool_to_load)
        self.command.mdi(command)
        self.command.wait_complete()

    def _update_coolant(self):
        if self.stat.flood != self.widgets.tbtn_flood.get_active():
            self.widgets.tbtn_flood.set_active(self.stat.flood)
        if self.stat.mist != self.widgets.tbtn_mist.get_active():
            self.widgets.tbtn_mist.set_active(self.stat.mist)

    def _update_spindle(self):
        if self.stat.spindle[0]['direction'] > 0:
            if not self.widgets.rbt_forward.get_active():
                self.widgets.rbt_forward.set_active(True)
        elif self.stat.spindle[0]['direction'] < 0:
            if not self.widgets.rbt_reverse.get_active():
                self.widgets.rbt_reverse.set_active(True)
        elif not self.widgets.rbt_stop.get_active():
            self.widgets.rbt_stop.set_active(True)
        if not abs(self.stat.spindle[0]['speed']):
            self.widgets.rbt_stop.set_active(True)
        if self.stat.spindle[0]['speed'] == 0:
            speed = 0
        else:
            speed = self.stat.spindle[0]['speed']
        self.widgets.lbl_spindle_rpm.set_text("{0}".format(int(speed * self.data.spindle_override)))

    def _update_vc(self):
        if self.stat.spindle[0]['direction'] != 0:
            if self.stat.spindle[0]['speed'] == 0:
                speed = 0
            else:
                speed = self.stat.spindle[0]['speed']
            vc = abs(int(speed * self.data.spindle_override) * self.tool_diameter * 3.1416 / 1000)
        else:
            vc = 0
        if vc >= 100:
            text = "{0:d}".format(int(vc))
        elif vc >= 10:
            text = "{0:2.1f}".format(vc)
        else:
            text = "{0:.2f}".format(vc)
        self.widgets.lbl_vc.set_text(text)

    def update_dro(self):
        j = 0
        for i in self.data.axis_list:
            if self.stat.joint[j]['homed']:
                self["label_home_%s"%i].modify_fg(gtk.StateFlags.NORMAL, gdk.color_parse("#0000FF"))
                color = self.homed_color
            else:
                self["label_home_%s"%i].modify_fg(gtk.StateFlags.NORMAL, gdk.color_parse("#FF0000"))
                color = self.unhomed_color
            j += 1
            self.widgets["hal_dro_%s"%i].override_color(gtk.StateFlags.NORMAL, color)

    def _set_spindle(self, command):
        if self.stat.task_state == linuxcnc.STATE_ESTOP:
            return
        if self.stat.task_mode != linuxcnc.MODE_MANUAL:
            if self.stat.interp_state == linuxcnc.INTERP_READING or self.stat.interp_state == linuxcnc.INTERP_WAITING:
                if self.stat.spindle[0]['direction'] > 0:
                    self.widgets.rbt_forward.set_sensitive(True)
                    self.widgets.rbt_reverse.set_sensitive(False)
                    self.widgets.rbt_stop.set_sensitive(False)
                elif self.stat.spindle[0]['direction'] < 0:
                    self.widgets.rbt_forward.set_sensitive(False)
                    self.widgets.rbt_reverse.set_sensitive(True)
                    self.widgets.rbt_stop.set_sensitive(False)
                else:
                    self.widgets.rbt_forward.set_sensitive(False)
                    self.widgets.rbt_reverse.set_sensitive(False)
                    self.widgets.rbt_stop.set_sensitive(True)
                return
        rpm = self.check_spindle_range()
        try:
            rpm_out = rpm / self.stat.spindle[0]['override']
        except:
            rpm_out = 0
        if command == "stop":
            self.command.spindle(0)
        elif command == "forward":
            self.command.spindle(1, rpm_out)
        elif command == "reverse":
            self.command.spindle(-1, rpm_out)
        else:
            print(_("Something went wrong, we have an unknown spindle widget {0}").format(command))

    def update_progress(self):
        if self.stat.task_mode != linuxcnc.MODE_AUTO:
            return
        if self.program_length > 0:
            progress = float(self.current_line) / float(self.program_length)
        else:
            progress = 0.0
        self.widgets.pgm_progress.set_fraction(progress)
        self.widgets.pgm_progress.set_text("{0:2.1f} % Complete".format(progress * 100))

    def init_jog_increments(self):
        list = self.data.jog_increments[:]
        del list[-1]
        # populate the increments combobox with rearranged increment values
        model = self.widgets.cmb_increments.get_model()
        model.clear()
        model.append(["continuous"])
        for index, inc in enumerate(list):
            model.append((inc,))
        self.widgets.cmb_increments.set_active(0)
        self.data.current_jogincr_index = index + 1

    def check_spindle_range(self):
        rpm = (self.stat.settings[2])
        if rpm == 0:
            rpm = abs(self.spindle_start_rpm)
        spindle_override = self.widgets.spc_spindle.get_value() / 100
        real_spindle_speed = rpm * spindle_override
        return real_spindle_speed

    def init_file_to_load(self):
        default_path = self.gscreen.inifile.find("DISPLAY", "PROGRAM_PREFIX")
        if not default_path:
            print("Path %s from DISPLAY , PROGRAM_PREFIX does not exist" % default_path)
            print("Trying default path...")
            default_path = "~/linuxcnc/nc_files/"
        open_file = self.gscreen.inifile.find("DISPLAY", "OPEN_FILE")
        if not open_file:
            print("No file to open")
        else:
            self.file_to_load = default_path + open_file

    def update_toolinfo(self, tool):
        toolinfo = self.widgets.tooledit1.get_toolinfo(tool)
        if toolinfo:
            # toolinfo[0] = cell toggle
            # toolinfo[1] = tool number
            # toolinfo[2] = pocket number
            # toolinfo[3] = X offset
            # toolinfo[4] = Y offset
            # toolinfo[5] = Z offset
            # toolinfo[6] = A offset
            # toolinfo[7] = B offset
            # toolinfo[8] = C offset
            # toolinfo[9] = U offset
            # toolinfo[10] = V offset
            # toolinfo[11] = W offset
            # toolinfo[12] = tool diameter
            # toolinfo[13] = frontangle
            # toolinfo[14] = backangle
            # toolinfo[15] = tool orientation
            # toolinfo[16] = tool info
            self.widgets.enter_tool.set_text(str(toolinfo[1]))
            # this assumes that all values in the tool table are metric
            self.widgets.lbl_tool_diameter.set_text(toolinfo[12])
            self.tool_diameter = float(toolinfo[12])
            self.widgets.lbl_tool_comment.set_text(toolinfo[16])
        if tool <= 0:
            self.widgets.enter_tool.set_text("0")
            self.widgets.lbl_tool_diameter.set_text("----")
            self.widgets.lbl_tool_comment.set_text("NO TOOL LOADED")
        if self.load_tool:
            self.load_tool = False
            self.on_hal_status_interp_idle(None)
            return
        if "G43" in self.data.active_gcodes and self.stat.task_mode != linuxcnc.MODE_AUTO:
            self.command.mode(linuxcnc.MODE_MDI)
            self.command.wait_complete()
            self.command.mdi("G43")
            self.command.wait_complete()

    def _update_slider(self, widgetlist):
        for widget in widgetlist:
            value = self.widgets[widget].get_value()
            min = self.widgets[widget].get_property("min")
            max = self.widgets[widget].get_property("max")
            self.widgets[widget].set_range(min * self.factor, max * self.factor)
            self.widgets[widget].set_value(value * self.factor)

        self.scale_jog_vel = self.scale_jog_vel * self.factor
        self.fast_jog = self.fast_jog * self.factor
        self.slow_jog = self.slow_jog * self.factor

    def init_offsetpage(self):
        self.gscreen.init_offsetpage()
        self.widgets.offsetpage1.set_display_follows_program_units()
        if self.stat.program_units != 1:
            self.widgets.offsetpage1.set_to_mm()
            self.widgets.offsetpage1.machine_units_mm = _MM
        else:
            self.widgets.offsetpage1.set_to_inch()
            self.widgets.offsetpage1.machine_units_mm = _INCH
        self.widgets.offsetpage1.set_row_visible("1", False)
        self.widgets.offsetpage1.set_font("sans 12")
        self.widgets.offsetpage1.set_foreground_color(gdk.RGBA(0.151, 0.813, 0.845, 1.0))
        self.widgets.offsetpage1.selection_mask = ("Tool", "G5x", "Rot")
        systemlist = ["Tool", "G5x", "Rot", "G92", "G54", "G55", "G56", "G57", "G58", "G59", "G59.1",
                      "G59.2", "G59.3"]
        names = []
        for system in systemlist:
            system_name = "system_name_{0}".format(system)
            name = self.gscreen.prefs.getpref(system_name, system, str)
            names.append([system, name])
        self.widgets.offsetpage1.set_names(names)

    def on_offset_axis_return(self, widget, result, calc, userdata, userdata2):
        value = calc.get_value()
        if result == gtk.ResponseType.ACCEPT:
            if value is not None:
                r = self.axis_to_ref
                self.gscreen.prefs.putpref("offset_axis_{}".format(r), value, str)
                self.command.mode(linuxcnc.MODE_MDI)
                self.command.wait_complete()
                command = "G10 L20 P0 {}{}".format(r, value)
                self.command.mdi(command)
                self.gscreen.reload_plot()
                self.command.mode(linuxcnc.MODE_MDI)
                self.command.wait_complete()
        widget.destroy()
        self.data.entry_dialog = None

    def on_blockheight_return(self, widget, result, calc, userdata, userdata2):
        blockheight = calc.get_value()
        if result == gtk.ResponseType.ACCEPT:
            if blockheight == "CANCEL" or blockheight == "ERROR":
                return
            if blockheight is not None or blockheight is not False or blockheight == 0:
                self.halcomp["block_height"] = blockheight
                self.gscreen.prefs.putpref("blockheight", blockheight, float)
            else:
                self.halcomp["block_height"] = 0.0
                self.gscreen.prefs.putpref("blockheight", 0.0, float)
        widget.destroy()
        self.data.entry_dialog = None

