Browse Source

Merge branch 'master' of http://github.com/eidheim/jucipp

merge-requests/365/head
Jørgen Lien Sellæg 11 years ago
parent
commit
d94c229b8b
  1. 4
      juci/CMakeLists.txt
  2. 10
      juci/config.json
  3. 61
      juci/entry.cc
  4. 22
      juci/entry.h
  5. 68
      juci/entrybox.cc
  6. 43
      juci/entrybox.h
  7. 1
      juci/menu.xml
  8. 277
      juci/notebook.cc
  9. 15
      juci/notebook.h
  10. 288
      juci/selectiondialog.cc
  11. 35
      juci/selectiondialog.h
  12. 396
      juci/source.cc
  13. 34
      juci/source.h
  14. 29
      juci/window.cc

4
juci/CMakeLists.txt

@ -117,8 +117,8 @@ add_executable(${project_name}
api.cc api.cc
notebook.cc notebook.cc
notebook.h notebook.h
entry.h entrybox.h
entry.cc entrybox.cc
directories.h directories.h
directories.cc directories.cc
terminal.h terminal.h

10
juci/config.json

@ -2,7 +2,6 @@
"source": { "source": {
"colors": { "colors": {
"text_color": "black", "text_color": "black",
"search": "orange",
"string": "#CC0000", "string": "#CC0000",
"namespace_ref": "#990099", "namespace_ref": "#990099",
"type": "#0066FF", "type": "#0066FF",
@ -58,10 +57,11 @@
"edit_undo": "<control>z", "edit_undo": "<control>z",
"edit_redo": "<control>y", "edit_redo": "<control>y",
"edit_find": "<control>f", "edit_find": "<control>f",
"goto_declaration": "<control>d", "source_goto_declaration": "<control>d",
"goto_method": "<control>m", "source_goto_method": "<control>m",
"compile_and_run": "<control><alt>r", "source_rename": "<control>r",
"compile": "<control>r" "compile_and_run": "<control>Return",
"compile": "<control><shift>Return"
}, },
"directoryfilter": { "directoryfilter": {
"ignore": [ "ignore": [

61
juci/entry.cc

@ -1,61 +0,0 @@
#include "entry.h"
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);
}
bool Entry::on_key_press(GdkEventKey* key) {
if(key->keyval==GDK_KEY_Escape)
hide();
return false;
}
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();
};
}
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();
}

22
juci/entry.h

@ -1,22 +0,0 @@
#ifndef JUCI_ENTRY_H_
#define JUCI_ENTRY_H_
#include <iostream>
#include <functional>
#include "gtkmm.h"
class Entry: public Gtk::Box {
public:
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;
};
#endif // JUCI_ENTRY_H_

68
juci/entrybox.cc

@ -0,0 +1,68 @@
#include "entrybox.h"
namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
}
EntryBox::Entry::Entry(const std::string& content, std::function<void(const std::string& content)> on_activate, unsigned length) : Gtk::Entry(), on_activate(on_activate) {
set_max_length(length);
set_text(content);
signal_activate().connect([this](){
if(this->on_activate)
this->on_activate(get_text());
});
}
EntryBox::Button::Button(const std::string& label, std::function<void()> on_activate) : Gtk::Button(label), on_activate(on_activate) {
set_focus_on_click(false);
signal_clicked().connect([this](){
if(this->on_activate)
this->on_activate();
});
}
EntryBox::ToggleButton::ToggleButton(const std::string& label, std::function<void()> on_activate) : Gtk::ToggleButton(label), on_activate(on_activate) {
set_focus_on_click(false);
signal_clicked().connect([this](){
if(this->on_activate)
this->on_activate();
});
}
EntryBox::Label::Label(std::function<void(int state, const std::string& message)> update) : Gtk::Label(), update(update) {
if(this->update)
this->update(-1, "");
}
EntryBox::EntryBox() : Gtk::Box(Gtk::ORIENTATION_VERTICAL), upper_box(Gtk::ORIENTATION_HORIZONTAL), lower_box(Gtk::ORIENTATION_HORIZONTAL) {
pack_start(upper_box, Gtk::PACK_SHRINK);
pack_start(lower_box, Gtk::PACK_SHRINK);
}
void EntryBox::clear() {
hide();
entries.clear();
buttons.clear();
toggle_buttons.clear();
labels.clear();
}
void EntryBox::show() {
std::vector<Gtk::Widget*> focus_chain;
for(auto& entry: entries) {
upper_box.pack_start(entry, Gtk::PACK_SHRINK);
focus_chain.emplace_back(&entry);
}
for(auto& button: buttons)
upper_box.pack_start(button, Gtk::PACK_SHRINK);
for(auto& toggle_button: toggle_buttons)
upper_box.pack_start(toggle_button, Gtk::PACK_SHRINK);
for(auto& label: labels)
lower_box.pack_start(label, Gtk::PACK_SHRINK);
upper_box.set_focus_chain(focus_chain);
show_all();
if(entries.size()>0) {
entries.begin()->grab_focus();
entries.begin()->select_region(0, entries.begin()->get_text_length());
}
}

43
juci/entrybox.h

@ -0,0 +1,43 @@
#ifndef JUCI_ENTRYBOX_H_
#define JUCI_ENTRYBOX_H_
#include <list>
#include <functional>
#include "gtkmm.h"
class EntryBox : public Gtk::Box {
public:
class Entry : public Gtk::Entry {
public:
Entry(const std::string& content="", std::function<void(const std::string& content)> on_activate=nullptr, unsigned length=50);
std::function<void(const std::string& content)> on_activate;
};
class Button : public Gtk::Button {
public:
Button(const std::string& label, std::function<void()> on_activate=nullptr);
std::function<void()> on_activate;
};
class ToggleButton : public Gtk::ToggleButton {
public:
ToggleButton(const std::string& label, std::function<void()> on_activate=nullptr);
std::function<void()> on_activate;
};
class Label : public Gtk::Label {
public:
Label(std::function<void(int state, const std::string& message)> update=nullptr);
std::function<void(int state, const std::string& message)> update;
};
public:
EntryBox();
Gtk::Box upper_box;
Gtk::Box lower_box;
void clear();
void show();
std::list<Entry> entries;
std::list<Button> buttons;
std::list<ToggleButton> toggle_buttons;
std::list<Label> labels;
};
#endif // JUCI_ENTRYBOX_H_

1
juci/menu.xml

@ -21,6 +21,7 @@
<menu action='SourceMenu'> <menu action='SourceMenu'>
<menuitem action='SourceGotoDeclaration'/> <menuitem action='SourceGotoDeclaration'/>
<menuitem action='SourceGotoMethod'/> <menuitem action='SourceGotoMethod'/>
<menuitem action='SourceRename'/>
</menu> </menu>
<menu action='ProjectMenu'> <menu action='ProjectMenu'>
<menuitem action='ProjectCompileAndRun'/> <menuitem action='ProjectCompileAndRun'/>

277
juci/notebook.cc

@ -2,11 +2,13 @@
#include "notebook.h" #include "notebook.h"
#include "logging.h" #include "logging.h"
#include "singletons.h" #include "singletons.h"
#include <gtksourceview/gtksource.h> // c-library
#include <iostream> //TODO: remove #include <iostream> //TODO: remove
using namespace std; //TODO: remove using namespace std; //TODO: remove
namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
}
Notebook::View::View() { Notebook::View::View() {
pack2(notebook); pack2(notebook);
set_position(120); set_position(120);
@ -19,6 +21,19 @@ Notebook::Controller::Controller() :
clipboard = Gtk::Clipboard::get(); clipboard = Gtk::Clipboard::get();
view.pack1(directories.widget(), true, true); view.pack1(directories.widget(), true, true);
CreateKeybindings(); CreateKeybindings();
entry_box.signal_hide().connect([this]() {
if(CurrentPage()!=-1) {
CurrentSourceView()->grab_focus();
}
});
view.notebook.signal_switch_page().connect([this](Gtk::Widget* page, guint page_num) {
if(search_entry_shown && entry_box.labels.size()>0 && CurrentPage()!=-1) {
CurrentSourceView()->update_search_occurrences=[this](int number){
entry_box.labels.begin()->update(0, std::to_string(number));
};
CurrentSourceView()->search_highlight(last_search, case_sensitive_search, regex_search);
}
});
INFO("Notebook Controller Success"); INFO("Notebook Controller Success");
} // Constructor } // Constructor
@ -37,7 +52,7 @@ void Notebook::Controller::CreateKeybindings() {
OnCloseCurrentPage(); OnCloseCurrentPage();
}); });
menu->action_group->add(Gtk::Action::create("EditFind", "Find"), Gtk::AccelKey(menu->key_map["edit_find"]), [this]() { menu->action_group->add(Gtk::Action::create("EditFind", "Find"), Gtk::AccelKey(menu->key_map["edit_find"]), [this]() {
entry.show_search(""); show_search_and_replace();
}); });
menu->action_group->add(Gtk::Action::create("EditCopy", "Copy"), Gtk::AccelKey(menu->key_map["edit_copy"]), [this]() { menu->action_group->add(Gtk::Action::create("EditCopy", "Copy"), Gtk::AccelKey(menu->key_map["edit_copy"]), [this]() {
if (Pages() != 0) { if (Pages() != 0) {
@ -74,20 +89,22 @@ void Notebook::Controller::CreateKeybindings() {
INFO("Done Redo"); INFO("Done Redo");
}); });
menu->action_group->add(Gtk::Action::create("SourceGotoDeclaration", "Go to declaration"), Gtk::AccelKey(menu->key_map["goto_declaration"]), [this]() { menu->action_group->add(Gtk::Action::create("SourceGotoDeclaration", "Go to declaration"), Gtk::AccelKey(menu->key_map["source_goto_declaration"]), [this]() {
if(CurrentPage()!=-1) { if(CurrentPage()!=-1) {
if(CurrentSourceView()->get_declaration_location) { if(CurrentSourceView()->get_declaration_location) {
auto location=CurrentSourceView()->get_declaration_location(); auto location=CurrentSourceView()->get_declaration_location();
if(location.first.size()>0) { if(location.first.size()>0) {
open_file(location.first); open_file(location.first);
CurrentSourceView()->get_buffer()->place_cursor(CurrentSourceView()->get_buffer()->get_iter_at_offset(location.second)); CurrentSourceView()->get_buffer()->place_cursor(CurrentSourceView()->get_buffer()->get_iter_at_offset(location.second));
while(gtk_events_pending())
gtk_main_iteration();
CurrentSourceView()->scroll_to(CurrentSourceView()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); CurrentSourceView()->scroll_to(CurrentSourceView()->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
} }
} }
}); });
menu->action_group->add(Gtk::Action::create("SourceGotoMethod", "Go to method"), Gtk::AccelKey(menu->key_map["goto_method"]), [this]() { menu->action_group->add(Gtk::Action::create("SourceGotoMethod", "Go to method"), Gtk::AccelKey(menu->key_map["source_goto_method"]), [this]() {
if(CurrentPage()!=-1) { if(CurrentPage()!=-1) {
if(CurrentSourceView()->goto_method) { if(CurrentSourceView()->goto_method) {
CurrentSourceView()->goto_method(); CurrentSourceView()->goto_method();
@ -95,48 +112,146 @@ void Notebook::Controller::CreateKeybindings() {
} }
}); });
entry.button_apply_set_filename.signal_clicked().connect([this]() { menu->action_group->add(Gtk::Action::create("SourceRename", "Rename function/variable"), Gtk::AccelKey(menu->key_map["source_rename"]), [this]() {
std::string filename=entry(); entry_box.clear();
if(filename!="") { if(CurrentPage()!=-1) {
if(project_path!="" && !boost::filesystem::path(filename).is_absolute()) if(CurrentSourceView()->get_token && CurrentSourceView()->get_token_name) {
filename=project_path+"/"+filename; auto token=std::make_shared<std::string>(CurrentSourceView()->get_token());
boost::filesystem::path p(filename); if(token->size()>0 && CurrentSourceView()->get_token_name) {
if(boost::filesystem::exists(p)) { auto token_name=std::make_shared<std::string>(CurrentSourceView()->get_token_name());
//TODO: alert user that file already exists for(int c=0;c<Pages();c++) {
if(source_views.at(c)->view->tag_similar_tokens) {
source_views.at(c)->view->tag_similar_tokens(*token);
} }
else {
std::ofstream f(p.string().c_str());
if(f) {
open_file(boost::filesystem::canonical(p).string());
if(project_path!="")
directories.open_folder(project_path); //TODO: Do refresh instead
} }
else { entry_box.labels.emplace_back();
//TODO: alert user of error creating file auto label_it=entry_box.labels.begin();
label_it->update=[label_it](int state, const std::string& message){
label_it->set_text("Warning: only opened and parsed tabs will have its content renamed, and modified files will be saved.");
};
label_it->update(0, "");
entry_box.entries.emplace_back(*token_name, [this, token_name, token](const std::string& content){
if(CurrentPage()!=-1 && content!=*token_name) {
for(int c=0;c<Pages();c++) {
if(source_views.at(c)->view->rename_similar_tokens) {
auto number=source_views.at(c)->view->rename_similar_tokens(*token, content);
if(number>0) {
Singleton::terminal()->print("Replaced "+std::to_string(number)+" occurrences in file "+source_views.at(c)->view->file_path+"\n");
source_views.at(c)->view->save();
} }
f.close();
} }
} }
entry.hide(); entry_box.hide();
}); }
entry.button_close.signal_clicked().
connect(
[this]() {
entry.hide();
}); });
entry.button_next.signal_clicked(). auto entry_it=entry_box.entries.begin();
connect( entry_box.buttons.emplace_back("Rename", [this, entry_it](){
[this]() { entry_it->activate();
search(true);
}); });
entry.button_prev.signal_clicked(). entry_box.show();
connect( }
[this]() { }
search(false); }
}); });
INFO("Notebook signal handlers sucsess"); INFO("Notebook signal handlers sucsess");
} }
void Notebook::Controller::show_search_and_replace() {
entry_box.clear();
entry_box.labels.emplace_back();
auto label_it=entry_box.labels.begin();
label_it->update=[label_it](int state, const std::string& message){
if(state==0) {
int number=stoi(message);
if(number==0)
label_it->set_text("");
else if(number==1)
label_it->set_text("1 result found");
else if(number>1)
label_it->set_text(std::to_string(number)+" results found");
}
};
entry_box.entries.emplace_back(last_search, [this](const std::string& content){
if(CurrentPage()!=-1)
CurrentSourceView()->search_forward();
});
auto search_entry_it=entry_box.entries.begin();
search_entry_it->set_placeholder_text("Find");
if(CurrentPage()!=-1) {
CurrentSourceView()->update_search_occurrences=[label_it](int number){
label_it->update(0, std::to_string(number));
};
CurrentSourceView()->search_highlight(search_entry_it->get_text(), case_sensitive_search, regex_search);
}
search_entry_it->signal_key_press_event().connect([this](GdkEventKey* event){
if(event->keyval==GDK_KEY_Return && event->state==GDK_SHIFT_MASK) {
if(CurrentPage()!=-1)
CurrentSourceView()->search_backward();
}
return false;
});
search_entry_it->signal_changed().connect([this, search_entry_it](){
last_search=search_entry_it->get_text();
if(CurrentPage()!=-1)
CurrentSourceView()->search_highlight(search_entry_it->get_text(), case_sensitive_search, regex_search);
});
entry_box.entries.emplace_back(last_replace, [this](const std::string &content){
if(CurrentPage()!=-1)
CurrentSourceView()->replace_forward(content);
});
auto replace_entry_it=entry_box.entries.begin();
replace_entry_it++;
replace_entry_it->set_placeholder_text("Replace");
replace_entry_it->signal_key_press_event().connect([this, replace_entry_it](GdkEventKey* event){
if(event->keyval==GDK_KEY_Return && event->state==GDK_SHIFT_MASK) {
if(CurrentPage()!=-1)
CurrentSourceView()->replace_backward(replace_entry_it->get_text());
}
return false;
});
replace_entry_it->signal_changed().connect([this, replace_entry_it](){
last_replace=replace_entry_it->get_text();
});
entry_box.buttons.emplace_back("Find", [this](){
if(CurrentPage()!=-1)
CurrentSourceView()->search_forward();
});
entry_box.buttons.emplace_back("Replace", [this, replace_entry_it](){
if(CurrentPage()!=-1)
CurrentSourceView()->replace_forward(replace_entry_it->get_text());
});
entry_box.buttons.emplace_back("Replace all", [this, replace_entry_it](){
if(CurrentPage()!=-1)
CurrentSourceView()->replace_all(replace_entry_it->get_text());
});
entry_box.toggle_buttons.emplace_back("Match case");
entry_box.toggle_buttons.back().set_active(case_sensitive_search);
entry_box.toggle_buttons.back().on_activate=[this, search_entry_it](){
case_sensitive_search=!case_sensitive_search;
if(CurrentPage()!=-1)
CurrentSourceView()->search_highlight(search_entry_it->get_text(), case_sensitive_search, regex_search);
};
entry_box.toggle_buttons.emplace_back("Use regex");
entry_box.toggle_buttons.back().set_active(regex_search);
entry_box.toggle_buttons.back().on_activate=[this, search_entry_it](){
regex_search=!regex_search;
if(CurrentPage()!=-1)
CurrentSourceView()->search_highlight(search_entry_it->get_text(), case_sensitive_search, regex_search);
};
entry_box.signal_hide().connect([this]() {
for(int c=0;c<Pages();c++) {
source_views.at(c)->view->update_search_occurrences=nullptr;
source_views.at(c)->view->search_highlight("", case_sensitive_search, regex_search);
}
search_entry_shown=false;
});
search_entry_shown=true;
entry_box.show();
}
void Notebook::Controller::open_file(std::string path) { void Notebook::Controller::open_file(std::string path) {
INFO("Notebook open file"); INFO("Notebook open file");
INFO("Notebook create page"); INFO("Notebook create page");
@ -160,12 +275,21 @@ void Notebook::Controller::open_file(std::string path) {
view.notebook.set_focus_child(*source_views.back()->view); view.notebook.set_focus_child(*source_views.back()->view);
CurrentSourceView()->get_buffer()->set_modified(false); CurrentSourceView()->get_buffer()->set_modified(false);
//Add star on tab label when the page is not saved: //Add star on tab label when the page is not saved:
CurrentSourceView()->get_buffer()->signal_modified_changed().connect([this]() { auto source_view=CurrentSourceView();
boost::filesystem::path file_path(CurrentSourceView()->file_path); CurrentSourceView()->get_buffer()->signal_modified_changed().connect([this, source_view]() {
boost::filesystem::path file_path(source_view->file_path);
std::string title=file_path.filename().string(); std::string title=file_path.filename().string();
if(CurrentSourceView()->get_buffer()->get_modified()) if(source_view->get_buffer()->get_modified())
title+="*"; title+="*";
view.notebook.set_tab_label_text(*(view.notebook.get_nth_page(CurrentPage())), title); int page=-1;
for(int c=0;c<Pages();c++) {
if(source_views.at(c)->view.get()==source_view) {
page=c;
break;
}
}
if(page!=-1)
view.notebook.set_tab_label_text(*(view.notebook.get_nth_page(page)), title);
}); });
} }
@ -183,41 +307,37 @@ void Notebook::Controller::OnCloseCurrentPage() {
} }
} }
void Notebook::Controller::OnFileNewFile() { void Notebook::Controller::OnFileNewFile() {
entry.show_set_filename(); entry_box.clear();
} entry_box.entries.emplace_back("untitled", [this](const std::string& content){
std::string filename=content;
void Notebook::Controller::search(bool forward) { if(filename!="") {
INFO("Notebook search"); if(project_path!="" && !boost::filesystem::path(filename).is_absolute())
auto start = CurrentSourceView()->search_start; filename=project_path+"/"+filename;
auto end = CurrentSourceView()->search_end; boost::filesystem::path p(filename);
// fetch buffer and greate settings if(boost::filesystem::exists(p)) {
auto buffer = CurrentSourceView()->get_source_buffer(); Singleton::terminal()->print("Error: "+p.string()+" already exists.\n");
auto settings = gtk_source_search_settings_new(); }
// get search text from entry else {
gtk_source_search_settings_set_search_text(settings, entry().c_str()); std::ofstream f(p.string().c_str());
// make sure the search continues if(f) {
gtk_source_search_settings_set_wrap_around(settings, true); open_file(boost::filesystem::canonical(p).string());
auto context = gtk_source_search_context_new(buffer->gobj(), settings); Singleton::terminal()->print("New file "+p.string()+" created.\n");
gtk_source_search_context_set_highlight(context, forward); if(project_path!="")
auto itr = buffer->get_insert()->get_iter(); directories.open_folder(project_path); //TODO: Do refresh instead
buffer->remove_tag_by_name("search", start ? start : itr, end ? end : itr); }
if (forward) { else {
DEBUG("Doing forward search"); Singleton::terminal()->print("Error: could not create new file "+p.string()+".\n");
gtk_source_search_context_forward(context, }
end ? end.gobj() : itr.gobj(), f.close();
start.gobj(), }
end.gobj()); }
} else { entry_box.hide();
DEBUG("Doing backward search"); });
gtk_source_search_context_backward(context, auto entry_it=entry_box.entries.begin();
start ? start.gobj() : itr.gobj(), entry_box.buttons.emplace_back("Create file", [this, entry_it](){
start.gobj(), entry_it->activate();
end.gobj()); });
} entry_box.show();
buffer->apply_tag_by_name("search", start, end);
CurrentSourceView()->scroll_to(end);
CurrentSourceView()->search_start = start;
CurrentSourceView()->search_end = end;
} }
void Notebook::Controller void Notebook::Controller
@ -255,10 +375,6 @@ int Notebook::Controller::Pages() {
return view.notebook.get_n_pages(); return view.notebook.get_n_pages();
} }
bool Notebook::Controller:: OnSaveFile() {
std::string path=CurrentSourceView()->file_path;
return OnSaveFile(path);
}
bool Notebook::Controller:: OnSaveFile(std::string path) { bool Notebook::Controller:: OnSaveFile(std::string path) {
INFO("Notebook save file with path"); INFO("Notebook save file with path");
if (path != "" && CurrentSourceView()->get_buffer()->get_modified()) { if (path != "" && CurrentSourceView()->get_buffer()->get_modified()) {
@ -268,7 +384,6 @@ bool Notebook::Controller:: OnSaveFile(std::string path) {
file.close(); file.close();
boost::filesystem::path path(CurrentSourceView()->file_path); boost::filesystem::path path(CurrentSourceView()->file_path);
std::string title=path.filename().string(); std::string title=path.filename().string();
view.notebook.set_tab_label_text(*view.notebook.get_nth_page(CurrentPage()), title);
CurrentSourceView()->get_buffer()->set_modified(false); CurrentSourceView()->get_buffer()->set_modified(false);
return true; return true;
} }
@ -323,7 +438,7 @@ void Notebook::Controller::AskToSaveDialog() {
case(Gtk::RESPONSE_YES): case(Gtk::RESPONSE_YES):
{ {
DEBUG("AskToSaveDialog: save file: yes, trying to save file"); DEBUG("AskToSaveDialog: save file: yes, trying to save file");
OnSaveFile(); CurrentSourceView()->save();
DEBUG("AskToSaveDialog: save file: yes, saved sucess"); DEBUG("AskToSaveDialog: save file: yes, saved sucess");
break; break;
} }

15
juci/notebook.h

@ -3,7 +3,7 @@
#include <iostream> #include <iostream>
#include "gtkmm.h" #include "gtkmm.h"
#include "entry.h" #include "entrybox.h"
#include "source.h" #include "source.h"
#include "directories.h" #include "directories.h"
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
@ -25,18 +25,25 @@ namespace Notebook {
int CurrentPage(); int CurrentPage();
void OnCloseCurrentPage(); void OnCloseCurrentPage();
void OnFileNewFile(); void OnFileNewFile();
bool OnSaveFile();
bool OnSaveFile(std::string path); bool OnSaveFile(std::string path);
void OnDirectoryNavigation(const Gtk::TreeModel::Path& path, void OnDirectoryNavigation(const Gtk::TreeModel::Path& path,
Gtk::TreeViewColumn* column); Gtk::TreeViewColumn* column);
void open_file(std::string filename); void open_file(std::string filename);
int Pages(); int Pages();
void search(bool forward);
View view; View view;
std::string OnSaveFileAs(); std::string OnSaveFileAs();
std::string project_path; std::string project_path;
Directories::Controller directories; //Todo: make private after creating open_directory() Directories::Controller directories; //Todo: make private after creating open_directory()
Entry entry;
EntryBox entry_box;
void show_search_and_replace();
std::string last_search;
std::string last_replace;
bool case_sensitive_search=true;
bool regex_search=false;
bool search_entry_shown=false;
sigc::connection delayed_search_label_update;
std::vector<std::unique_ptr<Source> > source_views; std::vector<std::unique_ptr<Source> > source_views;
private: private:
void CreateKeybindings(); void CreateKeybindings();

288
juci/selectiondialog.cc

@ -5,67 +5,70 @@ namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
} }
SelectionDialogBase::SelectionDialogBase(Gtk::TextView& text_view, bool popup): text_view(text_view), popup(popup) {} SelectionDialogBase::SelectionDialogBase(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark, bool show_search_entry): text_view(text_view),
start_mark(start_mark), show_search_entry(show_search_entry), list_view_text(1, false, Gtk::SelectionMode::SELECTION_BROWSE) {
void SelectionDialogBase::init() { if(!show_search_entry)
if(popup)
window=std::unique_ptr<Gtk::Window>(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP)); window=std::unique_ptr<Gtk::Window>(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP));
else else
window=std::unique_ptr<Gtk::Dialog>(new Gtk::Dialog()); window=std::unique_ptr<Gtk::Dialog>(new Gtk::Dialog());
scrolled_window=std::unique_ptr<Gtk::ScrolledWindow>(new Gtk::ScrolledWindow()); list_view_text.set_search_entry(search_entry);
list_view_text=std::unique_ptr<Gtk::ListViewText>(new Gtk::ListViewText(1, false, Gtk::SelectionMode::SELECTION_BROWSE));
search_entry=std::unique_ptr<Gtk::Entry>(new Gtk::Entry());
list_view_text->set_search_entry(*search_entry);
window->set_default_size(0, 0); window->set_default_size(0, 0);
window->property_decorated()=false; window->property_decorated()=false;
window->set_skip_taskbar_hint(true); window->set_skip_taskbar_hint(true);
scrolled_window->set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC); scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
list_view_text->set_enable_search(true); list_view_text.set_enable_search(true);
list_view_text->set_headers_visible(false); list_view_text.set_headers_visible(false);
list_view_text->set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL); list_view_text.set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL);
list_view_text->set_activate_on_single_click(true); list_view_text.set_activate_on_single_click(true);
list_view_text->set_hover_selection(false); list_view_text.set_hover_selection(false);
list_view_text->set_rules_hint(true); list_view_text.set_rules_hint(true);
//list_view_text->set_fixed_height_mode(true); //TODO: This is buggy on OS X, remember to post an issue on GTK+ 3 //list_view_text.set_fixed_height_mode(true); //TODO: This is buggy on OS X, remember to post an issue on GTK+ 3
last_selected=-1; list_view_text.signal_realize().connect([this](){
list_view_text->signal_cursor_changed().connect(sigc::mem_fun(*this, &SelectionDialog::cursor_changed), true);
list_view_text->signal_realize().connect([this](){
resize(); resize();
}); });
list_view_text->clear_items(); list_view_text.signal_event_after().connect([this](GdkEvent* event){
} if(event->type==GDK_KEY_PRESS || event->type==GDK_BUTTON_PRESS) {
update_tooltips();
void SelectionDialogBase::append(const std::string& row) { }
list_view_text->append(row); });
} if(show_search_entry) {
search_entry.signal_event_after().connect([this](GdkEvent* event){
if(event->type==GDK_KEY_PRESS || event->type==GDK_BUTTON_PRESS) {
update_tooltips();
}
});
}
void SelectionDialogBase::show() { scrolled_window.add(list_view_text);
scrolled_window->add(*list_view_text); if(!show_search_entry)
if(popup) window->add(scrolled_window);
window->add(*scrolled_window);
else { else {
auto dialog=(Gtk::Dialog*)window.get(); auto dialog=(Gtk::Dialog*)window.get();
dialog->get_vbox()->pack_start(*search_entry, false, false); dialog->get_vbox()->pack_start(search_entry, false, false);
dialog->get_vbox()->pack_start(*scrolled_window, true, true); dialog->get_vbox()->pack_start(scrolled_window, true, true);
dialog->set_transient_for((Gtk::Window&)(*text_view.get_toplevel())); dialog->set_transient_for((Gtk::Window&)(*text_view.get_toplevel()));
} }
if(rows.size()>0) { }
list_view_text->get_selection()->select(*list_view_text->get_model()->children().begin());
list_view_text->scroll_to_row(list_view_text->get_selection()->get_selected_rows()[0]);
}
SelectionDialogBase::~SelectionDialogBase() {
text_view.get_buffer()->delete_mark(start_mark);
}
void SelectionDialogBase::add_row(const std::string& row, const std::string& tooltip) {
list_view_text.append(row);
if(tooltip.size()>0)
tooltip_texts[row]=tooltip;
}
void SelectionDialogBase::show() {
move(); move();
window->show_all(); window->show_all();
search_entry->show();
shown=true;
} }
void SelectionDialogBase::hide() { void SelectionDialogBase::hide() {
shown=false;
window->hide(); window->hide();
if(tooltips) if(tooltips)
tooltips->hide(); tooltips->hide();
@ -73,16 +76,19 @@ void SelectionDialogBase::hide() {
on_hide(); on_hide();
} }
void SelectionDialogBase::cursor_changed() { void SelectionDialogBase::update_tooltips() {
auto selected=list_view_text->get_selected(); if(list_view_text.get_selected().size()>0) {
if(selected.size()>0) { auto it=list_view_text.get_selection()->get_selected();
if(selected[0]!=last_selected || last_selected==-1) { std::string row;
it->get_value(0, row);
if(row!=last_row || last_row.size()==0) {
if(tooltips) if(tooltips)
tooltips->hide(); tooltips->hide();
auto row = rows.at(list_view_text->get_text(selected[0])); auto it=tooltip_texts.find(row);
if(row.second.size()>0) { if(it!=tooltip_texts.end()) {
auto tooltip_text=it->second;
if(tooltip_text.size()>0) {
tooltips=std::unique_ptr<Tooltips>(new Tooltips()); tooltips=std::unique_ptr<Tooltips>(new Tooltips());
auto tooltip_text=row.second;
auto get_tooltip_buffer=[this, tooltip_text]() { auto get_tooltip_buffer=[this, tooltip_text]() {
auto tooltip_buffer=Gtk::TextBuffer::create(text_view.get_buffer()->get_tag_table()); auto tooltip_buffer=Gtk::TextBuffer::create(text_view.get_buffer()->get_tag_table());
//TODO: Insert newlines to tooltip_text (use 80 chars, then newline?) //TODO: Insert newlines to tooltip_text (use 80 chars, then newline?)
@ -94,12 +100,13 @@ void SelectionDialogBase::cursor_changed() {
} }
} }
} }
else if(tooltips) last_row=row;
}
else {
last_row="";
if(tooltips)
tooltips->hide(); tooltips->hide();
if(selected.size()>0) }
last_selected=selected[0];
else
last_selected=-1;
} }
void SelectionDialogBase::move() { void SelectionDialogBase::move() {
@ -118,10 +125,10 @@ void SelectionDialogBase::move() {
void SelectionDialogBase::resize() { void SelectionDialogBase::resize() {
INFO("SelectionDialog set size"); INFO("SelectionDialog set size");
if(list_view_text->get_realized()) { if(list_view_text.get_realized()) {
int row_width=0, row_height; int row_width=0, row_height;
Gdk::Rectangle rect; Gdk::Rectangle rect;
list_view_text->get_cell_area(list_view_text->get_model()->get_path(list_view_text->get_model()->children().begin()), *(list_view_text->get_column(0)), rect); list_view_text.get_cell_area(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()), *(list_view_text.get_column(0)), rect);
row_width=rect.get_width(); row_width=rect.get_width();
row_height=rect.get_height(); row_height=rect.get_height();
@ -131,21 +138,22 @@ void SelectionDialogBase::resize() {
if(row_width>text_view.get_width()/2) if(row_width>text_view.get_width()/2)
row_width=text_view.get_width()/2; row_width=text_view.get_width()/2;
else else
scrolled_window->set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC); scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC);
int window_height=std::min(row_height*(int)rows.size(), row_height*10); int window_height=std::min(row_height*(int)list_view_text.get_model()->children().size(), row_height*10);
if(!popup) if(show_search_entry)
window_height+=search_entry->get_height(); window_height+=search_entry.get_height();
window->resize(row_width, window_height); window->resize(row_width, window_height);
} }
} }
SelectionDialog::SelectionDialog(Gtk::TextView& text_view) : SelectionDialogBase(text_view, false) {} SelectionDialog::SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark) : SelectionDialogBase(text_view, start_mark, true) {}
void SelectionDialog::show() { void SelectionDialog::show() {
SelectionDialogBase::show(); SelectionDialogBase::show();
std::shared_ptr<std::string> search_key(new std::string()); std::shared_ptr<std::string> search_key(new std::string());
auto filter_model=Gtk::TreeModelFilter::create(list_view_text->get_model()); auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model());
filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){ filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){
std::string row_lc; std::string row_lc;
iter->get_value(0, row_lc); iter->get_value(0, row_lc);
@ -156,113 +164,137 @@ void SelectionDialog::show() {
return true; return true;
return false; return false;
}); });
list_view_text->set_model(filter_model);
list_view_text->set_search_equal_func([this](const Glib::RefPtr<Gtk::TreeModel>& model, int column, const Glib::ustring& key, const Gtk::TreeModel::iterator& iter) { list_view_text.set_model(filter_model);
list_view_text.set_search_equal_func([this](const Glib::RefPtr<Gtk::TreeModel>& model, int column, const Glib::ustring& key, const Gtk::TreeModel::iterator& iter) {
return false; return false;
}); });
search_entry->signal_changed().connect([this, search_key, filter_model](){
*search_key=search_entry->get_text(); search_entry.signal_changed().connect([this, search_key, filter_model](){
*search_key=search_entry.get_text();
filter_model->refilter(); filter_model->refilter();
list_view_text->set_search_entry(*search_entry); //TODO:Report the need of this to GTK's git (bug) list_view_text.set_search_entry(search_entry); //TODO:Report the need of this to GTK's git (bug)
}); });
search_entry->signal_event().connect([this, search_key, filter_model](GdkEvent* event) {
search_entry.signal_event().connect([this](GdkEvent* event) {
if(event->type==GDK_KEY_PRESS) { if(event->type==GDK_KEY_PRESS) {
auto key=(GdkEventKey*)event; auto key=(GdkEventKey*)event;
if(key->keyval==GDK_KEY_Down) { if(key->keyval==GDK_KEY_Down && list_view_text.get_model()->children().size()>0) {
auto it=list_view_text->get_selection()->get_selected(); auto it=list_view_text.get_selection()->get_selected();
if(it) { if(it) {
it++; it++;
if(it) { if(it) {
list_view_text->set_cursor(list_view_text->get_model()->get_path(it)); list_view_text.set_cursor(list_view_text.get_model()->get_path(it));
} }
} }
else else
list_view_text->set_cursor(list_view_text->get_model()->get_path(list_view_text->get_model()->children().begin())); list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
return true; return true;
} }
if(key->keyval==GDK_KEY_Up) { if(key->keyval==GDK_KEY_Up && list_view_text.get_model()->children().size()>0) {
auto it=list_view_text->get_selection()->get_selected(); auto it=list_view_text.get_selection()->get_selected();
if(it) { if(it) {
it--; it--;
if(it) { if(it) {
list_view_text->set_cursor(list_view_text->get_model()->get_path(it)); list_view_text.set_cursor(list_view_text.get_model()->get_path(it));
} }
} }
else {
auto last_it=list_view_text.get_model()->children().end();
last_it--;
list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it));
}
return true; return true;
} }
} }
return false; return false;
}); });
auto activate=[this](){ auto activate=[this](){
if(on_select && list_view_text->get_selected().size()>0) { if(on_select && list_view_text.get_selected().size()>0) {
auto it=list_view_text->get_selection()->get_selected(); auto it=list_view_text.get_selection()->get_selected();
std::string row; std::string row;
it->get_value(0, row); it->get_value(0, row);
std::string selected = rows.at(row).first; on_select(row, true);
on_select(selected);
} }
window->hide(); hide();
}; };
search_entry->signal_activate().connect([this, activate](){ search_entry.signal_activate().connect([this, activate](){
activate(); activate();
}); });
list_view_text->signal_row_activated().connect([this, activate](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { list_view_text.signal_row_activated().connect([this, activate](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) {
activate(); activate();
}); });
window->signal_focus_out_event().connect([this](GdkEventFocus*){ window->signal_focus_out_event().connect([this](GdkEventFocus*){
window->hide(); hide();
return true; return true;
}); });
list_view_text->set_cursor(list_view_text->get_model()->get_path(list_view_text->get_model()->children().begin()));
if(list_view_text.get_model()->children().size()>0) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
update_tooltips();
}
} }
CompletionDialog::CompletionDialog(Gtk::TextView& text_view) : SelectionDialogBase(text_view, true) {} CompletionDialog::CompletionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark) : SelectionDialogBase(text_view, start_mark, false) {}
void CompletionDialog::show() { void CompletionDialog::show() {
SelectionDialogBase::show(); SelectionDialogBase::show();
show_offset=text_view.get_buffer()->get_insert()->get_iter().get_offset(); show_offset=text_view.get_buffer()->get_insert()->get_iter().get_offset();
list_view_text->signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { if(show_offset==start_mark->get_iter().get_offset()) {
if(shown) { std::shared_ptr<std::string> search_key(new std::string());
select(); auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model());
filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){
std::string row_lc;
iter->get_value(0, row_lc);
auto search_key_lc=*search_key;
std::transform(row_lc.begin(), row_lc.end(), row_lc.begin(), ::tolower);
std::transform(search_key_lc.begin(), search_key_lc.end(), search_key_lc.begin(), ::tolower);
if(row_lc.find(search_key_lc)!=std::string::npos)
return true;
return false;
});
list_view_text.set_model(filter_model);
search_entry.signal_changed().connect([this, search_key, filter_model](){
*search_key=search_entry.get_text();
filter_model->refilter();
list_view_text.set_search_entry(search_entry); //TODO:Report the need of this to GTK's git (bug)
});
} }
list_view_text.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) {
select();
}); });
auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter());
if(text.size()>0) { if(text.size()>0) {
search_entry->set_text(text); search_entry.set_text(text);
list_view_text->set_search_entry(*search_entry); list_view_text.set_search_entry(search_entry);
} }
row_in_entry=false; if(list_view_text.get_model()->children().size()>0) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
update_tooltips();
}
} }
void CompletionDialog::select(bool hide_window) { void CompletionDialog::select(bool hide_window) {
row_in_entry=true; row_in_entry=true;
auto selected=list_view_text->get_selected();
std::pair<std::string, std::string> select; if(list_view_text.get_selected().size()>0) {
if(selected.size()>0) { auto it=list_view_text.get_selection()->get_selected();
select = rows.at(list_view_text->get_text(selected[0])); std::string row;
text_view.get_buffer()->erase(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); it->get_value(0, row);
text_view.get_buffer()->insert(start_mark->get_iter(), select.first); if(on_select)
on_select(row, hide_window);
} }
if(hide_window) { if(hide_window) {
hide(); hide();
char find_char=select.first.back();
if(find_char==')' || find_char=='>') {
if(find_char==')')
find_char='(';
else
find_char='<';
size_t pos=select.first.find(find_char);
if(pos!=std::string::npos) {
auto start_offset=start_mark->get_iter().get_offset()+pos+1;
auto end_offset=start_mark->get_iter().get_offset()+select.first.size()-1;
if(start_offset!=end_offset)
text_view.get_buffer()->select_range(text_view.get_buffer()->get_iter_at_offset(start_offset), text_view.get_buffer()->get_iter_at_offset(end_offset));
}
}
} }
} }
@ -275,11 +307,14 @@ bool CompletionDialog::on_key_release(GdkEventKey* key) {
} }
else { else {
auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter());
if(text.size()>0) { search_entry.set_text(text);
search_entry->set_text(text); list_view_text.set_search_entry(search_entry);
list_view_text->set_search_entry(*search_entry); if(text=="") {
if(list_view_text.get_model()->children().size()>0) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
}
} }
cursor_changed(); update_tooltips();
} }
return false; return false;
} }
@ -293,35 +328,44 @@ bool CompletionDialog::on_key_press(GdkEventKey* key) {
text_view.get_buffer()->erase(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); text_view.get_buffer()->erase(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter());
row_in_entry=false; row_in_entry=false;
if(key->keyval==GDK_KEY_BackSpace) { if(key->keyval==GDK_KEY_BackSpace) {
update_tooltips();
return true; return true;
} }
} }
update_tooltips();
return false; return false;
} }
if(key->keyval==GDK_KEY_Shift_L || key->keyval==GDK_KEY_Shift_R || key->keyval==GDK_KEY_Alt_L || key->keyval==GDK_KEY_Alt_R || key->keyval==GDK_KEY_Control_L || key->keyval==GDK_KEY_Control_R || key->keyval==GDK_KEY_Meta_L || key->keyval==GDK_KEY_Meta_R) if(key->keyval==GDK_KEY_Shift_L || key->keyval==GDK_KEY_Shift_R || key->keyval==GDK_KEY_Alt_L || key->keyval==GDK_KEY_Alt_R || key->keyval==GDK_KEY_Control_L || key->keyval==GDK_KEY_Control_R || key->keyval==GDK_KEY_Meta_L || key->keyval==GDK_KEY_Meta_R)
return false; return false;
if(key->keyval==GDK_KEY_Down) { if(key->keyval==GDK_KEY_Down && list_view_text.get_model()->children().size()>0) {
auto it=list_view_text->get_selection()->get_selected(); auto it=list_view_text.get_selection()->get_selected();
if(it) { if(it) {
it++; it++;
if(it) { if(it) {
list_view_text->set_cursor(list_view_text->get_model()->get_path(it)); list_view_text.set_cursor(list_view_text.get_model()->get_path(it));
} }
} }
else
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
select(false); select(false);
cursor_changed(); update_tooltips();
return true; return true;
} }
if(key->keyval==GDK_KEY_Up) { if(key->keyval==GDK_KEY_Up && list_view_text.get_model()->children().size()>0) {
auto it=list_view_text->get_selection()->get_selected(); auto it=list_view_text.get_selection()->get_selected();
if(it) { if(it) {
it--; it--;
if(it) { if(it) {
list_view_text->set_cursor(list_view_text->get_model()->get_path(it)); list_view_text.set_cursor(list_view_text.get_model()->get_path(it));
}
} }
else {
auto last_it=list_view_text.get_model()->children().end();
last_it--;
list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it));
} }
select(false); select(false);
cursor_changed(); update_tooltips();
return true; return true;
} }
if(key->keyval==GDK_KEY_Return || key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) { if(key->keyval==GDK_KEY_Return || key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) {
@ -329,5 +373,7 @@ bool CompletionDialog::on_key_press(GdkEventKey* key) {
return true; return true;
} }
hide(); hide();
if(key->keyval==GDK_KEY_Escape)
return true;
return false; return false;
} }

