Browse Source

Working search back and forward

merge-requests/365/head
Jørgen Lien Sellæg 11 years ago
parent
commit
a0dfad181c
  1. 4
      README.md
  2. 5
      juci/CMakeLists.txt
  3. 3
      juci/cmake/Modules/FindLibClang.cmake
  4. 14
      juci/config.cc
  5. 9
      juci/config.h
  6. 2
      juci/config.json
  7. 4
      juci/directories.cc
  8. 121
      juci/entry.cc
  9. 46
      juci/entry.h
  10. 46
      juci/juci.cc
  11. 20
      juci/juci.h
  12. 6
      juci/menu.xml
  13. 488
      juci/notebook.cc
  14. 83
      juci/notebook.h
  15. 85
      juci/selectiondialog.cc
  16. 24
      juci/selectiondialog.h
  17. 594
      juci/source.cc
  18. 87
      juci/source.h
  19. 91
      juci/terminal.cc
  20. 54
      juci/terminal.h
  21. 67
      juci/window.cc
  22. 7
      juci/window.h

4
README.md

@ -1,4 +1,6 @@
# juCi++ a lightweight C++-IDE with support for C++11 and C++14.
# juCi++
###### a lightweight C++-IDE with support for C++11 and C++14.
## About
juCi++ is a lightweight C++-IDE written in C++. You can write plugins
in Python and configure the IDE from the config.json file.

5
juci/CMakeLists.txt

