From 15e9136122c3a2854e481639731644c15a54ac3e Mon Sep 17 00:00:00 2001
From: Arnaud Blanchard <arnaud.blanchard@ensea.fr>
Date: Sun, 20 Oct 2019 23:03:46 +0200
Subject: [PATCH] Create gtk_channels projec to have a gtk interface managing
 channels

---
 e_bash/Channel.cpp                   | 224 ++++++++++++++++++++++++++
 {gtk_channels => e_bash}/Channel.hpp |   8 +-
 e_bash/Plot.cpp                      |  18 +++
 e_bash/Plot.hpp                      |  24 +++
 gtk_channels/CMakeLists.txt          |   3 +-
 gtk_channels/Channel.cpp             | 224 --------------------------
 gtk_channels/ChannelColumns.cpp      |  42 +++++
 gtk_channels/ChannelColumns.hpp      |  32 ++++
 gtk_channels/ChannelWidget.cpp       | 231 +++++++++++++++++++++++++++
 gtk_channels/ChannelWidget.hpp       |  41 +++++
 gtk_channels/ContextMenu.cpp         |  40 +++++
 gtk_channels/ContextMenu.hpp         |  32 ++++
 gtk_channels/Process.cpp             |  74 ++++++---
 gtk_channels/Process.h               |  19 +--
 gtk_channels/gtk_channels.cpp        |  11 +-
 15 files changed, 751 insertions(+), 272 deletions(-)
 create mode 100644 e_bash/Channel.cpp
 rename {gtk_channels => e_bash}/Channel.hpp (60%)
 create mode 100644 e_bash/Plot.cpp
 create mode 100644 e_bash/Plot.hpp
 delete mode 100644 gtk_channels/Channel.cpp
 create mode 100644 gtk_channels/ChannelColumns.cpp
 create mode 100644 gtk_channels/ChannelColumns.hpp
 create mode 100644 gtk_channels/ChannelWidget.cpp
 create mode 100644 gtk_channels/ChannelWidget.hpp
 create mode 100644 gtk_channels/ContextMenu.cpp
 create mode 100644 gtk_channels/ContextMenu.hpp