35
juci/selectiondialog.h

@ -4,47 +4,44 @@
#include "gtkmm.h" #include "gtkmm.h"
#include "logging.h" #include "logging.h"
#include "tooltips.h" #include "tooltips.h"
#include <unordered_map>
class SelectionDialogBase { class SelectionDialogBase {
public: public:
SelectionDialogBase(Gtk::TextView& text_view, bool popup); SelectionDialogBase(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark, bool show_search_entry);
virtual void init(); //TODO: use constructor instead of init ~SelectionDialogBase();
virtual void append(const std::string& row); virtual void add_row(const std::string& row, const std::string& tooltip="");
virtual void show(); virtual void show();
virtual void hide(); virtual void hide();
virtual void move(); virtual void move();
std::map<std::string, std::pair<std::string, std::string> > rows; //TODO: remove, instead add on_select. Also remember to destroy start_mark in destructor
std::function<void()> on_hide; std::function<void()> on_hide;
bool shown=false; std::function<void(const std::string& selected, bool hide_window)> on_select;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark; Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
protected: protected:
virtual void resize(); virtual void resize();
virtual void cursor_changed(); virtual void update_tooltips();
Gtk::TextView& text_view; Gtk::TextView& text_view;
std::unique_ptr<Gtk::Window> window; std::unique_ptr<Gtk::Window> window;
std::unique_ptr<Gtk::ScrolledWindow> scrolled_window; Gtk::ScrolledWindow scrolled_window;
std::unique_ptr<Gtk::ListViewText> list_view_text; Gtk::ListViewText list_view_text;
std::unique_ptr<Gtk::Entry> search_entry; Gtk::Entry search_entry;
bool show_search_entry;
std::unique_ptr<Tooltips> tooltips; std::unique_ptr<Tooltips> tooltips;
int last_selected; std::unordered_map<std::string, std::string> tooltip_texts;
private: std::string last_row;
bool popup;
}; };
class SelectionDialog : public SelectionDialogBase { class SelectionDialog : public SelectionDialogBase {
public: public:
SelectionDialog(Gtk::TextView& text_view); SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark);
void init() {SelectionDialogBase::init();}
void show(); void show();
std::function<void(std::string selected)> on_select;
}; };
class CompletionDialog : public SelectionDialogBase { class CompletionDialog : public SelectionDialogBase {
public: public:
CompletionDialog(Gtk::TextView& text_view); CompletionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark);
void init() {SelectionDialogBase::init();}
void show(); void show();
bool on_key_release(GdkEventKey* key); bool on_key_release(GdkEventKey* key);
bool on_key_press(GdkEventKey* key); bool on_key_press(GdkEventKey* key);
@ -53,7 +50,7 @@ private:
void select(bool hide_window=true); void select(bool hide_window=true);
int show_offset; int show_offset;
bool row_in_entry; bool row_in_entry=false;
}; };
#endif // JUCI_SELECTIONDIALOG_H_ #endif // JUCI_SELECTIONDIALOG_H_

