Browse Source

Merged theme and eidheim

merge-requests/365/head
Jørgen Lien Sellæg 10 years ago
parent
commit
10154d6205
  1. 2
      docs/install.md
  2. 4
      src/CMakeLists.txt
  3. 263
      src/cmake.cc
  4. 28
      src/cmake.h
  5. 12
      src/config.cc
  6. 3
      src/config.h
  7. 94
      src/directories.cc
  8. 8
      src/directories.h
  9. 21
      src/files.h
  10. 1
      src/juci.cc
  11. 47
      src/notebook.cc
  12. 7
      src/notebook.h
  13. 1
      src/singletons.cc
  14. 2
      src/singletons.h
  15. 71
      src/source.cc
  16. 8
      src/source.h
  17. 254
      src/terminal.cc
  18. 31
      src/terminal.h
  19. 101
      src/window.cc
  20. 8
      src/window.h

2
docs/install.md

@ -6,7 +6,7 @@ First dependencies:
```sh
$ sudo apt-get install libboost-python-dev libboost-filesystem-dev libboost-log-dev libboost-test-dev
libboost-thread-dev libboost-system-dev libgtkmm-3.0-dev libgtksourceview2.0-dev libgtksourceviewmm-3.0-dev
libpython-dev libclang-dev make cmake gcc
libpython-dev libclang-dev make cmake gcc g++
```
Install the project:
```sh

4
src/CMakeLists.txt

@ -79,7 +79,9 @@ if(${validation})
tooltips.h
tooltips.cc
singletons.h
singletons.cc)
singletons.cc
cmake.h
cmake.cc)
add_library(${module} SHARED
api

263
src/cmake.cc

@ -0,0 +1,263 @@
#include "cmake.h"
#include "sourcefile.h"
#include <regex>
#include "singletons.h"
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
CMake::CMake(const boost::filesystem::path &path) {
const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) {
for(auto &line: juci::filesystem::read_lines(cmake_path)) {
const std::regex project_regex("^ *project *\\(.*$");
std::smatch sm;
if(std::regex_match(line, sm, project_regex)) {
return true;
}
}
return false;
};
auto search_path=boost::filesystem::path(path);
auto search_cmake_path=search_path;
search_cmake_path+="/CMakeLists.txt";
if(boost::filesystem::exists(search_cmake_path))
paths.emplace(paths.begin(), search_cmake_path);
if(find_cmake_project(search_cmake_path))
project_path=search_path;
else {
do {
search_path=search_path.parent_path();
search_cmake_path=search_path;
search_cmake_path+="/CMakeLists.txt";
if(boost::filesystem::exists(search_cmake_path))
paths.emplace(paths.begin(), search_cmake_path);
if(find_cmake_project(search_cmake_path)) {
project_path=search_path;
break;
}
} while(search_path!=search_path.root_directory());
}
if(project_path!="") {
if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json"))
create_compile_commands(project_path.string());
}
}
bool CMake::create_compile_commands(const std::string &path) {
Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n");
//TODO: Windows...
if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1", path)==EXIT_SUCCESS)
return true;
return false;
}
void CMake::read_files() {
for(auto &path: paths)
files.emplace_back(juci::filesystem::read(path));
}
void CMake::remove_tabs() {
for(auto &file: files) {
for(auto &chr: file) {
if(chr=='\t')
chr=' ';
}
}
}
void CMake::remove_comments() {
for(auto &file: files) {
size_t pos=0;
size_t comment_start;
bool inside_comment=false;
while(pos<file.size()) {
if(!inside_comment && file[pos]=='#') {
comment_start=pos;
inside_comment=true;
}
if(inside_comment && file[pos]=='\n') {
file.erase(comment_start, pos-comment_start);
pos-=pos-comment_start;
inside_comment=false;
}
pos++;
}
if(inside_comment)
file.erase(comment_start);
}
}
void CMake::remove_newlines_inside_parentheses() {
for(auto &file: files) {
size_t pos=0;
bool inside_para=false;
bool inside_quote=false;
char last_char=0;
while(pos<file.size()) {
if(!inside_quote && file[pos]=='"' && last_char!='\\')
inside_quote=true;
else if(inside_quote && file[pos]=='"' && last_char!='\\')
inside_quote=false;
else if(!inside_quote && file[pos]=='(')
inside_para=true;
else if(!inside_quote && file[pos]==')')
inside_para=false;
else if(inside_para && file[pos]=='\n')
file.replace(pos, 1, 1, ' ');
last_char=file[pos];
pos++;
}
}
}
void CMake::find_variables() {
for(auto &file: files) {
size_t pos=0;
while(pos<file.size()) {
auto start_line=pos;
auto end_line=file.find('\n', start_line);
if(end_line==std::string::npos)
end_line=file.size();
if(end_line>start_line) {
auto line=file.substr(start_line, end_line-start_line);
const std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$");
std::smatch sm;
if(std::regex_match(line, sm, set_regex)) {
std::string data=sm[2];
while(data.size()>0 && data.back()==' ')
data.pop_back();
parse_variable_parameters(data);
variables[sm[1]]=data;
}
}
pos=end_line+1;
}
}
}
void CMake::parse_variable_parameters(std::string &data) {
size_t pos=0;
bool inside_quote=false;
char last_char=0;
while(pos<data.size()) {
if(!inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=true;
data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
pos--;
}
else if(inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=false;
data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
pos--;
}
else if(!inside_quote && data[pos]==' ' && pos+1<data.size() && data[pos+1]==' ') {
data.erase(pos, 1);
pos--;
}
last_char=data[pos];
pos++;
}
for(auto &var: variables) {
auto pos=data.find("${"+var.first+'}');
while(pos!=std::string::npos) {
data.replace(pos, var.first.size()+3, var.second);
pos=data.find("${"+var.first+'}');
}
}
//Remove variables we do not know:
pos=data.find("${");
auto pos_end=data.find("}", pos+2);
while(pos!=std::string::npos && pos_end!=std::string::npos) {
data.erase(pos, pos_end-pos+1);
pos=data.find("${");
pos_end=data.find("}", pos+2);
}
}
void CMake::parse() {
read_files();
remove_tabs();
remove_comments();
remove_newlines_inside_parentheses();
find_variables();
parsed=true;
}
std::vector<std::string> CMake::get_function_parameters(std::string &data) {
std::vector<std::string> parameters;
size_t pos=0;
size_t parameter_pos=0;
bool inside_quote=false;
char last_char=0;
while(pos<data.size()) {
if(!inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=true;
data.erase(pos, 1);
pos--;
}
else if(inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=false;
data.erase(pos, 1);
pos--;
}
else if(!inside_quote && pos+1<data.size() && data[pos]==' ' && data[pos+1]==' ') {
data.erase(pos, 1);
pos--;
}
else if(!inside_quote && data[pos]==' ') {
parameters.emplace_back(data.substr(parameter_pos, pos-parameter_pos));
if(pos+1<data.size())
parameter_pos=pos+1;
}
last_char=data[pos];
pos++;
}
parameters.emplace_back(data.substr(parameter_pos));
for(auto &var: variables) {
for(auto &parameter: parameters) {
auto pos=parameter.find("${"+var.first+'}');
while(pos!=std::string::npos) {
parameter.replace(pos, var.first.size()+3, var.second);
pos=parameter.find("${"+var.first+'}');
}
}
}
return parameters;
}
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > CMake::get_functions_parameters(const std::string &name) {
if(!parsed)
parse();
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > functions;
size_t file_c=0;
for(auto &file: files) {
size_t pos=0;
while(pos<file.size()) {
auto start_line=pos;
auto end_line=file.find('\n', start_line);
if(end_line==std::string::npos)
end_line=file.size();
if(end_line>start_line) {
auto line=file.substr(start_line, end_line-start_line);
const std::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$");
std::smatch sm;
if(std::regex_match(line, sm, function_regex)) {
std::string data=sm[1];
while(data.size()>0 && data.back()==' ')
data.pop_back();
auto parameters=get_function_parameters(data);
functions.emplace(functions.begin(), paths[file_c], parameters);
}
}
pos=end_line+1;
}
file_c++;
}
return functions;
}

28
src/cmake.h

@ -0,0 +1,28 @@
#ifndef JUCI_CMAKE_H_
#define JUCI_CMAKE_H_
#include <boost/filesystem.hpp>
#include <vector>
#include <unordered_map>
class CMake {
public:
CMake(const boost::filesystem::path &path);
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > get_functions_parameters(const std::string &name);
static bool create_compile_commands(const std::string &path);
std::vector<boost::filesystem::path> paths;
std::vector<std::string> files;
boost::filesystem::path project_path;
std::unordered_map<std::string, std::string> variables;
private:
void read_files();
void remove_tabs();
void remove_comments();
void remove_newlines_inside_parentheses();
void find_variables();
void parse_variable_parameters(std::string &data);
void parse();
std::vector<std::string> get_function_parameters(std::string &data);
bool parsed=false;
};
#endif //JUCI_CMAKE_H_

12
src/config.cc

@ -12,7 +12,6 @@ MainConfig::MainConfig() {
GenerateSource();
GenerateTheme();
GenerateDirectoryFilter();
GenerateTerminalCommands();
}
void MainConfig::GenerateTheme() {
@ -54,17 +53,6 @@ void MainConfig::GenerateSource() {
DEBUG("Source cfg fetched");
}
void MainConfig::GenerateTerminalCommands() {
auto terminal_cfg=Singleton::Config::terminal();
boost::property_tree::ptree source_json = cfg.get_child("project");
boost::property_tree::ptree compile_commands_json = source_json.get_child("compile_commands");
boost::property_tree::ptree run_commands_json = source_json.get_child("run_commands");
for (auto &i : compile_commands_json)
terminal_cfg->compile_commands.emplace_back(i.second.get_value<std::string>());
for (auto &i : run_commands_json)
terminal_cfg->run_command=(i.second.get_value<std::string>()); //TODO: run_commands array->one run_command?
}
void MainConfig::GenerateDirectoryFilter() {
auto dir_cfg=Singleton::Config::directories();
DEBUG("Fetching directory filter");

3
src/config.h

@ -12,10 +12,7 @@ public:
void GenerateSource();
void GenerateDirectoryFilter();
void GenerateTheme();
void GenerateTerminalCommands();
private:
boost::property_tree::ptree cfg;
};
#endif

94
src/directories.cc

@ -5,6 +5,9 @@
#include <algorithm>
#include "boost/algorithm/string.hpp"
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
}
@ -38,21 +41,39 @@ Directories::Directories() {
}
void Directories::open_folder(const boost::filesystem::path& dir_path) {
auto new_path=dir_path;
INFO("Open folder");
if(new_path=="") {
if(current_path=="")
return;
new_path=current_path;
}
std::vector<Gtk::TreeModel::Path> expanded_paths;
if(last_dir_path==dir_path) {
if(current_path==new_path) {
tree_view.map_expanded_rows([&expanded_paths](Gtk::TreeView* tree_view, const Gtk::TreeModel::Path& path){
expanded_paths.emplace_back(path);
});
}
tree_store->clear();
tree_view.get_column(0)->set_title(get_cmakelists_variable(dir_path, "project"));
add_paths(dir_path, Gtk::TreeModel::Row(), 0);
if(dir_path!="")
cmake=std::unique_ptr<CMake>(new CMake(new_path));
auto project=cmake->get_functions_parameters("project");
if(project.size()>0 && project[0].second.size()>0)
tree_view.get_column(0)->set_title(project[0].second[0]);
else
tree_view.get_column(0)->set_title("");
add_paths(new_path, Gtk::TreeModel::Row(), 0);
for(auto &path: expanded_paths)
tree_view.expand_row(path, false);
last_dir_path=dir_path;
current_path=new_path;
if(selected_path.size()>0)
select_path(selected_path);
DEBUG("Folder opened");
}
@ -62,6 +83,7 @@ void Directories::select_path(const std::string &path) {
auto tree_path=Gtk::TreePath(iter);
tree_view.expand_to_path(tree_path);
tree_view.set_cursor(tree_path);
selected_path=path;
return true;
}
return false;
@ -118,67 +140,3 @@ void Directories::add_paths(const boost::filesystem::path& dir_path, const Gtk::
}
}
}
std::string Directories::get_cmakelists_variable(const boost::filesystem::path& dir_path, std::string command_name) {
INFO("fetches cmake variable value for: "+command_name);
std::string project_name;
std::string project_name_var;
boost::filesystem::directory_iterator end_itr;
for (boost::filesystem::directory_iterator itr( dir_path );itr != end_itr;++itr ) {
if (itr->path().filename().string() == "CMakeLists.txt") {
for (auto &line : juci::filesystem::read_lines(itr->path())) {
if (line.find(command_name+"(", 0) != std::string::npos
|| line.find(command_name+" (", 0) != std::string::npos ) {
size_t variable_start = line.find("{", 0);
size_t variable_end = line.find("}", variable_start);
project_name_var = line.substr(variable_start+1,
(variable_end)-variable_start-1);
boost::algorithm::trim(project_name_var);
if (variable_start == std::string::npos) { // not a variabel
variable_start = line.find("(", 0);
variable_end = line.find(' ', variable_start);
if(variable_end != std::string::npos){
return line.substr(variable_start+1,
(variable_end)-variable_start-1);
}
variable_end = line.find("#", variable_start);
if(variable_end != std::string::npos){
return line.substr(variable_start+1,
(variable_end)-variable_start-1);
}
variable_end = line.find(")", variable_start);
return line.substr(variable_start+1,
(variable_end)-variable_start-1);
if (variable_start == std::string::npos) { // not a variable
variable_start = line.find("(", 0);
variable_end = line.find(")", variable_start);
INFO("Wasn't a variable, returning value");
return line.substr(variable_start+1,
(variable_end)-variable_start-1);
}
break;
}
}
}
for (auto &line : juci::filesystem::read_lines(itr->path())) {
if (line.find("set(", 0) != std::string::npos
|| line.find("set (", 0) != std::string::npos) {
if( line.find(project_name_var, 0) != std::string::npos) {
size_t variable_start = line.find(project_name_var, 0)
+project_name_var.length();
size_t variable_end = line.find(")", variable_start);
project_name = line.substr(variable_start+1,
variable_end-variable_start-1);
boost::algorithm::trim(project_name);
INFO("found variable, returning value");
return project_name;
}
}
}
break;
}
}
INFO("Couldn't find value in CMakeLists.txt");
return "no project name";
}

8
src/directories.h

@ -5,6 +5,7 @@
#include <vector>
#include <string>
#include "boost/filesystem.hpp"
#include "cmake.h"
class Directories : public Gtk::ScrolledWindow {
public:
@ -27,11 +28,12 @@ public:
};
Directories();
void open_folder(const boost::filesystem::path& dir_path);
void open_folder(const boost::filesystem::path& dir_path="");
void select_path(const std::string &path);
std::string get_cmakelists_variable(const boost::filesystem::path& dir_path, std::string command_name);
std::function<void(const std::string &file)> on_row_activated;
std::unique_ptr<CMake> cmake;
boost::filesystem::path current_path;
private:
void add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row, unsigned depth);
@ -39,7 +41,7 @@ private:
Gtk::TreeView tree_view;
Glib::RefPtr<Gtk::TreeStore> tree_store;
ColumnRecord column_record;
boost::filesystem::path last_dir_path;
std::string selected_path;
};
#endif // JUCI_DIRECTORIES_H_

21
src/files.h

@ -68,27 +68,6 @@ const std::string configjson =
" \"cmakelists.txt\",\n"
" \"in-lowercase.pls\"\n"
" ]\n"
" },\n"
" \"project\": {\n"
" \"run_commands\": [\n"
" \"./.build/\"\n"
" ],\n"
" \"compile_commands\": [\n"
" \"rm -rf ./.build\",\n"
" \"mkdir ./.build\",\n"
" \"cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B./.build -H.\",\n"
" \"cd ./.build/; make\",\n"
" \"cp ./.build/compile_commands.json compile_commands.json\"\n"
" ]\n"
" },\n"
" \"example\": {\n"
" \"key\": \"value\",\n"
" \"key2\": [\n"
" \"val1\",\n"
" \"val2\",\n"
" 3\n"
" ],\n"
" \"key3\": \"value\"\n"
" }\n"
"}\n";

1
src/juci.cc

@ -37,7 +37,6 @@ void app::on_activate() {
add_window(*window);
window->show();
if(directory!="") {
window->notebook.project_path=directory;
window->directories.open_folder(directory);
}
for(auto &f: files)

47
src/notebook.cc

@ -3,6 +3,8 @@
#include "sourcefile.h"
#include "singletons.h"
#include <fstream>
#include <regex>
#include "cmake.h"
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
@ -11,7 +13,7 @@ namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
}
Notebook::Notebook() : Gtk::Notebook() {
Notebook::Notebook(Directories &directories) : Gtk::Notebook(), directories(directories) {
Gsv::init();
}
@ -50,19 +52,26 @@ void Notebook::open(std::string path) {
}
can_read.close();
auto tmp_project_path=project_path;
if(tmp_project_path=="") {
tmp_project_path=boost::filesystem::path(path).parent_path().string();
}
auto language=Source::guess_language(path);
if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) {
if(boost::filesystem::exists(tmp_project_path+"/CMakeLists.txt") && !boost::filesystem::exists(tmp_project_path+"/compile_commands.json")) {
make_compile_commands();
std::string project_path;
if(directories.cmake && directories.cmake->project_path!="")
project_path=directories.cmake->project_path.string();
else {
auto parent_path=boost::filesystem::path(path).parent_path();
project_path=parent_path.string();
CMake cmake(parent_path);
if(cmake.project_path!="") {
project_path=cmake.project_path.string();
Singleton::terminal()->print("Project path for "+path+" set to "+project_path+"\n");
}
source_views.emplace_back(new Source::ClangView(path, tmp_project_path));
else
Singleton::terminal()->print("Error: could not find project path for "+path+"\n");
}
source_views.emplace_back(new Source::ClangView(path, project_path));
}
else
source_views.emplace_back(new Source::GenericView(path, tmp_project_path, language));
source_views.emplace_back(new Source::GenericView(path, language));
scrolled_windows.emplace_back(new Gtk::ScrolledWindow());
hboxes.emplace_back(new Gtk::HBox());
@ -111,11 +120,13 @@ bool Notebook::save(int page) {
Singleton::terminal()->print("File saved to: " +view->file_path+"\n");
//If CMakeLists.txt have been modified:
//TODO: recreate cmake even without directories open?
if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") {
if(make_compile_commands()) {
if(directories.cmake && directories.cmake->project_path!="" && boost::filesystem::path(view->file_path)>=directories.cmake->project_path && CMake::create_compile_commands(directories.cmake->project_path.string())) {
directories.open_folder();
for(auto source_view: source_views) {
if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view)) {
if(project_path==source_view->project_path) {
if(directories.cmake->project_path.string()==source_clang_view->project_path) {
if(source_clang_view->restart_parse())
Singleton::terminal()->print("Reparsing "+source_clang_view->file_path+"\n");
else
@ -133,18 +144,6 @@ bool Notebook::save(int page) {
return false;
}
bool Notebook::make_compile_commands() {
if(project_path.size()>0) {
Singleton::terminal()->print("Creating "+boost::filesystem::path(project_path+"/compile_commands.json").string()+"\n");
//TODO: Windows...
if(Singleton::terminal()->execute(project_path, "cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1")) {
//TODO: refresh directories
return true;
}
}
return false;
}
bool Notebook::save_current() {
INFO("Notebook save current file");
if(get_current_page()==-1)
@ -168,7 +167,7 @@ bool Notebook::close_current_page() {
scrolled_windows.erase(scrolled_windows.begin()+page);
hboxes.erase(hboxes.begin()+page);
if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view))
source_clang_view->delete_object();
source_clang_view->async_delete();
else
delete source_view;
}

7
src/notebook.h

@ -8,10 +8,11 @@
#include <type_traits>
#include <map>
#include <sigc++/sigc++.h>
#include "directories.h"
class Notebook : public Gtk::Notebook {
public:
Notebook();
Notebook(Directories &directories);
Source::View* get_view(int page);
int size();
Source::View* get_current_view();
@ -19,11 +20,11 @@ public:
void open(std::string filename);
bool save(int page);
bool save_current();
std::string project_path;
private:
bool make_compile_commands();
bool make_compile_commands(const std::string &path);
bool save_modified_dialog();
Directories &directories;
std::vector<Source::View*> source_views; //Is NOT freed in destructor, this is intended for quick program exit.
std::vector<std::unique_ptr<Gtk::ScrolledWindow> > scrolled_windows;
std::vector<std::unique_ptr<Gtk::HBox> > hboxes;

1
src/singletons.cc

@ -1,7 +1,6 @@
#include "singletons.h"
std::unique_ptr<Source::Config> Singleton::Config::source_=std::unique_ptr<Source::Config>(new Source::Config());
std::unique_ptr<Terminal::Config> Singleton::Config::terminal_=std::unique_ptr<Terminal::Config>(new Terminal::Config());
std::unique_ptr<Directories::Config> Singleton::Config::directories_=std::unique_ptr<Directories::Config>(new Directories::Config());
std::unique_ptr<Terminal> Singleton::terminal_=std::unique_ptr<Terminal>();
std::unique_ptr<Theme::Config> Singleton::Config::theme_ = std::unique_ptr<Theme::Config>(new Theme::Config());

2
src/singletons.h

@ -16,7 +16,6 @@ public:
class Config {
public:
static Source::Config *source() {return source_.get();}
static Terminal::Config *terminal() {return terminal_.get();}
static Directories::Config *directories() {return directories_.get();}
static Theme::Config *theme() { return theme_.get(); }
static Window::Config *window() { return window_.get(); }
@ -24,7 +23,6 @@ public:
static std::unique_ptr<Source::Config> source_;
static std::unique_ptr<Theme::Config> theme_;
static std::unique_ptr<Window::Config> window_;
static std::unique_ptr<Terminal::Config> terminal_;
static std::unique_ptr<Directories::Config> directories_;
};
static std::string config_dir() { return std::string(getenv("HOME")) + "/.juci/config/"; }

71
src/source.cc

@ -36,8 +36,7 @@ Glib::RefPtr<Gsv::Language> Source::guess_language(const std::string &file_path)
//////////////
//// View ////
//////////////
Source::View::View(const std::string& file_path, const std::string& project_path):
file_path(file_path), project_path(project_path) {
Source::View::View(const std::string& file_path): file_path(file_path) {
set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
get_source_buffer()->begin_not_undoable_action();
juci::filesystem::read(file_path, get_buffer());
@ -254,7 +253,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
/////////////////////
//// GenericView ////
/////////////////////
Source::GenericView::GenericView(const std::string& file_path, const std::string& project_path, Glib::RefPtr<Gsv::Language> language) : View(file_path, project_path) {
Source::GenericView::GenericView(const std::string& file_path, Glib::RefPtr<Gsv::Language> language) : View(file_path) {
if(language) {
get_source_buffer()->set_language(language);
Singleton::terminal()->print("Language for file "+file_path+" set to "+language->get_name()+".\n");
@ -273,8 +272,8 @@ Source::GenericView::GenericView(const std::string& file_path, const std::string
////////////////////////
clang::Index Source::ClangViewParse::clang_index(0, 0);
Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path):
Source::View(file_path, project_path) {
Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path) :
Source::View(file_path), project_path(project_path) {
INFO("Tagtable beeing filled");
for (auto &item : Singleton::Config::source()->tags) {
auto scheme = get_source_buffer()->get_style_scheme();
@ -321,6 +320,34 @@ Source::View(file_path, project_path) {
//TODO: clear tag_class and param_spec?
parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path);
//GTK-calls must happen in main thread, so the parse_thread
//sends signals to the main thread that it is to call the following functions:
parse_start.connect([this]{
if(parse_thread_buffer_map_mutex.try_lock()) {
parse_thread_buffer_map=get_buffer_map();
parse_thread_mapped=true;
parse_thread_buffer_map_mutex.unlock();
}
parse_thread_go=true;
});
parse_done.connect([this](){
if(parse_thread_mapped) {
if(parsing_mutex.try_lock()) {
INFO("Updating syntax");
update_syntax();
update_diagnostics();
update_types();
clang_readable=true;
set_status("");
parsing_mutex.unlock();
INFO("Syntax updated");
}
parsing_in_progress->done("done");
}
else {
parse_thread_go=true;
}
});
init_parse();
get_buffer()->signal_changed().connect([this]() {
@ -363,36 +390,6 @@ void Source::ClangViewParse::init_parse() {
end_offset);
update_syntax();
//GTK-calls must happen in main thread, so the parse_thread
//sends signals to the main thread that it is to call the following functions:
parse_start.connect([this]{
if(parse_thread_buffer_map_mutex.try_lock()) {
parse_thread_buffer_map=get_buffer_map();
parse_thread_mapped=true;
parse_thread_buffer_map_mutex.unlock();
}
parse_thread_go=true;
});
parse_done.connect([this](){
if(parse_thread_mapped) {
if(parsing_mutex.try_lock()) {
INFO("Updating syntax");
update_syntax();
update_diagnostics();
update_types();
clang_readable=true;
set_status("");
parsing_mutex.unlock();
INFO("Syntax updated");
}
parsing_in_progress->done("done");
}
else {
parse_thread_go=true;
}
});
set_status("parsing...");
if(parse_thread.joinable())
parse_thread.join();
@ -560,7 +557,7 @@ void Source::ClangViewParse::update_types() {
bool Source::ClangViewParse::on_motion_notify_event(GdkEventMotion* event) {
delayed_tooltips_connection.disconnect();
if(clang_readable) {
if(clang_readable && event->state==0) {
Gdk::Rectangle rectangle(event->x, event->y, 1, 1);
Tooltips::init();
type_tooltips.show(rectangle);
@ -1082,7 +1079,7 @@ Source::ClangView::ClangView(const std::string& file_path, const std::string& pr
});
}
void Source::ClangView::delete_object() {
void Source::ClangView::async_delete() {
parsing_in_progress->cancel("canceled, freeing resources in the background");
parse_thread_stop=true;
delete_thread=std::thread([this](){

8
src/source.h

@ -46,7 +46,7 @@ namespace Source {
class View : public Gsv::View {
public:
View(const std::string& file_path, const std::string& project_path);
View(const std::string& file_path);
~View();
void search_highlight(const std::string &text, bool case_sensitive, bool regex);
@ -58,7 +58,6 @@ namespace Source {
void replace_all(const std::string &replacement);
std::string file_path;
std::string project_path;
std::function<std::pair<std::string, unsigned>()> get_declaration_location;
std::function<void()> goto_method;
@ -84,12 +83,13 @@ namespace Source {
class GenericView : public View {
public:
GenericView(const std::string& file_path, const std::string& project_path, Glib::RefPtr<Gsv::Language> language);
GenericView(const std::string& file_path, Glib::RefPtr<Gsv::Language> language);
};
class ClangViewParse : public View {
public:
ClangViewParse(const std::string& file_path, const std::string& project_path);
std::string project_path;
protected:
void init_parse();
void start_reparse();
@ -169,7 +169,7 @@ namespace Source {
class ClangView : public ClangViewRefactor {
public:
ClangView(const std::string& file_path, const std::string& project_path);
void delete_object();
void async_delete();
bool restart_parse();
private:
Glib::Dispatcher do_delete_object;

254
src/terminal.cc

@ -2,6 +2,84 @@
#include <iostream>
#include "logging.h"
#include "singletons.h"
#include <unistd.h>
#include <sys/wait.h>
#include <unordered_map>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
int execute_status=-1;
std::mutex async_and_sync_execute_mutex;
std::unordered_map<pid_t, std::vector<int> > async_execute_descriptors;
std::unordered_map<pid_t, int> async_execute_status;
//TODO: Windows...
//Coppied partially from http://www.linuxprogrammingblog.com/code-examples/sigaction
void signal_execl_exit(int sig, siginfo_t *siginfo, void *context) {
async_and_sync_execute_mutex.lock();
if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) {
close(async_execute_descriptors.at(siginfo->si_pid)[0]);
close(async_execute_descriptors.at(siginfo->si_pid)[1]);
close(async_execute_descriptors.at(siginfo->si_pid)[2]);
}
int status;
while (waitpid(siginfo->si_pid, &status, WNOHANG) > 0) {}
if(async_execute_descriptors.find(siginfo->si_pid)!=async_execute_descriptors.end()) {
async_execute_status[siginfo->si_pid]=status;
async_execute_descriptors.erase(siginfo->si_pid);
}
else
execute_status=status;
async_and_sync_execute_mutex.unlock();
}
//TODO: Windows...
//TODO: Someone who knows this stuff see if I have done something stupid.
//Copied partially from http://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2
pid_t popen3(const char *command, int *input_descriptor, int *output_descriptor, int *error_descriptor) {
pid_t pid;
int p_stdin[2], p_stdout[2], p_stderr[2];
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0) {
close(p_stdin[1]);
dup2(p_stdin[0], 0);
close(p_stdout[0]);
dup2(p_stdout[1], 1);
close(p_stderr[0]);
dup2(p_stderr[1], 2);
setpgid(0, 0);
execl("/bin/sh", "sh", "-c", command, NULL);
perror("execl");
exit(1);
}
if (input_descriptor == NULL)
close(p_stdin[1]);
else
*input_descriptor = p_stdin[1];
if (output_descriptor == NULL)
close(p_stdout[0]);
else
*output_descriptor = p_stdout[0];
if (error_descriptor == NULL)
close(p_stderr[0]);
else
*error_descriptor = p_stderr[0];
return pid;
}
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) {
waiting_print.connect([this](){
@ -48,85 +126,153 @@ Terminal::Terminal() {
scrolled_window.add(text_view);
add(scrolled_window);
change_folder_command = "";
text_view.signal_size_allocate().connect([this](Gtk::Allocation& allocation){
auto end=text_view.get_buffer()->create_mark(text_view.get_buffer()->end());
text_view.scroll_to(end);
text_view.get_buffer()->delete_mark(end);
});
bold_tag=text_view.get_buffer()->create_tag();
bold_tag->property_weight()=PANGO_WEIGHT_BOLD;
async_print_dispatcher.connect([this](){
async_print_strings_mutex.lock();
if(async_print_strings.size()>0) {
for(auto &string_bold: async_print_strings)
print(string_bold.first, string_bold.second);
async_print_strings.clear();
}
async_print_strings_mutex.unlock();
});
//Coppied from http://www.linuxprogrammingblog.com/code-examples/sigaction
struct sigaction act;
memset (&act, '\0', sizeof(act));
act.sa_sigaction = &signal_execl_exit;
act.sa_flags = SA_SIGINFO;
sigaction(SIGCHLD, &act, NULL);
}
bool Terminal::execute(const std::string &path, const std::string &command) {
int Terminal::execute(const std::string &command, const std::string &path) {
boost::filesystem::path boost_path;
if(path=="")
boost_path=boost::filesystem::current_path();
else
std::string cd_path_and_command;
if(path!="") {
boost_path=boost::filesystem::path(path);
//TODO: Windows...
auto cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command;
cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command;
}
else
cd_path_and_command=command;
FILE* p = NULL;
FILE* p;
p = popen(cd_path_and_command.c_str(), "r");
if (p == NULL) {
print("Error: Failed to run command" + command + "\n");
return false;
return -1;
}
else {
char buffer[1028];
while (fgets(buffer, 1028, p) != NULL) {
char buffer[1024];
while (fgets(buffer, 1024, p) != NULL) {
print(buffer);
while(gtk_events_pending())
gtk_main_iteration();
}
async_and_sync_execute_mutex.lock();
int exit_code=pclose(p);
if(exit_code==0)
return true;
else
return false;
if(exit_code==-1)
exit_code=execute_status;
async_and_sync_execute_mutex.unlock();
return exit_code;
}
}
void Terminal::async_execute(const std::string &path, const std::string &command) {
}
void Terminal::async_execute(const std::string &command, const std::string &path, std::function<void(int exit_code)> callback) {
std::thread async_execute_thread([this, command, path, callback](){
boost::filesystem::path boost_path;
std::string cd_path_and_command;
if(path!="") {
boost_path=boost::filesystem::path(path);
void Terminal::set_change_folder_command(boost::filesystem::path CMake_path) {
INFO("Terminal: set_change_folder_command");
path = CMake_path.string();
change_folder_command = "cd "+ path + "; ";
}
//TODO: Windows...
cd_path_and_command="cd "+boost_path.string()+" && "+command;
}
else
cd_path_and_command=command;
void Terminal::compile() {
INFO("Terminal: compile");
text_view.get_buffer()->set_text("");
DEBUG("Terminal: compile: running cmake command");
std::vector<std::string> commands = Singleton::Config::terminal()->compile_commands;
for (size_t it = 0; it < commands.size(); ++it) {
execute_command(commands.at(it), "r");
int input_descriptor, output_descriptor, error_descriptor;
async_and_sync_execute_mutex.lock();
auto pid=popen3(cd_path_and_command.c_str(), &input_descriptor, &output_descriptor, &error_descriptor);
async_execute_descriptors[pid]={input_descriptor, output_descriptor, error_descriptor};
async_and_sync_execute_mutex.unlock();
if (pid<=0) {
async_print("Error: Failed to run command: " + command + "\n");
if(callback)
callback(-1);
}
else {
std::thread error_thread([this, error_descriptor](){
char buffer[1024];
ssize_t n;
while ((n=read(error_descriptor, buffer, 1024)) > 0) {
std::string message;
for(int c=0;c<n;c++)
message+=buffer[c];
async_print(message, true);
}
print("\n");
DEBUG("Terminal: compile: compile done");
});
error_thread.detach();
char buffer[1024];
ssize_t n;
while ((n=read(output_descriptor, buffer, 1024)) > 0) {
std::string message;
for(int c=0;c<n;c++)
message+=buffer[c];
async_print(message);
}
async_and_sync_execute_mutex.lock();
int exit_code=async_execute_status.at(pid);
async_execute_status.erase(pid);
if(async_execute_descriptors.find(pid)!=async_execute_descriptors.end()) //cleanup in case signal_execl_exit is not called
async_execute_descriptors.erase(pid);
async_and_sync_execute_mutex.unlock();
if(callback)
callback(exit_code);
}
});
async_execute_thread.detach();
}
void Terminal::run(std::string executable) {
INFO("Terminal: run");
print("juCi++ execute: " + executable + "\n");
DEBUG("Terminal: compile: running run command: ");
DEBUG_VAR(executable);
execute_command("cd "+Singleton::Config::terminal()->run_command + "; ./"+executable, "r");
print("\n");
void Terminal::kill_executing() {
async_and_sync_execute_mutex.lock();
int status;
for(auto &pid: async_execute_descriptors) {
close(async_execute_descriptors.at(pid.first)[0]);
close(async_execute_descriptors.at(pid.first)[1]);
close(async_execute_descriptors.at(pid.first)[2]);
kill(-pid.first, SIGINT); //signal_execl_exit is not always called after kill!
while(waitpid(-pid.first, &status, WNOHANG) > 0) {}
async_execute_status[pid.first]=status;
}
async_and_sync_execute_mutex.unlock();
}
int Terminal::print(std::string message){
int Terminal::print(const std::string &message, bool bold){
INFO("Terminal: PrintMessage");
text_view.get_buffer()->insert(text_view.get_buffer()->end(), "> "+message);
if(bold)
text_view.get_buffer()->insert_with_tag(text_view.get_buffer()->end(), message, bold_tag);
else
text_view.get_buffer()->insert(text_view.get_buffer()->end(), message);
return text_view.get_buffer()->end().get_line();
}
void Terminal::print(int line_nr, std::string message){
void Terminal::print(int line_nr, const std::string &message, bool bold){
INFO("Terminal: PrintMessage at line " << line_nr);
auto iter=text_view.get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line())
iter++;
if(bold)
text_view.get_buffer()->insert_with_tag(iter, message, bold_tag);
else
text_view.get_buffer()->insert(iter, message);
}
@ -135,21 +281,13 @@ std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string st
return in_progress;
}
void Terminal::execute_command(std::string command, std::string mode) {
INFO("Terminal: execute_command");
command = change_folder_command+command;
DEBUG("Terminal: PrintMessage: running command");
FILE* p = NULL;
std::cout << command << std::endl;
p = popen(command.c_str(), mode.c_str());
if (p == NULL) {
print("juCi++ ERROR: Failed to run command" + command + "\n");
}
else {
char buffer[1028];
while (fgets(buffer, 1028, p) != NULL) {
print(buffer);
}
pclose(p);
}
void Terminal::async_print(const std::string &message, bool bold) {
async_print_strings_mutex.lock();
bool dispatch=true;
if(async_print_strings.size()>0)
dispatch=false;
async_print_strings.emplace_back(message, bold);
async_print_strings_mutex.unlock();
if(dispatch)
async_print_dispatcher();
}

31
src/terminal.h

@ -10,12 +10,6 @@
class Terminal : public Gtk::HBox {
public:
class Config {
public:
std::vector<std::string> compile_commands;
std::string run_command;
};
class InProgress {
public:
InProgress(const std::string& start_msg);
@ -31,25 +25,22 @@ public:
};
Terminal();
bool execute(const std::string &path, const std::string &command);
void async_execute(const std::string &path, const std::string &command);
void set_change_folder_command(boost::filesystem::path CMake_path); //TODO: remove
void run(std::string executable); //TODO: remove
void compile(); //TODO: remove
int print(std::string message);
void print(int line_nr, std::string message);
int execute(const std::string &command, const std::string &path="");
void async_execute(const std::string &command, const std::string &path="", std::function<void(int exit_code)> callback=nullptr);
void kill_executing();
int print(const std::string &message, bool bold=false);
void print(int line_nr, const std::string &message, bool bold=false);
std::shared_ptr<InProgress> print_in_progress(std::string start_msg);
void async_print(const std::string &message, bool bold=false);
private:
void execute_command(std::string command, std::string mode); //TODO: remove
Gtk::TextView text_view;
Gtk::ScrolledWindow scrolled_window;
std::string change_folder_command; //TODO: remove
std::string path; //TODO: remove
const std::string cmake_sucsess = "Build files have been written to:"; //TODO: remove
const std::string make_built = "Built target"; //TODO: remove
const std::string make_executable = "Linking CXX executable"; //TODO: remove
Glib::Dispatcher async_print_dispatcher;
std::vector<std::pair<std::string, bool> > async_print_strings;
std::mutex async_print_strings_mutex;
Glib::RefPtr<Gtk::TextTag> bold_tag;
};
#endif // JUCI_TERMINAL_H_

101
src/window.cc

@ -21,7 +21,7 @@ void Window::generate_keybindings() {
}
}
Window::Window() : box(Gtk::ORIENTATION_VERTICAL) {
Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compiling(false) {
INFO("Create Window");
set_title("juCi++");
set_default_size(600, 400);
@ -97,6 +97,11 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL) {
notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) {
entry_box.hide();
});
compile_success.connect([this](){
directories.open_folder();
});
INFO("Window created");
} // Window constructor
@ -203,41 +208,52 @@ void Window::create_menu() {
});
menu.action_group->add(Gtk::Action::create("ProjectCompileAndRun", "Compile And Run"), Gtk::AccelKey(menu.key_map["compile_and_run"]), [this]() {
if(notebook.get_current_page()==-1)
if(notebook.get_current_page()==-1 || compiling)
return;
notebook.save_current();
if (running.try_lock()) {
std::thread execute([this]() {
std::string path = notebook.get_current_view()->file_path;
size_t pos = path.find_last_of("/\\");
if(pos != std::string::npos) {
path.erase(path.begin()+pos,path.end());
Singleton::terminal()->set_change_folder_command(path);
CMake cmake(notebook.get_current_view()->file_path);
directories.open_folder();
auto executables = cmake.get_functions_parameters("add_executable");
std::string executable;
boost::filesystem::path path;
if(executables.size()>0 && executables[0].second.size()>0) {
executable=executables[0].second[0];
path=executables[0].first.parent_path();
path+="/"+executables[0].second[0];
}
if(cmake.project_path!="") {
if(path!="") {
compiling=true;
Singleton::terminal()->print("Compiling and executing "+path.string()+"\n");
//TODO: Windows...
Singleton::terminal()->async_execute("make", cmake.project_path.string(), [this, path](int exit_code){
compiling=false;
if(exit_code==EXIT_SUCCESS) {
compile_success();
//TODO: Windows...
Singleton::terminal()->async_execute(path.string(), path.parent_path().string(), [this, path](int exit_code){
Singleton::terminal()->async_print(path.string()+" returned: "+std::to_string(exit_code)+'\n');
});
}
Singleton::terminal()->compile();
std::string executable = directories.get_cmakelists_variable(path,"add_executable");
Singleton::terminal()->run(executable);
running.unlock();
});
execute.detach();
}
else
Singleton::terminal()->print("Could not find an executable, please use add_executable in CMakeLists.txt\n");
}
});
menu.action_group->add(Gtk::Action::create("ProjectCompile", "Compile"), Gtk::AccelKey(menu.key_map["compile"]), [this]() {
if(notebook.get_current_page()==-1)
if(notebook.get_current_page()==-1 || compiling)
return;
notebook.save_current();
if (running.try_lock()) {
std::thread execute([this]() {
std::string path = notebook.get_current_view()->file_path;
size_t pos = path.find_last_of("/\\");
if(pos != std::string::npos){
path.erase(path.begin()+pos,path.end());
Singleton::terminal()->set_change_folder_command(path);
}
Singleton::terminal()->compile();
running.unlock();
CMake cmake(notebook.get_current_view()->file_path);
directories.open_folder();
if(cmake.project_path!="") {
compiling=true;
Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n");
//TODO: Windows...
Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](int exit_code){
compiling=false;
if(exit_code==EXIT_SUCCESS)
compile_success();
});
execute.detach();
}
});
@ -250,8 +266,11 @@ void Window::create_menu() {
}
bool Window::on_key_press_event(GdkEventKey *event) {
if(event->keyval==GDK_KEY_Escape)
if(event->keyval==GDK_KEY_Escape) {
if(entry_box.entries.size()==0)
Singleton::terminal()->kill_executing();
entry_box.hide();
}
#ifdef __APPLE__ //For Apple's Command-left, right, up, down keys
else if((event->state & GDK_META_MASK)>0) {
if(event->keyval==GDK_KEY_Left) {
@ -293,6 +312,7 @@ void Window::hide() {
if(!notebook.close_current_page())
return;
}
Singleton::terminal()->kill_executing();
Gtk::Window::hide();
}
@ -301,16 +321,16 @@ void Window::new_file_entry() {
entry_box.entries.emplace_back("untitled", [this](const std::string& content){
std::string filename=content;
if(filename!="") {
if(notebook.project_path!="" && !boost::filesystem::path(filename).is_absolute())
filename=notebook.project_path+"/"+filename;
if(directories.current_path!="" && !boost::filesystem::path(filename).is_absolute())
filename=directories.current_path.string()+"/"+filename;
boost::filesystem::path p(filename);
if(boost::filesystem::exists(p)) {
Singleton::terminal()->print("Error: "+p.string()+" already exists.\n");
}
else {
if(juci::filesystem::write(p)) {
if(notebook.project_path!="")
directories.open_folder(notebook.project_path);
if(directories.current_path!="")
directories.open_folder();
notebook.open(boost::filesystem::canonical(p).string());
Singleton::terminal()->print("New file "+p.string()+" created.\n");
}
@ -329,8 +349,8 @@ void Window::new_file_entry() {
void Window::open_folder_dialog() {
Gtk::FileChooserDialog dialog("Please choose a folder", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
if(notebook.project_path.size()>0)
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), notebook.project_path.c_str());
if(directories.current_path!="")
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str());
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
@ -342,17 +362,14 @@ void Window::open_folder_dialog() {
if(result==Gtk::RESPONSE_OK) {
std::string project_path=dialog.get_filename();
notebook.project_path=project_path;
directories.open_folder(project_path);
if(notebook.get_current_page()!=-1)
directories.select_path(notebook.get_current_view()->file_path);
}
}
void Window::open_file_dialog() {
Gtk::FileChooserDialog dialog("Please choose a file", Gtk::FILE_CHOOSER_ACTION_OPEN);
if(notebook.project_path.size()>0)
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), notebook.project_path.c_str());
if(directories.current_path!="")
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str());
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
@ -406,8 +423,8 @@ void Window::save_file_dialog() {
if(file) {
file << notebook.get_current_view()->get_buffer()->get_text();
file.close();
if(notebook.project_path!="")
directories.open_folder(notebook.project_path);
if(directories.current_path!="")
directories.open_folder();
notebook.open(path);
Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path+"\n");
}

8
src/window.h

@ -7,19 +7,22 @@
#include "notebook.h"
#include "menu.h"
#include <boost/property_tree/json_parser.hpp>
#include <atomic>
class Window : public Gtk::Window {
public:
Window();
Notebook notebook;
Directories directories;
Notebook notebook;
class Config {
public:
boost::property_tree::ptree keybindings;
};
protected:
bool on_key_press_event(GdkEventKey *event);
bool on_delete_event (GdkEventAny *event);
private:
Gtk::Box box;
Gtk::VPaned vpaned;
@ -28,8 +31,9 @@ private:
Gtk::VBox terminal_vbox;
Gtk::HBox status_hbox;
EntryBox entry_box;
std::mutex running;
Menu menu;
std::atomic<bool> compiling;
Glib::Dispatcher compile_success;
void create_menu();
void hide();

Loading…
Cancel
Save