"""
Copyright 2007-2011 Free Software Foundation, Inc.
This file is part of GNU Radio

GNU Radio Companion 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.

GNU Radio Companion 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
"""

from __future__ import absolute_import, print_function

import logging
import os
import time
import subprocess

from gi.repository import Gtk, Gio, GLib, GObject
from getpass import getuser

from gi.repository import Pango

from . import Constants, Dialogs, Actions, FileDialogs, Utils, Bars

from .MainWindow import MainWindow
from .PropsDialog import PropsDialog

from ..core import Messages

import psutil
import sys
import shutil
from . import gparms as gp
from . import outvars as ov
from . import showparm as sparm
from . import solveblocks as slvblk
from . import outputblocks as outblk
from . import displayname as disp
from . import runprocess as runp
from . import showlist as slist
from .canvas.connection import Connection
from ..SolveBlock import SolveBlock
from ..OutputBlock import OutputBlock
from ..core.utils import gutils as gu
from .BlockTreeWindow import show_doc_1

log = logging.getLogger(__name__)

class Application(Gtk.Application):
    """
    The action handler will setup all the major window components,
    and handle button presses and flow graph operations from the GUI.
    """

    def __init__(self, file_paths, platform):
        Gtk.Application.__init__(self)
        """
        Application constructor.
        Create the main window, setup the message handler, import the preferences,
        and connect all of the action handlers. Finally, enter the gtk main loop and block.

        Args:
            file_paths: a list of flow graph file passed from command line
            platform: platform module
        """
        self.dict_outvars = platform.dict_outvars.copy()
        self.d_slvparms = platform.d_slvparms.copy()
        self.d_slv_categories = platform.d_slv_categories.copy()
        self.d_outparms = platform.d_outparms.copy()

        self.clipboard = None
        self.dialog = None

        # Setup the main window
        self.platform = platform
        self.config = platform.config

        log.debug('Application()')
        # Connect all actions to _handle_action
        for x in Actions.get_actions():
            Actions.connect(x, handler=self._handle_action)
            Actions.actions[x].enable()
            if x.startswith('app.'):
                self.add_action(Actions.actions[x])
            # Setup the shortcut keys
            # These are the globally defined shortcuts
            keypress = Actions.actions[x].keypresses
            if keypress:
                self.set_accels_for_action(x, keypress)

        # Initialize
        self.init_file_paths = [os.path.abspath(file_path) for file_path in file_paths]
        self.init = False
        for file_path in file_paths:
            print('Application.py: file_path =', file_path)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        log.debug('Application.do_startup()')

        # Setup the menu
        log.debug('Creating menu')
        '''
        self.menu = Bars.Menu()
        self.set_menu()
        if self.prefers_app_menu():
            self.set_app_menu(self.menu)
        else:
            self.set_menubar(self.menu)
        '''

    def do_activate(self):
        Gtk.Application.do_activate(self)
        log.debug('Application.do_activate()')

        self.main_window = MainWindow(self, self.platform)
        self.main_window.connect('delete-event', self._quit)
        self.get_focus_flag = self.main_window.get_focus_flag

        #setup the messages
        Messages.register_messenger(self.main_window.add_console_line)
        Messages.send_init(self.platform)

        log.debug('Calling Actions.APPLICATION_INITIALIZE')
        Actions.APPLICATION_INITIALIZE()

    def _quit(self, window, event):
        """
        Handle the delete event from the main window.
        Generated by pressing X to close, alt+f4, or right click+close.
        This method in turns calls the state handler to quit.

        Returns:
            true
        """
        Actions.APPLICATION_QUIT()
        return True

    def display_message(self, message):
        dialog_msg = Gtk.MessageDialog(
            transient_for=self.main_window,
            flags=0,
            message_type=Gtk.MessageType.ERROR,
            buttons=Gtk.ButtonsType.OK,
            text=message,
        )
        response = dialog_msg.run()
        if response == Gtk.ResponseType.OK:
            dialog_msg.destroy()

    def show_doc_2(self, pdf_file):
        cmd_exists = lambda x: shutil.which(x) is not None
        l_pdf_readers = ['okular', 'evince', 'xpdf', 'acroread']
        for x in l_pdf_readers:
            if cmd_exists(x):
                cmd = x + ' ' + pdf_file
                os.system(cmd)
                break

    def run_process(self, cmd, s_time):
        process1 = runp.RunProcess(self.main_window, cmd, s_time)

        flag_done = False

        while not flag_done:
            response = process1.run()
            if response == Gtk.ResponseType.OK:
                if psutil.pid_exists(process1.sub_proc.pid):
                    self.display_message('process still running')
                else:

                    if process1.flag_killed:
                        self.display_message('process was stopped by user')
                    elif process1.flag_normal:
                        self.display_message('process successful')
                    elif process1.flag_error:
                        self.display_message('Check for errors in the display window')
                    elif process1.flag_timelimit:
                        self.display_message('time exceeded, process halted')
                    process1.destroy()
                    flag_done = True

            if response == Gtk.ResponseType.CANCEL:

                if psutil.pid_exists(process1.sub_proc.pid):
                    while process1.sub_proc.poll() is None:
                        os.kill(process1.sub_proc.pid, 9)
                    process1.flag_killed = True
                    s_message = "terminated process"
                    self.display_message(s_message)
                else:
                    s_message = "process already completed\nno need to stop"
                    self.display_message(s_message)

    def gparm_add(self, widget, parm_dict):
        dialog = gp.AddGparm(self.main_window)
        flag_done = False

        while not flag_done:
            response = dialog.run()
            if response == Gtk.ResponseType.OK:

                s_name = dialog.entry_name.get_text()
                s_value = dialog.entry_value.get_text()

                if s_name in parm_dict.keys():
                    self.display_message(s_name + ' already exists!')
                elif len(s_name.split()) != 1:
                    self.display_message('gparms name should be a single word.')
                else:
                    parm_dict[s_name] = s_value
                    flag_done = True
                    self.main_window.current_page.saved = False
            elif response == Gtk.ResponseType.CANCEL:
                flag_done = True

        dialog.destroy()

    def gparm_delete(self, widget, parm_dict):
        if len(parm_dict) == 0:
            self.display_message('The gparms list\n is empty!')
            return

        dialog = gp.DelGparm(self.main_window, parm_dict)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:

            l_names = list(parm_dict.keys())
            l_values = list(parm_dict.values())
            parm_dict.clear()

            for i in range(len(l_names)):
                if not dialog.tick[i].get_active():
                    parm_dict[l_names[i]] = l_values[i]

            self.main_window.current_page.saved = False

        dialog.destroy()

    def gparm_edit(self, widget, parm_dict):
        if len(parm_dict) == 0:
            self.display_message('The gparms list\n is empty!')
            return

        dialog = gp.EditGparm(self.main_window, parm_dict)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:

            l_names = list(parm_dict.keys())
            l_values = list(parm_dict.values())

            for i in range(len(l_names)):
                l_values[i] = dialog.value[i].get_text()

            parm_dict.clear()
            for i in range(len(l_names)):
                parm_dict[l_names[i]] = l_values[i]

            self.main_window.current_page.saved = False

        dialog.destroy()

    def get_next_outvar_name(self, ov_dict):
        if len(ov_dict) == 0:
            n_max = -1
        else:
            n_max = max(map(
              lambda x : int(x.split('outvar')[1]) if x.startswith('outvar') else -1 ,
              ov_dict.keys()))
        ovname_next = 'outvar' + str(n_max + 1)
        return ovname_next

    def outvar_add_connection(self, widget, ov_dict, connection):

        ovname_next = self.get_next_outvar_name(ov_dict)

        l1 = list(connection.export_data())

        if l1[0].startswith('ground') or l1[2].startswith('ground'):
            print('outvar_add_connection: ground connection not allowed as outvar.')
            self.display_message('ground connection not allowed as outvar.')
            return
        if l1[1].startswith('b') or l1[3].startswith('b'):
            print('outvar_add_connection: bus connection not allowed as outvar.')
            self.display_message('bus connection not allowed as outvar.')
            return

        for v in ov_dict.values():
            if l1 == v[1]:
                print('outvar_add_connection: wire already exists in outvars.')
                self.display_message('wire already exists in outvars!')
                return

        ov_value = l1

        dialog = ov.AddOutvarConnection(self.main_window, ovname_next, ov_value)
        flag_done = False

        while not flag_done:
            response = dialog.run()
            if response == Gtk.ResponseType.OK:

                s_name = dialog.entry_name.get_text()
                s_value = dialog.entry_value.get_text()

                if s_name in ov_dict.keys():
                    self.display_message(s_name + ' already exists in outvars!')
                elif len(s_name.split()) != 1:
                    self.display_message('outvar name should be a single word.')
                else:
                    ov_dict[s_name] = ['connection', l1]
                    flag_done = True
                    self.main_window.current_page.saved = False
            elif response == Gtk.ResponseType.CANCEL:
                flag_done = True

        dialog.destroy()

    def outvar_add_outvar(self, widget, ov_dict, l_ov_values, block_name):

        if len(l_ov_values) == 0:
            print('outvar_add_outvar: the selected block has no outvars.')
            self.display_message('the selected block has no outvars.')
            return