diff --git a/e_bash/Channel.cpp b/e_bash/Channel.cpp
new file mode 100644
index 0000000..fc438e1
--- /dev/null
+++ b/e_bash/Channel.cpp
@@ -0,0 +1,224 @@
+//
+//  Channel.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 29/09/2019.
+//
+
+#include "Channel.hpp"
+#include "blc_channel.h"
+#include "string"
+#include <vector>
+#include <vte/vte.h>
+#include <thread>
+
+
+using namespace Gtk;
+using  namespace std;
+
+struct Process{
+    pid_t pid;
+    
+    char  **argv;
+    string name;
+    string program_name;
+    string channel;
+    
+    void run();
+    void watch();
+};
+
+void Process::watch(){
+    char pipe_buffer[PIPE_SIZE];
+    ssize_t pipe_read_nb=0, pipe_buffer_writen_nb=0;
+    int fd_max, status;
+    ssize_t size=-1, written_nb, read_nb;
+    fd_set fd_read_set, fd_write_set;
+    
+    fd_max=MAX(stdout_fileno, stderr_fileno);
+  //  if (input_process) fd_max=MAX(fd_max, input_process->stdout_fileno);
+  //  fd_max=MAX(fd_max, stdin_fileno);
+    
+    do
+    {
+        size=0;
+        FD_ZERO(&fd_read_set);
+      //  FD_ZERO(&fd_write_set);
+        
+        //if (input_process) FD_SET(input_process->stdout_fileno, &fd_read_set);
+        FD_SET(stdout_fileno, &fd_read_set);
+        FD_SET(stderr_fileno, &fd_read_set);
+      //  if (stdin_fileno!=-1 && pipe_read_nb) FD_SET(stdin_fileno, &fd_write_set);
+        
+        SYSTEM_ERROR_CHECK(::select(fd_max+1, &fd_read_set, NULL, NULL, NULL), -1, "process: '%s' '%s'", name, program->name);
+        
+   /*     if (stdin_fileno!=-1 && pipe_read_nb && FD_ISSET(stdin_fileno, &fd_write_set)){ //something to write and input ready;
+            SYSTEM_ERROR_CHECK(written_nb=write(stdin_fileno, &pipe_buffer[pipe_buffer_writen_nb], pipe_read_nb-pipe_buffer_writen_nb),-1, "pipe_buffer_writen_nb '%ld', pipe_read_nb '%ld'", pipe_buffer_writen_nb, pipe_read_nb);
+            SYSTEM_ERROR_CHECK(written_nb=write(STDERR_FILENO, &pipe_buffer[pipe_buffer_writen_nb], pipe_read_nb-pipe_buffer_writen_nb),-1, "pipe_buffer_writen_nb '%ld', pipe_read_nb '%ld'", pipe_buffer_writen_nb, pipe_read_nb);
+            
+            pipe_buffer_writen_nb+=written_nb;
+            size+=written_nb;
+        }
+        
+        if (input_process && FD_ISSET(input_process->stdout_fileno, &fd_read_set)){
+            SYSTEM_ERROR_CHECK(read_nb=read(input_process->stdout_fileno, &pipe_buffer[pipe_read_nb], sizeof(pipe_buffer)-pipe_read_nb), -1, "input pipe");
+            pipe_read_nb+=read_nb;
+            size+=read_nb;
+        }
+        
+        if (pipe_read_nb && pipe_buffer_writen_nb==pipe_read_nb){ //Everything has been written
+            pipe_buffer_writen_nb=0;
+            pipe_read_nb=0;
+        }*/
+        
+        if (FD_ISSET(stdout_fileno, &fd_read_set)){
+            SYSTEM_ERROR_CHECK(size=read(stdout_fileno, pipe_buffer, sizeof(pipe_buffer)), -1, "stdout");
+            if (output_process) SYSTEM_ERROR_CHECK(write(next_process_pipe[1], pipe_buffer, size), -1, "next_process_pipe");
+            buffer_to_vte_terminal(pipe_buffer, size, stdout_terminal);
+        }
+        
+        if (FD_ISSET(stderr_fileno, &fd_read_set)){
+            SYSTEM_ERROR_CHECK(size=read(stderr_fileno, pipe_buffer, sizeof(pipe_buffer)), -1, "stderr");
+            buffer_to_vte_terminal(pipe_buffer, size, stderr_terminal);
+        }
+    }
+    while(size!=0);
+    
+    close(stdout_fileno);
+    close(stderr_fileno);
+    
+    waitpid(pid, &status, 0);
+    pid=-1;
+    
+    g_signal_handler_disconnect(G_OBJECT(stderr_terminal), commit_handler);
+    
+    // if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(err_toggle_button))) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(err_toggle_button), false);
+    
+    gdk_threads_add_idle (thread_safe_end_process, this);
+}
+
+void Process_old::run(){
+    int stdin_pipe[2], stderr_pipe[2], stdout_pipe[2];
+ 
+    SYSTEM_ERROR_CHECK(pipe(stdin_pipe), -1, NULL);
+    SYSTEM_ERROR_CHECK(pipe(stdout_pipe), -1, NULL);
+    SYSTEM_ERROR_CHECK(pipe(stderr_pipe), -1, NULL);
+   
+    pid=fork();
+    
+    if (pid==0){
+        /*Connection to virtual termainals*/
+        SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(dup2(stdin_pipe[0] , STDIN_FILENO ), -1, EINTR, NULL);
+        SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(dup2(stdout_pipe[1], STDOUT_FILENO), -1, EINTR, NULL);
+        SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(dup2(stderr_pipe[1], STDERR_FILENO), -1, EINTR, NULL);
+     
+        blc_close_pipe(stdin_pipe);
+        blc_close_pipe(stdout_pipe);
+        blc_close_pipe(stderr_pipe);
+        SYSTEM_ERROR_CHECK(execvp(program_name.data(), argv), -1, "Executing  program '%s'",  program_name.data());
+    }
+    close(stdin_pipe[0]);
+    close(stdout_pipe[1]);
+    close(stderr_pipe[1]);
+    
+    thread thread([](){
+        watch();
+    });
+    
+    //commit_handler=g_signal_connect(G_OBJECT(stderr_terminal), "commit", G_CALLBACK(input_cp), this);
+}
+
+
+struct ModelColumns : public TreeModel::ColumnRecord{
+    Gtk::TreeModelColumn<string> name, type, format, dimensions, icon;
+    
+    ModelColumns(){
+        add(name);
+        add(type);
+        add(format);
+        add(dimensions);
+        add(icon);
+    }
+    
+    void add_channel( Glib::RefPtr<Gtk::ListStore>  &list, blc_channel *channel){
+        uint32_t type_str, format_str;
+        char dims_str[256];
+        
+        auto row = *(list->append()); //* is because it is an iterator and we want the content of the ref.
+        row[name] = channel->name;
+        row[type] = string(UINT32_TO_STRING(type_str, channel->type), 4);
+        row[format] = string(UINT32_TO_STRING(format_str, channel->format), 4);
+        channel->sprint_dims(dims_str, 256);
+        row[dimensions] = dims_str;
+        row[icon] =  Gtk::Stock::YES.id; //dialog-information";
+    }
+}m_Columns;
+
+static void refresh_channels(void* user_data){
+    
+    auto channel_widget = (ChannelWidget *)user_data;
+    Glib::signal_idle().connect([&channel_widget](){ //important to use an idle otherwise Thread problems
+        channel_widget->refresh_channel_list();
+        return false;
+    });
+}
+
+ChannelWidget::ChannelWidget(){
+    Gtk::CellRendererPixbuf     cellRendererPixBuf;
+    
+    list_store = Gtk::ListStore::create(m_Columns);
+    set_model(list_store);
+    
+    append_column("Name", m_Columns.name);
+    append_column("Type", m_Columns.type);
+    append_column("Format", m_Columns.format);
+    append_column("Dimensions", m_Columns.dimensions);
+    
+    int numcols = append_column("image ", cellRendererPixBuf);
+    get_column(numcols-1)->add_attribute(cellRendererPixBuf, "stock-id",  m_Columns.icon);
+    
+    refresh_channel_list();
+    blc_channel_check_for_event(refresh_channels, this);
+    show_all_children();
+     
+    signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){
+        vector <char*> argv_vec;
+        Process *process = new Process();
+        Gtk::TreeIter iter=this->list_store->get_iter(path);
+        Gtk::TreeModel::Row row = *iter;
+        
+        process->program_name="o_gnuplot";
+        process->name=process->program_name;
+        process->channel=((string)row[m_Columns.name]).data();
+        
+        fprintf(stderr, "Row %s\n", ((string)row[m_Columns.name]).data());
+        
+        argv_vec.emplace_back((char*)process->program_name.data());
+        argv_vec.emplace_back((char*)process->channel.data());
+        argv_vec.emplace_back(nullptr);
+        process->argv=argv_vec.data();
+        process->run();
+        
+        return false;
+    });
+    
+    
+    signal_button_release_event().connect([](GdkEventButton* event){
+        if  (event->button == 3){
+            exit(1);
+        };
+        return false;
+    });
+}
+
+void ChannelWidget::refresh_channel_list(){
+    int channels_nb;
+    blc_channel *channel_infos=nullptr;
+    channels_nb = blc_channel_get_all_infos(&channel_infos);
+    
+    list_store->clear();
+    for(blc_channel *channel=channel_infos;  channel != channel_infos+channels_nb; channel++){
+        m_Columns.add_channel(list_store, channel);
+    }
+    free(channel_infos);
+}
diff --git a/gtk_channels/Channel.hpp b/e_bash/Channel.hpp
similarity index 60%
rename from gtk_channels/Channel.hpp
rename to e_bash/Channel.hpp
index 098345b..20471e8 100644
--- a/gtk_channels/Channel.hpp
+++ b/e_bash/Channel.hpp
@@ -10,16 +10,10 @@
 
 #include <gtkmm.h>
 