396
juci/source.cc

@ -7,6 +7,7 @@
#include <algorithm> #include <algorithm>
#include <regex> #include <regex>
#include "singletons.h" #include "singletons.h"
#include <gtksourceview/gtksource.h>
#include <iostream> //TODO: remove #include <iostream> //TODO: remove
using namespace std; //TODO: remove using namespace std; //TODO: remove
@ -37,27 +38,108 @@ file_path(file_path), project_path(project_path) {
get_source_buffer()->get_undo_manager()->begin_not_undoable_action(); get_source_buffer()->get_undo_manager()->begin_not_undoable_action();
get_source_buffer()->set_text(s.get_content()); get_source_buffer()->set_text(s.get_content());
get_source_buffer()->get_undo_manager()->end_not_undoable_action(); get_source_buffer()->get_undo_manager()->end_not_undoable_action();
search_start = search_end = this->get_buffer()->end();
override_font(Pango::FontDescription(Singleton::Config::source()->font)); get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(0));
override_background_color(Gdk::RGBA(Singleton::Config::source()->background)); search_settings = gtk_source_search_settings_new();
override_background_color(Gdk::RGBA(Singleton::Config::source()->background_selected), Gtk::StateFlags::STATE_FLAG_SELECTED); gtk_source_search_settings_set_wrap_around(search_settings, true);
for (auto &item : Singleton::Config::source()->tags) { search_context = gtk_source_search_context_new(get_source_buffer()->gobj(), search_settings);
get_source_buffer()->create_tag(item.first)->property_foreground() = item.second; gtk_source_search_context_set_highlight(search_context, true);
//TODO: why does this not work?: Might be best to use the styles from sourceview. These has to be read from file, search-matches got style "search-match"
//TODO: in header if trying again: GtkSourceStyle* search_match_style;
//TODO: We can drop this, only work on newer versions of gtksourceview.
//search_match_style=(GtkSourceStyle*)g_object_new(GTK_SOURCE_TYPE_STYLE, "background-set", 1, "background", "#00FF00", NULL);
//gtk_source_search_context_set_match_style(search_context, search_match_style);
//TODO: either use lambda if possible or create a gtkmm wrapper around search_context (including search_settings):
//TODO: (gtkmm's Gtk::Object has connect_property_changed, so subclassing this might be an idea)
g_signal_connect(search_context, "notify::occurrences-count", G_CALLBACK(search_occurrences_updated), this);
}
bool Source::View::save() {
INFO("Source save file");
if (file_path != "" && get_buffer()->get_modified()) {
std::ofstream file;
file.open (file_path);
file << get_buffer()->get_text();
file.close();
get_buffer()->set_modified(false);
Singleton::terminal()->print("File saved to: " +file_path+"\n");
return true;
} }
return false;
}
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(0)); void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data) {
signal_size_allocate().connect([this](Gtk::Allocation& allocation){ auto view=(Source::View*)data;
if(!after_user_input) if(view->update_search_occurrences)
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); view->update_search_occurrences(gtk_source_search_context_get_occurrences_count(view->search_context));
}); }
signal_event().connect([this](GdkEvent* event){ Source::View::~View() {
if(event->type==GDK_KEY_PRESS || event->type==GDK_BUTTON_PRESS || event->type==GDK_SCROLL) g_clear_object(&search_context);
after_user_input=true; g_clear_object(&search_settings);
return false; }
});
void Source::View::search_highlight(const std::string &text, bool case_sensitive, bool regex) {
gtk_source_search_settings_set_case_sensitive(search_settings, case_sensitive);
gtk_source_search_settings_set_regex_enabled(search_settings, regex);
gtk_source_search_settings_set_search_text(search_settings, text.c_str());
search_occurrences_updated(NULL, NULL, this);
}
void Source::View::search_forward() {
Gtk::TextIter insert, selection_bound;
get_buffer()->get_selection_bounds(insert, selection_bound);
auto& start=selection_bound;
Gtk::TextIter match_start, match_end;
if(gtk_source_search_context_forward(search_context, start.gobj(), match_start.gobj(), match_end.gobj())) {
get_buffer()->select_range(match_start, match_end);
scroll_to(get_buffer()->get_insert());
}
}
void Source::View::search_backward() {
Gtk::TextIter insert, selection_bound;
get_buffer()->get_selection_bounds(insert, selection_bound);
auto &start=insert;
Gtk::TextIter match_start, match_end;
if(gtk_source_search_context_backward(search_context, start.gobj(), match_start.gobj(), match_end.gobj())) {
get_buffer()->select_range(match_start, match_end);
scroll_to(get_buffer()->get_insert());
}
}
void Source::View::replace_forward(const std::string &replacement) {
Gtk::TextIter insert, selection_bound;
get_buffer()->get_selection_bounds(insert, selection_bound);
auto &start=insert;
Gtk::TextIter match_start, match_end;
if(gtk_source_search_context_forward(search_context, start.gobj(), match_start.gobj(), match_end.gobj())) {
auto offset=match_start.get_offset();
gtk_source_search_context_replace(search_context, match_start.gobj(), match_end.gobj(), replacement.c_str(), replacement.size(), NULL);
get_buffer()->select_range(get_buffer()->get_iter_at_offset(offset), get_buffer()->get_iter_at_offset(offset+replacement.size()));
scroll_to(get_buffer()->get_insert());
}
}
void Source::View::replace_backward(const std::string &replacement) {
Gtk::TextIter insert, selection_bound;
get_buffer()->get_selection_bounds(insert, selection_bound);
auto &start=selection_bound;
Gtk::TextIter match_start, match_end;
if(gtk_source_search_context_backward(search_context, start.gobj(), match_start.gobj(), match_end.gobj())) {
auto offset=match_start.get_offset();
gtk_source_search_context_replace(search_context, match_start.gobj(), match_end.gobj(), replacement.c_str(), replacement.size(), NULL);
get_buffer()->select_range(get_buffer()->get_iter_at_offset(offset), get_buffer()->get_iter_at_offset(offset+replacement.size()));
scroll_to(get_buffer()->get_insert());
}
}
void Source::View::replace_all(const std::string &replacement) {
gtk_source_search_context_replace_all(search_context, replacement.c_str(), replacement.size(), NULL);
} }
string Source::View::get_line(size_t line_number) { string Source::View::get_line(size_t line_number) {
@ -156,14 +238,83 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
return Gsv::View::on_key_press_event(key); return Gsv::View::on_key_press_event(key);
} }
///////////////////////// /////////////////////
//// ClangViewParse /// //// GenericView ////
///////////////////////// /////////////////////
Source::GenericView::GenericView(const std::string& file_path, const std::string& project_path) : View(file_path, project_path) {
auto style_scheme_manager=Gsv::StyleSchemeManager::get_default();
//TODO: add?: style_scheme_manager->prepend_search_path("~/.juci/");
auto scheme=style_scheme_manager->get_scheme("classic");
if(scheme) {
get_source_buffer()->set_style_scheme(scheme);
auto style=scheme->get_style("def:comment");
if(style)
cout << "TODO, in progress: def:comment in scheme " << scheme->get_name() << " has color " << style->property_foreground() << endl;
}
auto language_manager=Gsv::LanguageManager::get_default();
bool result_uncertain = false;
auto content_type = Gio::content_type_guess(file_path, get_buffer()->get_text(), result_uncertain);
if(result_uncertain) {
content_type.clear();
}
auto language=language_manager->guess_language(file_path, content_type);
if(!language) {
auto path=boost::filesystem::path(file_path);
auto filename=path.filename().string();
auto extension=path.extension();
if(filename=="CMakeLists.txt")
language=language_manager->get_language("cmake");
}
if(language) {
get_source_buffer()->set_language(language);
Singleton::terminal()->print("Language for file "+file_path+" set to "+language->get_name()+".\n");
}
auto completion=get_completion();
auto completion_words=Gsv::CompletionWords::create("", Glib::RefPtr<Gdk::Pixbuf>());
completion_words->register_provider(get_buffer());
completion->add_provider(completion_words);
completion->property_show_headers()=false;
completion->property_show_icons()=false;
completion->property_accelerators()=0;
}
////////////////////////
//// ClangViewParse ////
////////////////////////
clang::Index Source::ClangViewParse::clang_index(0, 0); clang::Index Source::ClangViewParse::clang_index(0, 0);
Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path): Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path):
Source::View(file_path, project_path), Source::View(file_path, project_path),
parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) {
override_font(Pango::FontDescription(Singleton::Config::source()->font));
override_background_color(Gdk::RGBA(Singleton::Config::source()->background));
override_background_color(Gdk::RGBA(Singleton::Config::source()->background_selected), Gtk::StateFlags::STATE_FLAG_SELECTED);
for (auto &item : Singleton::Config::source()->tags) {
get_source_buffer()->create_tag(item.first)->property_foreground() = item.second;
}
//Create underline color tags for diagnostic warnings and errors:
auto diagnostic_tag=get_buffer()->get_tag_table()->lookup("diagnostic_warning");
auto diagnostic_tag_underline=Gtk::TextTag::create("diagnostic_warning_underline");
get_buffer()->get_tag_table()->add(diagnostic_tag_underline);
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", diagnostic_tag->property_foreground_rgba().get_value());
}
diagnostic_tag=get_buffer()->get_tag_table()->lookup("diagnostic_error");
diagnostic_tag_underline=Gtk::TextTag::create("diagnostic_error_underline");
get_buffer()->get_tag_table()->add(diagnostic_tag_underline);
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", diagnostic_tag->property_foreground_rgba().get_value());
}
//TODO: clear tag_class and param_spec?
int start_offset = get_source_buffer()->begin().get_offset(); int start_offset = get_source_buffer()->begin().get_offset();
int end_offset = get_source_buffer()->end().get_offset(); int end_offset = get_source_buffer()->end().get_offset();
auto buffer_map=get_buffer_map(); auto buffer_map=get_buffer_map();
@ -278,6 +429,7 @@ void Source::ClangViewParse::start_reparse() {
clang_readable=false; clang_readable=false;
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
delayed_reparse_connection=Glib::signal_timeout().connect([this]() { delayed_reparse_connection=Glib::signal_timeout().connect([this]() {
clang_readable=false;
parse_thread_go=true; parse_thread_go=true;
return false; return false;
}, 1000); }, 1000);
@ -323,11 +475,13 @@ void Source::ClangViewParse::update_syntax() {
return; return;
} }
auto buffer = get_source_buffer(); auto buffer = get_source_buffer();
buffer->remove_all_tags(buffer->begin(), buffer->end()); for(auto &tag: last_syntax_tags)
buffer->remove_tag_by_name(tag, buffer->begin(), buffer->end());
last_syntax_tags.clear();
for (auto &range : ranges) { for (auto &range : ranges) {
std::string type = std::to_string(range.kind); std::string type = std::to_string(range.kind);
try { try {
Singleton::Config::source()->types.at(type); last_syntax_tags.emplace(Singleton::Config::source()->types.at(type));
} catch (std::exception) { } catch (std::exception) {
continue; continue;
} }
@ -341,6 +495,8 @@ void Source::ClangViewParse::update_syntax() {
void Source::ClangViewParse::update_diagnostics() { void Source::ClangViewParse::update_diagnostics() {
diagnostic_tooltips.clear(); diagnostic_tooltips.clear();
get_buffer()->remove_tag_by_name("diagnostic_warning_underline", get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag_by_name("diagnostic_error_underline", get_buffer()->begin(), get_buffer()->end());
auto diagnostics=clang_tu->get_diagnostics(); auto diagnostics=clang_tu->get_diagnostics();
for(auto &diagnostic: diagnostics) { for(auto &diagnostic: diagnostics) {
if(diagnostic.path==file_path) { if(diagnostic.path==file_path) {
@ -363,16 +519,7 @@ void Source::ClangViewParse::update_diagnostics() {
}; };
diagnostic_tooltips.emplace_back(get_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); diagnostic_tooltips.emplace_back(get_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end));
auto tag=get_buffer()->create_tag(); get_buffer()->apply_tag_by_name(diagnostic_tag_name+"_underline", start, end);
tag->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(tag->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
auto diagnostic_tag=get_buffer()->get_tag_table()->lookup(diagnostic_tag_name);
if(diagnostic_tag!=0)
tag->set_property("underline-rgba", diagnostic_tag->property_foreground_rgba().get_value());
}
get_buffer()->apply_tag(tag, start, end);
} }
} }
} }
@ -380,7 +527,7 @@ void Source::ClangViewParse::update_diagnostics() {
void Source::ClangViewParse::update_types() { void Source::ClangViewParse::update_types() {
type_tooltips.clear(); type_tooltips.clear();
for(auto &token: *clang_tokens) { for(auto &token: *clang_tokens) {
if(token.has_type()) { if(token.get_kind()==clang::Token_Identifier && token.has_type()) {
auto start=get_buffer()->get_iter_at_offset(token.offsets.first); auto start=get_buffer()->get_iter_at_offset(token.offsets.first);
auto end=get_buffer()->get_iter_at_offset(token.offsets.second); auto end=get_buffer()->get_iter_at_offset(token.offsets.second);
auto get_tooltip_buffer=[this, &token]() { auto get_tooltip_buffer=[this, &token]() {
@ -388,7 +535,7 @@ void Source::ClangViewParse::update_types() {
tooltip_buffer->insert_at_cursor("Type: "+token.get_type()); tooltip_buffer->insert_at_cursor("Type: "+token.get_type());
auto brief_comment=token.get_brief_comments(); auto brief_comment=token.get_brief_comments();
if(brief_comment!="") if(brief_comment!="")
tooltip_buffer->insert_at_cursor("\n\n"+brief_comment+"."); tooltip_buffer->insert_at_cursor("\n\n"+brief_comment);
return tooltip_buffer; return tooltip_buffer;
}; };
@ -547,57 +694,57 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
//// ClangViewAutocomplete /// //// ClangViewAutocomplete ///
////////////////////////////// //////////////////////////////
Source::ClangViewAutocomplete::ClangViewAutocomplete(const std::string& file_path, const std::string& project_path): Source::ClangViewAutocomplete::ClangViewAutocomplete(const std::string& file_path, const std::string& project_path):
Source::ClangViewParse(file_path, project_path), completion_dialog(*this), autocomplete_cancel_starting(false) { Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(false) {
completion_dialog.on_hide=[this](){
start_reparse();
};
get_buffer()->signal_changed().connect([this](){ get_buffer()->signal_changed().connect([this](){
if(completion_dialog.shown) if(completion_dialog_shown)
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
start_autocomplete(); start_autocomplete();
}); });
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){ get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){
if(mark->get_name()=="insert") { if(mark->get_name()=="insert") {
autocomplete_cancel_starting=true; autocomplete_cancel_starting=true;
if(completion_dialog.shown) { if(completion_dialog_shown) {
completion_dialog.hide(); completion_dialog->hide();
} }
} }
}); });
signal_scroll_event().connect([this](GdkEventScroll* event){ signal_scroll_event().connect([this](GdkEventScroll* event){
if(completion_dialog.shown) if(completion_dialog_shown)
completion_dialog.hide(); completion_dialog->hide();
return false; return false;
}, false); }, false);
signal_key_release_event().connect([this](GdkEventKey* key){ signal_key_release_event().connect([this](GdkEventKey* key){
if(completion_dialog.shown) { if(completion_dialog_shown) {
if(completion_dialog.on_key_release(key)) if(completion_dialog->on_key_release(key))
return true; return true;
} }
return false; return false;
}, false); }, false);
} }
bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) { bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
last_keyval=key->keyval; last_keyval=key->keyval;
if(completion_dialog.shown) { if(completion_dialog_shown) {
if(completion_dialog.on_key_press(key)) if(completion_dialog->on_key_press(key))
return true; return true;
} }
return ClangViewParse::on_key_press_event(key); return ClangViewParse::on_key_press_event(key);
} }
bool Source::ClangViewAutocomplete::on_focus_out_event(GdkEventFocus* event) { bool Source::ClangViewAutocomplete::on_focus_out_event(GdkEventFocus* event) {
if(completion_dialog.shown) { autocomplete_cancel_starting=true;
completion_dialog.hide(); if(completion_dialog_shown) {
completion_dialog->hide();
} }
return Source::ClangViewParse::on_focus_out_event(event); return Source::ClangViewParse::on_focus_out_event(event);
} }
void Source::ClangViewAutocomplete::start_autocomplete() { void Source::ClangViewAutocomplete::start_autocomplete() {
if(!has_focus())
return;
if(!((last_keyval>='0' && last_keyval<='9') || if(!((last_keyval>='0' && last_keyval<='9') ||
(last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') || (last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') ||
last_keyval=='_' || last_keyval=='>' || last_keyval=='.' || last_keyval==':')) { last_keyval=='_' || last_keyval=='>' || last_keyval=='.' || last_keyval==':')) {
@ -613,7 +760,7 @@ void Source::ClangViewAutocomplete::start_autocomplete() {
prefix_mutex.lock(); prefix_mutex.lock();
prefix=sm[3].str(); prefix=sm[3].str();
prefix_mutex.unlock(); prefix_mutex.unlock();
if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !completion_dialog.shown) { if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !completion_dialog_shown) {
autocomplete(); autocomplete();
} }
else if(last_keyval=='.' && autocomplete_starting) else if(last_keyval=='.' && autocomplete_starting)
@ -623,13 +770,13 @@ void Source::ClangViewAutocomplete::start_autocomplete() {
prefix_mutex.lock(); prefix_mutex.lock();
prefix=sm[3].str(); prefix=sm[3].str();
prefix_mutex.unlock(); prefix_mutex.unlock();
if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !completion_dialog.shown) { if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !completion_dialog_shown) {
autocomplete(); autocomplete();
} }
} }
else else
autocomplete_cancel_starting=true; autocomplete_cancel_starting=true;
if(autocomplete_starting || completion_dialog.shown) if(autocomplete_starting || completion_dialog_shown)
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
} }
} }
@ -644,15 +791,36 @@ void Source::ClangViewAutocomplete::autocomplete() {
autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){ autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){
autocomplete_starting=false; autocomplete_starting=false;
if(!autocomplete_cancel_starting) { if(!autocomplete_cancel_starting) {
if(completion_dialog.start_mark)
get_buffer()->delete_mark(completion_dialog.start_mark);
auto start_iter=get_buffer()->get_insert()->get_iter(); auto start_iter=get_buffer()->get_insert()->get_iter();
for(size_t c=0;c<prefix.size();c++) for(size_t c=0;c<prefix.size();c++)
start_iter--; start_iter--;
completion_dialog.start_mark=get_buffer()->create_mark(start_iter); completion_dialog=std::unique_ptr<CompletionDialog>(new CompletionDialog(*this, get_buffer()->create_mark(start_iter)));
auto rows=std::make_shared<std::unordered_map<std::string, std::string> >();
std::map<std::string, std::pair<std::string, std::string> > rows; completion_dialog->on_hide=[this](){
completion_dialog.init(); start_reparse();
completion_dialog_shown=false;
};
completion_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) {
auto row = rows->at(selected);
get_buffer()->erase(completion_dialog->start_mark->get_iter(), get_buffer()->get_insert()->get_iter());
get_buffer()->insert(completion_dialog->start_mark->get_iter(), row);
if(hide_window) {
char find_char=row.back();
if(find_char==')' || find_char=='>') {
if(find_char==')')
find_char='(';
else
find_char='<';
size_t pos=row.find(find_char);
if(pos!=std::string::npos) {
auto start_offset=completion_dialog->start_mark->get_iter().get_offset()+pos+1;
auto end_offset=completion_dialog->start_mark->get_iter().get_offset()+row.size()-1;
if(start_offset!=end_offset)
get_buffer()->select_range(get_buffer()->get_iter_at_offset(start_offset), get_buffer()->get_iter_at_offset(end_offset));
}
}
}
};
for (auto &data : *ac_data) { for (auto &data : *ac_data) {
std::stringstream ss; std::stringstream ss;
std::string return_value; std::string return_value;
@ -667,17 +835,16 @@ void Source::ClangViewAutocomplete::autocomplete() {
} }
auto ss_str=ss.str(); auto ss_str=ss.str();
if (ss_str.length() > 0) { // if length is 0 the result is empty if (ss_str.length() > 0) { // if length is 0 the result is empty
auto pair=std::pair<std::string, std::string>(ss_str, data.brief_comments); (*rows)[ss.str() + " --> " + return_value] = ss_str;
rows[ss.str() + " --> " + return_value] = pair; completion_dialog->add_row(ss.str() + " --> " + return_value, data.brief_comments);
completion_dialog.append(ss.str() + " --> " + return_value);
} }
} }
if (rows.empty()) { if (rows->empty()) {
rows["No suggestions found..."] = std::pair<std::string, std::string>(); (*rows)["No suggestions found..."] = "";
completion_dialog.append("No suggestions found..."); completion_dialog->add_row("No suggestions found...");
} }
completion_dialog.rows=std::move(rows); completion_dialog_shown=true;
completion_dialog.show(); completion_dialog->show();
} }
else else
start_autocomplete(); start_autocomplete();
@ -743,47 +910,89 @@ get_autocomplete_suggestions(int line_number, int column, std::map<std::string,
//////////////////////////// ////////////////////////////
Source::ClangViewRefactor::ClangViewRefactor(const std::string& file_path, const std::string& project_path): Source::ClangViewRefactor::ClangViewRefactor(const std::string& file_path, const std::string& project_path):
Source::ClangViewAutocomplete(file_path, project_path), selection_dialog(*this) { Source::ClangViewAutocomplete(file_path, project_path) {
similar_tokens_tag=get_buffer()->create_tag(); similar_tokens_tag=get_buffer()->create_tag();
similar_tokens_tag->property_weight()=Pango::WEIGHT_BOLD; similar_tokens_tag->property_weight()=Pango::WEIGHT_BOLD;
get_buffer()->signal_changed().connect([this]() { get_buffer()->signal_changed().connect([this]() {
if(last_similar_tokens_tagged!="") { if(!renaming && last_similar_tokens_tagged!="") {
get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end());
last_similar_tokens_tagged=""; last_similar_tokens_tagged="";
} }
}); });
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){ get_token=[this]() -> std::string {
if(mark->get_name()=="insert") {
bool found=false;
if(clang_readable) { if(clang_readable) {
for(auto &token: *clang_tokens) { for(auto &token: *clang_tokens) {
if(token.has_type()) { if(token.get_kind()==clang::Token_Identifier && token.has_type()) {
auto insert_offset=(unsigned)get_buffer()->get_insert()->get_iter().get_offset(); auto insert_offset=(unsigned)get_buffer()->get_insert()->get_iter().get_offset();
if(insert_offset>=token.offsets.first && insert_offset<=token.offsets.second) { if(insert_offset>=token.offsets.first && insert_offset<=token.offsets.second) {
found=true;
auto referenced=token.get_cursor().get_referenced(); auto referenced=token.get_cursor().get_referenced();
if(referenced) { if(referenced)
auto usr_and_spelling=referenced.get_usr()+token.get_spelling(); return referenced.get_usr();
if(last_similar_tokens_tagged!=usr_and_spelling) { }
get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end()); }
auto offsets=clang_tokens->get_similar_token_offsets(token);
for(auto &offset: offsets) {
get_buffer()->apply_tag(similar_tokens_tag, get_buffer()->get_iter_at_offset(offset.first), get_buffer()->get_iter_at_offset(offset.second));
} }
last_similar_tokens_tagged=usr_and_spelling; }
break; return "";
};
get_token_name=[this]() -> std::string {
if(clang_readable) {
for(auto &token: *clang_tokens) {
if(token.get_kind()==clang::Token_Identifier && token.has_type()) {
auto insert_offset=(unsigned)get_buffer()->get_insert()->get_iter().get_offset();
if(insert_offset>=token.offsets.first && insert_offset<=token.offsets.second) {
return token.get_spelling();
} }
} }
} }
} }
return "";
};
tag_similar_tokens=[this](const std::string &usr){
if(clang_readable) {
if(usr.size()>0 && last_similar_tokens_tagged!=usr) {
get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end());
auto offsets=clang_tokens->get_similar_token_offsets(usr);
for(auto &offset: offsets) {
get_buffer()->apply_tag(similar_tokens_tag, get_buffer()->get_iter_at_offset(offset.first), get_buffer()->get_iter_at_offset(offset.second));
} }
last_similar_tokens_tagged=usr;
} }
if(!found && last_similar_tokens_tagged!="") { }
if(usr.size()==0 && last_similar_tokens_tagged!="") {
get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end());
last_similar_tokens_tagged=""; last_similar_tokens_tagged="";
} }
};
rename_similar_tokens=[this](const std::string &usr, const std::string &text) {
size_t number=0;
if(clang_readable) {
auto offsets=clang_tokens->get_similar_token_offsets(usr);
std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > marks;
for(auto &offset: offsets) {
marks.emplace_back(get_buffer()->create_mark(get_buffer()->get_iter_at_offset(offset.first)), get_buffer()->create_mark(get_buffer()->get_iter_at_offset(offset.second)));
number++;
}
for(auto &mark: marks) {
renaming=true;
get_buffer()->erase(mark.first->get_iter(), mark.second->get_iter());
get_buffer()->insert_with_tag(mark.first->get_iter(), text, similar_tokens_tag);
get_buffer()->delete_mark(mark.first);
get_buffer()->delete_mark(mark.second);
}
renaming=false;
}
return number;
};
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){
if(mark->get_name()=="insert") {
auto usr=get_token();
tag_similar_tokens(usr);
} }
}); });
@ -791,7 +1000,7 @@ Source::ClangViewAutocomplete(file_path, project_path), selection_dialog(*this)
std::pair<std::string, unsigned> location; std::pair<std::string, unsigned> location;
if(clang_readable) { if(clang_readable) {
for(auto &token: *clang_tokens) { for(auto &token: *clang_tokens) {
if(token.has_type()) { if(token.get_kind()==clang::Token_Identifier && token.has_type()) {
auto insert_offset=(unsigned)get_buffer()->get_insert()->get_iter().get_offset(); auto insert_offset=(unsigned)get_buffer()->get_insert()->get_iter().get_offset();
if(insert_offset>=token.offsets.first && insert_offset<=token.offsets.second) { if(insert_offset>=token.offsets.first && insert_offset<=token.offsets.second) {
auto referenced=token.get_cursor().get_referenced(); auto referenced=token.get_cursor().get_referenced();
@ -809,26 +1018,23 @@ Source::ClangViewAutocomplete(file_path, project_path), selection_dialog(*this)
goto_method=[this](){ goto_method=[this](){
if(clang_readable) { if(clang_readable) {
if(selection_dialog.start_mark) selection_dialog=std::unique_ptr<SelectionDialog>(new SelectionDialog(*this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter())));
get_buffer()->delete_mark(selection_dialog.start_mark); auto rows=std::make_shared<std::unordered_map<std::string, unsigned> >();
selection_dialog.start_mark=get_buffer()->create_mark(get_buffer()->get_insert()->get_iter());
std::map<std::string, std::pair<std::string, std::string> > rows;
selection_dialog.init();
auto methods=clang_tokens->get_cxx_methods(); auto methods=clang_tokens->get_cxx_methods();
if(methods.size()==0) if(methods.size()==0)
return; return;
for(auto &method: methods) { for(auto &method: methods) {
rows[method.first]=std::pair<std::string, std::string>(std::to_string(method.second), ""); (*rows)[method.first]=method.second;
selection_dialog.append(method.first); selection_dialog->add_row(method.first);
} }
selection_dialog.rows=std::move(rows); //TODO see if rows gets destroyed when selection_dialog gets destroyed.
selection_dialog.on_select=[this](std::string selected) { selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) {
auto offset=stoul(selected); auto offset=rows->at(selected);
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset)); get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
delayed_tooltips_connection.disconnect(); delayed_tooltips_connection.disconnect();
}; };
selection_dialog.show(); selection_dialog->show();
} }
}; };
} }

34
juci/source.h

@ -13,6 +13,7 @@
#include "terminal.h" #include "terminal.h"
#include "tooltips.h" #include "tooltips.h"
#include "selectiondialog.h" #include "selectiondialog.h"
#include <set>
class Source { class Source {
public: public:
@ -47,23 +48,41 @@ public:
class View : public Gsv::View { class View : public Gsv::View {
public: public:
View(const std::string& file_path, const std::string& project_path); View(const std::string& file_path, const std::string& project_path);
~View();
bool save();
void search_highlight(const std::string &text, bool case_sensitive, bool regex);
std::function<void(int number)> update_search_occurrences;
void search_forward();
void search_backward();
void replace_forward(const std::string &replacement);
void replace_backward(const std::string &replacement);
void replace_all(const std::string &replacement);
std::string get_line(size_t line_number); std::string get_line(size_t line_number);
std::string get_line_before_insert(); std::string get_line_before_insert();
std::string file_path; std::string file_path;
std::string project_path; std::string project_path;
Gtk::TextIter search_start, search_end;
std::function<std::pair<std::string, unsigned>()> get_declaration_location; std::function<std::pair<std::string, unsigned>()> get_declaration_location;
std::function<void()> goto_method; std::function<void()> goto_method;
bool after_user_input=false; std::function<std::string()> get_token;
std::function<std::string()> get_token_name;
std::function<void(const std::string &token)> tag_similar_tokens;
std::function<size_t(const std::string &token, const std::string &text)> rename_similar_tokens;
protected: protected:
bool on_key_press_event(GdkEventKey* key); bool on_key_press_event(GdkEventKey* key);
private:
GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings;
static void search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data);
}; // class View }; // class View
class GenericView : public View { class GenericView : public View {
public: public:
GenericView(const std::string& file_path, const std::string& project_path): GenericView(const std::string& file_path, const std::string& project_path);
View(file_path, project_path) {}
}; };
class ClangViewParse : public View { class ClangViewParse : public View {
@ -89,6 +108,7 @@ public:
int end_offset); int end_offset);
int reparse(const std::map<std::string, std::string> &buffers); int reparse(const std::map<std::string, std::string> &buffers);
void update_syntax(); void update_syntax();
std::set<std::string> last_syntax_tags;
void update_diagnostics(); void update_diagnostics();
void update_types(); void update_types();
Tooltips diagnostic_tooltips; Tooltips diagnostic_tooltips;
@ -120,7 +140,8 @@ public:
private: private:
void start_autocomplete(); void start_autocomplete();
void autocomplete(); void autocomplete();
CompletionDialog completion_dialog; std::unique_ptr<CompletionDialog> completion_dialog;
bool completion_dialog_shown=false;
std::vector<Source::AutoCompleteData> get_autocomplete_suggestions(int line_number, int column, std::map<std::string, std::string>& buffer_map); std::vector<Source::AutoCompleteData> get_autocomplete_suggestions(int line_number, int column, std::map<std::string, std::string>& buffer_map);
Glib::Dispatcher autocomplete_done; Glib::Dispatcher autocomplete_done;
sigc::connection autocomplete_done_connection; sigc::connection autocomplete_done_connection;
@ -137,7 +158,8 @@ public:
private: private:
Glib::RefPtr<Gtk::TextTag> similar_tokens_tag; Glib::RefPtr<Gtk::TextTag> similar_tokens_tag;
std::string last_similar_tokens_tagged; std::string last_similar_tokens_tagged;
SelectionDialog selection_dialog; std::unique_ptr<SelectionDialog> selection_dialog;
bool renaming=false;
}; };
class ClangView : public ClangViewRefactor { class ClangView : public ClangViewRefactor {

29
juci/window.cc

@ -2,6 +2,10 @@
#include "logging.h" #include "logging.h"
#include "singletons.h" #include "singletons.h"
namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
}
Window::Window() : Window::Window() :
window_box_(Gtk::ORIENTATION_VERTICAL) { window_box_(Gtk::ORIENTATION_VERTICAL) {
INFO("Create Window"); INFO("Create Window");
@ -69,12 +73,27 @@ Window::Window() :
window_box_.pack_start(menu->get_widget(), Gtk::PACK_SHRINK); window_box_.pack_start(menu->get_widget(), Gtk::PACK_SHRINK);
window_box_.pack_start(Singleton::notebook()->entry, Gtk::PACK_SHRINK); window_box_.pack_start(Singleton::notebook()->entry_box, Gtk::PACK_SHRINK);
paned_.set_position(300); paned_.set_position(300);
paned_.pack1(Singleton::notebook()->view, true, false); paned_.pack1(Singleton::notebook()->view, true, false);
paned_.pack2(Singleton::terminal()->view, true, true); paned_.pack2(Singleton::terminal()->view, true, true);
window_box_.pack_end(paned_); window_box_.pack_end(paned_);
show_all_children(); show_all_children();
signal_key_press_event().connect([this](GdkEventKey* key) {
if(key->keyval==GDK_KEY_Escape)
Singleton::notebook()->entry_box.hide();
return false;
});
Singleton::notebook()->entry_box.signal_show().connect([this](){
std::vector<Gtk::Widget*> focus_chain;
focus_chain.emplace_back(&Singleton::notebook()->entry_box);
window_box_.set_focus_chain(focus_chain);
});
Singleton::notebook()->entry_box.signal_hide().connect([this](){
window_box_.unset_focus_chain();
});
INFO("Window created"); INFO("Window created");
} // Window constructor } // Window constructor
@ -160,13 +179,9 @@ void Window::OnOpenFile() {
} }
} }
//TODO: Move to notebook. Maybe also replace bool with void?
bool Window::SaveFile() { bool Window::SaveFile() {
if(Singleton::notebook()->OnSaveFile()) { return Singleton::notebook()->CurrentSourceView()->save();
Singleton::terminal()->print("File saved to: " +
Singleton::notebook()->CurrentSourceView()->file_path+"\n");
return true;
}
return false;
} }
bool Window::SaveFileAs() { bool Window::SaveFileAs() {
if(Singleton::notebook()->OnSaveFile(Singleton::notebook()->OnSaveFileAs())){ if(Singleton::notebook()->OnSaveFile(Singleton::notebook()->OnSaveFileAs())){

Loading…
Cancel
Save