#       default name for the outvar:
        ovname_next = self.get_next_outvar_name(ov_dict)

        dialog = ov.AddOutvarOutvar(self.main_window, ovname_next, l_ov_values)
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            for i in range(len(l_ov_values)):
                if dialog.l_buttons[i].get_active():
                    i_active = i
                    break
            s_name = dialog.entry_name.get_text()
            s_value = l_ov_values[i_active]
            if s_name in ov_dict.keys():
                self.display_message(s_name + ' already exists in outvars!')
            elif len(s_name.split()) != 1:
                print(s_name, 'outvar name should be a single word.')
                self.display_message('outvar name should be a single word.')
            else:
                l = [block_name, s_value]
                if l in list(map(lambda x: x[1] , ov_dict.values())):
                    self.display_message('outvar already exists!')
                else:
                    ov_dict[s_name] = ['outvar',  [block_name, s_value]]
                    self.main_window.current_page.saved = False

        dialog.destroy()

    def outvar_delete(self, widget, ov_dict, l_output_blocks, l_solve_blocks):
        if len(ov_dict) == 0:
            self.display_message('The outvars list\n is empty!')
            return

        dialog = ov.DelOutvar(self.main_window, ov_dict)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:

            l_names = list(ov_dict.keys())
            l_values = list(ov_dict.values())
            ov_dict.clear()

            for i in range(len(l_names)):
                if not dialog.tick[i].get_active():
                    ov_dict[l_names[i]] = l_values[i]

            for i in range(len(l_names)):
                if dialog.tick[i].get_active():
                    ov_name = l_names[i]
                    for out in l_output_blocks:
                        if ov_name in out.l_outvars:
                            i_ov = out.l_outvars.index(ov_name)
                            del out.l_outvars[i_ov]

            self.main_window.current_page.saved = False

        dialog.destroy()

#       if any output blocks have become empty, remove those

        l_empty_output_blocks = []

        for out in l_output_blocks:
            if len(out.l_outvars) == 0:
                print('outvar_delete: output block <' + out.name + '> is empty')
                l_empty_output_blocks.append(out.name)

        for out_name in l_empty_output_blocks:
            l_out_names = list(map(lambda x: x.name, l_output_blocks))

            i_out = l_out_names.index(out_name)
            i_slv = int(l_output_blocks[i_out].index_slv)

            print('outvar_delete: out_name:', out_name, 'i_out:', i_out, 'i_slv:', i_slv)
            print('type(i_out):', type(i_out), 'type(i_slv):', type(i_slv))

            i_slv_out = l_solve_blocks[i_slv].l_out.index(out_name)
            del l_solve_blocks[i_slv].l_out[i_slv_out]
            del l_output_blocks[i_out]

        self.main_window.current_page.saved = False

    def outvar_edit(self, widget, ov_dict, l_output_blocks):
        if len(ov_dict) == 0:
            self.display_message('The outvars list\n is empty!')
            return

        dialog = ov.EditOutvar(self.main_window, ov_dict)
        flag_done = False

        while not flag_done:
            response = dialog.run()
            if response == Gtk.ResponseType.OK:

                l_names = list(ov_dict.values())
                l_values = list(ov_dict.keys())

                for i in range(len(l_names)):
                    l_values[i] = dialog.value[i].get_text()
                flag_accept = True
                if len(l_values) != len(set(l_values)):
                    self.display_message('Duplicate outvar name found.')
                    flag_accept = False

                if flag_accept:
#                   handle output blocks

                    l_1 = []
                    for out in l_output_blocks:
                        l_2 = []
                        for ov_name in out.l_outvars:
                            l_2.append(ov_dict[ov_name])
                        l_1.append(l_2)