-
-
 struct ChannelWidget:Gtk::TreeView{
-    std::string blaar_dir;
-    Glib::RefPtr<Gtk::TreeStore>  tree_store;
+    Glib::RefPtr<Gtk::ListStore>  list_store;
     
     ChannelWidget();
-    void add_process(Gtk::TreeModel::Row &row);
-    void display_process(Gtk::TreeModel::Row &row);
-
     void refresh_channel_list();
 };
 #endif /* Channel_hpp */
diff --git a/e_bash/Plot.cpp b/e_bash/Plot.cpp
new file mode 100644
index 0000000..6497535
--- /dev/null
+++ b/e_bash/Plot.cpp
@@ -0,0 +1,18 @@
+//
+//  Plot.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 30/09/2019.
+//
+
+#include "Plot.hpp"
+
+
+
+
+
+
+Plot::Plot(){
+    
+    
+}
diff --git a/e_bash/Plot.hpp b/e_bash/Plot.hpp
new file mode 100644
index 0000000..1bd8f03
--- /dev/null
+++ b/e_bash/Plot.hpp
@@ -0,0 +1,24 @@
+//
+//  Plot.hpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 30/09/2019.
+//
+
+#ifndef Plot_hpp
+#define Plot_hpp
+
+#include <stdio.h>
+#include <gtkmm-plplot.h>
+
+
+struct Plot{
+    Gtk::PLplot::Plot2D plot;
+    Gtk::PLplot::Canvas canvas(plot);
+    
+    Plot();
+ //   plot.
+};
+
+
+#endif /* Plot_hpp */
diff --git a/gtk_channels/CMakeLists.txt b/gtk_channels/CMakeLists.txt
index b2f390d..c57fe92 100644
--- a/gtk_channels/CMakeLists.txt
+++ b/gtk_channels/CMakeLists.txt
@@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 2.6)
 project(gtk_channels)
 
 find_package(blc_channel REQUIRED)
+find_package(blc_image REQUIRED)
 find_package(blc_program REQUIRED)
 find_package(blc_process REQUIRED)
 
@@ -17,5 +18,5 @@ pkg_check_modules(VTE3  vte-2.91 REQUIRED) #libvtemm is not yet common
 add_definitions(-std=c++14 ${BL_DEFINITIONS} ${GTK3_CFLAGS_OTHER} )
 include_directories(${BL_INCLUDE_DIRS} ${GTK3_INCLUDE_DIRS} ${GTK3_SOURCE_VIEW_INCLUDE_DIRS} ${VTE_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS} ${VTE3_INCLUDE_DIRS})
 link_directories(${GTK3_LIBRARY_DIRS}  ${VTE_LIBRARY_DIRS} /usr/local/lib)
-add_executable(gtk_channels gtk_channels.cpp Channel.cpp  Process.cpp parse_help.cpp)
+add_executable(gtk_channels gtk_channels.cpp ChannelWidget.cpp ContextMenu.cpp ChannelColumns.cpp Process.cpp parse_help.cpp)
 target_link_libraries(gtk_channels ${BL_LIBRARIES} ${GTK3_LIBRARIES} ${GTK3_SOURCE_VIEW_LIBRARIES} ${VTE_LIBRARIES} ${GTKMM_LIBRARIES} ${VTE3_LIBRARIES})
