diff --git a/gtk_channels/CMakeLists.txt b/gtk_channels/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b2f390d9aca6d06b39548a201640bb6dab194f64 --- /dev/null +++ b/gtk_channels/CMakeLists.txt @@ -0,0 +1,21 @@ +# Set the minimum version of cmake required to build this project + +cmake_minimum_required(VERSION 2.6) +project(gtk_channels) + +find_package(blc_channel REQUIRED) +find_package(blc_program REQUIRED) +find_package(blc_process REQUIRED) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK3 REQUIRED gtk+-3.0) +pkg_check_modules(GTK3_SOURCE_VIEW REQUIRED gtksourceview-4) +pkg_check_modules(GTKMM gtkmm-3.0 REQUIRED) +pkg_check_modules(VTE3 vte-2.91 REQUIRED) #libvtemm is not yet common +#pkg_check_modules(GTKMMPLPLOT REQUIRED gtkmm-plplot-2.0) + +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) +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 new file mode 100644 index 0000000000000000000000000000000000000000..f502fcb26689a680179bcd85406279dd9a53ed1c --- /dev/null +++ b/gtk_channels/Channel.cpp @@ -0,0 +1,224 @@ +// +// 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/Channel.hpp b/gtk_channels/Channel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..098345b5d24f681c1aa144fdae66834a0141c3b6 --- /dev/null +++ b/gtk_channels/Channel.hpp @@ -0,0 +1,25 @@ +// +// Channel.hpp +// e_bash +// +// Created by Arnaud Blanchard on 29/09/2019. +// + +#ifndef Channel_hpp +#define Channel_hpp + +#include <gtkmm.h> + + + +struct ChannelWidget:Gtk::TreeView{ + std::string blaar_dir; + Glib::RefPtr<Gtk::TreeStore> tree_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/gtk_channels/Graph.cpp b/gtk_channels/Graph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..82c96b177a9fc587b1449abc98d62479683f43f5 --- /dev/null +++ b/gtk_channels/Graph.cpp @@ -0,0 +1,9 @@ +// +// Graph.cpp +// e_bash +// +// Created by Arnaud Blanchard on 25/08/2017. +// +// + +#include "Graph.h" diff --git a/gtk_channels/Link.cpp b/gtk_channels/Link.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ecf57df018f35e51b4a5d882741e558f0ca6329 --- /dev/null +++ b/gtk_channels/Link.cpp @@ -0,0 +1,146 @@ +// +// Link.cpp +// e_bash +// +// Created by Arnaud Blanchard on 30/08/2017. +// +// + +#include "common.h" +#include "Link.h" +#include "Process_old.h" +#include <blc_core.h> //APPEND_ITEM + +#include <gtk/gtk.h> + +Link::Link(Script *script, Process_old *input_process, char *output_channel, Process_old *output_process, char *input_channel, int sync):sync(sync), script(script), selected(0), input_process(input_process), output_process(output_process){ + Link *link=this; + // printf("Input program: %s nodes:%d:%s\n", input_process->program->name, output_process->program->output_channels_nb, output_channel); + // printf("Out program: %s nodes:%d:%s\n", output_process->program->name, output_process->program->input_channels_nb, input_channel); + + asprintf(&name, "link_%d", script->links_nb); + + // edge=agedge(current_script->main_graph, input_process->node, output_process->node, name, 1); +// agsafeset(edge, (char*)"label", (char*)name, (char*)""); + if (sync==0) { + // agsafeset(edge, (char*)"style", (char*)"dashed", (char*)""); + // agsafeset(edge, (char*)"constraint", (char*)"false", (char*)""); + + } + + /* agsafeset(edge,(char*)"tailport",output_channel, (char*)""); + agsafeset(edge,(char*)"headport",input_channel, (char*)"");*/ + APPEND_ITEM(&script->links, &script->links_nb, &link); + if (input_process->output_links_nb==0 && output_process->input_links_nb==0) output_process->input_process=input_process; + APPEND_ITEM(&input_process->output_links, &input_process->output_links_nb, &link); + APPEND_ITEM(&output_process->input_links, &output_process->input_links_nb, &link); +} + +Link::~Link(){ + Link *tmp_link=this; + + + + REMOVE_ITEM(&input_process->output_links, &input_process->output_links_nb, &tmp_link); + REMOVE_ITEM(&output_process->input_links, &output_process->input_links_nb, &tmp_link); + if (input_process->output_links_nb==0 && output_process->input_links_nb==0) output_process->input_process=NULL; + + + if (selected) REMOVE_ITEM(&script->selected_links,&script->selected_links_nb, &tmp_link); + REMOVE_ITEM(&script->links,&script->links_nb, &tmp_link); + // agdelete(script->main_graph, edge); + free(name); +} + +Link *Link::dialog(Script *script, Process_old *input_process, Process_old *output_process){ + char **channel; + Link *link; + + GtkWidget *dialog, *box, *in_combobox, *out_combobox, *sync_checkbox; + GtkWidget *grid; + + dialog=gtk_dialog_new_with_buttons("New link", NULL, GTK_DIALOG_MODAL, "_OK", GTK_RESPONSE_ACCEPT, "_Cancel", GTK_RESPONSE_REJECT, NULL); + box=gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + + grid=gtk_grid_new(); + + in_combobox=gtk_combo_box_text_new(); + out_combobox=gtk_combo_box_text_new(); + sync_checkbox=gtk_check_button_new(); + + gtk_grid_attach(GTK_GRID(grid), gtk_label_new(input_process->program->name), 0, 0, 1, 1); + FOR_EACH(channel, input_process->program->output_channels, input_process->program->output_channels_nb) gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(in_combobox), NULL, *channel); + + gtk_grid_attach(GTK_GRID(grid), gtk_label_new(output_process->program->name), 0, 1, 1, 1); + FOR_EACH(channel, output_process->program->input_channels, output_process->program->input_channels_nb) gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(out_combobox), NULL, *channel); + + gtk_grid_attach(GTK_GRID(grid), gtk_label_new("Synchronize"), 0, 2, 1, 1); + + + gtk_combo_box_set_active(GTK_COMBO_BOX(in_combobox), 0); + gtk_combo_box_set_active(GTK_COMBO_BOX(out_combobox), 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sync_checkbox), 1); + + gtk_grid_attach(GTK_GRID(grid), in_combobox, 1, 0, 1, 1); + gtk_grid_attach(GTK_GRID(grid), out_combobox, 1, 1, 1, 1); + gtk_grid_attach(GTK_GRID(grid), sync_checkbox, 1, 2, 1, 1); + + + gtk_container_add(GTK_CONTAINER(box), grid); + gtk_widget_show_all(dialog); + + switch (gtk_dialog_run(GTK_DIALOG(dialog))) + { + case GTK_RESPONSE_ACCEPT: + link=new Link(script, input_process, gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(in_combobox)), + output_process, gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(out_combobox)), + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_checkbox))); + break; + case GTK_RESPONSE_CANCEL: + link=NULL; + break; + } + gtk_widget_destroy(dialog); + + return link; +} + + +void Link::edit(){ + + dialog(script, input_process, output_process); +} + +void Link::select(){ + Link *tmp_link; + if (selected==0){ +// agsafeset( edge, (char*) "penwidth", (char*) "3", (char*)"1"); + + // agsafeset( edge, (char*) "color", (char*) "red", "black"); + tmp_link=this; + APPEND_ITEM(&script->selected_links, &script->selected_links_nb, &tmp_link); + selected=1; + } +} + +void Link::unselect(){ + Link *tmp_link; + + if (selected){ + /* if (sync) agset((void*) edge, (char*) "style", (char*) "dashed"); + else agset((void*) edge, (char*) "style", (char*) "solid");*/ + // agsafeset((void*) edge, (char*) "color", (char*) "black", "black"); +// agsafeset( edge, (char*) "penwidth", (char*) "1", (char*)"1"); + + + tmp_link=this; + + REMOVE_ITEM(&script->selected_links, &script->selected_links_nb, &tmp_link); + selected=0; + } + +} + + + + diff --git a/gtk_channels/Link.h b/gtk_channels/Link.h new file mode 100644 index 0000000000000000000000000000000000000000..98cc30db88dc8b8a859eae6fa779cd4e8998c2f1 --- /dev/null +++ b/gtk_channels/Link.h @@ -0,0 +1,38 @@ +// +// Link.hpp +// e_bash +// +// Created by Arnaud Blanchard on 30/08/2017. +// +// + +#ifndef LINK_H +#define LINK_H + +#include "Process_old.h" +#include "Script.h" + +class Script; +class Process_old; + +class Link{ + int sync; + Script *script; + +public: + char *name; + int selected; + Process_old *input_process, *output_process; + + + Link(Script *script, Process_old *input_process, char *output_channel, Process_old *output_process, char *input_channel, int sync); + ~Link(); + + void edit(); + void select(); + void unselect(); + + static Link *dialog(Script *script, Process_old *input_process, Process_old *output_process); +}; + +#endif /* Link_hpp */ diff --git a/gtk_channels/Menu.cpp b/gtk_channels/Menu.cpp new file mode 100644 index 0000000000000000000000000000000000000000..930bbab8344594dd414647a4035b39779f76d34d --- /dev/null +++ b/gtk_channels/Menu.cpp @@ -0,0 +1,131 @@ +// +// menu.cpp +// e_bash +// +// Created by Arnaud Blanchard on 23/08/2017. +// +// + +#include "Script.h" //current_script +#include "Menu.h" +#include "common.h" +#include "blc_program.h" +#include "parse_help.h" +#include <stdio.h> +#include <gtk/gtk.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <libgen.h> //basename + +void action_input_cb(GtkWidget *widget, Program *menu_action) +{ + char *argv[2]; + char path[PATH_MAX+1]; + (void)widget; + + // SPRINTF(path, "%s/%s", blaar_bin_dir, menu_action->program_name); + + argv[0]=basename(path); + argv[1]=NULL; + // execute_process_with_iter(create_command_line(dirname(path), argv)); +} + +static void add_process_cb(GtkWidget *widget, Program *program){ + (void)widget; + + current_script->add_process(program, NULL); +} + +static void process_dialog_cb(GtkWidget *widget, Program *program){ + (void)widget; + + current_script->add_process(program, program->argv_dialog()); +} + +static int possible_default(int positional_arguments_nb, PROGRAM_TYPE type){ + return ((positional_arguments_nb==0 && type==INPUT_PROGRAM) || (positional_arguments_nb==1 && type==FUNCTION_PROGRAM)); +} + +/**Read the list of programs in directory_str and add it to the menu if the type (INPUT_PROGRAM, FUNCTION_PROGRAM) match. + */ +static void add_directory_programs(GtkWidget *menu_shell, char const *directory_str, PROGRAM_TYPE type){ + + GtkWidget *default_item, *action_button, *menu, *menu_item; + DIR *directory; + struct stat status; + struct dirent program_dirent, *result; + + char const *description; + char token[NAME_MAX+1]; + int positional_arguments_nb, optional_arguments_nb; + Program *program; + struct blc_optional_argument *optional_arguments; + struct blc_positional_argument *positional_arguments; + + SYSTEM_ERROR_CHECK(directory = opendir(directory_str), NULL, "Opening dir '%s'.", directory_str); + do{ + if (readdir_r(directory, &program_dirent, &result) != 0) EXIT_ON_ERROR("readdir_r error parsing dir."); + else if (result){ + if (result->d_type==DT_REG){ + + if (((type==INPUT_PROGRAM) && ( strncmp(result->d_name, "i_", 2)==0)) || ((type==FUNCTION_PROGRAM) && (( strncmp(result->d_name, "f_", 2)==0) || ( strncmp(result->d_name, "o_", 2)==0)))){ + fstat(result->d_reclen, &status); + description=blc_parse_help(directory_str, result->d_name, &optional_arguments, &optional_arguments_nb, &positional_arguments, &positional_arguments_nb, NULL); + + + if (positional_arguments_nb || optional_arguments_nb>1){ + program=new Program(result->d_name, type, description, optional_arguments, optional_arguments_nb, positional_arguments, positional_arguments_nb); + + if (possible_default(positional_arguments_nb, type)) + { + menu=gtk_menu_new(); + menu_item = gtk_menu_item_new_with_label(result->d_name); + + gtk_container_add(GTK_CONTAINER(menu_shell), menu_item); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu); + default_item = gtk_menu_item_new_with_label("default"); + action_button = gtk_menu_item_new_with_label("options ..."); + gtk_container_add(GTK_CONTAINER(menu), default_item); + gtk_container_add(GTK_CONTAINER(menu), action_button); + + g_signal_connect(G_OBJECT(default_item), "activate", G_CALLBACK(add_process_cb), program); + } + else{ + SPRINTF(token, "%s ...",result->d_name); + action_button = gtk_menu_item_new_with_label(token); + gtk_container_add(GTK_CONTAINER(menu_shell), action_button); + + } + g_signal_connect(G_OBJECT(action_button), "activate", G_CALLBACK(process_dialog_cb), program); + } + else{ + if (possible_default(positional_arguments_nb, type)){ + default_item = gtk_menu_item_new_with_label(result->d_name); + program=new Program(result->d_name, type, description); + gtk_container_add(GTK_CONTAINER(menu_shell), default_item); + g_signal_connect(G_OBJECT(default_item), "activate", G_CALLBACK(add_process_cb), program); + } + } + } + } + } + }while(result); + closedir(directory); +} + +BlaarMenu::BlaarMenu(PROGRAM_TYPE menu_type){ + + widget=gtk_menu_new(); + + if (menu_type==INPUT_PROGRAM){ + add_directory_programs(widget, blaar_bin_dir, INPUT_PROGRAM); + add_directory_programs(widget, blaar_scripts_dir, INPUT_PROGRAM); + } + else if (menu_type==FUNCTION_PROGRAM){ + add_directory_programs(widget, blaar_bin_dir, FUNCTION_PROGRAM); + add_directory_programs(widget, blaar_scripts_dir, FUNCTION_PROGRAM); + } + else EXIT_ON_ERROR("Wrong option '%d'", menu_type); + gtk_widget_show_all(widget); +} + diff --git a/gtk_channels/Menu.h b/gtk_channels/Menu.h new file mode 100644 index 0000000000000000000000000000000000000000..b95214dbea5cd92918cf0bc9c7d6f8c3b254c581 --- /dev/null +++ b/gtk_channels/Menu.h @@ -0,0 +1,21 @@ +// +// Menu.h +// e_bash +// +// Created by Arnaud Blanchard on 25/08/2017. +// +// + +#ifndef MENU_H +#define MENU_H + +#include "common.h" +#include <gtk/gtk.h> + +struct BlaarMenu{ + + BlaarMenu(PROGRAM_TYPE menu_type); + GtkWidget *widget; +}; + +#endif diff --git a/gtk_channels/Plot.cpp b/gtk_channels/Plot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64975352edded099353005281bc3fea77bec7900 --- /dev/null +++ b/gtk_channels/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/gtk_channels/Plot.hpp b/gtk_channels/Plot.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1bd8f03a02e9d35cef83a4660d44759a36a0feb7 --- /dev/null +++ b/gtk_channels/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/Process.cpp b/gtk_channels/Process.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75d857959eb9fa600f0af5c8726e2e937a1f14f8 --- /dev/null +++ b/gtk_channels/Process.cpp @@ -0,0 +1,68 @@ +// +// Process.cpp +// e_bash +// +// Created by Arnaud Blanchard on 30/08/2017. +// +// +#include "Process.h" +#include "blc_core.h" +#include <errno.h> +#include <unistd.h> //execve + +#include <thread> + +#include <vte/vte.h> +#include <sys/pipe.h> +#include <gtkmm.h> + + +using namespace std; +using namespace Gtk; + +void TerminalSpawnAsyncCallback (VteTerminal *terminal, GPid pid, GError *error, gpointer user_data){ + + char **argv=(char**)user_data; + if (error){ + fprintf(stderr, "Process error: '%s'\n", error->message); + for (auto arg=argv; *arg!=nullptr; arg++){ + fprintf(stderr, "%s ", *arg); + } + fprintf(stderr, "\n"); + } + else fprintf(stderr, "Success pid %d\n", pid); +}; + +Process::Process(string const &directory, string const &program):directory(directory),program_name(program){ + command_path=directory+program; + arg_vector.emplace_back(command_path.data()); +}; + +Process::~Process(){ + for (char const *&arg: arg_vector ){ + free(&arg); + } +}; + +void Process::add_arg(string const &arg){ + arg_vector.emplace_back(strdup(arg.data())); +} + +void Process::run(){ + + 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()); + + +} + +void Process::display_terminal(){ + + Widget *mm_terminal=Glib::wrap(GTK_WIDGET(terminal)); + + terminal_window=new Window(); + terminal_window->add(*mm_terminal); + terminal_window->show_all(); +} diff --git a/gtk_channels/Process.h b/gtk_channels/Process.h new file mode 100644 index 0000000000000000000000000000000000000000..fd44a8b0a06ec495ba298f286d4561981391b4fb --- /dev/null +++ b/gtk_channels/Process.h @@ -0,0 +1,40 @@ +// +// Process.hpp +// e_bash +// +// Created by Arnaud Blanchard on 30/08/2017. +// +// + +#ifndef PROCESS_H +#define PROCESS_H + +#include <vector> +#include <string> +#include <thread> +#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; + + Process(std::string const &directory, std::string const &program_name); + ~Process(); + + void add_arg(std::string const &arg); + void run(); + void display_terminal(); +}; +#endif /* Process_hpp */ diff --git a/gtk_channels/Program.cpp b/gtk_channels/Program.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a209e32166c792836f4e44468e1ad73a3de696b --- /dev/null +++ b/gtk_channels/Program.cpp @@ -0,0 +1,294 @@ +// +// Process.cpp +// e_bash +// +// Created by Arnaud Blanchard on 29/08/2017. +// +// + +#include "Program.h" + +#include <blc_core.h> +#include <string.h> + + +static void toggled_option_cb(GtkToggleButton *button, gpointer widget){ + gtk_widget_set_sensitive(GTK_WIDGET(widget), gtk_toggle_button_get_active(button)); +} + +Program::Program(char const *program_name, PROGRAM_TYPE type, char const *description): description(description), type(type){ + this->name=strdup(program_name); +} + +Program::Program(char const*program_name, PROGRAM_TYPE type, char const*description, blc_optional_argument *optional_arguments, int optional_arguments_nb, blc_positional_argument *positional_arguments, int positional_arguments_nb) +: description(description), optional_arguments(optional_arguments), optional_arguments_nb(optional_arguments_nb), positional_arguments(positional_arguments), positional_arguments_nb(positional_arguments_nb), type(type) +{ + blc_positional_argument *positional_argument; + blc_optional_argument *optional_argument; + char const *port_name; + + input_channels_nb=0; + input_channels=NULL; + output_channels_nb=0; + output_channels=NULL; + + this->name=strdup(program_name); + + FOR_EACH(positional_argument, positional_arguments, positional_arguments_nb){ + if (strcmp(positional_argument->name, "blc_channel-in")==0){ + asprintf((char**)&port_name, "%d", input_channels_nb); + APPEND_ITEM(&input_channels, &input_channels_nb, &port_name); + } + else if (strcmp(positional_argument->name, "blc_channel-out")==0){ + asprintf((char**)&port_name, "%d", output_channels_nb); + APPEND_ITEM(&output_channels, &output_channels_nb, &port_name); + } + } + + FOR_EACH(optional_argument, optional_arguments, optional_arguments_nb){ + if (optional_argument->value){ + if (strcmp(optional_argument->value, "blc_channel-in")==0){ + if (optional_argument->longname) port_name=optional_argument->longname; + else port_name=optional_argument->shortname; + APPEND_ITEM(&input_channels, &input_channels_nb, &port_name); + } + else if (strcmp(optional_argument->value, "blc_channel-out")==0){ + if (optional_argument->longname) port_name=optional_argument->longname; + else port_name=optional_argument->shortname; + APPEND_ITEM(&output_channels, &output_channels_nb, &port_name); + } + } + } +} + +static void remove_args(char ***argv, char **arg_pt, int args_to_remove_nb){ + int i, argc; + + for (argc=0; argv[argc]!=NULL; argc++); //We ount current arg_nb + + for(i=0; arg_pt[i+args_to_remove_nb]!=NULL; i++){ + arg_pt[i]=arg_pt[i+args_to_remove_nb]; + } + arg_pt[i]=NULL; + + MANY_REALLOCATIONS(argv, argc-args_to_remove_nb+1); +} + +static char const* find_option(blc_optional_argument *option, char ***argv){ + + char **arg_pt; + char const *arg=NULL; + + for (arg_pt=*argv; *arg_pt!=NULL; arg_pt++){ + if (strncmp(*arg_pt, "--", 2)== 0){ //It may be a long option + if (strncmp((*arg_pt)+2, option->longname, strlen(option->longname))==0) { + if (option->value) { + arg=strpbrk(*arg_pt, " ="); + while(arg[0]==' ') arg++; //Remove spaces + remove_args(argv, arg_pt, 1); + break; + } + else { + arg="1"; //It is a flag + remove_args(argv, arg_pt, 1); + break; + } + } + } + else if ((*arg_pt)[0]=='-'){ //It may be a short option + if ((*arg_pt)[1]==option->shortname[0]){ + if (option->value){ + arg=*arg_pt+2; //We remove first two characters ( -<letter> ) and remove spaces after if there are + while(arg[0]==' ') arg++; + remove_args(argv, arg_pt, 1); + break; + } + else { + arg="1"; //It is a flag + remove_args(argv, arg_pt, 1); + break; + } + } + } + } + return arg; //option not found +} + +/**Display a dialog menu with all the possible options and parameters + If exisiting_argv is not NULL, the default options are already filled + */ +char **Program::argv_dialog(char **existing_argv) +{ + blc_optional_argument *option; + char const *pos; + char const *value; + char *arg; + char const *existing_value; + char **argv=NULL; + int argc, existing_id; + int index; + + char token[NAME_MAX+1]; + char text[NAME_MAX+1]; + int line_id; + + GtkWidget **optional_displays=NULL, **positional_displays; + int token_size; + GtkWidget *dialog, *box; + GtkWidget *parameter_label, *description_label, *grid, *help_label; + int i; + + // dialog=gtk_dialog_new_with_buttons(name, main_window->gobj(), GTK_DIALOG_MODAL, "_OK", GTK_RESPONSE_ACCEPT, "_Cancel", GTK_RESPONSE_REJECT, NULL); + box=gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + description_label=gtk_label_new(description); + grid=gtk_grid_new(); + + line_id=0; + + optional_displays=MANY_ALLOCATIONS(optional_arguments_nb, GtkWidget*); + FOR(i, optional_arguments_nb) { + option=&optional_arguments[i]; + + if (existing_argv) existing_value=find_option(option, &existing_argv); + else existing_value=NULL; + + if (option->value==NULL) optional_displays[i]=gtk_label_new(""); //This is a flag + else { + if (strchr(option->value, '|') || existing_value){ + index=0; + pos=option->value; + optional_displays[i]=gtk_combo_box_text_new_with_entry(); + token_size=0; + sscanf(pos, "%*[ =]%n", &token_size); //remove spaces or equal + pos+=token_size; + existing_id=-1; + while(sscanf(pos, "%[^| ]%n", token, &token_size)==1) { + pos+=token_size; + token_size=0; + sscanf(pos, "%*[| ]%n", &token_size); //remove spaces or equal + pos+=token_size; + gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(optional_displays[i]), NULL, token); + if ((existing_value) && (strcmp(token, existing_value)==0)) gtk_combo_box_set_active(GTK_COMBO_BOX(optional_displays[i]), index); + index++; + } + + if (existing_value==NULL) gtk_combo_box_set_active(GTK_COMBO_BOX(optional_displays[i]), 0); + else { + if (gtk_combo_box_get_active(GTK_COMBO_BOX(optional_displays[i]))==-1){ + gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(optional_displays[i]), NULL, existing_value); + gtk_combo_box_set_active(GTK_COMBO_BOX(optional_displays[i]), index); + } + } + } + else { + optional_displays[i]=gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(optional_displays[i]), option->value); + } + } + + if (option->longname) SPRINTF(text, "--%s", option->longname); + else SPRINTF(text, "-%s", option->shortname); + + parameter_label=gtk_toggle_button_new_with_label(text); + gtk_widget_set_hexpand(parameter_label, 1); + gtk_widget_set_halign(parameter_label, GTK_ALIGN_START); + gtk_grid_attach(GTK_GRID(grid), parameter_label, 0, line_id, 1, 1); + help_label=gtk_label_new(option->help); + gtk_widget_set_halign(help_label, GTK_ALIGN_START); + if (optional_displays[i]) gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(optional_displays[i]), 1, line_id, 1, 1); + // else optional_displays[i]=help_label; + + if (existing_value==NULL) gtk_widget_set_sensitive(GTK_WIDGET(optional_displays[i]), FALSE); + else gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(parameter_label), TRUE); + + g_signal_connect(G_OBJECT(parameter_label), "toggled", G_CALLBACK(toggled_option_cb), optional_displays[i]); + + gtk_grid_attach(GTK_GRID(grid), help_label, 2, line_id, 1, 1); + line_id++; + } + + positional_displays=MANY_ALLOCATIONS(positional_arguments_nb, GtkWidget*); + FOR(i, positional_arguments_nb) { + parameter_label=gtk_label_new(positional_arguments[i].name); + positional_displays[i]=gtk_entry_new(); + + if (existing_argv && existing_argv[1]) { + gtk_entry_set_text(GTK_ENTRY(positional_displays[i]), existing_argv[1]); //0 is the program name + remove_args(&existing_argv, &existing_argv[1], 1); + } + else if (positional_arguments[i].value) gtk_entry_set_text(GTK_ENTRY(positional_displays[i]), positional_arguments[i].value); + + gtk_widget_set_hexpand(parameter_label, 0); + + gtk_widget_set_halign(parameter_label, GTK_ALIGN_START); + gtk_grid_attach(GTK_GRID(grid), parameter_label, 0, line_id, 1, 1); + gtk_grid_attach(GTK_GRID(grid), positional_displays[i], 1, line_id, 1, 1); + line_id++; + } + + gtk_label_set_line_wrap (GTK_LABEL(description_label), 1); + gtk_widget_set_halign(description_label, GTK_ALIGN_START); + + gtk_container_add(GTK_CONTAINER(box), description_label); + gtk_container_add(GTK_CONTAINER(box), grid); + + gtk_widget_show_all(dialog); + + switch (gtk_dialog_run(GTK_DIALOG(dialog))) + { + case GTK_RESPONSE_ACCEPT: + // SPRINTF(path, "%s/%s", functions_dir, program_name); + argv=NULL; + argc=0; + value=strdup(name); + APPEND_ITEM(&argv, &argc, &value); + FOR(i, optional_arguments_nb){ + if( gtk_widget_get_sensitive(GTK_WIDGET(optional_displays[i]))) { + if (GTK_IS_ENTRY(optional_displays[i])) value=strdup(gtk_entry_get_text(GTK_ENTRY(optional_displays[i]))); + else if (GTK_IS_COMBO_BOX_TEXT(optional_displays[i])) value=strdup(gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(optional_displays[i]))); + else value=NULL; //It is a flag + + if (optional_arguments[i].shortname){ + if (value==NULL) SYSTEM_ERROR_CHECK(asprintf(&arg, "-%s", optional_arguments[i].shortname), -1, NULL); + else{ + if (strchr(value, ' ')) SYSTEM_ERROR_CHECK(asprintf(&arg, "-%s\"%s\"", optional_arguments[i].shortname, value), -1, NULL); //protect spaces + else SYSTEM_ERROR_CHECK(asprintf(&arg, "-%s%s", optional_arguments[i].shortname, value), -1, NULL); + } + } + else if (optional_arguments[i].longname){ + if (value==NULL) SYSTEM_ERROR_CHECK(asprintf(&arg, "--%s", optional_arguments[i].longname), -1, NULL); + else { + if (strchr(value, ' ')) SYSTEM_ERROR_CHECK(asprintf(&arg, "--%s=\"%s\"", optional_arguments[i].longname, value), -1, NULL); //protect spaces + else SYSTEM_ERROR_CHECK(asprintf(&arg, "--%s=%s", optional_arguments[i].longname, value), -1, NULL); + } + } + else EXIT_ON_ERROR("An option must have at least a short or long name"); + APPEND_ITEM(&argv, &argc, &arg); + } + } + + FOR(i, positional_arguments_nb){ + value=strdup(gtk_entry_get_text(GTK_ENTRY(positional_displays[i]))); + APPEND_ITEM(&argv, &argc, &value); + } + arg=NULL; + APPEND_ITEM(&argv, &argc, &arg); + // execute_process_with_iter(create_command_line(dirname(path), argv)); + + break; + case GTK_RESPONSE_CANCEL:break; + } + + gtk_widget_destroy (dialog); + + if (existing_argv){ + for (i=0; existing_argv[i]!=NULL; i++) free(existing_argv[i]); + } + FREE(existing_argv); + FREE(optional_displays); + FREE(positional_displays); + return argv; +} + + + diff --git a/gtk_channels/Program.h b/gtk_channels/Program.h new file mode 100644 index 0000000000000000000000000000000000000000..9f8cfede673e9836cb32c6aaa46b6de91d461a6c --- /dev/null +++ b/gtk_channels/Program.h @@ -0,0 +1,38 @@ +// +// Process.hpp +// e_bash +// +// Created by Arnaud Blanchard on 29/08/2017. +// +// + +#ifndef PROGRAM_H +#define PROGRAM_H + +#include "parse_help.h" +#include <stdlib.h> + +typedef enum {INPUT_PROGRAM, FUNCTION_PROGRAM } PROGRAM_TYPE; +typedef enum {INPUT_CHANNEL, OUTPUT_CHANNEL} CHANNEL_DIRECTION; + +class Program +{ +public: + + char const *description; + blc_optional_argument *optional_arguments; + int optional_arguments_nb; + blc_positional_argument *positional_arguments; + int positional_arguments_nb; + + PROGRAM_TYPE type; + char const *name; + char ** argv_dialog(char **argv=NULL); + char **input_channels, **output_channels, **input_channels_help, **output_channels_help; + int input_channels_nb, output_channels_nb; + + Program(char const*program_name, PROGRAM_TYPE type, char const*description); + Program(char const*program_name, PROGRAM_TYPE type, char const*description, blc_optional_argument *, int, blc_positional_argument *, int); +}; + +#endif /* PROGRAM_H */ diff --git a/gtk_channels/Script.cpp b/gtk_channels/Script.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e53878b40dc5050d0317242f1fc29fbc7259549 --- /dev/null +++ b/gtk_channels/Script.cpp @@ -0,0 +1,431 @@ + +#include "common.h" +#include "gtk_common.h" +#include <gtksourceview/gtksource.h> +#include "Script.h" +#include "blc_core.h" +#include <stdarg.h> +#include "Process_old.h" +#include "Link.h" + + +Script **Script::array=NULL; +int Script::nb=0; + +void status_push(char const *format, ...){ + va_list arguments; + char *message=NULL; + va_start(arguments, format); + vasprintf(&message, format, arguments); + gtk_statusbar_push(GTK_STATUSBAR(general_statusbar), statusbar_context_id, message); + FREE(message); + va_end(arguments); +} + +typedef enum { + ACTION_NOTHING = 0, ACTION_CREATE_LINK, ACTION_CREATE_LINK_AND_PROCESS, ACTION_SELECT_PROCESS +} ACTION_TYPE; + +ACTION_TYPE action; + +static int motion_gtk_cb(GtkWidget *widget, GdkEventButton *event, Script *script){ + Process_old *process; + (void) widget; + + if (script->initial_process) + { + process = script->get_process(event->x, event->y); + if (process == NULL){ + STATUS_PUSH("Release button to create a link from '%s' to a new process.", script->initial_process->name); + action = ACTION_CREATE_LINK_AND_PROCESS; + } + else if (process==script->initial_process){ + if (action == ACTION_CREATE_LINK_AND_PROCESS){ + STATUS_PUSH("Release button to create a recursive link to '%s'.", script->initial_process->name); + action = ACTION_CREATE_LINK; + } + } + else{ + action = ACTION_CREATE_LINK; + STATUS_PUSH("Release button to create a link from '%s' to '%s'.", script->initial_process->name, process->name); + } + } + return TRUE; +} + +static int button_press_gtk_cb(GtkWidget *widget, GdkEventButton *event, Script *script){ + Process_old *process=NULL; + Link *link=NULL; + + if (event->button == 1){ + process=script->get_process(event->x, event->y); + if (process==NULL) link=script->get_link(event->x, event->y); + + if (event->type == GDK_BUTTON_PRESS){ + action = ACTION_NOTHING; + + script->initial_link=link; + if (process){ + STATUS_PUSH(process->name); + script->initial_process=process; + } + else if (link) STATUS_PUSH(link->name); + } + else + if (event->type == GDK_2BUTTON_PRESS){ + if (process && process==script->initial_process){ + process->edit(); + } + else if (link && link==script->initial_link){ + link->edit(); + } + /* if (initial_link != NULL) initial_link->edit(); + else if (initial_group != NULL){ + strcpy(previous_group_name, initial_group->no_name); + if (initial_group->edit()) + { + agdelete(leto->main_graph, initial_group->ag_node); + initial_group->add_to_graph(leto->graphs[initial_group->ech_temps]); + leto->update_links_with_new_group_name(previous_group_name, initial_group->no_name); + + leto->update_layout(); + } + } + } + else if (event->type == GDK_BUTTON_PRESS) + {*/ + } + } + + return TRUE; +} + +static int button_release_gtk_cb(GtkWidget *widget, GdkEventButton *event, Script *script){ + Link *link; + Process_old *process; + char *output_channel=NULL, *input_channel=NULL; + Process_old *input_process, *output_process; + + int need_redraw = 0; + (void) widget; + + process=script->get_process(event->x, event->y); + if (process==NULL) link=script->get_link(event->x, event->y); + + if (event->button == 1){ + switch (action){ + case ACTION_NOTHING: + if ((process) && (process == script->initial_process)){ + if (event->state & GDK_CONTROL_MASK){ + if (process->selected) process->unselect(); + else process->select(); + need_redraw=1; + + } + else{ + script->unselect_all(); + process->select(); + need_redraw=1; + } + } + else + if (link && link== script->initial_link){ + if (event->state & GDK_CONTROL_MASK){ + if (link->selected) link->unselect(); + else link->select(); + need_redraw=1; + + } + else{ + script->unselect_all(); + link->select(); + need_redraw=1; + } + } else + { + if (script->unselect_all()) need_redraw=1; + } + + break; + + case ACTION_CREATE_LINK: + input_process=script->initial_process; + output_process=process; + + if (input_process->program->output_channels_nb==0) STATUS_PUSH("'%s' does not have ouput channel", input_process->program->name); + else if (input_process->program->output_channels_nb==1) output_channel=input_process->program->output_channels[0]; + + if (output_process->program->input_channels_nb==0) STATUS_PUSH("'%s' does not have input channel", output_process->program->name); + else if (output_process->program->input_channels_nb==1) input_channel=output_process->program->input_channels[0]; + + + if ((input_channel) && (output_channel)) { + script->add_link(input_process, output_channel, output_process, input_channel, 1); + } + else Link::dialog(script, input_process, output_process); + + action=ACTION_NOTHING; + + /* + new_link = script->add_link_dialog(initial_group, final_group); + if (new_link != NULL) + { + blc_say("link_created between %s and %s created.", initial_group->no_name, final_group->no_name); + set_selected_link(new_link); + } + else blc_say("Link creation canceled"); + set_selected_link(new_link); + type_group::unselect_all();*/ + break; + /* + case LETO_ACTION_CREATE_LINK_AND_GROUP: + blc_say("Creating a new group"); + new_group = script->add_group_dialog(); + if (new_group != NULL) + { + blc_say("A new group '%s' has been created, creating a new link.", new_group->no_name); + if (initial_group != NULL) new_link = script->add_link_dialog(initial_group, new_group); + else if (final_group != NULL) new_link = script->add_link_dialog(new_group, final_group); + if (new_link != NULL) + { + blc_say("A new link has been created."); + } + else blc_say("Link creation canceled.", new_group->no_name); + } + else blc_say("Group creation canceled"); + set_selected_link(new_link); + type_group::unselect_all(); + new_group->select(); + break;*/ + } + } + if (need_redraw) gtk_widget_queue_draw(script->widget); + script->initial_link=NULL; + script->initial_process=NULL; + + return TRUE; +} + + +void Script::delete_selection(){ + + Link **link_pt; + + // gvFreeLayout(gvc, main_graph); + while(selected_processes) { + + //We slect the links associated to the process in order to remove them + FOR_EACH(link_pt, links, links_nb) if ((link_pt[0]->input_process==selected_processes[0]) || (link_pt[0]->output_process==selected_processes[0])) link_pt[0]->select(); + delete selected_processes[0]; + } + while(selected_links) delete selected_links[0]; + generate_layout(); +} + + + +static int draw_callback (GtkWidget *widget, cairo_t *cr, Script *script){ + Process_old **process_pt; + +// gvRenderContext(script->gvc, script->main_graph, "png:cairo", cr); // FIXME: Trick The export of cairo to png stdout has been desactivated in the graphviz plugin (gvrender_pango.c) + return FALSE; +} + +void Script::init(){ + GtkWidget *drawing_area_scrolled_window, scrolled_window2, *label; + GtkTextBuffer text_buffer; + GtkSourceLanguage *language; + GtkSourceLanguageManager *manager; + + char const * const*ids; + int i; + + processes=NULL; + processes_nb=0; + links=NULL; + links_nb=0; + + selected_processes=NULL; + selected_processes_nb=0; + selected_links=NULL; + selected_links_nb=0; + + Script *script=this; //we cannot use &this + last_process=NULL; + + APPEND_ITEM(&array, &nb, &script); + + widget=gtk_paned_new(GTK_ORIENTATION_VERTICAL); + drawing_area_scrolled_window=gtk_scrolled_window_new(NULL, NULL); + source_view=gtk_source_view_new(); + + grid=gtk_grid_new(); + drawing_area=gtk_layout_new(NULL, NULL); + gtk_widget_set_hexpand(widget, 1); + gtk_widget_set_vexpand(widget, 1); + gtk_paned_set_position (GTK_PANED(widget), 400); + gtk_widget_set_events(drawing_area, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK ); + gtk_container_add(GTK_CONTAINER(drawing_area_scrolled_window), drawing_area); + gtk_container_add(GTK_CONTAINER(drawing_area), grid); + gtk_container_add(GTK_CONTAINER(widget), drawing_area_scrolled_window); + gtk_container_add(GTK_CONTAINER(widget), source_view); + + gtk_widget_set_size_request(source_view, -1, 100); + + g_signal_connect(drawing_area, "draw", G_CALLBACK(draw_callback), this); + g_signal_connect(drawing_area, "button-press-event", G_CALLBACK(button_press_gtk_cb), this); + g_signal_connect(drawing_area, "button-release-event", G_CALLBACK(button_release_gtk_cb), this); + g_signal_connect(drawing_area, "motion-notify-event", G_CALLBACK(motion_gtk_cb), this); +} + +Script::Script(){ + + init(); + // main_graph = agopen((char*)"main", Agdirected, NULL); + + //Graph attribute + /* agattr(main_graph, AGRAPH, (char*)"rankdir", (char*)"LR"); + agattr(main_graph, AGRAPH, (char*)"bgcolor", (char*)"transparent"); + agattr(main_graph, AGRAPH, (char*)"resolution", (char*)"72"); + agattr(main_graph, AGRAPH, (char*)"splines", (char*)"true"); + agattr(main_graph, AGRAPH, (char*)"overlap", (char*)"false");*/ + // agattr(main_graph, AGRAPH, (char*)"compound", (char*)"TRUE"); + + //Node attributes + /* agattr(main_graph, AGNODE, (char*)"shape", (char*)"none"); + agattr(main_graph, AGNODE, (char*)"style", (char*)"filled"); + agattr(main_graph, AGNODE, (char*)"fillcolor", (char*)"white"); + agattr( main_graph, AGNODE, (char*)"fontsize", (char*)"16"); + + + agattr( main_graph, AGEDGE, (char*)"penwidth", (char*)"1"); + agattr( main_graph, AGEDGE, (char*)"color", (char*)"black"); + agattr( main_graph, AGEDGE, (char*)"fontsize", (char*)"16"); + + input_subgraph = agsubg(main_graph, (char*)"input", true); + agattr(input_subgraph, AGRAPH, (char*)"rank", (char*)"same"); + + gvLayout(gvc, main_graph, "dot");*/ +} + +Script::Script(char const *path){ + + FILE *file; + + init(); + + SYSTEM_ERROR_CHECK(file=fopen(path, "r"), NULL, "Error opening '%s'", path); + // main_graph = agread(file, NULL); + SYSTEM_ERROR_CHECK(fclose(file), -1, NULL); + generate_layout(); +} + + +void Script::add_process(Program *program, char **argv){ + new Process_old(this, program, argv); + generate_layout(); +} + +void Script::add_link( Process_old *input_process, char *output_channel, Process_old *output_process, char *input_channel, int sync){ + // gvFreeLayout(gvc, main_graph); + new Link(this, input_process, output_channel, output_process, input_channel, sync); + generate_layout(); +} + +int Script::unselect_all(){ + int unselected_items_nb=0; + while(selected_processes_nb){ + selected_processes[0]->unselect(); + unselected_items_nb++; + + } + while(selected_links_nb){ + selected_links[0]->unselect(); + unselected_items_nb++; + } + return unselected_items_nb; +} + +void Script::save_bash(char const *path){ + char *command; + FILE *file; + Process_old **process_pt; + + SYSTEM_ERROR_CHECK(file=fopen(path, "w"), NULL, "Opening '%s'", path); + + fprintf(file, "source blaar.sh\n\n"); + + FOR_EACH(process_pt, processes, processes_nb){ + if (process_pt[0]->program->type==INPUT_PROGRAM){ + command=blc_create_command_line_from_argv(process_pt[0]->argv); + fprintf(file, "%s |\n", command); + free(command); + } + } + SYSTEM_ERROR_CHECK(fclose(file), -1, "Closing '%s'", path); +} + +Process_old *Script::get_process(double x, double y){ + Process_old **process_pt; + + y=height-y; + + FOR_EACH(process_pt, processes, processes_nb){ + /* node_box = ND_bb(process_pt[0]->node); + if (x > node_box.LL.x && x < node_box.UR.x && y > node_box.LL.y && y < node_box.UR.y) return process_pt[0];*/ + } + return NULL; +} + + +Link *Script::get_link(double x, double y){ + /* + Link **link_pt; + pointf pos; + + y=height-y; + + FOR_EACH(link_pt, links, links_nb){ + pos=ED_label(link_pt[0]->edge)->pos; + if ((fabs(x - pos.x) < 10) && (fabs(y - pos.y) < 10)) return link_pt[0]; + }*/ + return NULL; +} + + +void Script::generate_layout(){ + char *command_line; + + cairo_surface_t *surface; + cairo_t *cr; + Process_old **process_pt, *previous_process=NULL, *process; + GtkTextBuffer *buffer; + + buffer=gtk_text_view_get_buffer (GTK_TEXT_VIEW(source_view)); + gtk_text_buffer_set_text(buffer, "", -1); + // gvLayout(gvc, main_graph, "dot"); + + //Tricks to update node position but it is not used + surface=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1000, 1000); + cr=cairo_create(surface); +// gvRenderContext(gvc, main_graph, "svg:cairo", cr); + cairo_destroy(cr); + FOR_EACH(process_pt, processes, processes_nb) { + process=process_pt[0]; + if (previous_process){ + if ( previous_process->output_links && previous_process->output_links[0]->output_process==process) gtk_text_buffer_insert_at_cursor(buffer, " ", -1); + else gtk_text_buffer_insert_at_cursor(buffer, "\n", -1); + } + + command_line=blc_create_command_line_from_argv(process->argv); + gtk_text_buffer_insert_at_cursor(buffer, command_line, -1); + FREE(command_line); + if (process_pt!=processes+processes_nb-1) gtk_text_buffer_insert_at_cursor(buffer, " |", -1); + previous_process=process; + } +} + + + + + diff --git a/gtk_channels/Script.h b/gtk_channels/Script.h new file mode 100644 index 0000000000000000000000000000000000000000..07c90e0ed77e6a3e30508b9d368c055375b533d4 --- /dev/null +++ b/gtk_channels/Script.h @@ -0,0 +1,64 @@ +// +// Script.hpp +// e_bash +// +// Created by Arnaud Blanchard on 25/08/2017. +// +// + +#ifndef SCRIPT_H +#define SCRIPT_H + +//#include "common.h" +#include <gtk/gtk.h> +#include "Program.h" +#include "Process.h" +#include "Link.h" + + +class Process_old; + +class Script{ + int width; +public: + int height; + GtkWidget *drawing_area, *source_view, *status_image, *grid; + Process_old *initial_process; + Link *initial_link; + Process_old **processes, **selected_processes; + int processes_nb, selected_processes_nb; + Link **links, **selected_links; + int links_nb, selected_links_nb; + /* Agraph_t *main_graph, *input_subgraph; + GVC_t *gvc;*/ + GtkWidget *widget; + Process_old *last_process; + static Script **array; + static int nb; + + void init(); + + Script(); + Script(char const *filename); + + void add_process(Program *program, char **argv); + void add_link( Process_old *input_process, char *output_channel, Process_old *output_process, char *input_channel, int sync); + Process_old *get_process(double x, double y); + Link *get_link(double x, double y); + void save_bash(char const *filename); + void update_display(); + int unselect_all(); + + void delete_selection(); + void generate_layout(); + void run(); + void pause(); + void stop(); + + +}; + +//extern Script *current_script; + + +#endif diff --git a/gtk_channels/Window.cpp b/gtk_channels/Window.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3f4b4e449827e26ee47b79d77562bcf4ec01a5d --- /dev/null +++ b/gtk_channels/Window.cpp @@ -0,0 +1,9 @@ +// +// Window.c +// e_bash +// +// Created by Arnaud Blanchard on 28/08/2017. +// +// + +#include "Window.h" diff --git a/gtk_channels/common.h b/gtk_channels/common.h new file mode 100644 index 0000000000000000000000000000000000000000..bf5e222ef294370a258482dd257e4390d6cca701 --- /dev/null +++ b/gtk_channels/common.h @@ -0,0 +1,22 @@ +// +// common.h +// e_bash +// +// Created by Arnaud Blanchard on 25/08/2017. +// +// + +#ifndef COMMON_H +#define COMMON_H + +#include <sys/syslimits.h> //PATH_MAX + +#include "Script.h" + +extern char blaar_bin_dir[PATH_MAX]; +extern char blaar_scripts_dir[PATH_MAX]; + +extern Script *current_script; + + +#endif diff --git a/gtk_channels/gtk_channels.cpp b/gtk_channels/gtk_channels.cpp new file mode 100644 index 0000000000000000000000000000000000000000..699fa8a7733b4cf6e77a1e9f0166c193a354ad7f --- /dev/null +++ b/gtk_channels/gtk_channels.cpp @@ -0,0 +1,51 @@ + +#include "blc_program.h" +#include "libgen.h" +#include <gtkmm.h> +#include "Channel.hpp" +#include "thread" + +using namespace std; +using namespace Gtk; + +struct App:Application{ + // App(int &argc, char **&argv); + ApplicationWindow main_window; + GtkScrolledWindow channel_window; + ChannelWidget channel_widget; + + //Overrides of default signal handlers: + void on_startup() override; //This is called for the first instance of the app + void on_activate() override; +}; + +/*App::App(int &argc, char **&argv):Application(argc, argv){ + Glib::set_application_name("e_bash"); +}*/ + +void App::on_startup(){ + Application::on_startup(); + add_action("quit", [](){ + exit(0); + }); + } + +void App::on_activate(){ + + Application::on_activate(); + add_window(main_window); + main_window.add(channel_widget); + main_window.show_all(); +} + +int main(int argc, char **argv) +{ + App app;//(argc, argv); + + blc_program_set_description("Graphical interface for blaar programs"); + blc_program_init(&argc, &argv, blc_quit); + + return app.run(0, 0); +} + + diff --git a/gtk_channels/gtk_common.h b/gtk_channels/gtk_common.h new file mode 100644 index 0000000000000000000000000000000000000000..f480b44870902becf62513b74f9dbdd1a1011aca --- /dev/null +++ b/gtk_channels/gtk_common.h @@ -0,0 +1,25 @@ +// +// gtk_common.h +// e_bash +// +// Created by Arnaud Blanchard on 24/08/2017. +// +// + +#ifndef GTK_COMMON_H +#define GTK_COMMON_H + +#include <gtkmm.h> +#include <gtk/gtk.h> + +#define STATUS_PUSH(...) status_push( __VA_ARGS__) + + +extern GtkWidget *general_statusbar, *tool_run_pause, *tool_stop; +extern guint statusbar_context_id; +extern GtkWidget *image_start, *image_pause, *image_stop; + +void status_push(char const *format, ...); + + +#endif /* gtk_common_h */ diff --git a/gtk_channels/parse_help.cpp b/gtk_channels/parse_help.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7793b57a13521f6061bbb8e06f682b226104eb5a --- /dev/null +++ b/gtk_channels/parse_help.cpp @@ -0,0 +1,220 @@ +// +// parse_help.cpp +// e_bash +// +// Created by Arnaud Blanchard on 23/08/2017. +// +// + +#include "parse_help.h" +#include "blc_core.h" +#include <unistd.h> +#include <errno.h> //EINTR +#include <libgen.h> //basename +#include <stdio.h> //popen + +//This are in blc_program +#define POSITIONAL_ARGUMENTS_TITLE "\npositional arguments:\n" +#define OPTIONAL_ARGUMENTS_TITLE "\noptional arguments:\n" + +void blc_remove_spaces(char const **text) +{ + while ((*text)[0]==' ') (*text)++; +} + +static void free_optional_argument(struct blc_optional_argument *argument) +{ + if (argument->shortname) FREE(argument->shortname); + if (argument->longname) FREE(argument->longname); + if (argument->help) FREE(argument->help); +} + +static void blc_get_help(blc_mem *help, char const *directory, char const *program_name) +{ + char buffer[LINE_MAX]; + int error; + ssize_t n; + FILE *answer; + + SPRINTF(buffer,"cd %s; ./%s -h 2>&1", directory, program_name); // The 'cd <directory>;' is necessary for the scripts to find blaar.sh ( popen use 'sh' and dos not use envirnment variables' + SYSTEM_ERROR_CHECK(answer = popen(buffer, "r"), NULL, "Executing '%s'", buffer); + + do{ + SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(n=fread( buffer, 1, sizeof(buffer), answer), -1, EINTR, "Reading pipe of '%s'.", buffer); + help->append(buffer, n); + }while(n!=0); + error=pclose(answer); + help->append_text("");//Eof line + if (error!=0) { + PRINT_WARNING("Error '%d' executing '%s' message '%s'", error, buffer, help->chars); + help->allocate(0); + } +} + +char *blc_parse_description(char const *str, char const **optional_argument_description, char const **positional_argument_description) +{ + char const *pt; + char *description; + char const *end_description; + + pt=strstr(str, POSITIONAL_ARGUMENTS_TITLE); + if (pt){ + end_description=pt; + if (positional_argument_description) *positional_argument_description=pt+strlen(POSITIONAL_ARGUMENTS_TITLE); + pt=strstr(pt, OPTIONAL_ARGUMENTS_TITLE); + } + else { + if (positional_argument_description) *positional_argument_description=NULL; + pt=strstr(str, OPTIONAL_ARGUMENTS_TITLE); + end_description=pt; + } + if (pt){ + if (optional_argument_description) *optional_argument_description=pt+strlen(OPTIONAL_ARGUMENTS_TITLE); + } + else + { + if (optional_argument_description) *optional_argument_description=NULL; + } + + + if (end_description==NULL) description=strdup(str); + else description=strndup(str, end_description-str); + return description; +} + +/**Parse text in optional_arguments_description to find optional_arguments. +It returns the number of arguments and update pos at the end of the optional arguments bloc.*/ + +int blc_parse_optional_arguments(struct blc_optional_argument **results, char const **pos, char const *optional_arguments_description) +{ + char const*help; + char token[NAME_MAX+1]; + char text[NAME_MAX+1]; + int token_size; + int arg=1; + int results_nb=0; + struct blc_optional_argument argument; + + *results=NULL; + + while(arg) + { + help=NULL; + blc_remove_spaces(pos); + if (sscanf(*pos, "[--%[^] ]%n", token, &token_size)==1){ + *pos+=token_size; + SPRINTF(text, "--%s", token); + argument.shortname=NULL; + argument.longname=strdup(token); + help=strstr(optional_arguments_description, text); + } + else if (sscanf(*pos, "[-%[^] ]%n", token, &token_size)==1){ + *pos+=token_size; + SPRINTF(text, "-%s", token); + argument.shortname=strdup(token); + argument.longname=NULL; + if (optional_arguments_description) //Test not useful ? + { + help=strstr(optional_arguments_description, text); + if (help) { + help+=strlen(text); + if (sscanf(help, ", --%s%n", token, &token_size)==1) { + help+=token_size; + argument.longname=strdup(token); + } + } + } + + }else break; + token_size=0; + sscanf(*pos, "%*[= ]%n", &token_size); //remove spaces or equal + *pos+=token_size; + if (sscanf(*pos, "%[^] ]%n", token, &token_size)==1) { + *pos+=token_size; + argument.value=strdup(token); //FIXME: Memory may not be free + } + else argument.value=NULL; + + if (help){ + help+=strlen(text); + blc_remove_spaces(&help); + if (argument.value) { + help+=strlen(argument.value); + blc_remove_spaces(&help); + } + argument.help=strndup(help, strchr(help, '\n') - help); + } + else argument.help=NULL; + + if (argument.longname==NULL || strcmp(argument.longname, "help")!=0) APPEND_ITEM(results, &results_nb, &argument); + else free_optional_argument(&argument); + + token_size=0; + sscanf(*pos, "%*[] ]%n", &token_size); + *pos+=token_size; + } + return results_nb; +} + +int blc_parse_positional_arguments(struct blc_positional_argument **results, char const **pos, char const *positional_arguments_description) +{ + char const *help; + char token[NAME_MAX+1]; + int token_size; + int results_nb=0; + struct blc_positional_argument argument; + + *results=NULL; + while (sscanf(*pos, "%[^\n ]%n", token, &token_size)==1) + { + *pos+=token_size; + argument.name=strdup(token); + argument.value=NULL; + help=strstr(positional_arguments_description, token); + if (help) + { + help+=strlen(token); + blc_remove_spaces(&help); + argument.help=strndup(help, strchr(help, '\n') - help); + } + else argument.help=NULL; + APPEND_ITEM(results, &results_nb, &argument); + } + + while (sscanf(*pos, "[%s]%n", token, &token_size)==1) + { + *pos+=token_size; + argument.name=strdup(token); + argument.value=NULL; + help=strstr(positional_arguments_description, token) + strlen(token); + while(help[0]==' ') help++; + argument.help=strndup(help, strchr(help, '\n') - help); + APPEND_ITEM(results, &results_nb, &argument); + } + return results_nb; +} + +char const *blc_parse_help(char const *directory, char const *program_name, blc_optional_argument **optional_arguments, int *optional_arguments_nb, blc_positional_argument **positional_arguments, int *positional_arguments_nb, char **epilog) +{ + char const *str; + char token[LINE_MAX]; + blc_mem help; + char const *description; + char const *description_start; + const char *positional_argument_description; + const char *optional_argument_description; + (void)epilog; + + blc_get_help(&help, directory, program_name); + + if (help.chars==NULL) description=NULL; + else { + SPRINTF(token, "usage: %s", program_name); + str=strstr(help.chars, token)+strlen(token); + description_start=strchr(str, '\n')+1; + description=blc_parse_description(description_start, &optional_argument_description, &positional_argument_description); + *optional_arguments_nb=blc_parse_optional_arguments(optional_arguments, &str, optional_argument_description); + *positional_arguments_nb=blc_parse_positional_arguments(positional_arguments, &str, positional_argument_description); + } + return description; +} diff --git a/gtk_channels/parse_help.h b/gtk_channels/parse_help.h new file mode 100644 index 0000000000000000000000000000000000000000..6b34bfcc6c5d873059ded1f8bcf98951c9fdc2fc --- /dev/null +++ b/gtk_channels/parse_help.h @@ -0,0 +1,23 @@ +#ifndef BLC_PARSE_HELP +#define BLC_PARSE_HELP + + +typedef struct blc_optional_argument +{ + char const *shortname; + char const *longname; + char const *help; + char const *value; +}blc_optional_argument; + +typedef struct blc_positional_argument +{ + char const *name; + char const *help; + char const *value; +}blc_positional_argument; + + +char const *blc_parse_help(char const *directory, char const *program_name, blc_optional_argument **optional_arguments, int *optional_arguments_nb, blc_positional_argument **positional_arguments, int *positional_arguments_nb, char **epilog); + +#endif