#                   re-assign ov_dict values
                    ov_dict.clear()
                    for i in range(len(l_names)):
                        ov_dict[l_values[i]] = l_names[i]
                    flag_done = True

                    l_new_1 = []
                    for i_out, out in enumerate(l_output_blocks):
                        l_new_2 = []
                        for i_ov, ov_name in enumerate(out.l_outvars):
                            ov_value = l_1[i_out][i_ov]
                            for k, v in ov_dict.items():
                                if ov_value == v:
                                    l_new_2.append(k)
                                    break
                        l_new_1.append(l_new_2)

                    for i_out, out in enumerate(l_output_blocks):
                        for i_ov in range(len(out.l_outvars)):
                            out.l_outvars[i_ov] = l_new_1[i_out][i_ov]

                    self.main_window.current_page.saved = False
            elif response == Gtk.ResponseType.CANCEL:
                flag_done = True

        dialog.destroy()

    def solve_add(self, widget, l_solve_blocks, d_slvparms):
        dialog = slvblk.AddSolveBlock(self.main_window)

        flag_done = False

        while not flag_done:
            response = dialog.run()
            if response == Gtk.ResponseType.OK:

                s_name = dialog.entry_name.get_text()
                s_value = dialog.entry_value.get_text()

                l_slv_names = list(map(lambda x: x.name, l_solve_blocks))
                l_slv_indices = list(map(lambda x: x.index, l_solve_blocks))

                if s_name in l_slv_names:
                    self.display_message(s_name + ' already exists!')
                else:
                    if s_value in l_slv_indices:
                        message='index ' + s_value \
                           + ' is already assigned to another solve block.'
                        self.display_message(message)
                    elif len(s_name.split()) != 1:
                        self.display_message('solve block name should be a single word.')
                    else:
                        slv = SolveBlock(d_slvparms, s_name, s_value)
                        l_solve_blocks.append(slv)
                        l_1 = sorted(l_solve_blocks, key=lambda x: x.index)
                        l_solve_blocks.clear()
                        for slv in l_1:
                            l_solve_blocks.append(slv)
                        flag_done = True
                        self.main_window.current_page.saved = False
            elif response == Gtk.ResponseType.CANCEL:
                flag_done = True

        dialog.destroy()

    def solve_delete(self, widget, l_solve_blocks, l_output_blocks):
        if len(l_solve_blocks) == 0:
            self.display_message('The solve blocks list\n is empty!')
            return

        dialog = slvblk.DelSolveBlock(self.main_window, l_solve_blocks)
        response = dialog.run()

        if response == Gtk.ResponseType.OK:

            l_1 = [slv.name for i, slv in enumerate(l_solve_blocks)
                   if dialog.tick[i].get_active()]

            l_out_names = list(map(lambda x: x.name, l_output_blocks))

            for name1 in l_1:
                for i, slv in enumerate(l_solve_blocks):
                    if slv.name == name1:
                        for out_name in l_solve_blocks[i].l_out:
                            i_out = l_out_names.index(out_name)
                            del l_output_blocks[i_out]

                        del l_solve_blocks[i]
                        break
            self.main_window.current_page.saved = False

        dialog.destroy()

    def solve_pick(self, widget, l_solve_blocks, d_slv_categories):
        if len(l_solve_blocks) == 0:
            self.display_message('The solve blocks list\n is empty!')
            return
        elif len(l_solve_blocks) == 1:
#           some code repetition here for dialog1; clean up later:
            i_active = 0
            dialog1 = slvblk.EditSolveBlock(self.main_window,
                l_solve_blocks[i_active], d_slv_categories)
            response = dialog1.run()
            if response == Gtk.ResponseType.OK:
                gu.assign_parms_1(dialog1.d_widgets_1,
                   l_solve_blocks[i_active].d_parms)
                l_solve_blocks[i_active].index = \
                l_solve_blocks[i_active].d_parms['block_index']
                l_1 = sorted(l_solve_blocks, key=lambda x: x.index)
                l_solve_blocks.clear()
                for slv in l_1:
                    l_solve_blocks.append(slv)

                self.main_window.current_page.saved = False

            dialog1.destroy()
        else:
            dialog = slvblk.PickSolveBlock(self.main_window, l_solve_blocks)

            response = dialog.run()
            if response == Gtk.ResponseType.OK:
#               check which radio button is active and present
#               the corresponding solve block for editing