diff --git a/gtk_channels/Channel.cpp b/gtk_channels/Channel.cpp
deleted file mode 100644
index f502fcb..0000000
--- a/gtk_channels/Channel.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-//
-//  Channel.cpp
-//  e_bash
-//
-//  Created by Arnaud Blanchard on 29/09/2019.
-//
-
-#include "Process.h"
-#include "Channel.hpp"
-#include "blc_channel.h"
-#include "string"
-#include <vector>
-#include <forward_list>
-
-#include <vte/vte.h>
-#include <thread>
-#include <iostream>
-
-using namespace Gtk;
-using namespace Glib; //signal_idle,RefPtr
-
-using  namespace std;
-
-vector <char*> argv_vec;
-
-
-struct ChannelMenu:Gtk::Menu{
-    ChannelMenu(Gtk::TreeView &tree_view);
-
-};
-
-
-
-
-struct ModelColumns : public TreeModel::ColumnRecord{
-    TreeModelColumn<int> id, pid;
-    TreeModelColumn<string> name, type, format, dimensions, icon;
-    TreeModelColumn<Process*> process;
-    
-    
-    ModelColumns(){
-        add(id);
-        add(name);
-        add(type);
-        add(format);
-        add(dimensions);
-        add(icon);
-        add(pid);
-        add(process);
-    }
-    
-    void add_channel(RefPtr<TreeStore>  &tree, blc_channel *channel){
-        uint32_t type_str, format_str;
-        char dims_str[256];
-        TreeModel::Row childrow;
-        
-        auto row = *(tree->append()); //* is because it is an iterator and we want the content of the ref.
-        //row[id] = channel->id;
-        row[name] = channel->name;
-        row[type] = string(UINT32_TO_STRING(type_str, channel->type), 4);
-        row[format] = string(UINT32_TO_STRING(format_str, channel->format), 4);
-        channel->sprint_dims(dims_str, 256);
-        row[dimensions] = dims_str;
-        row[process]=nullptr;
-        row[id]=channel->id;
-        
-        // row[icon] =  Gtk::Stock::YES.id; //dialog-information";
-    }
-}m_Columns;
-
-static void refresh_channels(void* user_data){
-    
-    auto channel_widget = (ChannelWidget *)user_data;
-    signal_idle().connect([channel_widget](){ //important to use an idle otherwise Thread problems
-        channel_widget->refresh_channel_list();
-        return false;
-    });
-}
-
-ChannelMenu::ChannelMenu(TreeView &tree_view){
-    MenuItem *item = new MenuItem("_Remove", true);
-    append(*item);
-    accelerate(*this);
-    show_all();
-    item->signal_activate().connect([&tree_view](){
-        tree_view.get_selection()->selected_foreach_iter([](const TreeModel::iterator& iter){
-            TreeModel::Row row = *iter;
-            string name= row[m_Columns.name];
-            blc_remove_channel_with_name(name.data());
-        });;
-    });
-}
-
-
-void ChannelWidget::add_process( TreeModel::Row &row){
-    TreeModel::Row childrow;
-    string command;
-    
-    Process *process = new Process(blaar_dir+"/bin/", "o_gnuplot");
-    process->add_arg(((string)row[m_Columns.name]).data());
-    process->run();
-    
-    command=blc_program_name;
-    
-    for (auto arg = process->arg_vector.begin()+1; arg!=process->arg_vector.end()-1; arg++){
-        command+=" ";
-        command+=*arg;
-    }
-    
-    childrow = *(tree_store->append(row.children()));
-    childrow[m_Columns.name]=command;
-    childrow[m_Columns.icon]="utilities-terminal";
-    childrow[m_Columns.process]= process;
-    show_all_children();
-}
-
-void ChannelWidget::display_process( TreeModel::Row &row){
-    Process *process=row[m_Columns.process];
-    
-    process->display_terminal();
-}
-
-
-ChannelWidget::ChannelWidget(){
-    CellRendererPixbuf     cellRendererPixBuf;
-    
-    this->set_enable_search();
-   // this->m
-    
-    blaar_dir=getenv("BLAAR_DIR");
-    if (blaar_dir.empty()) EXIT_ON_ERROR("Your environment variable 'BLAAR_DIR' is not defined.");
-    
-    tree_store = TreeStore::create(m_Columns);
-    set_model(tree_store);
-    
-    append_column("Name", m_Columns.name);
-    append_column("Type", m_Columns.type);
-    append_column("Format", m_Columns.format);
-    append_column("Dimensions", m_Columns.dimensions);
-    
-    int numcols = append_column("image ", cellRendererPixBuf);
-    get_column(numcols-1)->add_attribute(cellRendererPixBuf, "icon-name",  m_Columns.icon);
-    
-    refresh_channel_list();
-    blc_channel_check_for_event(refresh_channels, this);
-    get_selection()->set_mode(SELECTION_MULTIPLE);
-    show_all_children();
-    
-   // set_events(Gdk::BUTTON_PRESS_MASK);
-    
-    signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){
-        TreeIter iter=this->tree_store->get_iter(path);
-        TreeModel::Row row = *iter;
-        if (tree_store->iter_depth(iter) == 0) this->add_process(row);
-        else display_process(row);
-    });
-    
-    get_selection()->signal_changed().connect([](){
-        return true;
-    });
-    
-    signal_button_release_event().connect([this](GdkEventButton const *event){
-    
-        if  (event->button == 3){
-            ChannelMenu *m_Menu_Popup = new ChannelMenu(*this);
-            m_Menu_Popup->popup_at_pointer((GdkEvent*)event);
-        
-        }
-        return true;
-    });
-}
-
-/*
-void ChannelWidget::menu_add_program_item(Menu &menu, string name, string program){
-    MenuItem *item = new MenuItem(name);
-    
-    item->signal_activate().connect([this](){
-        auto refSelection = get_selection();
-        if (refSelection) {
-            TreeModel::iterator iter = refSelection->get_selected();
-            TreeModel::Row row = *iter;
-            for (TreeModel::iterator iter = refSelection-> (); ){
-                
-                string name= row[m_Columns.name];
-                blc_remove_channel_with_name(name.data());
-            }
-        }
-    });
-    menu.append(*item);
-};*/
-
-void ChannelWidget::refresh_channel_list(){
-    bool found;
-    int channels_nb;
-    blc_channel *channel_infos=nullptr;
-    channels_nb = blc_channel_get_all_infos(&channel_infos);
-    list<TreeModel::iterator> list_iters_without_channels;
-    
-    /* Generate a list of iter on row to be able to remove the row if the channel does not exist anymore*/
-    tree_store->foreach_iter([&list_iters_without_channels, this](TreeModel::iterator const &iter){
-        if (this->tree_store->iter_depth(iter)==0) list_iters_without_channels.push_back(iter); //copy iter
-        return false;
-    });
-    
-    for(blc_channel *channel=channel_infos;  channel != channel_infos+channels_nb; channel++){
-        found=false;
-        auto list_iter=list_iters_without_channels.begin();
-        while(list_iter != list_iters_without_channels.end()){
-            TreeModel::Row row = *(*list_iter);
-            if (row[m_Columns.id]==channel->id){
-                found=true;
-                list_iter=list_iters_without_channels.erase(list_iter);
-            }
-            else list_iter++;
-        }
-        
-        if (found==false)  m_Columns.add_channel(tree_store, channel);
-    }
-    for(auto list_iter: list_iters_without_channels){
-        tree_store->erase(*list_iter);
-    }
-    this->show_all_children();
-    free(channel_infos);
-}
diff --git a/gtk_channels/ChannelColumns.cpp b/gtk_channels/ChannelColumns.cpp
new file mode 100644
index 0000000..bd1ce5b
--- /dev/null
+++ b/gtk_channels/ChannelColumns.cpp
@@ -0,0 +1,42 @@
+//
+//  ChannelColumns.cpp
+//  gtk_channels
+//
+//  Created by Arnaud Blanchard on 13/10/2019.
+//
+
+#include "ChannelColumns.hpp"
+
+using namespace Glib;
+using namespace Gtk;
+using namespace std;
+
+ChannelColumns::ChannelColumns(){
+    add(id);
+    add(name);
+    add(type);
+    add(format);
+    add(dimensions);
+    add(terminal_icon);
+    add(play_pause_icon);
+    add(play_pause_sensitive);
+    add(stop_icon);
+    add(stop_sensitive);
+    add(pid);
+    add(process);
+}
+
+void ChannelColumns::add_channel(RefPtr<TreeStore> tree, blc_channel const &channel){
+    uint32_t type_str, format_str;
+    char dims_str[256];
+    TreeModel::Row childrow;
+    
+    auto row = *(tree->append()); //* is because it is an iterator and we want the content of the ref.
+    row[name] = channel.name;
+    row[type] = string(UINT32_TO_STRING(type_str, channel.type), 4);
+    row[format] = string(UINT32_TO_STRING(format_str, channel.format), 4);
+    channel.sprint_dims(dims_str, 256);
+    row[dimensions] = dims_str;
+    row[process]=nullptr;
+    row[id]=channel.id;
+}
diff --git a/gtk_channels/ChannelColumns.hpp b/gtk_channels/ChannelColumns.hpp
new file mode 100644
index 0000000..ee45e53
--- /dev/null
+++ b/gtk_channels/ChannelColumns.hpp
@@ -0,0 +1,32 @@
+//
+//  ChannelColumns.hpp
+//  gtk_channels
+//
+//  Created by Arnaud Blanchard on 13/10/2019.
+//
+
+#ifndef ChannelColumns_hpp
+#define ChannelColumns_hpp
+
+#include "blc_channel.h"
+#include "Process.h"
+
+#include <stdio.h>
+#include <string>
+#include <gtkmm.h>
+
+struct ChannelWidget;
+
+struct ChannelColumns : public Gtk::TreeModel::ColumnRecord{
+    Gtk::TreeModelColumn<int> id, pid;
+    Gtk::TreeModelColumn<bool> play_pause_sensitive, stop_sensitive;
+    Gtk::TreeModelColumn<std::string> name, type, format, dimensions, terminal_icon, play_pause_icon, stop_icon;
+    Gtk::TreeModelColumn<Process*> process;
+    
+    ChannelWidget *channel_widget;
+    
+    ChannelColumns();
+    void add_channel(Glib::RefPtr<Gtk::TreeStore> tree, blc_channel const &channel);
+};
+
+#endif /* ChannelColumns_hpp */
diff --git a/gtk_channels/ChannelWidget.cpp b/gtk_channels/ChannelWidget.cpp
new file mode 100644
index 0000000..ee4a028
--- /dev/null
+++ b/gtk_channels/ChannelWidget.cpp
@@ -0,0 +1,231 @@
+//
+//  Channel.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 29/09/2019.
+//
+
+#include "ChannelColumns.hpp"
+#include "Process.h"
+#include "ChannelWidget.hpp"
+#include "blc_channel.h"
+#include "blc_image.h"
+
+#include <string>
+#include <vector>
+#include <forward_list>
+
+#include <vte/vte.h>
+#include <thread>
+#include <iostream>
+#include <numeric>
+
+using namespace Gtk;
+using namespace Glib; //signal_idle,RefPtr
+using  namespace std;
+
+static void refresh_channels(void* user_data){
+    auto channel_widget = (ChannelWidget *)user_data;
+    signal_idle().connect([channel_widget](){ //important to use an idle otherwise Thread problems
+        channel_widget->refresh_channel_list();
+        return false;
+    });
+}
+
+ChannelColumns ChannelWidget::columns;
+
+ChannelWidget::ChannelWidget(){
+    char const *blaar_dir=getenv("BLAAR_DIR");
+    if (blaar_dir==nullptr) EXIT_ON_ERROR("Your environment variable 'BLAAR_DIR' is not defined.");
+    
+    program_dir=blaar_dir;
+    program_dir+="/bin";
+    
+    append_column("Name", columns.name);
+    append_column("Type", columns.type);
+    append_column("Format", columns.format);
+    append_column("Dimensions", columns.dimensions);
+    
+    tree_store = TreeStore::create(columns);
+    set_model(tree_store);
+    set_activate_on_single_click();
+    
+    TreeViewColumn *col = new TreeViewColumn(" ", terminal_cell_renderer);
+    col->add_attribute(terminal_cell_renderer, "icon-name",  columns.terminal_icon);
+    append_column(*col);
+    
+    col =  new TreeViewColumn(" ");
+    col->pack_start(play_pause_cell_renderer);
+    col->pack_start(stop_cell_renderer);
+    
+    col->add_attribute(play_pause_cell_renderer, "icon-name",  columns.play_pause_icon);
+    col->add_attribute(stop_cell_renderer, "icon-name",  columns.stop_icon);
+    col->add_attribute(play_pause_cell_renderer, "sensitive", columns.play_pause_sensitive);
+    col->add_attribute(stop_cell_renderer, "sensitive", columns.stop_sensitive);
+    append_column(*col);
+    
+    signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){
+        int x, y;
+        TreeIter iter=this->tree_store->get_iter(path);
+        TreeModel::Row row = *iter;
+        
+        Process *process=row[columns.process];
+        
+        if (tree_store->iter_depth(iter) != 0){ //It is a process
+            if (column->get_cell_position(terminal_cell_renderer, x, y)){//x, y are not used but it allow to chack the existanace of the cell renderer
+                process->display_terminal();
+            }
+            else if (column->get_cell_position(play_pause_cell_renderer, x, y)){
+                process->signal(SIGTSTP);
+            }
+        }
+    });
+    
+    signal_button_release_event().connect([this](GdkEventButton const *event){
+        if  (event->button == 3){
+            ContextMenu *menu=create_menu();
+            menu->popup_at_pointer((GdkEvent*)event);
+        }
+        return true;
+    });
+    
+    refresh_channel_list();
+    blc_channel_check_for_event(refresh_channels, this);
+    get_selection()->set_mode(SELECTION_MULTIPLE);
+    show_all_children();
+}
+
+blc_channel *ChannelWidget::find_channel(int  id){
+    blc_channel *channel;
+    channel= lower_bound(channels, channels+channels_nb, id, [](blc_channel const &a, int id){
+        return (a.id < id);
+    });
+    return channel;
+}
+
+void ChannelWidget::after_launch(Process *process, std::vector<TreeModel::Path> rows){
+    auto process_rows=make_shared<vector<TreeIter>>();
+    string command = accumulate(process->arg_vector.begin()+1, process->arg_vector.end()-1, process->program_name, [](auto const &a, auto const &b){
+        return a+" "+b;
+    });
+    
+    for (auto const &path : rows){
+        TreeNodeChildren node = tree_store->get_iter(path)->children();
+        TreeIter row_iter=tree_store->append(node);
+         process_rows->emplace_back(row_iter);
+        TreeRow row=*row_iter;
+        row[columns.name]=command;
+        row[columns.terminal_icon]="utilities-terminal";
+        row[columns.play_pause_icon]="media-playback-pause";
+        row[columns.play_pause_sensitive]=true;
+
+        row[columns.stop_icon]="media-playback-stop";
+        row[columns.stop_sensitive]=true;
+
+        row[columns.process]= process;
+        expand_row(path, true);
+   //     process_rows->push_back(row_iter);
+    }
+    
+    process->on_quit([process_rows](int status){
+        for (auto &row_iter : *process_rows){
+            TreeRow row=*row_iter;
+
+          //  row[columns.name]=command;
+         //   row[columns.terminal_icon]="utilities-terminal";
+            if (status==0)  row[columns.terminal_icon]="process-stop";
+            else row[columns.terminal_icon]="dialog-error";
+            row[columns.play_pause_icon]="media-playback-start";
+            row[columns.stop_sensitive]=false;
+        }
+    });
+    show_all_children();
+}
+
+void ChannelWidget::on_quit(Process *process, int status, std::vector<TreeModel::Path> rows){
+    for (auto const &path : rows){
+        TreeRow row = *tree_store->get_iter(path);
+      //  row[columns.name]=command;
+     //   row[columns.terminal_icon]="utilities-terminal";
+        if (status==0)  row[columns.terminal_icon]="process-stop";
+        else  row[columns.terminal_icon]="dialog-error";
+        row[columns.stop_icon]="media-playback-start";
+        expand_row(path, true);
+    }
+}
+
+ContextMenu *ChannelWidget::create_menu(){
+    bool only_channels=true, can_keyboard=true, can_image=true,  can_graph=true, can_history_graph=true;
+    menu=new ContextMenu(program_dir);
+
+    menu->row_selection=get_selection();
+    menu->row_selection->selected_foreach_iter ([&](TreeModel::iterator const &iter){
+        if (tree_store->iter_depth(iter)==0){
+            blc_channel *channel=find_channel((*iter)[columns.id]);
+            if (channel->total_length > 64) can_keyboard=false;
+            if (blc_image_get_bytes_per_pixel(channel)==0) can_image=false;
+            if (channel->dims_nb > 1) can_history_graph=false;
+            else if (channel->dims_nb > 2) can_graph=false;
+            menu->selected_channels.emplace_back(channel);
+        }
+        else only_channels=false;
+    });
+    
+    if (only_channels){
+        menu->add_item("_Remove", true, [this]{
+            for( auto &channel : menu->selected_channels){
+                channel->remove();
+            }
+        });
+        menu->add_separator();
+        
+        Glib::RefPtr<TreeModel> tree_model=tree_store;
+        
+        /* This is the simpler I can do for a callback
+         It means this->after_launch( first_argument (i.e. process)) will be called.*/
+        auto after_launch_callback=bind(&ChannelWidget::after_launch, this, placeholders::_1,  menu->row_selection->get_selected_rows(tree_model));//This is a way to pass the rows, without process needing to know what is row
+        if (can_keyboard) menu->add_program("keyboard", "i_keyboard", {"--display","--output"}, after_launch_callback);
+        if (can_image) menu->add_program("image", "o_gtk_image", {},  after_launch_callback);
+        if (can_graph) menu->add_program("graph", "o_gnuplot", {},  after_launch_callback);
+        if (can_history_graph) menu->add_program("history graph", "o_gnuplot", {"--history=100"},  after_launch_callback);
+    }
+    menu->show_all();
+    return menu;
+}
+
+/**
+ We look for all channels. We get a list of actual row and look for differences to add or remove channel in the known list.
+ This suppose that a channel with a same id has the same properties. It is a property of blc_channel.
+ */
+void ChannelWidget::refresh_channel_list(){
+    ///  We define a functor to compare blc_channel and TreeIter by id
+    struct compare_id{
+        bool operator()(blc_channel const &a, TreeIter const &b) { return a.id < (*b)[columns.id]; };
+        bool operator()(TreeIter const &a, blc_channel const &b) { return  (*a)[columns.id]< b.id ; };
+    };
+            
+    vector<blc_channel> news_channels;
+    vector<TreeIter> channels_to_remove;
+            
+    FREE(channels);
+    tie(channels, channels_nb)=blc_channel::get_all_infos();
+            
+    //We get children to be able to use iterator
+            auto children=tree_store->children();
+            
+            //We get a list of new channels
+            set_difference(channels, channels+channels_nb, children.begin(), children.end(), inserter(news_channels, news_channels.begin()), compare_id());
+            
+            //We get a list of channels to remove
+            set_difference(children.begin(), children.end(), channels, channels+channels_nb, inserter(channels_to_remove, channels_to_remove.begin()), compare_id());
+            
+            for(auto const &tree_iter : channels_to_remove)  tree_store->erase(tree_iter);
+            for(auto const &channel :news_channels)  columns.add_channel(tree_store, channel);
+            
+            this->show_all_children();
+            
+            //We free the list of channel before getting a new list. This is not optimum but it does not happen often.
+            for_each(channels, channels+channels_nb, [](blc_channel channel){
+                channel.~blc_channel(); //We destroy the dims inside
+            });
+            }
diff --git a/gtk_channels/ChannelWidget.hpp b/gtk_channels/ChannelWidget.hpp
new file mode 100644
index 0000000..794f852
--- /dev/null
+++ b/gtk_channels/ChannelWidget.hpp
@@ -0,0 +1,41 @@
+
+
+//
+//  ChannelWidget.hpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 29/09/2019.
+//
+
+#ifndef ChannelWidget_hpp
+#define ChannelWidget_hpp
+
+#include <gtkmm.h>
+#include <string>
+#include <deque>
+#include <set>
+#include "blc_channel.h"
+#include "ChannelColumns.hpp"
+#include "ContextMenu.hpp"
+
+struct ChannelWidget:Gtk::TreeView{
+    blc_channel* channels;  //This structure is coherent with blc_channel implementation
+    ContextMenu *menu=nullptr;
+    Gtk::CellRendererPixbuf  terminal_cell_renderer, play_pause_cell_renderer, stop_cell_renderer;
+    Glib::RefPtr<Gtk::TreeStore>  tree_store;
+    int channels_nb;
+    std::string program_dir;
+    
+    static ChannelColumns columns;
+
+    ChannelWidget();
+    blc_channel *find_channel(int  id);
+    ContextMenu *create_menu();
+
+    void display_process(Gtk::TreeModel::Row &row);
+    void refresh_channel_list();
+    void after_launch(Process *process, std::vector<Gtk::TreeModel::Path> rows);
+    void on_quit(Process *process, int status, std::vector<Gtk::TreeModel::Path> rows);
+
+};
+#endif /* Channel_hpp */
diff --git a/gtk_channels/ContextMenu.cpp b/gtk_channels/ContextMenu.cpp
new file mode 100644
index 0000000..ffa658e
--- /dev/null
+++ b/gtk_channels/ContextMenu.cpp
@@ -0,0 +1,40 @@
+//
+//  ContextMenu.cpp
+//  gtk_channels
+//
+//  Created by Arnaud Blanchard on 13/10/2019.
+//
+
+#include "ChannelWidget.hpp"
+#include "blc_image.h"
+
+using namespace Gtk;
+using namespace std;
+
+ContextMenu::ContextMenu(string program_dir):program_dir(move(program_dir)){}
+
+void ContextMenu::add_item(string name, bool shortcut, function <void()> action){
+    MenuItem *item=new MenuItem(name, shortcut);
+    append(*item);
+    item->signal_activate().connect(action);
+}
+
+void ContextMenu::add_separator(){
+    SeparatorMenuItem *separator_item = new SeparatorMenuItem();
+    append(*separator_item);
+}
+
+void ContextMenu::add_program(char const *menu_name, std::string program_name, initializer_list<const char*> const optional_arguments, function <void(Process *process)> after_launch){
+    auto item=new MenuItem(menu_name);
+    auto args_vec=make_shared<vector<const char*>>(optional_arguments); //This will be destroyed by process
+    append(*item);
+    
+    item->signal_activate().connect([this, args_vec, program_name, after_launch]{
+        args_vec->reserve(args_vec->size()+selected_channels.size());
+        for(auto const &channel:selected_channels){
+            args_vec->push_back(strdup(channel->name));
+        }
+        auto process = new Process(program_dir, program_name, args_vec);
+        process->run(after_launch);
+    });
+}
diff --git a/gtk_channels/ContextMenu.hpp b/gtk_channels/ContextMenu.hpp
new file mode 100644
index 0000000..99815f1
--- /dev/null
+++ b/gtk_channels/ContextMenu.hpp
@@ -0,0 +1,32 @@
+//
+//  ContextMenu.hpp
+//  gtk_channels
+//
+//  Created by Arnaud Blanchard on 13/10/2019.
+//
+
+#ifndef ContextMenu_hpp
+#define ContextMenu_hpp
+
+#include <gtkmm.h>
+#include <stdio.h>
+#include "blc_channel.h"
+#include <string>
+#include <vector>
+
+#include "Process.h"
+
+
+struct ContextMenu:Gtk::Menu{
+    std::string program_dir;
+    std::vector<blc_channel*> selected_channels;
+    Glib::RefPtr<Gtk::TreeSelection> row_selection;
+    
+    ContextMenu(std::string program_dir);
+    void after_launch(Process *process);
+    void add_item(std::string name, bool shortcut, std::function<void ()>action);
+    void add_separator();
+    void add_program(char const *menu_name, std::string program_name, std::initializer_list<const char*> const optional_args, std::function <void(Process*)> after_launch);
+};
+
+#endif /* ContextMenu_hpp */
diff --git a/gtk_channels/Process.cpp b/gtk_channels/Process.cpp
index 75d8579..4bb8bf0 100644
--- a/gtk_channels/Process.cpp
+++ b/gtk_channels/Process.cpp
@@ -9,33 +9,55 @@
 #include "blc_core.h"
 #include <errno.h>
 #include <unistd.h> //execve