@ -3,7 +3,7 @@ set(project_name juci)
set(module juci_to_python_api)
project (${project_name})
add_definitions(-DBOOST_LOG_DYN_LINK)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -std=c++11 -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -std=c++11 -pthread -Wall -Wno-reorder")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules/")
#You are of course using Homebrew:
@ -99,6 +99,7 @@ endif()
# name of the executable on Windows will be example.exe
add_executable(${project_name}
#list of every needed file to create the executable
juci.h
juci.cc
keybindings.h
keybindings.cc
@ -106,6 +107,8 @@ add_executable(${project_name}
menu.cc
source.h
source.cc
selectiondialog.h
selectiondialog.cc
config.h
config.cc
sourcefile.h

3
juci/cmake/Modules/FindLibClang.cmake

@ -14,7 +14,8 @@
# Known LLVM release numbers.
# most recent versions come first
set(LIBCLANG_KNOWN_LLVM_VERSIONS 3.6
set(LIBCLANG_KNOWN_LLVM_VERSIONS 3.6.1
3.6
3.5.1
3.5.0 #Arch Linux
3.5 #LLVM Debian/Ubuntu packages from http://llvm.org/apt/

14
juci/config.cc

@ -2,7 +2,7 @@
#include "logging.h"
MainConfig::MainConfig() :
keybindings_cfg_(), source_cfg() {
keybindings_cfg(), source_cfg() {
INFO("Reading config file");
boost::property_tree::json_parser::read_json("config.json", cfg_);
INFO("Config file read");
@ -55,10 +55,10 @@ void MainConfig::GenerateTerminalCommands() {
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_.InsertCompileCommand(i.second.get_value<std::string>());
terminal_cfg.compile_commands.emplace_back(i.second.get_value<std::string>());
}
for (auto &i : run_commands_json) {
terminal_cfg_.SetRunCommand(i.second.get_value<std::string>());
terminal_cfg.run_command=(i.second.get_value<std::string>()); //TODO: run_commands array->one run_command?
}
}
@ -68,11 +68,11 @@ void MainConfig::GenerateKeybindings() {
std::ifstream menu_xml("menu.xml");
if (menu_xml.is_open()) {
while (getline(menu_xml, line))
keybindings_cfg_.AppendXml(line);
keybindings_cfg.AppendXml(line);
}
boost::property_tree::ptree keys_json = cfg_.get_child("keybindings");
for (auto &i : keys_json)
keybindings_cfg_.key_map()[i.first] = i.second.get_value<std::string>();
keybindings_cfg.key_map()[i.first] = i.second.get_value<std::string>();
DEBUG("Keybindings fetched");
}
@ -82,8 +82,8 @@ void MainConfig::GenerateDirectoryFilter() {
boost::property_tree::ptree ignore_json = dir_json.get_child("ignore");
boost::property_tree::ptree except_json = dir_json.get_child("exceptions");
for ( auto &i : except_json )
dir_cfg_.AddException(i.second.get_value<std::string>());
dir_cfg.AddException(i.second.get_value<std::string>());
for ( auto &i : ignore_json )
dir_cfg_.AddIgnore(i.second.get_value<std::string>());
dir_cfg.AddIgnore(i.second.get_value<std::string>());
DEBUG("Directory filter fetched");
}

9
juci/config.h

@ -12,10 +12,10 @@
class MainConfig {
public:
Source::Config source_cfg;
Terminal::Config terminal_cfg;
Keybindings::Config keybindings_cfg;
Directories::Config dir_cfg;
MainConfig();
Keybindings::Config& keybindings_cfg() { return keybindings_cfg_; }
Directories::Config& dir_cfg() { return dir_cfg_; }
Terminal::Config& terminal_cfg() { return terminal_cfg_; }
void PrintMenu();
void GenerateSource();
void GenerateKeybindings();
@ -24,8 +24,5 @@ public:
private:
boost::property_tree::ptree cfg_;
boost::property_tree::ptree key_tree_;
Keybindings::Config keybindings_cfg_;
Directories::Config dir_cfg_;
Terminal::Config terminal_cfg_;
};
#endif

2
juci/config.json

@ -41,8 +41,6 @@
},
"keybindings": {
"new_file": "<control>n",
"new_h_file": "<control><alt>h",
"new_cc_file": "<control><alt>c",
"open_folder": "<control><alt>o",
"open_file": "<control>o",
"save": "<control>s",

4
juci/directories.cc

@ -43,8 +43,6 @@ list_dirs(const boost::filesystem::path& dir_path,
unsigned row_id) {
boost::filesystem::directory_iterator end_itr;
unsigned dir_counter = row_id;
unsigned file_counter = 0;
Gtk::TreeModel::Row child;
Gtk::TreeModel::Row row;
DEBUG("");
@ -82,7 +80,7 @@ list_dirs(const boost::filesystem::path& dir_path,
int Directories::Controller::count(const std::string path) {
int count = 0;
for (int i = 0; i < path.size(); i++)
for (size_t i = 0; i < path.size(); i++)
if (path[i] == '/')
count++;
return count;

121
juci/entry.cc

@ -1,74 +1,61 @@
#include "entry.h"
Entry::View::View() :
view_(Gtk::ORIENTATION_HORIZONTAL),
button_apply_(Gtk::Stock::APPLY),
button_close_(Gtk::Stock::CLOSE),
button_next_("Next"),
button_prev_("Prev"){
}
Gtk::Box& Entry::View::view() {
return view_;
}
void Entry::View::OnShowSetFilenName(std::string exstension) {
entry_.set_max_length(50);
entry_.set_text(exstension);
view_.pack_start(entry_);
view_.pack_end(button_close_, Gtk::PACK_SHRINK);
view_.pack_end(button_apply_, Gtk::PACK_SHRINK);
}
void Entry::View::OnShowSearch(std::string current){
entry_.set_max_length(50);
entry_.set_text(current);
view_.pack_start(entry_);
view_.pack_start(button_next_, Gtk::PACK_SHRINK);
view_.pack_start(button_prev_, Gtk::PACK_SHRINK);
view_.pack_end(button_close_, Gtk::PACK_SHRINK);
}
void Entry::View::OnHideEntry(bool is_new_file)
{
view_.remove(entry_);
view_.remove(button_close_);
if(!is_new_file){
view_.remove(button_next_);
view_.remove(button_prev_);
}else{
view_.remove(button_apply_);
}
}
Entry::Controller::Controller() {
}
Gtk::Box& Entry::Controller::view() {
return view_.view();
}
void Entry::Controller::OnShowSetFilenName(std::string exstension) {
view_.OnShowSetFilenName(exstension);
view_.view().show_all();
view_.entry().grab_focus();
view_.entry().set_position(0);
Entry::Entry() :
Gtk::Box(Gtk::ORIENTATION_HORIZONTAL),
button_apply_set_filename(Gtk::Stock::APPLY),
button_close(Gtk::Stock::CLOSE),
button_next("Next"),
button_prev("Prev"){
entry.signal_activate().connect([this](){
if(activate) {
activate();
}
});
entry.signal_key_press_event().connect(sigc::mem_fun(*this, &Entry::on_key_press), false);
}
void Entry::Controller::OnShowSearch(std::string current){
view_.OnShowSearch(current);
view_.view().show_all();
view_.entry().grab_focus();
view_.entry().set_position(0);
}
void Entry::Controller::OnHideEntries(bool is_new_file){
view_.OnHideEntry(is_new_file);
}
std::string Entry::Controller::text(){
return view_.entry().get_text();
}
Gtk::Button& Entry::Controller::button_apply(){
return view_.button_apply();
}
Gtk::Button& Entry::Controller::button_close(){
return view_.button_close();
bool Entry::on_key_press(GdkEventKey* key) {
if(key->keyval==GDK_KEY_Escape)
hide();
return false;
}
Gtk::Button& Entry::Controller::button_next(){
return view_.button_next();
void Entry::show_set_filename() {
hide();
entry.set_max_length(50);
entry.set_text("");
pack_start(entry);
pack_end(button_close, Gtk::PACK_SHRINK);
pack_end(button_apply_set_filename, Gtk::PACK_SHRINK);
show_all();
entry.grab_focus();
entry.set_position(0);
activate=[this](){
button_apply_set_filename.clicked();
};
}
Gtk::Button& Entry::Controller::button_prev(){
return view_.button_prev();
void Entry::show_search(const std::string& current){
hide();
entry.set_max_length(50);
entry.set_text(current);
pack_start(entry);
pack_start(button_next, Gtk::PACK_SHRINK);
pack_start(button_prev, Gtk::PACK_SHRINK);
pack_end(button_close, Gtk::PACK_SHRINK);
show_all();
entry.grab_focus();
entry.set_position(0);
activate=[this](){
button_next.clicked();
};
}
void Entry::hide() {
auto widgets=get_children();
for(auto &w: widgets)
remove(*w);
}
std::string Entry::operator()() {
return entry.get_text();
}

46
juci/entry.h

@ -2,43 +2,21 @@
#define JUCI_ENTRY_H_
#include <iostream>
#include <functional>
#include "gtkmm.h"
#include "keybindings.h"
namespace Entry {
class View {
class Entry: public Gtk::Box {
public:
View();
Gtk::Box& view();
Gtk::Entry& entry(){return entry_;}
Gtk::Button& button_apply(){return button_apply_;};
Gtk::Button& button_close(){return button_close_;};
Gtk::Button& button_next(){return button_next_;};
Gtk::Button& button_prev(){return button_prev_;};
void OnShowSetFilenName(std::string exstension);
void OnShowSearch(std::string current);
void OnHideEntry(bool is_new_file);
protected:
Gtk::Box view_;
Gtk::Entry entry_;
Gtk::Button button_apply_, button_close_, button_next_, button_prev_;
Entry();
void show_set_filename();
void show_search(const std::string& current);
void hide();
std::string operator()();
Gtk::Entry entry;
Gtk::Button button_apply_set_filename, button_close, button_next, button_prev;
private:
bool on_key_press(GdkEventKey* key);
std::function<void()> activate;
};
class Controller {
public:
Controller();
Gtk::Box& view();
Gtk::Button& button_apply();
Gtk::Button& button_close();
Gtk::Button& button_next();
Gtk::Button& button_prev();
std::string text();
void OnShowSetFilenName(std::string exstension);
void OnShowSearch(std::string current);
void OnHideEntries(bool is_new_file);
View view_;
};// class controller
} // namespace notebook
#endif // JUCI_ENTRY_H_

46
juci/juci.cc

@ -1,5 +1,4 @@
#include "window.h"
#include "logging.h"
#include "juci.h"
void init_logging() {
add_common_attributes();
@ -8,12 +7,43 @@ void init_logging() {
INFO("Logging initalized");
}
int Juci::on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine> &cmd) {
Glib::set_prgname("juci");
Glib::OptionContext ctx("[PATH ...]");
Glib::OptionGroup gtk_group(gtk_get_option_group(true));
ctx.add_group(gtk_group);
int argc;
char **argv = cmd->get_arguments(argc);
ctx.parse(argc, argv);
if(argc>=2) {
for(int c=1;c<argc;c++) {
boost::filesystem::path p=boost::filesystem::canonical(argv[c]);
if(boost::filesystem::exists(p)) {
if(boost::filesystem::is_regular_file(p))
files.emplace_back(p.string());
else if(directory=="" && boost::filesystem::is_directory(p))
directory=p.string();
}
}
}
activate();
return 0;
}
void Juci::on_activate() {
window = std::unique_ptr<Window>(new Window());
add_window(*window);
window->show();
if(directory!="") {
//TODO: use the following instead, window->notebook.open_directory(directory);
window->notebook.project_path=directory;
window->notebook.directories.open_folder(directory);
}
for(auto &f: files)
window->notebook.OnOpenFile(f);
}
int main(int argc, char *argv[]) {
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(
argc,
argv,
"no.sout.juci");
init_logging();
Window window;
return app->run(window);
return Juci().run(argc, argv);
}

20
juci/juci.h

@ -0,0 +1,20 @@
#ifndef JUCI_JUCI_H_
#define JUCI_JUCI_H_
#include "window.h"
#include "logging.h"
class Juci : public Gtk::Application {
public:
Juci(): Gtk::Application("no.sout.juci", Gio::APPLICATION_HANDLES_COMMAND_LINE) {}
int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine> &cmd);
void on_activate();
private:
std::unique_ptr<Window> window;
std::string directory;
std::vector<std::string> files;
};
#endif // JUCI_JUCI_H_

6
juci/menu.xml

@ -1,11 +1,7 @@
<ui>
<menubar name='MenuBar'>
<menu action='FileMenu'>
<menu action='FileNew'>
<menuitem action='FileNewStandard'/>
<menuitem action='FileNewCC'/>
<menuitem action='FileNewH'/>
</menu>
<menuitem action='FileNewFile'/>
<menuitem action='FileOpenFile'/>
<menuitem action='FileOpenFolder'/>
<menuitem action='FileSave'/>

488
juci/notebook.cc

@ -4,32 +4,22 @@
#include <gtksourceview/gtksource.h> // c-library
Notebook::Model::Model() {
cc_extension_ = ".cpp";
h_extension_ = ".hpp";
scrollvalue_ = 50;
Notebook::View::View() {
pack2(notebook);
set_position(120);
}
Notebook::View::View() : notebook_() {
view_.pack2(notebook_);
view_.set_position(120);
}
Notebook::Controller::Controller(Gtk::Window* window,
Keybindings::Controller& keybindings,
Notebook::Controller::Controller(Keybindings::Controller& keybindings,
Terminal::Controller& terminal,
Source::Config& source_cfg,
Directories::Config& dir_cfg) :
directories_(dir_cfg),
source_config_(source_cfg) {
terminal(terminal),
directories(dir_cfg),
source_config(source_cfg) {
INFO("Create notebook");
window_ = window;
OnNewPage("untitled");
refClipboard_ = Gtk::Clipboard::get();
ispopup = false;
view().pack1(directories_.widget(), true, true);
view.pack1(directories.widget(), true, true);
CreateKeybindings(keybindings);
end = CurrentTextView().get_source_buffer()->end();
start = CurrentTextView().get_source_buffer()->end();
INFO("Notebook Controller Success");
} // Constructor
@ -37,7 +27,7 @@ Notebook::Controller::Controller(Gtk::Window* window,
void Notebook::Controller::CreateKeybindings(Keybindings::Controller
&keybindings) {
INFO("Notebook create signal handlers");
directories().m_TreeView.signal_row_activated()
directories.m_TreeView.signal_row_activated()
.connect(sigc::mem_fun(*this,
&Notebook::Controller::OnDirectoryNavigation));
@ -46,31 +36,12 @@ void Notebook::Controller::CreateKeybindings(Keybindings::Controller
Gtk::Stock::FILE));
keybindings.action_group_menu()->
add(Gtk::Action::create("FileNewStandard",
"New empty file"),
add(Gtk::Action::create("FileNewFile",
"New file"),
Gtk::AccelKey(keybindings.config_
.key_map()["new_file"]),
[this]() {
is_new_file_ = true;
OnFileNewEmptyfile();
});
keybindings.action_group_menu()->
add(Gtk::Action::create("FileNewCC",
"New source file"),
Gtk::AccelKey(keybindings.config_
.key_map()["new_cc_file"]),
[this]() {
is_new_file_ = true;
OnFileNewCCFile();
});
keybindings.action_group_menu()->
add(Gtk::Action::create("FileNewH",
"New header file"),
Gtk::AccelKey(keybindings.config_
.key_map()["new_h_file"]),
[this]() {
is_new_file_ = true;
OnFileNewHeaderFile();
OnFileNewFile();
});
keybindings.action_group_menu()->
add(Gtk::Action::create("WindowCloseTab",
@ -86,9 +57,7 @@ void Notebook::Controller::CreateKeybindings(Keybindings::Controller
Gtk::AccelKey(keybindings.config_
.key_map()["edit_find"]),
[this]() {
is_new_file_ = false;
OnEditSearch();
// TODO(Oyvang) Zalox, Forgi)Create function OnEditFind();
entry.show_search("");
});
keybindings.action_group_menu()->
add(Gtk::Action::create("EditCopy",
@ -97,7 +66,9 @@ void Notebook::Controller::CreateKeybindings(Keybindings::Controller
.key_map()["edit_copy"]),
[this]() {
OnEditCopy();
if (Pages() != 0) {
CurrentTextView().get_buffer()->copy_clipboard(refClipboard_);
}
});
keybindings.action_group_menu()->
add(Gtk::Action::create("EditCut",
@ -105,7 +76,9 @@ void Notebook::Controller::CreateKeybindings(Keybindings::Controller
Gtk::AccelKey(keybindings.config_
.key_map()["edit_cut"]),
[this]() {
OnEditCut();
if (Pages() != 0) {
CurrentTextView().get_buffer()->cut_clipboard(refClipboard_);
}
});
keybindings.action_group_menu()->
add(Gtk::Action::create("EditPaste",
@ -113,7 +86,9 @@ void Notebook::Controller::CreateKeybindings(Keybindings::Controller
Gtk::AccelKey(keybindings.config_
.key_map()["edit_paste"]),
[this]() {
OnEditPaste();
if (Pages() != 0) {
CurrentTextView().get_buffer()->paste_clipboard(refClipboard_);
}
});
keybindings.action_group_menu()->
@ -147,211 +122,88 @@ void Notebook::Controller::CreateKeybindings(Keybindings::Controller
INFO("Done Redo");
});
entry_.view_.entry().signal_activate().
connect(
[this]() {
if (is_new_file_) {
OnNewPage(entry_.text());
entry_.OnHideEntries(is_new_file_);
} else {
Search(true);
entry.button_apply_set_filename.signal_clicked().connect([this]() {
std::string filename=entry();
if(filename!="") {
if(project_path!="" && !boost::filesystem::path(filename).is_absolute())
filename=project_path+"/"+filename;
boost::filesystem::path p(filename);
if(boost::filesystem::exists(p)) {
//TODO: alert user that file already exists
}
else {
std::ofstream f(p.string().c_str());
if(f) {
OnOpenFile(boost::filesystem::canonical(p).string());
if(project_path!="")
directories.open_folder(project_path); //TODO: Do refresh instead
}
else {
//TODO: alert user of error creating file
}
f.close();
}
}
entry.hide();
});
entry_.button_apply().signal_clicked().
connect(
[this]() {
OnNewPage(entry_.text());
entry_.OnHideEntries(is_new_file_);
});
entry_.button_close().signal_clicked().
entry.button_close.signal_clicked().
connect(
[this]() {
entry_.OnHideEntries(is_new_file_);
entry.hide();
});
entry_.button_next().signal_clicked().
entry.button_next.signal_clicked().
connect(
[this]() {
Search(true);
search(true);
});
entry_.button_prev().signal_clicked().
entry.button_prev.signal_clicked().
connect(
[this]() {
Search(false);
search(false);
});
INFO("Notebook signal handlers sucsess");
}
bool Notebook::Controller:: OnMouseRelease(GdkEventButton* button) {
if (button->button == 1 && ispopup) {
popup_.response(Gtk::RESPONSE_DELETE_EVENT);
return true;
}
return false;
}
bool Notebook::Controller::OnKeyRelease(GdkEventKey* key) {
return GeneratePopup(key->keyval);
}
bool Notebook::Controller::GeneratePopup(int key_id) {
INFO("Notebook genereate popup, getting iters");
std::string path = text_vec_.at(CurrentPage())->parser.file_path;
if (!source_config().legal_extension(path.substr(path.find_last_of(".") + 1))) return false;
// Get function to fill popup with suggests item vector under is for testing
Gtk::TextIter beg = CurrentTextView().get_buffer()->get_insert()->get_iter();
Gtk::TextIter end = CurrentTextView().get_buffer()->get_insert()->get_iter();
Gtk::TextIter tmp = CurrentTextView().get_buffer()->get_insert()->get_iter();
Gtk::TextIter tmp1 = CurrentTextView().get_buffer()->get_insert()->get_iter();
Gtk::TextIter line =
CurrentTextView().get_buffer()->get_iter_at_line(tmp.get_line());
if (end.backward_char() && end.backward_char()) {
bool illegal_chars =
end.backward_search("\"", Gtk::TEXT_SEARCH_VISIBLE_ONLY, tmp, tmp1, line)
||
end.backward_search("//", Gtk::TEXT_SEARCH_VISIBLE_ONLY, tmp, tmp1, line);
INFO("Notebook genereate popup, checking key_id");
if (illegal_chars) {
return false;
}
std::string c = text_vec_[CurrentPage()]->buffer()->get_text(end, beg);
switch (key_id) {
case 46:
break;
case 58:
if (c != "::") return false;
break;
case 60:
if (c != "->") return false;
break;
case 62:
if (c != "->") return false;
break;
default:
return false;
}
} else {
return false;
}
INFO("Notebook genereate popup, getting autocompletions");
std::vector<Source::AutoCompleteData> acdata=text_vec_.at(CurrentPage())->parser.
get_autocomplete_suggestions(beg.get_line()+1,
beg.get_line_offset()+2);
std::map<std::string, std::string> items;
for (auto &data : acdata) {
std::stringstream ss;
std::string return_value;
for (auto &chunk : data.chunks) {
switch (chunk.kind) {
case clang::CompletionChunk_ResultType:
return_value = chunk.chunk;
break;
case clang::CompletionChunk_Informative: break;
default: ss << chunk.chunk; break;
}
}
if (ss.str().length() > 0) { // if length is 0 the result is empty
items[ss.str() + " --> " + return_value] = ss.str();
}
}
Gtk::ScrolledWindow popup_scroll_;
Gtk::ListViewText listview_(1, false, Gtk::SelectionMode::SELECTION_SINGLE);
popup_scroll_.set_policy(Gtk::PolicyType::POLICY_NEVER,
Gtk::PolicyType::POLICY_NEVER);
listview_.set_enable_search(true);
listview_.set_headers_visible(false);
listview_.set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL);
listview_.set_activate_on_single_click(true);
if (items.empty()) {
items["No suggestions found..."] = "";
}
for (auto &i : items) {
listview_.append(i.first);
}
popup_scroll_.add(listview_);
popup_.get_vbox()->pack_start(popup_scroll_);
popup_.set_transient_for(*window_);
popup_.show_all();
INFO("Notebook genereate popup, moving popup");
int popup_x = popup_.get_width();
int popup_y = items.size() * 20;
PopupSetSize(popup_scroll_, popup_x, popup_y);
int x, y;
FindPopupPosition(CurrentTextView(), popup_x, popup_y, x, y);
popup_.move(x, y+15);
INFO("Notebook genereate popup, create handler");
PopupSelectHandler(popup_, listview_, &items);
ispopup = true;
INFO("Notebook genereate popup, run popup");
popup_.run();
INFO("Notebook genereate popup, hide popup");
popup_.hide();
ispopup = false;
return true;
}
Notebook::Controller::~Controller() {
INFO("Notebook destructor");
for (auto &i : editor_vec_) delete i;
for (auto &i : scrolledtext_vec_) delete i;
}
Gtk::Paned& Notebook::Controller::view() {
return view_.view();
}
Gtk::Box& Notebook::Controller::entry_view() {
return entry_.view();
}
void Notebook::Controller::OnNewPage(std::string name) {
INFO("Notebook Generate new page");
OnCreatePage();
text_vec_.back()->on_new_empty_file();
Notebook().append_page(*editor_vec_.back(), name);
Notebook().show_all_children();
Notebook().set_current_page(Pages()-1);
Notebook().set_focus_child(text_vec_.at(Pages()-1)->view);
}
void Notebook::Controller::OnOpenFile(std::string path) {
INFO("Notebook open file");
OnCreatePage();
text_vec_.back()->on_open_file(path);
size_t pos = path.find_last_of("/\\");
INFO("Notebook create page");
text_vec_.emplace_back(new Source::Controller(source_config, path, project_path, terminal));
scrolledtext_vec_.push_back(new Gtk::ScrolledWindow());
editor_vec_.push_back(new Gtk::HBox());
scrolledtext_vec_.back()->add(*text_vec_.back()->view);
editor_vec_.back()->pack_start(*scrolledtext_vec_.back(), true, true);
size_t pos = path.find_last_of("/\\"); // TODO #windows
std::string filename=path;
if(pos!=std::string::npos)
filename=path.substr(pos+1);
Notebook().append_page(*editor_vec_.back(), filename);
Notebook().show_all_children();
Notebook().set_current_page(Pages()-1);
Notebook().set_focus_child(text_vec_.back()->view);
}
void Notebook::Controller::OnCreatePage() {
INFO("Notebook create page");
text_vec_.emplace_back(new Source::Controller(source_config(), text_vec_));
scrolledtext_vec_.push_back(new Gtk::ScrolledWindow());
editor_vec_.push_back(new Gtk::HBox());
scrolledtext_vec_.back()->add(text_vec_.back()->view);
editor_vec_.back()->pack_start(*scrolledtext_vec_.back(), true, true);
TextViewHandlers(text_vec_.back()->view);
Notebook().set_focus_child(*text_vec_.back()->view);
//Add star on tab label when the page is not saved:
text_vec_.back()->signal_buffer_changed=[this](bool was_saved) {
if(was_saved) {
std::string path=text_vec_.at(CurrentPage())->parser.file_path;
text_vec_.back()->buffer()->signal_changed().connect([this]() {
if(text_vec_.at(CurrentPage())->is_saved) {
std::string path=CurrentTextView().file_path;
size_t pos = path.find_last_of("/\\");
std::string filename=path;
if(pos!=std::string::npos)
filename=path.substr(pos+1);
Notebook().set_tab_label_text(*Notebook().get_nth_page(CurrentPage()), filename+"*");
Notebook().set_tab_label_text(*(Notebook().get_nth_page(CurrentPage())), filename+"*");
}
};
text_vec_.at(CurrentPage())->is_saved=false;
});
}
void Notebook::Controller::OnCloseCurrentPage() {
INFO("Notebook close page");
if (Pages() != 0) {
if(!text_vec_.back()->is_saved){
if(!text_vec_.at(CurrentPage())->is_saved){
AskToSaveDialog();
}
int page = CurrentPage();
@ -363,99 +215,60 @@ void Notebook::Controller::OnCloseCurrentPage() {
editor_vec_.erase(editor_vec_.begin()+page);
}
}
void Notebook::Controller::OnFileNewEmptyfile() {
entry_.OnShowSetFilenName("");
}
void Notebook::Controller::OnFileNewCCFile() {
entry_.OnShowSetFilenName(model_.cc_extension_);
}
void Notebook::Controller::OnFileNewHeaderFile() {
entry_.OnShowSetFilenName(model_.h_extension_);
}
void Notebook::Controller::OnEditCopy() {
if (Pages() != 0) {
Buffer(*text_vec_.at(CurrentPage()))->copy_clipboard(refClipboard_);
}
}
void Notebook::Controller::OnEditPaste() {
if (Pages() != 0) {
Buffer(*text_vec_.at(CurrentPage()))->paste_clipboard(refClipboard_);
}
}
void Notebook::Controller::OnEditCut() {
if (Pages() != 0) {
Buffer(*text_vec_.at(CurrentPage()))->cut_clipboard(refClipboard_);
}
}
std::string Notebook::Controller::GetCursorWord() {
INFO("Notebook get cursor word");
int page = CurrentPage();
std::string word;
Gtk::TextIter start, end;
start = Buffer(*text_vec_.at(page))->get_insert()->get_iter();
end = Buffer(*text_vec_.at(page))->get_insert()->get_iter();
if (!end.ends_line()) {
while (!end.ends_word()) {
end.forward_char();
}
}
if (!start.starts_line()) {
while (!start.starts_word()) {
start.backward_char();
}
}
word = Buffer(*text_vec_.at(page))->get_text(start, end);
// TODO(Oyvang) fix selected text
return word;
}
void Notebook::Controller::OnEditSearch() {
search_match_end_ =
Buffer(*text_vec_.at(CurrentPage()))->get_iter_at_offset(0);
entry_.OnShowSearch(GetCursorWord());
void Notebook::Controller::OnFileNewFile() {
entry.show_set_filename();
}
void Notebook::Controller::Search(bool forward) {
void Notebook::Controller::search(bool forward) {
INFO("Notebook search");
auto start = CurrentTextView().search_start;
auto end = CurrentTextView().search_end;
// fetch buffer and greate settings
auto buffer = CurrentTextView().get_source_buffer();
auto settings = gtk_source_search_settings_new();
// get search text from entry
gtk_source_search_settings_set_search_text(settings, entry_.text().c_str());
gtk_source_search_settings_set_search_text(settings, entry().c_str());
// make sure the search continues
gtk_source_search_settings_set_wrap_around(settings, true);
auto context = gtk_source_search_context_new(buffer->gobj(), settings);
gtk_source_search_context_set_highlight(context, forward);
auto itr = buffer->get_insert()->get_iter().gobj();
auto itr = buffer->get_insert()->get_iter();
buffer->remove_tag_by_name("search", start ? start : itr, end ? end : itr);
if (forward) {
DEBUG("Doing forward search");
gtk_source_search_context_forward(context,
end ? end.gobj() : itr,
end ? end.gobj() : itr.gobj(),
start.gobj(),
end.gobj());
} else {
DEBUG("Doing backward search");
gtk_source_search_context_backward(context,
start ? start.gobj() : itr.gobj(),
start.gobj(),
end.gobj());
}
buffer->apply_tag_by_name("search", start, end);
CurrentTextView().scroll_to(end);
CurrentTextView().search_start = start;
CurrentTextView().search_end = end;
}
void Notebook::Controller
::OnDirectoryNavigation(const Gtk::TreeModel::Path& path,
Gtk::TreeViewColumn* column) {
INFO("Notebook directory navigation");
Gtk::TreeModel::iterator iter = directories().m_refTreeModel->get_iter(path);
Gtk::TreeModel::iterator iter = directories.m_refTreeModel->get_iter(path);
if (iter) {
Gtk::TreeModel::Row row = *iter;
std::string upath = Glib::ustring(row[directories().view().m_col_path]);
std::string upath = Glib::ustring(row[directories.view().m_col_path]);
boost::filesystem::path fs_path(upath);
if (boost::filesystem::is_directory(fs_path)) {
directories().m_TreeView.row_expanded(path) ?
directories().m_TreeView.collapse_row(path) :
directories().m_TreeView.expand_row(path, false);
directories.m_TreeView.row_expanded(path) ?
directories.m_TreeView.collapse_row(path) :
directories.m_TreeView.expand_row(path, false);
} else {
std::stringstream sstm;
sstm << row[directories().view().m_col_path];
sstm << row[directories.view().m_col_path];
std::string file = sstm.str();
OnOpenFile(file);
}
@ -464,123 +277,22 @@ void Notebook::Controller
Source::View& Notebook::Controller::CurrentTextView() {
INFO("Getting sourceview");
return text_vec_.at(CurrentPage())->view;
return *text_vec_.at(CurrentPage())->view;
}
int Notebook::Controller::CurrentPage() {
return Notebook().get_current_page();
}
Glib::RefPtr<Gtk::TextBuffer>
Notebook::Controller::Buffer(Source::Controller &source) {
return source.view.get_buffer();
}
int Notebook::Controller::Pages() {
return Notebook().get_n_pages();
}
Gtk::Notebook& Notebook::Controller::Notebook() {
return view_.notebook();
}
void Notebook::Controller::BufferChangeHandler(Glib::RefPtr<Gtk::TextBuffer>
buffer) {
buffer->signal_end_user_action().connect(
[this]() {
//UpdateHistory();
});
}
void Notebook::Controller::TextViewHandlers(Gtk::TextView& textview) {
textview.signal_button_release_event().
connect(sigc::mem_fun(*this, &Notebook::Controller::OnMouseRelease), false);
textview.signal_key_release_event().
connect(sigc::mem_fun(*this, &Notebook::Controller::OnKeyRelease), false);
}
void Notebook::Controller::PopupSelectHandler(Gtk::Dialog &popup,
Gtk::ListViewText &listview,
std::map<std::string, std::string>
*items) {
listview.signal_row_activated().
connect([this, &listview, &popup, items](const Gtk::TreeModel::Path& path,
Gtk::TreeViewColumn*) {
std::string selected = items->
at(listview.get_text(listview.get_selected()[0]));
CurrentTextView().get_buffer()->insert_at_cursor(selected);
popup.response(Gtk::RESPONSE_DELETE_EVENT);
});
}
void Notebook::Controller::PopupSetSize(Gtk::ScrolledWindow &scroll,
int &current_x,
int &current_y) {
INFO("Notebook popup set size");
int textview_x = CurrentTextView().get_width();
int textview_y = 150;
bool is_never_scroll_x = true;
bool is_never_scroll_y = true;
if (current_x > textview_x) {
current_x = textview_x;
is_never_scroll_x = false;
}
if (current_y > textview_y) {
current_y = textview_y;
is_never_scroll_y = false;
}
scroll.set_size_request(current_x, current_y);
if (!is_never_scroll_x && !is_never_scroll_y) {
scroll.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC,
Gtk::PolicyType::POLICY_AUTOMATIC);
} else if (!is_never_scroll_x && is_never_scroll_y) {
scroll.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC,
Gtk::PolicyType::POLICY_NEVER);
} else if (is_never_scroll_x && !is_never_scroll_y) {
scroll.set_policy(Gtk::PolicyType::POLICY_NEVER,
Gtk::PolicyType::POLICY_AUTOMATIC);
}
}
std::string Notebook::Controller::CurrentPagePath(){
return text_vec_.at(CurrentPage())->parser.file_path;
}
void Notebook::Controller::FindPopupPosition(Gtk::TextView& textview,
int popup_x,
int popup_y,
int &x,
int &y) {
INFO("Notebook popup find position");
Gdk::Rectangle temp1, temp2;
textview.get_cursor_locations(
CurrentTextView().
get_buffer()->get_insert()->
get_iter(), temp1, temp2);
int textview_edge_x = 0;
int textview_edge_y = 0;
textview.buffer_to_window_coords(
Gtk::TextWindowType::TEXT_WINDOW_WIDGET,
temp1.get_x(),
temp1.get_y(),
x, y);
Glib::RefPtr<Gdk::Window> gdkw =
CurrentTextView().get_window(Gtk::TextWindowType::TEXT_WINDOW_WIDGET);
gdkw->get_origin(textview_edge_x, textview_edge_y);
x += textview_edge_x;
y += textview_edge_y;
if ((textview_edge_x-x)*-1 > textview.get_width()-popup_x) {
x -= popup_x;
if (x < textview_edge_x) x = textview_edge_x;
}
if ((textview_edge_y-y)*-1 > textview.get_height()-popup_y) {
y -= (popup_y+14) + 15;
if (x < textview_edge_y) y = textview_edge_y +15;
}
return view.notebook;
}
bool Notebook::Controller:: OnSaveFile() {
std::string path=text_vec_.at(CurrentPage())->parser.file_path;
std::string path=CurrentTextView().file_path;
return OnSaveFile(path);
}
bool Notebook::Controller:: OnSaveFile(std::string path) {
@ -590,7 +302,7 @@ bool Notebook::Controller:: OnSaveFile(std::string path) {
file.open (path);
file << CurrentTextView().get_buffer()->get_text();
file.close();
text_vec_.at(CurrentPage())->parser.file_path=path;
CurrentTextView().file_path=path;
size_t pos = path.find_last_of("/\\");
std::string filename=path;
if(pos!=std::string::npos)
@ -605,10 +317,9 @@ bool Notebook::Controller:: OnSaveFile(std::string path) {
std::string Notebook::Controller::OnSaveFileAs(){
INFO("Notebook save as");
Gtk::FileChooserDialog dialog("Please choose a file",
Gtk::FileChooserDialog dialog((Gtk::Window&)(*view.get_toplevel()), "Please choose a file",
Gtk::FILE_CHOOSER_ACTION_SAVE);
DEBUG("SET TRANSISTEN FPR");
dialog.set_transient_for(*window_);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("_Save", Gtk::RESPONSE_OK);
@ -620,7 +331,6 @@ std::string Notebook::Controller::OnSaveFileAs(){
case(Gtk::RESPONSE_OK): {
DEBUG("get_filename()");
std::string path = dialog.get_filename();
unsigned pos = path.find_last_of("/\\");
return path;
}
case(Gtk::RESPONSE_CANCEL): {
@ -637,11 +347,11 @@ std::string Notebook::Controller::OnSaveFileAs(){
void Notebook::Controller::AskToSaveDialog() {
INFO("AskToSaveDialog");
DEBUG("AskToSaveDialog: Finding file path");
Gtk::MessageDialog dialog(*window_, "Save file!",
Gtk::MessageDialog dialog((Gtk::Window&)(*view.get_toplevel()), "Save file!",
false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
dialog.set_secondary_text(
"Do you want to save: " +
text_vec_.at(CurrentPage())->parser.file_path+" ?");
CurrentTextView().file_path+" ?");
DEBUG("AskToSaveDialog: run dialog");
int result = dialog.run();

83
juci/notebook.h

@ -11,105 +11,52 @@
#include <map>
#include <sigc++/sigc++.h>
#include "clangmm.h"
#include "keybindings.h"
#include "terminal.h"
namespace Notebook {
class Model {
public:
Model();
std::string cc_extension_;
std::string h_extension_;
int scrollvalue_;
};
class View {
class View : public Gtk::Paned {
public:
View();
Gtk::Paned& view() {return view_;}
Gtk::Notebook& notebook() {return notebook_; }
protected:
Gtk::Paned view_;
Gtk::Notebook notebook_;
Gtk::Notebook notebook;
};
class Controller {
public:
Controller(Gtk::Window* window, Keybindings::Controller& keybindings,
Controller(Keybindings::Controller& keybindings,
Terminal::Controller& terminal,
Source::Config& config,
Directories::Config& dir_cfg);
~Controller();
Glib::RefPtr<Gtk::TextBuffer> Buffer(Source::Controller &source);
Source::View& CurrentTextView();
int CurrentPage();
Gtk::Box& entry_view();
Gtk::Notebook& Notebook();
std::string CurrentPagePath();
void OnBufferChange();
void BufferChangeHandler(Glib::RefPtr<Gtk::TextBuffer>
buffer);
void OnCloseCurrentPage();
std::string GetCursorWord();
void OnEditCopy();
void OnEditCut();
void OnEditPaste();
void OnEditSearch();
void OnFileNewCCFile();
void OnFileNewEmptyfile();
void OnFileNewHeaderFile();
void OnFileOpenFolder();
void OnFileNewFile();
bool OnSaveFile();
bool OnSaveFile(std::string path);
void OnDirectoryNavigation(const Gtk::TreeModel::Path& path,
Gtk::TreeViewColumn* column);
void OnNewPage(std::string name);
void OnOpenFile(std::string filename);
void OnCreatePage();
bool ScrollEventCallback(GdkEventScroll* scroll_event);
int Pages();
Directories::Controller& directories() { return directories_; }
Gtk::Paned& view();
bool GeneratePopup(int key);
void Search(bool forward);
Source::Config& source_config() { return source_config_; }
bool OnMouseRelease(GdkEventButton* button);
bool OnKeyRelease(GdkEventKey* key);
void search(bool forward);
View view;
std::string OnSaveFileAs();
bool LegalExtension(std::string extension);
Gtk::TextIter start, end;
protected:
void TextViewHandlers(Gtk::TextView& textview);
void PopupSelectHandler(Gtk::Dialog &popup,
Gtk::ListViewText &listview,
std::map<std::string, std::string>
*items);
std::string project_path;
Directories::Controller directories; //Todo: make private after creating open_directory()
Entry entry;
std::vector<std::unique_ptr<Source::Controller> > text_vec_;
private:
void CreateKeybindings(Keybindings::Controller& keybindings);
void FindPopupPosition(Gtk::TextView& textview,
int popup_x,
int popup_y,
int &x,
int &y);
void PopupSetSize(Gtk::ScrolledWindow& scroll,
int &current_x,
int &current_y);
void AskToSaveDialog();
Glib::RefPtr<Gtk::Builder> m_refBuilder;
Glib::RefPtr<Gio::SimpleActionGroup> refActionGroup;
Source::Config source_config_;
Directories::Controller directories_;
View view_;
Model model_;
bool is_new_file_;
Entry::Controller entry_;
Terminal::Controller& terminal;
Source::Config& source_config;
std::vector<std::unique_ptr<Source::Controller> > text_vec_;
std::vector<Gtk::ScrolledWindow*> scrolledtext_vec_;
std::vector<Gtk::HBox*> editor_vec_;
std::list<Gtk::TargetEntry> listTargets_;
Gtk::TextIter search_match_end_;
Gtk::TextIter search_match_start_;
Glib::RefPtr<Gtk::Clipboard> refClipboard_;
bool ispopup;
Gtk::Dialog popup_;
Gtk::Window* window_;
}; // class controller
} // namespace Notebook
#endif // JUCI_NOTEBOOK_H_

85
juci/selectiondialog.cc

@ -0,0 +1,85 @@
#include "selectiondialog.h"
SelectionDialog::SelectionDialog(Gtk::TextView& text_view): Gtk::Dialog(), text_view(text_view),
list_view_text(1, false, Gtk::SelectionMode::SELECTION_SINGLE) {
scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_NEVER);
list_view_text.set_enable_search(true);
list_view_text.set_headers_visible(false);
list_view_text.set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL);
list_view_text.set_activate_on_single_click(true);
}
void SelectionDialog::show(const std::map<std::string, std::string>& rows) {
for (auto &i : rows) {
list_view_text.append(i.first);
}
scrolled_window.add(list_view_text);
get_vbox()->pack_start(scrolled_window);
set_transient_for((Gtk::Window&)(*text_view.get_toplevel()));
show_all();
int popup_x = get_width();
int popup_y = rows.size() * 20;
adjust(popup_x, popup_y);
list_view_text.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) {
if(on_select)
on_select(list_view_text);
response(Gtk::RESPONSE_DELETE_EVENT);
});
signal_focus_out_event().connect(sigc::mem_fun(*this, &SelectionDialog::close), false);
run();
}
bool SelectionDialog::close(GdkEventFocus*) {
response(Gtk::RESPONSE_DELETE_EVENT);
return true;
}
void SelectionDialog::adjust(int current_x, int current_y) {
INFO("SelectionDialog set size");
int view_x = text_view.get_width();
int view_y = 150;
bool is_never_scroll_x = true;
bool is_never_scroll_y = true;
if (current_x > view_x) {
current_x = view_x;
is_never_scroll_x = false;
}
if (current_y > view_y) {
current_y = view_y;
is_never_scroll_y = false;
}
scrolled_window.set_size_request(current_x, current_y);
if (!is_never_scroll_x && !is_never_scroll_y) {
scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
} else if (!is_never_scroll_x && is_never_scroll_y) {
scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_NEVER);
} else if (is_never_scroll_x && !is_never_scroll_y) {
scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC);
}
INFO("SelectionDialog set position");
Gdk::Rectangle temp1, temp2;
text_view.get_cursor_locations(text_view.get_buffer()->get_insert()->get_iter(), temp1, temp2);
int view_edge_x = 0;
int view_edge_y = 0;
int x, y;
text_view.buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_WIDGET,
temp1.get_x(), temp1.get_y(), x, y);
Glib::RefPtr<Gdk::Window> gdkw = text_view.get_window(Gtk::TextWindowType::TEXT_WINDOW_WIDGET);
gdkw->get_origin(view_edge_x, view_edge_y);
x += view_edge_x;
y += view_edge_y;
if ((view_edge_x-x)*-1 > text_view.get_width()-current_x) {
x -= current_x;
if (x < view_edge_x) x = view_edge_x;
}
if ((view_edge_y-y)*-1 > text_view.get_height()-current_y) {
y -= (current_y+14) + 15;
if (x < view_edge_y) y = view_edge_y +15;
}
move(x, y+15);
}

24
juci/selectiondialog.h

@ -0,0 +1,24 @@
#ifndef JUCI_SELECTIONDIALOG_H_
#define JUCI_SELECTIONDIALOG_H_
#include "gtkmm.h"
#include "logging.h"
#include "source.h"
class SelectionDialog : public Gtk::Dialog {
public:
SelectionDialog(Gtk::TextView& text_view);
void show(const std::map<std::string, std::string>& rows);
bool close(GdkEventFocus*);
std::function<void(Gtk::ListViewText& list_view_text)> on_select;
private:
void adjust(int current_x, int current_y);
Gtk::TextView& text_view;
Gtk::ScrolledWindow scrolled_window;
Gtk::ListViewText list_view_text;
};
#endif // JUCI_SELECTIONDIALOG_H_

594
juci/source.cc

@ -6,6 +6,7 @@
#include "logging.h"
#include <algorithm>
#include <regex>
#include "selectiondialog.h"
bool Source::Config::legal_extension(std::string e) const {
std::transform(e.begin(), e.end(),e.begin(), ::tolower);
@ -20,8 +21,17 @@ bool Source::Config::legal_extension(std::string e) const {
//////////////
//// View ////
//////////////
Source::View::View() {
Source::View::View(const Source::Config& config, const std::string& file_path, const std::string& project_path):
config(config), file_path(file_path), project_path(project_path) {
Gsv::init();
set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
set_show_line_numbers(config.show_line_numbers);
set_highlight_current_line(config.highlight_current_line);
sourcefile s(file_path);
get_source_buffer()->get_undo_manager()->begin_not_undoable_action();
get_source_buffer()->set_text(s.get_content());
get_source_buffer()->get_undo_manager()->end_not_undoable_action();
search_start = search_end = this->get_buffer()->end();
}
string Source::View::get_line(size_t line_number) {
@ -40,60 +50,222 @@ string Source::View::get_line_before_insert() {
return line;
}
///////////////
//// Parser ///
///////////////
clang::Index Source::Parser::clang_index(0, 1);
//Basic indentation
bool Source::View::on_key_press(GdkEventKey* key) {
const std::regex spaces_regex(std::string("^(")+config.tab_char+"*).*$");
//Indent as in next or previous line
if(key->keyval==GDK_KEY_Return && key->state==0) {
int line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
string line(get_line_before_insert());
std::smatch sm;
if(std::regex_match(line, sm, spaces_regex)) {
if((line_nr+1)<get_source_buffer()->get_line_count()) {
string next_line=get_line(line_nr+1);
std::smatch sm2;
if(std::regex_match(next_line, sm2, spaces_regex)) {
if(sm2[1].str().size()>sm[1].str().size()) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
return true;
}
}
}
get_source_buffer()->insert_at_cursor("\n"+sm[1].str());
scroll_to(get_source_buffer()->get_insert());
return true;
}
}
//Indent right when clicking tab, no matter where in the line the cursor is. Also works on selected text.
else if(key->keyval==GDK_KEY_Tab && key->state==0) {
Gtk::TextIter selection_start, selection_end;
get_source_buffer()->get_selection_bounds(selection_start, selection_end);
int line_start=selection_start.get_line();
int line_end=selection_end.get_line();
for(int line=line_start;line<=line_end;line++) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line);
get_source_buffer()->insert(line_it, config.tab);
}
return true;
}
//Indent left when clicking shift-tab, no matter where in the line the cursor is. Also works on selected text.
else if((key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) && key->state==GDK_SHIFT_MASK) {
Gtk::TextIter selection_start, selection_end;
get_source_buffer()->get_selection_bounds(selection_start, selection_end);
int line_start=selection_start.get_line();
int line_end=selection_end.get_line();
for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
string line=get_line(line_nr);
if(!(line.size()>=config.tab_size && line.substr(0, config.tab_size)==config.tab))
return true;
}
for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_nr);
Gtk::TextIter line_plus_it=line_it;
for(unsigned c=0;c<config.tab_size;c++)
line_plus_it++;
get_source_buffer()->erase(line_it, line_plus_it);
}
return true;
}
//"Smart" backspace key
else if(key->keyval==GDK_KEY_BackSpace) {
Gtk::TextIter insert_it=get_source_buffer()->get_insert()->get_iter();
int line_nr=insert_it.get_line();
if(line_nr>0) {
string line=get_line(line_nr);
string previous_line=get_line(line_nr-1);
smatch sm;
if(std::regex_match(previous_line, sm, spaces_regex)) {
if(line==sm[1] || line==(std::string(sm[1])+config.tab) || (line+config.tab==sm[1])) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_nr);
get_source_buffer()->erase(line_it, insert_it);
}
}
}
}
return false;
}
//////////////////
//// ClangView ///
//////////////////
clang::Index Source::ClangView::clang_index(0, 0);
Source::ClangView::ClangView(const Source::Config& config, const std::string& file_path, const std::string& project_path, Terminal::Controller& terminal):
Source::View(config, file_path, project_path), terminal(terminal),
parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) {
override_font(Pango::FontDescription(config.font));
override_background_color(Gdk::RGBA(config.background));
for (auto &item : config.tags) {
get_source_buffer()->create_tag(item.first)->property_foreground() = item.second;
}
int start_offset = get_source_buffer()->begin().get_offset();
int end_offset = get_source_buffer()->end().get_offset();
auto buffer_map=get_buffer_map();
//Remove includes for first parse for initial syntax highlighting
auto& str=buffer_map[file_path];
std::size_t pos=0;
while((pos=str.find("#include", pos))!=std::string::npos) {
auto start_pos=pos;
pos=str.find('\n', pos+8);
if(pos==std::string::npos)
break;
if(start_pos==0 || str[start_pos-1]=='\n') {
str.replace(start_pos, pos-start_pos, pos-start_pos, ' ');
}
pos++;
}
init_syntax_highlighting(buffer_map,
start_offset,
end_offset,
&ClangView::clang_index);
update_syntax(extract_tokens(0, get_source_buffer()->get_text().size())); //TODO: replace get_source_buffer()->get_text().size()
//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;
});
parsing_in_progress=this->terminal.print_in_progress("Parsing "+file_path);
parse_done.connect([this](){
if(parse_thread_mapped) {
INFO("Updating syntax");
update_syntax(extract_tokens(0, get_source_buffer()->get_text().size()));
parsing_in_progress->done("done");
INFO("Syntax updated");
}
else {
parse_thread_go=true;
}
});
parse_thread=std::thread([this]() {
while(true) {
while(!parse_thread_go && !parse_thread_stop)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if(parse_thread_stop)
break;
if(!parse_thread_mapped) {
parse_thread_go=false;
parse_start();
}
else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_map_mutex.try_lock()) {
reparse(parse_thread_buffer_map);
parse_thread_go=false;
parsing_mutex.unlock();
parse_thread_buffer_map_mutex.unlock();
parse_done();
}
}
});
Source::Parser::~Parser() {
get_source_buffer()->signal_changed().connect([this]() {
parse_thread_mapped=false;
parse_thread_go=true;
});
signal_key_press_event().connect(sigc::mem_fun(*this, &Source::ClangView::on_key_press), false);
signal_key_release_event().connect(sigc::mem_fun(*this, &Source::ClangView::on_key_release), false);
}
Source::ClangView::~ClangView() {
//TODO: Is it possible to stop the clang-process in progress?
parsing_in_progress->cancel("canceled");
parse_thread_stop=true;
if(parse_thread.joinable())
parse_thread.join();
parsing_mutex.lock(); //Be sure not to destroy while still parsing with libclang
parsing_mutex.unlock();
}
void Source::Parser::
init_syntax_highlighting(const std::string &filepath,
const std::string &project_path,
const std::map<std::string, std::string>
void Source::ClangView::
init_syntax_highlighting(const std::map<std::string, std::string>
&buffers,
int start_offset,
int end_offset,
clang::Index *index) {
this->project_path=project_path;
std::vector<string> arguments = get_compilation_commands();
tu_ = std::unique_ptr<clang::TranslationUnit>(new clang::TranslationUnit(index,
filepath,
file_path,
arguments,
buffers));
}
std::map<std::string, std::string> Source::Parser::
std::map<std::string, std::string> Source::ClangView::
get_buffer_map() const {
std::map<std::string, std::string> buffer_map;
for (auto &controller : controllers) {
buffer_map.operator[](controller->parser.file_path) =
controller->buffer()->get_text().raw();
}
buffer_map[file_path]=get_source_buffer()->get_text().raw();
return buffer_map;
}
// Source::Model::UpdateLine
int Source::Parser::
int Source::ClangView::
reparse(const std::map<std::string, std::string> &buffer) {
return tu_->ReparseTranslationUnit(file_path, buffer);
}
std::vector<Source::AutoCompleteData> Source::Parser::
get_autocomplete_suggestions(int line_number,
int column) {
std::vector<Source::AutoCompleteData> Source::ClangView::
get_autocomplete_suggestions(int line_number, int column) {
INFO("Getting auto complete suggestions");
std::vector<Source::AutoCompleteData> suggestions;
auto buffer_map=get_buffer_map();
std::map<std::string, std::string> buffer_map;
buffer_map[file_path]=get_source_buffer()->get_text(get_source_buffer()->begin(), get_source_buffer()->get_insert()->get_iter());
buffer_map[file_path]+="\n";
parsing_mutex.lock();
clang::CodeCompleteResults results(tu_.get(),
file_path,
buffer_map,
line_number,
column);
column-1);
for (int i = 0; i < results.size(); i++) {
const vector<clang::CompletionChunk> chunks_ = results.get(i).get_chunks();
std::vector<AutoCompleteChunk> chunks;
@ -108,22 +280,24 @@ get_autocomplete_suggestions(int line_number,
return suggestions;
}
std::vector<std::string> Source::Parser::
std::vector<std::string> Source::ClangView::
get_compilation_commands() {
clang::CompilationDatabase db(project_path+"/");
clang::CompilationDatabase db(project_path);
clang::CompileCommands commands(file_path, &db);
std::vector<clang::CompileCommand> cmds = commands.get_commands();
std::vector<std::string> arguments;
for (auto &i : cmds) {
std::vector<std::string> lol = i.get_command_as_args();
for (int a = 1; a < lol.size()-4; a++) {
for (size_t a = 1; a < lol.size()-4; a++) {
arguments.emplace_back(lol[a]);
}
}
if(boost::filesystem::path(file_path).extension()==".h") //TODO: temporary fix for .h-files (parse as c++)
arguments.emplace_back("-xc++");
return arguments;
}
std::vector<Source::Range> Source::Parser::
std::vector<Source::Range> Source::ClangView::
extract_tokens(int start_offset, int end_offset) {
std::vector<Source::Range> ranges;
clang::SourceLocation start(tu_.get(), file_path, start_offset);
@ -143,7 +317,36 @@ extract_tokens(int start_offset, int end_offset) {
return ranges;
}
void Source::Parser::
void Source::ClangView::update_syntax(const std::vector<Source::Range> &ranges) {
if (ranges.empty() || ranges.size() == 0) {
return;
}
auto buffer = get_source_buffer();
buffer->remove_all_tags(buffer->begin(), buffer->end());
for (auto &range : ranges) {
std::string type = std::to_string(range.kind);
try {
config.types.at(type);
} catch (std::exception) {
continue;
}
int linum_start = range.start.line_number-1;
int linum_end = range.end.line_number-1;
int begin = range.start.column_offset-1;
int end = range.end.column_offset-1;
if (end < 0) end = 0;
if (begin < 0) begin = 0;
Gtk::TextIter begin_iter =
buffer->get_iter_at_line_offset(linum_start, begin);
Gtk::TextIter end_iter =
buffer->get_iter_at_line_offset(linum_end, end);
buffer->apply_tag_by_name(config.types.at(type),
begin_iter, end_iter);
}
}
void Source::ClangView::
highlight_cursor(clang::Token *token,
std::vector<Source::Range> *source_ranges) {
clang::SourceLocation location = token->get_source_location(tu_.get());
@ -159,7 +362,7 @@ highlight_cursor(clang::Token *token,
Source::Location(end_line_num,
end_offset), (int) cursor.kind());
}
void Source::Parser::
void Source::ClangView::
highlight_token(clang::Token *token,
std::vector<Source::Range> *source_ranges,
int token_kind) {
@ -175,264 +378,179 @@ highlight_token(clang::Token *token,
end_offset), token_kind);
}
////////////////////
//// Controller ////
////////////////////
// Source::Controller::Controller()
// Constructor for Controller
Source::Controller::Controller(const Source::Config &config,
const std::vector<std::unique_ptr<Source::Controller> > &controllers) :
config(config), parser(controllers), parse_thread_go(false), parse_thread_mapped(false), parse_thread_stop(false) {
INFO("Source Controller with childs constructed");
view.signal_key_press_event().connect(sigc::mem_fun(*this, &Source::Controller::on_key_press), false);
view.set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
view.override_font(Pango::FontDescription(config.font));
view.set_show_line_numbers(config.show_line_numbers);
view.set_highlight_current_line(config.highlight_current_line);
view.override_background_color(Gdk::RGBA(config.background));
for (auto &item : config.tags) {
buffer()->create_tag(item.first)->property_foreground() = item.second;
}
buffer()->signal_changed().connect([this]() {
if(signal_buffer_changed)
signal_buffer_changed(is_saved);
is_saved=false;
parse_thread_mapped=false;
parse_thread_go=true;
});
}
Source::Controller::~Controller() {
parse_thread_stop=true;
if(parse_thread.joinable())
parse_thread.join();
}
void Source::Controller::update_syntax(const std::vector<Source::Range> &ranges) {
if (ranges.empty() || ranges.size() == 0) {
return;
}
auto buffer = view.get_buffer();
buffer->remove_all_tags(buffer->begin(), buffer->end());
for (auto &range : ranges) {
std::string type = std::to_string(range.kind);
try {
config.types.at(type);
} catch (std::exception) {
continue;
}
int linum_start = range.start.line_number-1;
int linum_end = range.end.line_number-1;
int begin = range.start.column_offset-1;
int end = range.end.column_offset-1;
if (end < 0) end = 0;
if (begin < 0) begin = 0;
Gtk::TextIter begin_iter =
buffer->get_iter_at_line_offset(linum_start, begin);
Gtk::TextIter end_iter =
buffer->get_iter_at_line_offset(linum_end, end);
buffer->apply_tag_by_name(config.types.at(type),
begin_iter, end_iter);
}
}
void Source::Controller::on_new_empty_file() {
string filename("/tmp/untitled");
sourcefile s(filename);
parser.file_path=filename;
parser.project_path=filename;
s.save("");
}
void Source::Controller::on_open_file(const string &filepath) {
parser.file_path=filepath;
sourcefile s(filepath);
auto buffer_map=parser.get_buffer_map();
buffer_map[filepath] = s.get_content();
buffer()->get_undo_manager()->begin_not_undoable_action();
buffer()->set_text(s.get_content());
is_saved=true;
buffer()->get_undo_manager()->end_not_undoable_action();
int start_offset = buffer()->begin().get_offset();
int end_offset = buffer()->end().get_offset();
if (config.legal_extension(filepath.substr(filepath.find_last_of(".") + 1))) {
parser.init_syntax_highlighting(filepath,
parser.file_path.substr(0, parser.file_path.find_last_of('/')),
buffer_map,
start_offset,
end_offset,
&Parser::clang_index);
update_syntax(parser.extract_tokens(start_offset, end_offset));
//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()) {
this->parse_thread_buffer_map=parser.get_buffer_map();
parse_thread_mapped=true;
parse_thread_buffer_map_mutex.unlock();
bool Source::ClangView::on_key_release(GdkEventKey* key) {
INFO("Source::ClangView::on_key_release getting iters");
// Get function to fill popup with suggests item vector under is for testing
Gtk::TextIter beg = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter end = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter tmp = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter tmp1 = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter line = get_source_buffer()->get_iter_at_line(tmp.get_line());
if (end.backward_char() && end.backward_char()) {
bool illegal_chars =
end.backward_search("\"", Gtk::TEXT_SEARCH_VISIBLE_ONLY, tmp, tmp1, line)
||
end.backward_search("//", Gtk::TEXT_SEARCH_VISIBLE_ONLY, tmp, tmp1, line);
INFO("Source::ClangView::on_key_release checking key->keyval");
if (illegal_chars) {
return false;
}
parse_thread_go=true;
});
parse_done.connect([this](){
if(parse_thread_mapped) {
INFO("Updating syntax");
update_syntax(parser.extract_tokens(0, buffer()->get_text().size()));
INFO("Syntax updated");
std::string c = get_source_buffer()->get_text(end, beg);
switch (key->keyval) {
case 46:
break;
case 58:
if (c != "::") return false;
break;
case 60:
if (c != "->") return false;
break;
case 62:
if (c != "->") return false;
break;
default:
return false;
}
else {
parse_thread_go=true;
} else {
return false;
}
});
parse_thread=std::thread([this]() {
while(true) {
while(!parse_thread_go && !parse_thread_stop)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if(parse_thread_stop)
INFO("Source::ClangView::on_key_release getting autocompletions");
std::vector<Source::AutoCompleteData> acdata=get_autocomplete_suggestions(beg.get_line()+1,
beg.get_line_offset()+2);
std::map<std::string, std::string> rows;
for (auto &data : acdata) {
std::stringstream ss;
std::string return_value;
for (auto &chunk : data.chunks) {
switch (chunk.kind) {
case clang::CompletionChunk_ResultType:
return_value = chunk.chunk;
break;
if(!parse_thread_mapped) {
parse_thread_go=false;
parse_start();
case clang::CompletionChunk_Informative: break;
default: ss << chunk.chunk; break;
}
else if (parse_thread_mapped && parser.parsing_mutex.try_lock() && parse_thread_buffer_map_mutex.try_lock()) {
parser.reparse(this->parse_thread_buffer_map);
parse_thread_go=false;
parser.parsing_mutex.unlock();
parse_thread_buffer_map_mutex.unlock();
parse_done();
}
if (ss.str().length() > 0) { // if length is 0 the result is empty
rows[ss.str() + " --> " + return_value] = ss.str();
}
});
}
if (rows.empty()) {
rows["No suggestions found..."] = "";
}
Glib::RefPtr<Gsv::Buffer> Source::Controller::buffer() {
return view.get_source_buffer();
SelectionDialog selection_dialog(*this);
selection_dialog.on_select=[this, &rows](Gtk::ListViewText& list_view_text){
std::string selected = rows.at(list_view_text.get_text(list_view_text.get_selected()[0]));
get_source_buffer()->insert_at_cursor(selected);
};
selection_dialog.show(rows);
return true;
}
//TODO: move indentation to Parser, replace indentation methods with a better implementation or
//maybe use libclang
bool Source::Controller::on_key_press(GdkEventKey* key) {
const std::regex bracket_regex("^( *).*\\{ *$");
const std::regex no_bracket_statement_regex("^( *)(if|for|else if|catch|while) *\\(.*[^;}] *$");
const std::regex no_bracket_no_para_statement_regex("^( *)(else|try|do) *$");
const std::regex spaces_regex("^( *).*$");
//Clang indentation
//TODO: replace indentation methods with a better implementation or maybe use libclang
bool Source::ClangView::on_key_press(GdkEventKey* key) {
const std::regex bracket_regex(std::string("^(")+config.tab_char+"*).*\\{ *$");
const std::regex no_bracket_statement_regex(std::string("^(")+config.tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$");
const std::regex no_bracket_no_para_statement_regex(std::string("^(")+config.tab_char+"*)(else|try|do) *$");
const std::regex spaces_regex(std::string("^(")+config.tab_char+"*).*$");
//Indent as in previous line, and indent right after if/else/etc
//Indent depending on if/else/etc and brackets
if(key->keyval==GDK_KEY_Return && key->state==0) {
string line(view.get_line_before_insert());
string line(get_line_before_insert());
std::smatch sm;
if(std::regex_match(line, sm, bracket_regex)) {
buffer()->insert_at_cursor("\n"+sm[1].str()+config.tab+"\n"+sm[1].str()+"}");
auto insert_it = buffer()->get_insert()->get_iter();
int line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
if((line_nr+1)<get_source_buffer()->get_line_count()) {
string next_line=get_line(line_nr+1);
std::smatch sm2;
if(std::regex_match(next_line, sm2, spaces_regex)) {
if(sm2[1].str()==sm[1].str()+config.tab) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config.tab);
scroll_to(get_source_buffer()->get_insert());
return true;
}
}
}
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config.tab+"\n"+sm[1].str()+"}");
auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<config.tab_size+sm[1].str().size();c++)
insert_it--;
view.scroll_to(buffer()->get_insert());
buffer()->place_cursor(insert_it);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
return true;
}
else if(std::regex_match(line, sm, no_bracket_statement_regex)) {
buffer()->insert_at_cursor("\n"+sm[1].str()+config.tab);
view.scroll_to(buffer()->get_insert());
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config.tab);
scroll_to(get_source_buffer()->get_insert());
return true;
}
else if(std::regex_match(line, sm, no_bracket_no_para_statement_regex)) {
buffer()->insert_at_cursor("\n"+sm[1].str()+config.tab);
view.scroll_to(buffer()->get_insert());
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config.tab);
scroll_to(get_source_buffer()->get_insert());
return true;
}
else if(std::regex_match(line, sm, spaces_regex)) {
std::smatch sm2;
size_t line_nr=buffer()->get_insert()->get_iter().get_line();
size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
if(line_nr>0 && sm[1].str().size()>=config.tab_size) {
string previous_line=view.get_line(line_nr-1);
string previous_line=get_line(line_nr-1);
if(!std::regex_match(previous_line, sm2, bracket_regex)) {
if(std::regex_match(previous_line, sm2, no_bracket_statement_regex)) {
buffer()->insert_at_cursor("\n"+sm2[1].str());
view.scroll_to(buffer()->get_insert());
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
return true;
}
else if(std::regex_match(previous_line, sm2, no_bracket_no_para_statement_regex)) {
buffer()->insert_at_cursor("\n"+sm2[1].str());
view.scroll_to(buffer()->get_insert());
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
return true;
}
}
}
buffer()->insert_at_cursor("\n"+sm[1].str());
view.scroll_to(buffer()->get_insert());
}
return true;
}
//Indent right when clicking tab, no matter where in the line the cursor is. Also works on selected text.
if(key->keyval==GDK_KEY_Tab && key->state==0) {
Gtk::TextIter selection_start, selection_end;
buffer()->get_selection_bounds(selection_start, selection_end);
int line_start=selection_start.get_line();
int line_end=selection_end.get_line();
for(int line=line_start;line<=line_end;line++) {
Gtk::TextIter line_it = buffer()->get_iter_at_line(line);
buffer()->insert(line_it, config.tab);
}
return true;
}
//Indent left when clicking shift-tab, no matter where in the line the cursor is. Also works on selected text.
else if((key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) && key->state==GDK_SHIFT_MASK) {
Gtk::TextIter selection_start, selection_end;
buffer()->get_selection_bounds(selection_start, selection_end);
int line_start=selection_start.get_line();
int line_end=selection_end.get_line();
for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
string line=view.get_line(line_nr);
if(!(line.size()>=config.tab_size && line.substr(0, config.tab_size)==config.tab))
return true;
}
for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
Gtk::TextIter line_it = buffer()->get_iter_at_line(line_nr);
Gtk::TextIter line_plus_it=line_it;
for(unsigned c=0;c<config.tab_size;c++)
line_plus_it++;
buffer()->erase(line_it, line_plus_it);
}
return true;
}
//Indent left when writing } on a new line
else if(key->keyval==GDK_KEY_braceright) {
string line=view.get_line_before_insert();
string line=get_line_before_insert();
if(line.size()>=config.tab_size) {
for(auto c: line) {
if(c!=' ')
if(c!=config.tab_char)
return false;
}
Gtk::TextIter insert_it = buffer()->get_insert()->get_iter();
Gtk::TextIter line_it = buffer()->get_iter_at_line(insert_it.get_line());
Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line());
Gtk::TextIter line_plus_it=line_it;
for(unsigned c=0;c<config.tab_size;c++)
line_plus_it++;
buffer()->erase(line_it, line_plus_it);
get_source_buffer()->erase(line_it, line_plus_it);
}
return false;
}
//"Smart" backspace key
else if(key->keyval==GDK_KEY_BackSpace) {
Gtk::TextIter insert_it=buffer()->get_insert()->get_iter();
int line_nr=insert_it.get_line();
if(line_nr>0) {
string line=view.get_line(line_nr);
string previous_line=view.get_line(line_nr-1);
smatch sm;
if(std::regex_match(previous_line, sm, spaces_regex)) {
if(line==sm[1]) {
Gtk::TextIter line_it = buffer()->get_iter_at_line(line_nr);
buffer()->erase(line_it, insert_it);
}
}
return Source::View::on_key_press(key);
}
////////////////////
//// Controller ////
////////////////////
// Source::Controller::Controller()
// Constructor for Controller
Source::Controller::Controller(const Source::Config &config,
const std::string& file_path, std::string project_path, Terminal::Controller& terminal) {
if(project_path=="") {
project_path=boost::filesystem::path(file_path).parent_path().string();
}
if (config.legal_extension(file_path.substr(file_path.find_last_of(".") + 1)))
view=std::unique_ptr<View>(new ClangView(config, file_path, project_path, terminal));
else
view=std::unique_ptr<View>(new GenericView(config, file_path, project_path));
INFO("Source Controller with childs constructed");
}
return false;
Glib::RefPtr<Gsv::Buffer> Source::Controller::buffer() {
return view->get_source_buffer();
}

87
juci/source.h

@ -10,10 +10,7 @@
#include <string>
#include <atomic>
#include "gtksourceviewmm.h"
namespace Notebook {
class Controller;
}
#include "terminal.h"
namespace Source {
class Config {
@ -22,6 +19,7 @@ namespace Source {
unsigned tab_size;
bool show_line_numbers, highlight_current_line;
std::string tab, background, font;
char tab_char=' ';
std::vector<std::string> extensions;
std::unordered_map<std::string, std::string> tags, types;
}; // class Config
@ -43,13 +41,6 @@ namespace Source {
int kind;
};
class View : public Gsv::View {
public:
View();
std::string get_line(size_t line_number);
std::string get_line_before_insert();
}; // class View
class AutoCompleteChunk {
public:
explicit AutoCompleteChunk(const clang::CompletionChunk &clang_chunk) :
@ -65,17 +56,37 @@ namespace Source {
std::vector<AutoCompleteChunk> chunks;
};
class Controller;
class View : public Gsv::View {
public:
View(const Source::Config& config, const std::string& file_path, const std::string& project_path);
std::string get_line(size_t line_number);
std::string get_line_before_insert();
std::string file_path;
std::string project_path;
Gtk::TextIter search_start, search_end;
protected:
const Source::Config& config;
bool on_key_press(GdkEventKey* key);
}; // class View
class Parser{
class GenericView : public View {
public:
Parser(const std::vector<std::unique_ptr<Source::Controller> > &controllers):
controllers(controllers) {}
~Parser();
GenericView(const Source::Config& config, const std::string& file_path, const std::string& project_path):
View(config, file_path, project_path) {
signal_key_press_event().connect(sigc::mem_fun(*this, &Source::GenericView::on_key_press), false);
}
private:
bool on_key_press(GdkEventKey* key) {
return Source::View::on_key_press(key);
}
};
class ClangView : public View {
public:
ClangView(const Source::Config& config, const std::string& file_path, const std::string& project_path, Terminal::Controller& terminal);
~ClangView();
// inits the syntax highligthing on file open
void init_syntax_highlighting(const std::string &filepath,
const std::string &project_path,
const std::map<std::string, std::string>
void init_syntax_highlighting(const std::map<std::string, std::string>
&buffers,
int start_offset,
int end_offset,
@ -83,9 +94,8 @@ namespace Source {
std::vector<Source::AutoCompleteData> get_autocomplete_suggestions(int line_number, int column);
int reparse(const std::map<std::string, std::string> &buffers);
std::vector<Range> extract_tokens(int, int);
void update_syntax(const std::vector<Range> &locations);
std::string file_path;
std::string project_path;
static clang::Index clang_index;
std::map<std::string, std::string> get_buffer_map() const;
std::mutex parsing_mutex;
@ -97,29 +107,11 @@ namespace Source {
void highlight_cursor(clang::Token *token,
std::vector<Range> *source_ranges);
std::vector<std::string> get_compilation_commands();
//controllers is needed here, no way around that I think
const std::vector<std::unique_ptr<Source::Controller> > &controllers;
};
class Controller {
public:
Controller(const Source::Config &config,
const std::vector<std::unique_ptr<Source::Controller> > &controllers);
~Controller();
void update_syntax(const std::vector<Range> &locations);
void on_new_empty_file();
void on_open_file(const std::string &filename);
Glib::RefPtr<Gsv::Buffer> buffer();
bool on_key_press(GdkEventKey* key);
bool on_key_release(GdkEventKey* key);
Terminal::Controller& terminal;
std::shared_ptr<Terminal::InProgress> parsing_in_progress;
bool is_saved = true;
Parser parser;
View view;
std::function<void(bool was_saved)> signal_buffer_changed;
private:
Glib::Dispatcher parse_done;
Glib::Dispatcher parse_start;
std::thread parse_thread;
@ -128,8 +120,17 @@ namespace Source {
std::atomic<bool> parse_thread_go;
std::atomic<bool> parse_thread_mapped;
std::atomic<bool> parse_thread_stop;
};
class Controller {
public:
Controller(const Source::Config &config,
const std::string& file_path, std::string project_path, Terminal::Controller& terminal);
Glib::RefPtr<Gsv::Buffer> buffer();
bool is_saved = true;
const Config& config;
std::unique_ptr<Source::View> view;
}; // class Controller
} // namespace Source
#endif // JUCI_SOURCE_H_

91
juci/terminal.cc

@ -1,36 +1,55 @@
#include "terminal.h"
#include <iostream>
#include <thread>
#include "logging.h"
Terminal::InProgress::InProgress(Controller& terminal, const std::string& start_msg): terminal(terminal), stop(false) {
waiting_print.connect([this](){
this->terminal.print(line_nr-1, ".");
});
start(start_msg);
}
Terminal::Config::Config() {
Terminal::InProgress::~InProgress() {
stop=true;
if(wait_thread.joinable())
wait_thread.join();
}
Terminal::Config::Config(Terminal::Config& original) :
run_command_(original.run_command_){
for (auto it = 0; it<original.compile_commands().size(); ++it) {
InsertCompileCommand(original.compile_commands().at(it));
void Terminal::InProgress::start(const std::string& msg) {
line_nr=this->terminal.print(msg+"...\n");
wait_thread=std::thread([this](){
size_t c=0;
while(!stop) {
if(c%100==0)
waiting_print();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
c++;
}
});
}
void Terminal::Config::InsertCompileCommand(std::string command){
compile_commands_.push_back(command);
void Terminal::InProgress::done(const std::string& msg) {
if(!stop) {
stop=true;
this->terminal.print(line_nr-1, msg);
}
}
void Terminal::Config::SetRunCommand(std::string command){
run_command_ = command;
void Terminal::InProgress::cancel(const std::string& msg) {
if(!stop) {
stop=true;
this->terminal.print(line_nr-1, msg);
}
}
Terminal::View::View(){
scrolledwindow_.add(textview_);
scrolledwindow_.set_size_request(-1,150);
view_.add(scrolledwindow_);
textview_.set_editable(false);
text_view.set_editable(false);
scrolled_window.add(text_view);
add(scrolled_window);
}
Terminal::Controller::Controller(Terminal::Config& cfg) :
config_(cfg) {
config(cfg) {
folder_command_ = "";
}
@ -44,37 +63,51 @@ void Terminal::Controller::SetFolderCommand( boost::filesystem::path
void Terminal::Controller::Compile(){
INFO("Terminal: Compile");
Terminal().get_buffer()->set_text("");
view.text_view.get_buffer()->set_text("");
DEBUG("Terminal: Compile: running cmake command");
std::vector<std::string> commands = config().compile_commands();
std::vector<std::string> commands = config.compile_commands;
for (size_t it = 0; it < commands.size(); ++it) {
ExecuteCommand(commands.at(it), "r");
}
PrintMessage("\n");
print("\n");
DEBUG("Terminal: Compile: compile done");
}
void Terminal::Controller::Run(std::string executable) {
INFO("Terminal: Run");
PrintMessage("juCi++ execute: " + executable + "\n");
print("juCi++ execute: " + executable + "\n");
DEBUG("Terminal: Compile: running run command: ");
DEBUG_VAR(executable);
ExecuteCommand("cd "+config().run_command() + "; ./"+executable, "r");
PrintMessage("\n");
ExecuteCommand("cd "+config.run_command + "; ./"+executable, "r");
print("\n");
}
void Terminal::Controller::PrintMessage(std::string message){
int Terminal::Controller::print(std::string message){
INFO("Terminal: PrintMessage");
Terminal().get_buffer()->
insert(Terminal().get_buffer()-> end(),"> "+message);
view.text_view.get_buffer()->insert(view.text_view.get_buffer()->end(), "> "+message);
auto mark_end=view.text_view.get_buffer()->create_mark(view.text_view.get_buffer()->end());
view.text_view.scroll_to(mark_end);
return mark_end->get_iter().get_line();
}
void Terminal::Controller::print(int line_nr, std::string message){
INFO("Terminal: PrintMessage at line " << line_nr);
auto iter=view.text_view.get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line())
iter++;
view.text_view.get_buffer()->insert(iter, message);
}
std::shared_ptr<Terminal::InProgress> Terminal::Controller::print_in_progress(std::string start_msg) {
std::shared_ptr<Terminal::InProgress> in_progress=std::shared_ptr<Terminal::InProgress>(new Terminal::InProgress(*this, start_msg));
return in_progress;
}
bool Terminal::Controller::ExistInConsole(std::string string) {
INFO("Terminal: ExistInConsole");
DEBUG("Terminal: PrintMessage: finding string in buffer");
double pos = Terminal().get_buffer()->
double pos = view.text_view.get_buffer()->
get_text().find(string);
if (pos == std::string::npos) return false;
return true;
@ -87,14 +120,12 @@ void Terminal::Controller::ExecuteCommand(std::string command, std::string mode)
FILE* p = NULL;
std::cout << command << std::endl;
p = popen(command.c_str(), mode.c_str());
std::cout << "KJØRTE FINT!" << std::endl;
if (p == NULL) {
PrintMessage("juCi++ ERROR: Failed to run command" + command + "\n");
print("juCi++ ERROR: Failed to run command" + command + "\n");
}else {
std::cout << "SKRIVER UT KOMMANDO RESULAT" << std::endl;
char buffer[1028];
while (fgets(buffer, 1028, p) != NULL) {
PrintMessage(buffer);
print(buffer);
}
pclose(p);
}

54
juci/terminal.h

@ -2,51 +2,59 @@
#define JUCI_TERMINAL_H_
#include <mutex>
#include <functional>
#include "gtkmm.h"
#include <boost/filesystem.hpp>
#include <thread>
#include <atomic>
namespace Terminal {
class Config {
public:
Config ();
Config(Terminal::Config& original);
std::vector<std::string>& compile_commands() { return compile_commands_; }
void InsertCompileCommand(std::string command);
std::string& run_command() { return run_command_; }
void SetRunCommand(std::string command);
private:
std::vector<std::string> compile_commands_;
std::string run_command_;
std::vector<std::string> compile_commands;
std::string run_command;
};
class View {
class View : public Gtk::HBox {
public:
View();
Gtk::HBox& view() {return view_;}
Gtk::TextView& textview() {return textview_;}
private:
Gtk::HBox view_;
Gtk::TextView textview_;
Gtk::ScrolledWindow scrolledwindow_;
Gtk::TextView text_view;
Gtk::ScrolledWindow scrolled_window;
}; // class view
class Controller;
//Temporary solution for displaying functions in progress, and when they are done.
class InProgress {
public:
InProgress(Controller& terminal, const std::string& start_msg);
~InProgress();
void done(const std::string& msg);
void cancel(const std::string& msg);
private:
void start(const std::string& msg);
Controller& terminal;
int line_nr;
std::atomic<bool> stop;
Glib::Dispatcher waiting_print;
std::thread wait_thread;
};
class Controller {
public:
Controller(Terminal::Config& cfg);
Gtk::HBox& view() {return view_.view();}
Gtk::TextView& Terminal(){return view_.textview();}
void SetFolderCommand(boost::filesystem::path CMake_path);
void Run(std::string executable);
void Compile();
Terminal::Config& config() { return config_; }
void PrintMessage(std::string message);
int print(std::string message);
void print(int line_nr, std::string message);
std::shared_ptr<InProgress> print_in_progress(std::string start_msg);
Terminal::View view;
private:
Terminal::Config config_;
Terminal::Config& config;
void ExecuteCommand(std::string command, std::string mode);
bool OnButtonRealeaseEvent(GdkEventKey* key);
bool ExistInConsole(std::string string);
Terminal::View view_;
std::string folder_command_;
std::string path_;
const std::string cmake_sucsess = "Build files have been written to:";

67
juci/window.cc

@ -4,13 +4,13 @@
Window::Window() :
window_box_(Gtk::ORIENTATION_VERTICAL),
main_config_(),
keybindings_(main_config_.keybindings_cfg()),
terminal_(main_config_.terminal_cfg()),
notebook_(this,keybindings(),
keybindings_(main_config_.keybindings_cfg),
terminal(main_config_.terminal_cfg),
notebook(keybindings(), terminal,
main_config_.source_cfg,
main_config_.dir_cfg()),
main_config_.dir_cfg),
menu_(keybindings()),
api_(menu_, notebook_) {
api_(menu_, notebook) {
INFO("Create Window");
set_title("juCi++");
set_default_size(600, 400);
@ -66,16 +66,16 @@ Window::Window() :
SaveFile();
if (running.try_lock()) {
std::thread execute([=]() {
std::string path = notebook_.CurrentPagePath();
int pos = path.find_last_of("/\\");
std::string path = notebook.CurrentTextView().file_path;
size_t pos = path.find_last_of("/\\");
if(pos != std::string::npos) {
path.erase(path.begin()+pos,path.end());
terminal_.SetFolderCommand(path);
terminal.SetFolderCommand(path);
}
terminal_.Compile();
std::string executable = notebook_.directories().
terminal.Compile();
std::string executable = notebook.directories.
GetCmakeVarValue(path,"add_executable");
terminal_.Run(executable);
terminal.Run(executable);
running.unlock();
});
execute.detach();
@ -92,22 +92,18 @@ Window::Window() :
SaveFile();
if (running.try_lock()) {
std::thread execute([=]() {
std::string path = notebook_.CurrentPagePath();
int pos = path.find_last_of("/\\");
std::string path = notebook.CurrentTextView().file_path;
size_t pos = path.find_last_of("/\\");
if(pos != std::string::npos){
path.erase(path.begin()+pos,path.end());
terminal_.SetFolderCommand(path);
terminal.SetFolderCommand(path);
}
terminal_.Compile();
terminal.Compile();
running.unlock();
});
execute.detach();
}
});
this->signal_button_release_event().
connect(sigc::mem_fun(*this,&Window::OnMouseRelease),false);
terminal_.Terminal().signal_button_release_event().
connect(sigc::mem_fun(*this,&Window::OnMouseRelease),false);
add_accel_group(keybindings_.ui_manager_menu()->get_accel_group());
add_accel_group(keybindings_.ui_manager_hidden()->get_accel_group());
@ -115,16 +111,18 @@ Window::Window() :
window_box_.pack_start(menu_.view(), Gtk::PACK_SHRINK);
window_box_.pack_start(notebook_.entry_view(), Gtk::PACK_SHRINK);
window_box_.pack_start(notebook.entry, Gtk::PACK_SHRINK);
paned_.set_position(300);
paned_.pack1(notebook_.view(), true, false);
paned_.pack2(terminal_.view(), true, true);
paned_.pack1(notebook.view, true, false);
paned_.pack2(terminal.view, true, true);
window_box_.pack_end(paned_);
show_all_children();
INFO("Window created");
} // Window constructor
void Window::OnWindowHide() {
for(size_t c=0;c<notebook.text_vec_.size();c++)
notebook.OnCloseCurrentPage(); //TODO: This only works on one page at the momemt. Change to notebook.close_page(page_nr);
hide();
}
void Window::OnFileOpenFolder() {
@ -142,7 +140,9 @@ void Window::OnFileOpenFolder() {
{
case(Gtk::RESPONSE_OK):
{
notebook_.directories().open_folder(dialog.get_filename());
std::string project_path=dialog.get_filename();
notebook.project_path=project_path;
notebook.directories.open_folder(project_path);
break;
}
case(Gtk::RESPONSE_CANCEL):
@ -190,7 +190,7 @@ void Window::OnOpenFile() {
switch (result) {
case(Gtk::RESPONSE_OK): {
std::string path = dialog.get_filename();
notebook_.OnOpenFile(path);
notebook.OnOpenFile(path);
break;
}
case(Gtk::RESPONSE_CANCEL): {
@ -201,25 +201,22 @@ void Window::OnOpenFile() {
}
}
}
bool Window::OnMouseRelease(GdkEventButton *button){
return notebook_.OnMouseRelease(button);
}
bool Window::SaveFile() {
if(notebook_.OnSaveFile()) {
terminal_.PrintMessage("File saved to: " +
notebook_.CurrentPagePath()+"\n");
if(notebook.OnSaveFile()) {
terminal.print("File saved to: " +
notebook.CurrentTextView().file_path+"\n");
return true;
}
terminal_.PrintMessage("File not saved");
terminal.print("File not saved");
return false;
}
bool Window::SaveFileAs() {
if(notebook_.OnSaveFile(notebook_.OnSaveFileAs())){
terminal_.PrintMessage("File saved to: " +
notebook_.CurrentPagePath()+"\n");
if(notebook.OnSaveFile(notebook.OnSaveFileAs())){
terminal.print("File saved to: " +
notebook.CurrentTextView().file_path+"\n");
return true;
}
terminal_.PrintMessage("File not saved");
terminal.print("File not saved");
return false;
}

7
juci/window.h

@ -15,13 +15,11 @@ public:
Gtk::Box window_box_;
virtual ~Window() { }
//private:
MainConfig main_config_;
Keybindings::Controller keybindings_;
Menu::Controller menu_;
Notebook::Controller notebook_;
Terminal::Controller terminal_;
Notebook::Controller notebook;
Terminal::Controller terminal;
PluginApi api_;
Keybindings::Controller& keybindings() { return keybindings_; }
@ -32,7 +30,6 @@ public:
void OnWindowHide();
void OnOpenFile();
void OnFileOpenFolder();
bool OnMouseRelease(GdkEventButton* button);
bool SaveFile();
bool SaveFileAs();
};

Loading…
Cancel
Save