#               not pythonic: look at it later
                for i in range(len(dialog.l_buttons)):
                    if dialog.l_buttons[i].get_active():
                        i_active = i
                        break

                dialog1 = slvblk.EditSolveBlock(self.main_window,
                    l_solve_blocks[i_active], d_slv_categories)
                response = dialog1.run()
                if response == Gtk.ResponseType.OK:
                    gu.assign_parms_1(dialog1.d_widgets_1,
                       l_solve_blocks[i_active].d_parms)
                    l_solve_blocks[i_active].index = \
                    l_solve_blocks[i_active].d_parms['block_index']
                    l_1 = sorted(l_solve_blocks, key=lambda x: x.index)

                    l_solve_blocks.clear()

                    for slv in l_1:
                        l_solve_blocks.append(slv)

                    self.main_window.current_page.saved = False

                dialog1.destroy()

            dialog.destroy()

    def solve_pick_1(self, widget, l_solve_blocks, d_slv_categories):
        if len(l_solve_blocks) == 0:
            self.display_message('The solve blocks list\n is empty!')
            return
        else:
            if len(l_solve_blocks) == 1:
                i_active = 0
            else:
                dialog = slvblk.PickSolveBlock(self.main_window, l_solve_blocks)
                response = dialog.run()
                if response == Gtk.ResponseType.OK:
                    for i in range(len(dialog.l_buttons)):
                        if dialog.l_buttons[i].get_active():
                            i_active = i
                            break
                dialog.destroy()

            for k, v in self.d_slvparms.items():
                l_solve_blocks[i_active].d_parms[k] = v['default']

    def solve_pick_2(self, widget, l_solve_blocks):
        if len(l_solve_blocks) == 0:
            self.display_message('The solve blocks list\n is empty!')
            k = -1
            return k
        else:
            if len(l_solve_blocks) == 1:
                i_active = 0
            else:
                dialog = slvblk.PickSolveBlock(self.main_window, l_solve_blocks)
                response = dialog.run()
                if response == Gtk.ResponseType.OK:
                    for i in range(len(dialog.l_buttons)):
                        if dialog.l_buttons[i].get_active():
                            i_active = i
                            break
                dialog.destroy()
            return i_active

    def output_add(self, widget, d_outparms, l_output_blocks, l_solve_blocks):
        if len(l_solve_blocks) == 0:
            self.display_message('The solve blocks list\n is empty!')
            return

        dialog = outblk.AddOutputBlock(self.main_window, l_solve_blocks)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:

            s1 = dialog.combo_1.get_active_text()
            print('output_add: s1:', s1)
            slv_name = s1.split(' (')[0]

            l_slv_names = list(map(lambda x: x.name, l_solve_blocks))
            i_slv = l_slv_names.index(slv_name)
            print('output_add: i_slv:', i_slv)

            if len(l_output_blocks) == 0:
                n_max = -1
            else:
                n_max = max(map(lambda x: int(x.name.split('$')[-1]), l_output_blocks))

            out_name = 'out$' + str(n_max+1)
            out_block = OutputBlock(self.d_outparms, str(i_slv), out_name)
            l_output_blocks.append(out_block)
            l_solve_blocks[i_slv].l_out.append(out_name)

            self.main_window.current_page.saved = False

        dialog.destroy()

    def output_delete(self, widget, l_output_blocks, l_solve_blocks):
        if len(l_output_blocks) == 0:
            self.display_message('The output blocks list\n is empty!')
            return

        dialog = outblk.PickOutputBlock(self.main_window, l_output_blocks,
            l_solve_blocks)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            for i in range(len(dialog.l_buttons)):
                if dialog.l_buttons[i].get_active():
                    i_active = i
                    break

            l1 = dialog.l_names[i_active].replace(':', ' ').split()
            slv_name, out_name = l1[0], l1[1]

            l_slv_names = list(map(lambda x: x.name, l_solve_blocks))
            l_out_names = list(map(lambda x: x.name, l_output_blocks))

            i_slv = l_slv_names.index(slv_name)
            i_out = l_out_names.index(out_name)

            i_slv_out = l_solve_blocks[i_slv].l_out.index(out_name)
            del l_solve_blocks[i_slv].l_out[i_slv_out]
            del l_output_blocks[i_out]

            self.main_window.current_page.saved = False

        dialog.destroy()

    def output_pick(self, widget, l_output_blocks, l_solve_blocks,
        d_outparms, l_outvars):

        if len(l_output_blocks) == 0:
            self.display_message('The output blocks list\n is empty!')
            return
        elif len(l_output_blocks) == 1:
            i_active = 0
            dialog1 = outblk.EditOutputBlock(self.main_window,
               l_output_blocks[i_active], d_outparms, l_outvars, l_solve_blocks)
            response = dialog1.run()
            if response == Gtk.ResponseType.OK:
                gu.assign_parms_1(dialog1.d_widgets_1,
                   l_output_blocks[i_active].d_parms)

                l_output_blocks[i_active].l_outvars.clear()
                for i, button in enumerate(dialog1.l_ov_buttons):
                    if button.get_active():
                        l_output_blocks[i_active].l_outvars.append(l_outvars[i])

                self.main_window.current_page.saved = False

            dialog1.destroy()
        else:
            dialog = outblk.PickOutputBlock(self.main_window, l_output_blocks,
                l_solve_blocks)

            response = dialog.run()
            if response == Gtk.ResponseType.OK:

                for i in range(len(dialog.l_buttons)):
                    if dialog.l_buttons[i].get_active():
                        i_active = i
                        break
                print('output_pick: i_active:', i_active)

                out_name = dialog.l_names[i_active].split(' ')[-1]
                i_output_block = -1
                for i1, out in enumerate(l_output_blocks):
                    if out.name == out_name:
                        i_output_block = i1
                        break
                if i_output_block == -1:
                    print('output_pick: i_output_block = -1? Halting...')
                    exit(0)

                dialog1 = outblk.EditOutputBlock(self.main_window,
                   l_output_blocks[i_output_block], d_outparms, l_outvars, l_solve_blocks)
                response = dialog1.run()
                if response == Gtk.ResponseType.OK:
                    gu.assign_parms_1(dialog1.d_widgets_1,
                       l_output_blocks[i_active].d_parms)

                    l_output_blocks[i_active].l_outvars.clear()
                    for i, button in enumerate(dialog1.l_ov_buttons):
                        if button.get_active():
                            l_output_blocks[i_active].l_outvars.append(l_outvars[i])

                    self.main_window.current_page.saved = False

                dialog1.destroy()

            dialog.destroy()

    def element_display(self, widget, e1, ov_dict):

        l1 = []
        for k, v in ov_dict.items():
            l2 = []
            s1 = v[0]
            if isinstance(e1, Connection):
                if s1 == 'connection':
                    if v[1] == list(e1.export_data()):
                        l2.append(k)
                        l2.append(str(v[1]))
            else:
                if s1 == 'outvar':
                    if v[1][0] == e1.name:
                        l2.append(k)
                        l2.append(str(v[1]))

            if l2:
                l1.append(l2)

        if isinstance(e1, Connection):
            s_name = str(list(e1.export_data()))
        else:
            s_name = e1.name

        dialog = disp.DisplayName(self.main_window, s_name, l1)
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            dialog.destroy()

        dialog.destroy()

    def show_param_select(self, widget, selected_block):
        l_drop = [
          'port_sep_x',
          'port_sep_y',
          'port_block_x',
          'port_block_y',
          'port_offset_l',
          'port_offset_r',
          'port_offset_t',
          'port_offset_b',
          'rotate_strict',
          'drawing_scheme',
          'mirror',
          'comment',
        ]
        l_parm_names_1 = list(selected_block.params.keys())
        l_parm_names = [x for x in l_parm_names_1 if x not in l_drop]

        if len(l_parm_names) == 0:
            print('show_param_select: the selected block has no parameters.')
            self.display_message('the selected block has no parameters.')
            return

        dialog = sparm.AddShowParm(self.main_window, l_parm_names)
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            for i in range(len(l_parm_names)):
                if dialog.l_buttons[i].get_active():
                    i_active = i
                    break
            parm_name = l_parm_names[i_active]

            main = self.main_window
            page = main.current_page
            flow_graph = page.flow_graph if page else None

            x, y = selected_block.coordinate
            if selected_block.rotation in (0, 180):
                offset1 = selected_block.height
            else:
                offset1 = selected_block.width
            c_new = (x, y + offset1 + Constants.SHOW_PARAMETER_SEP)
            b_new_name = flow_graph.add_new_block('show_parameter', c_new)

            b_new = [x for x in flow_graph.blocks if x.name == b_new_name][0]

            b_new.params['element_name'].set_value(selected_block.name)
            b_new.params['parameter_name'].set_value(parm_name)

            flow_graph.update()

        dialog.destroy()

    def _handle_action(self, action, *args):

        log.debug('_handle_action({0}, {1})'.format(action, args))
        main = self.main_window
        page = main.current_page
        flow_graph = page.flow_graph if page else None

        def flow_graph_update(fg=flow_graph):
            main.vars.update_gui(fg.blocks)
            fg.update()

        # Initialize/Quit
        if action == Actions.APPLICATION_INITIALIZE:
            log.debug('APPLICATION_INITIALIZE')
            file_path_to_show = self.config.file_open()
            for file_path in (self.init_file_paths or self.config.get_open_files()):
                if os.path.exists(file_path):
                    main.new_page(file_path, show=file_path_to_show == file_path)
            if not main.current_page:
                main.new_page()  # ensure that at least a blank page exists

            main.btwin.search_entry.hide()

            for action in (
                Actions.ELEMENT_DELETE,
                Actions.BLOCK_PARAM_MODIFY,
                Actions.BLOCK_ROTATE_CCW,
                Actions.BLOCK_ROTATE_CW,
                Actions.BLOCK_VALIGN_TOP,
                Actions.BLOCK_VALIGN_MIDDLE,
                Actions.BLOCK_VALIGN_BOTTOM,
                Actions.BLOCK_HALIGN_LEFT,
                Actions.BLOCK_HALIGN_CENTER,
                Actions.BLOCK_HALIGN_RIGHT,
                Actions.BLOCK_CUT,
                Actions.BLOCK_COPY,
                Actions.BLOCK_PASTE,
                Actions.OPEN_HIER,
                Actions.FLOW_GRAPH_SAVE,
                Actions.FLOW_GRAPH_UNDO,
                Actions.FLOW_GRAPH_REDO,
            ):
                action.disable()

            # Load preferences
            for action in (
                Actions.TOGGLE_BLOCKS_WINDOW,
                Actions.TOGGLE_CONSOLE_WINDOW,
                Actions.TOGGLE_SCROLL_LOCK,
                Actions.TOGGLE_AUTO_HIDE_PORT_LABELS,
                Actions.TOGGLE_SNAP_TO_GRID,
            ):
                action.set_enabled(True)
                if hasattr(action, 'load_from_preferences'):
                    action.load_from_preferences()

            # Hide the panels *IF* it's saved in preferences
            main.update_panel_visibility(main.BLOCKS, Actions.TOGGLE_BLOCKS_WINDOW.get_active())
            main.update_panel_visibility(main.CONSOLE, Actions.TOGGLE_CONSOLE_WINDOW.get_active())

            # Force an update on the current page to match loaded preferences.
            # In the future, change the __init__ order to load preferences first
            page = main.current_page
            if page:
                page.flow_graph.update()

            self.init = True
        elif action == Actions.APPLICATION_QUIT:
            if main.close_pages():
                while Gtk.main_level():
                    Gtk.main_quit()
                exit(0)
        # Selections
        elif action == Actions.ELEMENT_SELECT:
            pass #do nothing, update routines below
        elif action == Actions.NOTHING_SELECT:
            flow_graph.unselect()
        elif action == Actions.SELECT_ALL:
            flow_graph.select_all()
        # Enable/Disable
        # Cut/Copy/Paste
        elif action == Actions.BLOCK_CUT:
            Actions.BLOCK_COPY()
            Actions.ELEMENT_DELETE()
        elif action == Actions.BLOCK_COPY:
            self.clipboard = flow_graph.copy_to_clipboard()
        elif action == Actions.BLOCK_PASTE:
            if self.clipboard:
                flow_graph.paste_from_clipboard(self.clipboard)
                flow_graph_update()
                page.state_cache.save_new_state(flow_graph.export_data())
                page.saved = False
        # Create hier block

        elif action == Actions.GPARM_ADD:

            self.gparm_add(self.main_window, flow_graph.gparms)

        elif action == Actions.GPARM_DEL:
            self.gparm_delete(self.main_window, flow_graph.gparms)
        elif action == Actions.GPARM_EDIT:
            if 'hb' in flow_graph.get_option('generate_options'):
                if 'drawing_scheme' not in flow_graph.gparms.keys():
                    flow_graph.gparms['drawing_scheme'] = 'name'
                if 'rotate_strict' not in flow_graph.gparms.keys():
                    flow_graph.gparms['rotate_strict'] = 'no'
                if 'mirror' not in flow_graph.gparms.keys():
                    flow_graph.gparms['mirror'] = 'none'
                if 'port_sep_x' not in flow_graph.gparms.keys():
                    flow_graph.gparms['port_sep_x'] = '4'
                if 'port_sep_y' not in flow_graph.gparms.keys():
                    flow_graph.gparms['port_sep_y'] = '4'
                if 'port_block_x' not in flow_graph.gparms.keys():
                    flow_graph.gparms['port_block_x'] = '4'
                if 'port_block_y' not in flow_graph.gparms.keys():
                    flow_graph.gparms['port_block_y'] = '4'
                if 'port_offset_l' not in flow_graph.gparms.keys():
                    flow_graph.gparms['port_offset_l'] = '0'
                if 'port_offset_r' not in flow_graph.gparms.keys():
                    flow_graph.gparms['port_offset_r'] = '0'
                if 'port_offset_t' not in flow_graph.gparms.keys():
                    flow_graph.gparms['port_offset_t'] = '0'
                if 'port_offset_b' not in flow_graph.gparms.keys():
                    flow_graph.gparms['port_offset_b'] = '0'

            self.gparm_edit(self.main_window, flow_graph.gparms)
        elif action == Actions.OUTVAR_ADD:
            n1 = len(flow_graph.selected_elements)

            if n1 == 1:
                e1 = list(flow_graph.selected_elements)[0]
                if isinstance(e1, Connection):
                    self.outvar_add_connection(self.main_window, flow_graph.outvars, e1)
                else:
                    k = str(type(e1)).split("'")[-2].split('.')[-1]
                    l_outvars = self.dict_outvars[k]
                    self.outvar_add_outvar(self.main_window,
                        flow_graph.outvars, l_outvars, e1.name)

        elif action == Actions.OUTVAR_DEL:
            self.outvar_delete(self.main_window, flow_graph.outvars,
               flow_graph.l_output_blocks, flow_graph.l_solve_blocks)
        elif action == Actions.OUTVAR_EDIT:
            self.outvar_edit(self.main_window, flow_graph.outvars,
               flow_graph.l_output_blocks)
        elif action == Actions.SOLVEBLOCK_ADD:
            self.solve_add(self.main_window, flow_graph.l_solve_blocks,
               self.d_slvparms)
        elif action == Actions.SOLVEBLOCK_DEL:
            self.solve_delete(self.main_window, flow_graph.l_solve_blocks,
            flow_graph.l_output_blocks)
        elif action == Actions.SOLVEBLOCK_EDIT:
            self.solve_pick(self.main_window, flow_graph.l_solve_blocks,
               self.d_slv_categories)
        elif action == Actions.SOLVEBLOCK_RESET:
            self.solve_pick_1(self.main_window, flow_graph.l_solve_blocks,
               self.d_slv_categories)
        elif action == Actions.SOLVEBLOCK_DISP:
            print('solve disp clicked')
            i_picked = self.solve_pick_2(self.main_window, flow_graph.l_solve_blocks)
            print('i_picked:', i_picked)
            if i_picked > -1:

                l_out_names = list(map(lambda x: x.name, flow_graph.l_output_blocks))

                slv = flow_graph.l_solve_blocks[i_picked]
                s_title = 'Solve Block: ' + slv.name + ' (' + slv.index + ')'
                print('s_title:', s_title)
                l = []
                for k, v in slv.d_parms.items(): l.append([k, v])

                n1 = len(slv.l_out)
                if n1 != 0:
                    s1 = 'output blocks'
                    s2 = ''
                    for i, out_name in enumerate(slv.l_out):
                        s2 += out_name
                        if i < (n1-1): s2 += ', '
                    l.append([s1, s2])

                for out_name in slv.l_out:
                    i_out = l_out_names.index(out_name)
                    out = flow_graph.l_output_blocks[i_out]
                    for k, v in out.d_parms.items():
                        l.append([out_name + ': ' + k, v])
                    n1 = len(out.l_outvars)
                    s1 = out_name + ': outvars:'
                    s2 = ''
                    for i_ov, ov_name in enumerate(out.l_outvars):
                        s2 += ov_name
                        if i_ov < (n1-1): s2 += ', '
                    l.append([s1, s2])

                nmax = 0
                for l1 in l:
                    n2 = len(l1[0])
                    if n2 > nmax: nmax = n2
                    l1[0] += ' '
                print('nmax:', nmax)
                nmax1 = nmax + 7
                for l1 in l:
                    l1[0] = ' ' + l1[0].ljust(nmax1, '-') + ' '

                for l1 in l: print(l1)

                window_W = 450
                window_H = 600

                slist1 = slist.ShowList(l, window_W, window_H, s_title)
                slist1.show_all()

        elif action == Actions.OUTPUTBLOCK_ADD:
            self.output_add(self.main_window, self.d_outparms,
               flow_graph.l_output_blocks, flow_graph.l_solve_blocks)
        elif action == Actions.OUTPUTBLOCK_DEL:
            self.output_delete(self.main_window,
               flow_graph.l_output_blocks, flow_graph.l_solve_blocks)
        elif action == Actions.OUTPUTBLOCK_EDIT:

            l_outvars_1 = list(flow_graph.outvars.keys())
            self.output_pick(self.main_window,
               flow_graph.l_output_blocks, flow_graph.l_solve_blocks,
               self.d_outparms, l_outvars_1)
        elif action == Actions.ELEMENT_DISPLAY:
            n1 = len(flow_graph.selected_elements)

            if n1 == 1:
                e1 = list(flow_graph.selected_elements)[0]
                self.element_display(self.main_window, e1, flow_graph.outvars)

        elif action == Actions.DOC_DISPLAY:
            e1 = list(flow_graph.selected_elements)[0]
            show_doc_1(self.platform, e1.key)

        elif action == Actions.SHOW_PARAM:
            print('Application.py: show parameter selected.')

            n1 = len(flow_graph.selected_elements)
            print('paste selected clicked: n1:', n1)
            if n1 == 1:
                e1 = list(flow_graph.selected_elements)[0]
                print('outvar add clicked: type(e1):', type(e1))
                self.show_param_select(self.main_window, e1)

        elif action == Actions.PASTE_SELECTED:
            n1 = len(flow_graph.selected_elements)
            print('paste selected clicked: n1:', n1)
            x_off = flow_graph.click_right_pos_x
            y_off = flow_graph.click_right_pos_y
            print('flow_graph.click_right_pos_x, y:', x_off, y_off)
            if self.clipboard:
                flow_graph.paste_from_clipboard_1(self.clipboard, x_off, y_off)
                flow_graph_update()
                page.state_cache.save_new_state(flow_graph.export_data())
                page.saved = False

        # Move/Rotate/Delete/Create
        elif action == Actions.BLOCK_MOVE:
            page.state_cache.save_new_state(flow_graph.export_data())
            page.saved = False
        elif action in Actions.BLOCK_ALIGNMENTS:
            if flow_graph.align_selected(action):
                page.state_cache.save_new_state(flow_graph.export_data())
                page.saved = False
        elif action == Actions.BLOCK_ROTATE_CCW:
            if flow_graph.rotate_selected(90):
                flow_graph_update()
                page.state_cache.save_new_state(flow_graph.export_data())
                page.saved = False
        elif action == Actions.BLOCK_ROTATE_CW:
            if flow_graph.rotate_selected(-90):
                flow_graph_update()
                page.state_cache.save_new_state(flow_graph.export_data())
                page.saved = False
        elif action == Actions.ELEMENT_DELETE:
            if flow_graph.remove_selected():
                flow_graph_update()
                page.state_cache.save_new_state(flow_graph.export_data())
                Actions.NOTHING_SELECT()
                page.saved = False
        elif action == Actions.ELEMENT_CREATE:
            flow_graph_update()
            page.state_cache.save_new_state(flow_graph.export_data())
            Actions.NOTHING_SELECT()
            page.saved = False
        # Window stuff
        elif action == Actions.ABOUT_WINDOW_DISPLAY:
            Dialogs.show_about(main, self.platform.config)
        elif action == Actions.HELP_WINDOW_DISPLAY:
            Dialogs.show_help(main)
        elif action == Actions.KEYBOARD_SHORTCUTS_WINDOW_DISPLAY:
            Dialogs.show_keyboard_shortcuts(main)   
        elif action == Actions.SHOWDOC_WINDOW_DISPLAY:

            l3 = page.file_path.rsplit('/', 1)
            pdf_file = l3[0] + '/proj_' + l3[1].replace('.grc', '.pdf')

            if (os.path.exists(pdf_file)):
                self.show_doc_2(pdf_file)
            else:
                s = pdf_file + ' was not found.'
                self.display_message(s)
        elif action == Actions.TOGGLE_CONSOLE_WINDOW:
            action.set_active(not action.get_active())
            main.update_panel_visibility(main.CONSOLE, action.get_active())
            action.save_to_preferences()
        elif action == Actions.TOGGLE_BLOCKS_WINDOW:
            # This would be better matched to a Gio.PropertyAction, but to do
            # this, actions would have to be defined in the window not globally
            action.set_active(not action.get_active())
            main.update_panel_visibility(main.BLOCKS, action.get_active())
            action.save_to_preferences()
        elif action == Actions.TOGGLE_SCROLL_LOCK:
            action.set_active(not action.get_active())
            active = action.get_active()
            main.console.text_display.scroll_lock = active
            if active:
                main.console.text_display.scroll_to_end()
            action.save_to_preferences()
        elif action == Actions.CLEAR_CONSOLE:
            main.console.text_display.clear()
        elif action == Actions.SAVE_CONSOLE:
            file_path = FileDialogs.SaveConsole(main, page.file_path).run()
            if file_path is not None:
                main.console.text_display.save(file_path)
        elif action == Actions.TOGGLE_AUTO_HIDE_PORT_LABELS:
            action.set_active(not action.get_active())
            action.save_to_preferences()
            for page in main.get_pages():
                page.flow_graph.create_shapes()
        elif action == Actions.TOGGLE_SNAP_TO_GRID:
            action.set_active(not action.get_active())
            action.save_to_preferences()
        # Param Modifications
        elif action == Actions.BLOCK_PARAM_MODIFY:
            selected_block = args[0] if args[0] else flow_graph.selected_block
            if selected_block:
                self.dialog = PropsDialog(self.main_window, selected_block)
                response = Gtk.ResponseType.APPLY
                while response == Gtk.ResponseType.APPLY:  # rerun the dialog if Apply was hit
                    response = self.dialog.run()
                    if response in (Gtk.ResponseType.APPLY, Gtk.ResponseType.ACCEPT):
                        page.state_cache.save_new_state(flow_graph.export_data())
                        n = page.state_cache.get_current_state()
                        flow_graph.import_data(n)
                        flow_graph_update()

                        page.saved = False
                    else:  # restore the current state
                        n = page.state_cache.get_current_state()
                        flow_graph.import_data(n)
                        flow_graph_update()
                    if response == Gtk.ResponseType.APPLY:
                        # null action, that updates the main window
                        Actions.ELEMENT_SELECT()
                self.dialog.destroy()
                self.dialog = None
        elif action == Actions.EXTERNAL_UPDATE:
            page.state_cache.save_new_state(flow_graph.export_data())
            flow_graph_update()
            if self.dialog is not None:
                self.dialog.update_gui(force=True)
            page.saved = False
        elif action == Actions.VARIABLE_EDITOR_UPDATE:
            page.state_cache.save_new_state(flow_graph.export_data())
            flow_graph_update()
            page.saved = False
        # View Parser Errors
        # Undo/Redo
        elif action == Actions.FLOW_GRAPH_UNDO:
            n = page.state_cache.get_prev_state()
            if n:
                flow_graph.unselect()
                flow_graph.import_data(n)
                flow_graph_update()
                page.saved = False
        elif action == Actions.FLOW_GRAPH_REDO:
            n = page.state_cache.get_next_state()
            if n:
                flow_graph.unselect()
                flow_graph.import_data(n)
                flow_graph_update()
                page.saved = False
        # New/Open/Save/Close
        elif action == Actions.FLOW_GRAPH_NEW:
            main.new_page()
            args = (GLib.Variant('s', 'qt_gui'),)
            flow_graph = main.current_page.flow_graph
            flow_graph.options_block.params['generate_options'].set_value(str(args[0])[1:-1])
            flow_graph.options_block.params['author'].set_value(getuser())
            flow_graph_update(flow_graph)
        elif action == Actions.FLOW_GRAPH_NEW_TYPE:
            main.new_page()
            if args:
                flow_graph = main.current_page.flow_graph
                flow_graph.options_block.params['generate_options'].set_value(str(args[0])[1:-1])
                flow_graph_update(flow_graph)
        elif action == Actions.FLOW_GRAPH_OPEN:
            file_paths = args[0] if args[0] else FileDialogs.OpenFlowGraph(main, page.file_path).run()
            if file_paths: # Open a new page for each file, show only the first
                for i,file_path in enumerate(file_paths):
                    main.new_page(file_path, show=(i==0))
                    self.config.add_recent_file(file_path)
                    main.tool_bar.refresh_submenus()
                    #main.menu_bar.refresh_submenus()
        elif action == Actions.FLOW_GRAPH_CLOSE:
            main.close_page()
        elif action == Actions.FLOW_GRAPH_OPEN_RECENT:
            file_path = str(args[0])[1:-1]
            main.new_page(file_path, show=True)
            main.tool_bar.refresh_submenus()
        elif action == Actions.FLOW_GRAPH_SAVE:
            #read-only or undefined file path, do save-as
            if page.get_read_only() or not page.file_path:
                Actions.FLOW_GRAPH_SAVE_AS()
            #otherwise try to save
            else:
                try:
                    self.platform.save_flow_graph(page.file_path, flow_graph)
                    flow_graph.grc_file_path = page.file_path
                    page.saved = True
                except IOError:
                    Messages.send_fail_save(page.file_path)
                    page.saved = False
        elif action == Actions.FLOW_GRAPH_SAVE_AS:
            file_path = FileDialogs.SaveFlowGraph(main, page.file_path).run()

            if file_path is not None:
                if flow_graph.options_block.params['id'].get_value() == 'default':
                    file_name = os.path.basename(file_path).replace('.grc', '')
                    flow_graph.options_block.params['id'].set_value(file_name)
                    flow_graph_update(flow_graph)

                page.file_path = os.path.abspath(file_path)
                try:
                    self.platform.save_flow_graph(page.file_path, flow_graph)
                    flow_graph.grc_file_path = page.file_path
                    page.saved = True
                except IOError:
                    Messages.send_fail_save(page.file_path)
                    page.saved = False
                self.config.add_recent_file(file_path)
                main.tool_bar.refresh_submenus()
                #TODO
                #main.menu_bar.refresh_submenus()
        elif action == Actions.FLOW_GRAPH_SAVE_COPY:
            try:
                if not page.file_path:
                    # Make sure the current flowgraph has been saved
                    Actions.FLOW_GRAPH_SAVE_AS()
                else:
                    dup_file_path = page.file_path
                    dup_file_name = '.'.join(dup_file_path.split('.')[:-1]) + '_copy' # Assuming .grc extension at the end of file_path
                    dup_file_path_temp = dup_file_name + Constants.FILE_EXTENSION
                    count = 1
                    while os.path.exists(dup_file_path_temp):
                        dup_file_path_temp = '{}({}){}'.format(dup_file_name, count, Constants.FILE_EXTENSION)
                        count += 1
                    dup_file_path_user = FileDialogs.SaveFlowGraph(main, dup_file_path_temp).run()
                    if dup_file_path_user is not None:
                        self.platform.save_flow_graph(dup_file_path_user, flow_graph)
                        Messages.send('Saved Copy to: "' + dup_file_path_user + '"\n')
            except IOError:
                Messages.send_fail_save('Can not create a copy of the flowgraph\n')
        elif action == Actions.FLOW_GRAPH_DUPLICATE:
            previous = flow_graph
            # Create a new page
            main.new_page()
            page = main.current_page
            new_flow_graph = page.flow_graph
            # Import the old data and mark the current as not saved
            new_flow_graph.import_data(previous.export_data())
            flow_graph_update(new_flow_graph)
            page.state_cache.save_new_state(new_flow_graph.export_data())
            page.saved = False
        elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
            file_path, background_transparent = FileDialogs.SaveScreenShot(main, page.file_path).run()
            if file_path is not None:
                try:
                    Utils.make_screenshot(flow_graph, file_path, background_transparent)
                except ValueError:
                    Messages.send('Failed to generate screen shot\n')
        elif action == Actions.FLOW_GRAPH_GEN:
            if not page.saved or not page.file_path:
                Actions.FLOW_GRAPH_SAVE()  # only save if file path missing or not saved
            if page.saved and page.file_path:
                flow_graph = self.main_window.current_page.flow_graph
                if 'hb' in flow_graph.get_option('generate_options'):
                    generator = page.get_generator()
                    try:
                        Messages.send_start_gen(generator.file_path)
                        generator.write(flow_graph)
                    except Exception as e:
                        Messages.send_fail_gen(e)
                else:
                    cctfile_name = self.config.gseim_output_dir + '/' \
                       + page.file_path.split('/')[-1].split('.')[0] + '.in'

                    cmd = 'python3'
                    cmd += ' ' + self.config.gseim_exec_dir + '/gseim1.py'