-
 #include <thread>
-
 #include <vte/vte.h>
 #include <sys/pipe.h>
 #include <gtkmm.h>
+#include <signal.h>
 
 
 using namespace std;
 using namespace Gtk;
 
-void TerminalSpawnAsyncCallback (VteTerminal *terminal, GPid pid, GError *error, gpointer user_data){
-    
-    char **argv=(char**)user_data;
+static void TerminalSpawnAsyncCallback (VteTerminal *terminal, GPid pid, GError *error, gpointer user_data){
+    Process *process=(Process*)user_data;
     if (error){
         fprintf(stderr, "Process error: '%s'\n", error->message);
-        for (auto arg=argv; *arg!=nullptr; arg++){
-            fprintf(stderr, "%s ", *arg);
+        for (auto const &arg : process->arg_vector){
+            fprintf(stderr, "%s ", arg);
         }
         fprintf(stderr, "\n");
     }
-    else fprintf(stderr, "Success pid %d\n", pid);
+    else {
+        process->pid=pid;
+        fprintf(stderr, "Success pid %d\n", pid);
+        for (auto const &arg : process->arg_vector){
+                   fprintf(stderr, "%s ", arg);
+               }
+               fprintf(stderr, "\n");
+        process->after_launch(process);
+    }
 };
 
-Process::Process(string const &directory, string const &program):directory(directory),program_name(program){
-    command_path=directory+program;
+static void after_exectution(VteTerminal *vteterminal, gint status, gpointer user_data){
+    auto process=(Process*)user_data;
+    process->on_quit_callback(status);
+}
+
+
+void Process::on_quit(function<void(int exit_status)> on_quit){
+    on_quit_callback=on_quit;
+    g_signal_connect(G_OBJECT(terminal), "child-exited", G_CALLBACK(after_exectution) , this);
+}
+
+
+Process::Process(string const directory, string const program, shared_ptr<vector<const char*>> const optional_arguments):directory(move(directory)),program_name(move(program)){
+    command_path=directory+"/"+program;
     arg_vector.emplace_back(command_path.data());
+    
+    for( auto &arg : *optional_arguments){
+        arg_vector.emplace_back(arg);
+    }
 };
 
 Process::~Process(){
@@ -44,25 +66,27 @@ Process::~Process(){
     }
 };
 
-void Process::add_arg(string const &arg){
-    arg_vector.emplace_back(strdup(arg.data()));
-}
-
-void Process::run(){
-    
+void Process::run(function <void(Process *process)> after_launch){
+    this->after_launch=after_launch;
     terminal=VTE_TERMINAL(vte_terminal_new());
-    arg_vector.emplace_back(nullptr);
-    
-    vte_terminal_spawn_async(terminal, VTE_PTY_DEFAULT , nullptr, (char**)arg_vector.data(), nullptr, G_SPAWN_DEFAULT, nullptr, nullptr, nullptr, 1000, nullptr, TerminalSpawnAsyncCallback, arg_vector.data());
-    
-   
+    arg_vector.emplace_back(nullptr); //note the end of the list
+    vte_terminal_spawn_async(terminal, VTE_PTY_DEFAULT , nullptr, (char**)arg_vector.data(), nullptr, G_SPAWN_DEFAULT, nullptr, nullptr, nullptr, 1000, nullptr, TerminalSpawnAsyncCallback, (void*)this);
 }
 
 void Process::display_terminal(){
+    Widget *mm_terminal=Glib::wrap(GTK_WIDGET(terminal)); //This is because vte is not in C++/gtkmm
     
-    Widget *mm_terminal=Glib::wrap(GTK_WIDGET(terminal));
-          
-    terminal_window=new Window();
-    terminal_window->add(*mm_terminal);
+    if (terminal_window==nullptr){
+        terminal_window=new Window();
+        terminal_window->add(*mm_terminal);
+        terminal_window->signal_delete_event().connect([this](GdkEventAny*){
+            terminal_window->hide();
+            return true;
+        });
+    }
     terminal_window->show_all();
 }
+
+void Process::signal(int signal) const{
+    kill(pid, signal);
+}
diff --git a/gtk_channels/Process.h b/gtk_channels/Process.h
index fd44a8b..98b57f8 100644
--- a/gtk_channels/Process.h
+++ b/gtk_channels/Process.h
@@ -15,26 +15,27 @@
 #include <gtkmm.h>
 #include <vte/vte.h>
 
-
 struct Process{
     pid_t pid;
 
-    int stdin_fileno, stdout_fileno, stderr_fileno;
     std::string name;
     std::string directory;
     std::string program_name;
     std::string command_path;
-    std::string channel;
-    std::thread *run_thread;
     std::vector<const char*> arg_vector;
     VteTerminal *terminal;
-    Gtk::Window *terminal_window;
+    Gtk::Window *terminal_window=nullptr;
+    std::function<void(Process *)> after_launch;
+    std::function<void(int exit_status)> on_quit_callback;
+
     
-    Process(std::string const &directory, std::string const &program_name);
+    Process(std::string const directory, std::string const program_name, std::shared_ptr<std::vector<const char*>> const optional_arguments={});
     ~Process();
-
-    void add_arg(std::string const &arg);
-    void run();
+    
+    void run(std::function<void(Process*)> on_launch);
     void display_terminal();
+    void on_quit(std::function<void(int exit_status)> on_quit);
+    void signal(int signal) const;
+
 };
 #endif /* Process_hpp */
diff --git a/gtk_channels/gtk_channels.cpp b/gtk_channels/gtk_channels.cpp
index 699fa8a..2327f0d 100644
--- a/gtk_channels/gtk_channels.cpp
+++ b/gtk_channels/gtk_channels.cpp
@@ -2,14 +2,14 @@
 #include "blc_program.h"
 #include "libgen.h"
 #include <gtkmm.h>
-#include "Channel.hpp"
+#include "ChannelWidget.hpp"
 #include "thread"
 
 using namespace std;
 using namespace Gtk;
 
 struct App:Application{
-   // App(int &argc, char **&argv);
+    App(int &argc, char **&argv);
     ApplicationWindow main_window;
     GtkScrolledWindow channel_window;
     ChannelWidget channel_widget;
@@ -19,9 +19,8 @@ struct App:Application{
     void on_activate() override;
 };
 
-/*App::App(int &argc, char **&argv):Application(argc, argv){
-    Glib::set_application_name("e_bash");
-}*/
+App::App(int &argc, char **&argv):Application(argc, argv){
+}
 
 void App::on_startup(){
     Application::on_startup();
@@ -40,7 +39,7 @@ void App::on_activate(){
 
 int main(int argc, char **argv)
 {
-    App app;//(argc, argv);
+    App app(argc, argv); //AArgc argv allow to interpret option --g-fatal-warnings for debugging
     
     blc_program_set_description("Graphical interface for blaar programs");
     blc_program_init(&argc, &argv, blc_quit);
-- 
GitLab