#                   cmd += ' ' + self.config.gseim_output_dir + '/cct.in'
                    cmd += ' ' + cctfile_name
                    cmd += ' ' + self.config.block_lib_dir + '/'
                    cmd += ' ' + self.config.hier_block_lib_dir + '/'
                    cmd += ' ' + self.config.gseim_xbe_dir + '/'
                    cmd += ' ' + self.config.gseim_ebe_dir + '/'
                    cmd += ' ' + self.config.gseim_bbe_dir + '/'
                    cmd += ' ' + page.file_path

                    print('grc/gui/Application.py: cmd:', cmd)

                    self.run_process(cmd, '00:10:00')

        elif action == Actions.FLOW_GRAPH_EXEC:
            flow_graph = self.main_window.current_page.flow_graph

            if 'hb' in flow_graph.get_option('generate_options'):
                self.display_message('cannot run sub-circuit')
            else:
#               check if the flowgraph needs to be saved
                if not page.saved or not page.file_path:
                    Actions.FLOW_GRAPH_SAVE()
#               check if the .grc file is newer than the .in file

                cctfile_name = self.config.gseim_output_dir + '/' \
                  + page.file_path.split('/')[-1].split('.')[0] + '.in'

                if os.path.exists(cctfile_name):
                    t1 = os.path.getmtime(cctfile_name)
                    t2 = os.path.getmtime(page.file_path)
                    if (t2 > t1):
                        Actions.FLOW_GRAPH_GEN()
                else:
                    Actions.FLOW_GRAPH_GEN()

                cmd = self.config.gseim_cpp_dir + '/gsmain'
                cmd += ' ' + cctfile_name

                print('grc/gui/Application.py: cmd:', cmd)
                s = flow_graph.options_block.params['engine_output'].get_value()
                if s == 'GUI_window':
                    self.run_process(cmd, '00:10:00')
                elif s == 'console_window':
                    message = 'Engine output sent to console.\n'\
                       + '"GSEIM: Program Completed" will be displayed'\
                       + ' if it runs successfully.\n'\
                       + 'To view engine output in a GUI window, set the "engine output"'\
                       + ' parameter of the Options block to "GUI Window".'
                    self.display_message(message)
                    subprocess.Popen(cmd, shell=True)

        elif action == Actions.FLOW_GRAPH_PLOT:
            cmd = 'python3 ' + self.config.gseim_exec_dir + '/gseim_plot.py'
            cmd += ' ' + self.config.gseim_output_dir + '/'
            cmd += ' ' + self.config.gseim_output_dir + '/gseim_plot_history.dat'

            print('grc/gui/Application.py: cmd:', cmd)
            os.system(cmd)

        elif action == Actions.PAGE_CHANGE:  # pass and run the global actions
            flow_graph_update()
        elif action == Actions.FIND_BLOCKS:
            flow_graph.unselect()
            main.update_panel_visibility(main.BLOCKS, True)
            main.btwin.search_entry.show()
            main.btwin.search_entry.grab_focus()
        elif action == Actions.OPEN_HIER:

            if len(list(flow_graph.selected_blocks())) == 1:
                for b in flow_graph.selected_blocks():
                    grc_source = b.extra_data.get('grc_source', '')
                    if grc_source:
                        main.new_page(grc_source, show=True)
                    else:
                        self.display_message("open hier allowed only for hier blocks")
            else:
                self.display_message("open hier allowed only when a single block is selected")

        else:
            log.warning('!!! Action "%s" not handled !!!' % action)
        # Global Actions for all States
        page = main.current_page  # page and flow graph might have changed
        flow_graph = page.flow_graph if page else None

        selected_blocks = list(flow_graph.selected_blocks())
        selected_block = selected_blocks[0] if selected_blocks else None

        #update general buttons
        Actions.ELEMENT_DELETE.set_enabled(bool(flow_graph.selected_elements))
        Actions.BLOCK_PARAM_MODIFY.set_enabled(bool(selected_block))
        Actions.BLOCK_ROTATE_CCW.set_enabled(bool(selected_blocks))
        Actions.BLOCK_ROTATE_CW.set_enabled(bool(selected_blocks))
        #update alignment options
        for act in Actions.BLOCK_ALIGNMENTS:
            if act:
                act.set_enabled(len(selected_blocks) > 1)
        #update cut/copy/paste
        Actions.BLOCK_CUT.set_enabled(bool(selected_blocks))
        Actions.BLOCK_COPY.set_enabled(bool(selected_blocks))
        Actions.BLOCK_PASTE.set_enabled(bool(self.clipboard))

        Actions.OPEN_HIER.set_enabled(bool(selected_blocks))

        Actions.FIND_BLOCKS.enable()

        Actions.FLOW_GRAPH_SAVE.set_enabled(not page.saved)
        main.update()

        flow_graph.update_selected()
        page.drawing_area.queue_draw()

        return True  # Action was handled

