mirror of https://gitlab.com/cppit/jucipp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
717 lines
26 KiB
717 lines
26 KiB
|
6 years ago
|
#include "notebook.hpp"
|
||
|
|
#include "config.hpp"
|
||
|
|
#include "filesystem.hpp"
|
||
|
|
#include "project.hpp"
|
||
|
|
#include "selection_dialog.hpp"
|
||
|
|
#include "source_clang.hpp"
|
||
|
|
#include "source_generic.hpp"
|
||
|
|
#include "source_language_protocol.hpp"
|
||
|
8 years ago
|
#include <fstream>
|
||
|
6 years ago
|
#include <gtksourceview-3.0/gtksourceview/gtksourcemap.h>
|
||
|
8 years ago
|
#include <regex>
|
||
|
11 years ago
|
|
||
|
8 years ago
|
Notebook::TabLabel::TabLabel(const std::function<void()> &on_close) {
|
||
|
10 years ago
|
set_can_focus(false);
|
||
|
8 years ago
|
|
||
|
|
auto button = Gtk::manage(new Gtk::Button());
|
||
|
|
auto hbox = Gtk::manage(new Gtk::Box());
|
||
|
|
|
||
|
10 years ago
|
hbox->set_can_focus(false);
|
||
|
10 years ago
|
label.set_can_focus(false);
|
||
|
10 years ago
|
button->set_image_from_icon_name("window-close-symbolic", Gtk::ICON_SIZE_MENU);
|
||
|
|
button->set_can_focus(false);
|
||
|
|
button->set_relief(Gtk::ReliefStyle::RELIEF_NONE);
|
||
|
8 years ago
|
|
||
|
10 years ago
|
hbox->pack_start(label, Gtk::PACK_SHRINK);
|
||
|
|
hbox->pack_end(*button, Gtk::PACK_SHRINK);
|
||
|
|
add(*hbox);
|
||
|
8 years ago
|
|
||
|
10 years ago
|
button->signal_clicked().connect(on_close);
|
||
|
10 years ago
|
signal_button_press_event().connect([on_close](GdkEventButton *event) {
|
||
|
8 years ago
|
if(event->button == GDK_BUTTON_MIDDLE) {
|
||
|
10 years ago
|
on_close();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
});
|
||
|
8 years ago
|
|
||
|
10 years ago
|
show_all();
|
||
|
|
}
|
||
|
|
|
||
|
9 years ago
|
Notebook::Notebook() : Gtk::Paned(), notebooks(2) {
|
||
|
8 years ago
|
for(auto ¬ebook : notebooks) {
|
||
|
8 years ago
|
notebook.get_style_context()->add_class("juci_notebook");
|
||
|
10 years ago
|
notebook.set_scrollable();
|
||
|
|
notebook.set_group_name("source_notebooks");
|
||
|
|
notebook.signal_switch_page().connect([this](Gtk::Widget *widget, guint) {
|
||
|
8 years ago
|
auto hbox = dynamic_cast<Gtk::Box *>(widget);
|
||
|
|
for(size_t c = 0; c < hboxes.size(); ++c) {
|
||
|
|
if(hboxes[c].get() == hbox) {
|
||
|
10 years ago
|
focus_view(source_views[c]);
|
||
|
10 years ago
|
set_current_view(source_views[c]);
|
||
|
10 years ago
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
8 years ago
|
last_index = -1;
|
||
|
10 years ago
|
});
|
||
|
8 years ago
|
notebook.signal_page_added().connect([this](Gtk::Widget *widget, guint) {
|
||
|
|
auto hbox = dynamic_cast<Gtk::Box *>(widget);
|
||
|
|
for(size_t c = 0; c < hboxes.size(); ++c) {
|
||
|
|
if(hboxes[c].get() == hbox) {
|
||
|
10 years ago
|
focus_view(source_views[c]);
|
||
|
10 years ago
|
set_current_view(source_views[c]);
|
||
|
10 years ago
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
pack1(notebooks[0], true, true);
|
||
|
11 years ago
|
}
|
||
|
|
|
||
|
10 years ago
|
size_t Notebook::size() {
|
||
|
10 years ago
|
return source_views.size();
|
||
|
11 years ago
|
}
|
||
|
|
|
||
|
8 years ago
|
Source::View *Notebook::get_view(size_t index) {
|
||
|
|
if(index >= size())
|
||
|
10 years ago
|
return nullptr;
|
||
|
|
return source_views[index];
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
8 years ago
|
Source::View *Notebook::get_current_view() {
|
||
|
10 years ago
|
if(intermediate_view) {
|
||
|
8 years ago
|
for(auto view : source_views) {
|
||
|
|
if(view == intermediate_view)
|
||
|
10 years ago
|
return view;
|
||
|
|
}
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
for(auto view : source_views) {
|
||
|
|
if(view == current_view)
|
||
|
10 years ago
|
return view;
|
||
|
|
}
|
||
|
10 years ago
|
//In case there exist a tab that has not yet received focus again in a different notebook
|
||
|
8 years ago
|
for(int notebook_index = 0; notebook_index < 2; ++notebook_index) {
|
||
|
|
auto page = notebooks[notebook_index].get_current_page();
|
||
|
|
if(page >= 0)
|
||
|
10 years ago
|
return get_view(notebook_index, page);
|
||
|
|
}
|
||
|
10 years ago
|
return nullptr;
|
||
|
11 years ago
|
}
|
||
|
|
|
||
|
8 years ago
|
std::vector<Source::View *> &Notebook::get_views() {
|
||
|
10 years ago
|
return source_views;
|
||
|
11 years ago
|
}
|
||
|
|
|
||
|
6 years ago
|
void Notebook::open(const boost::filesystem::path &file_path_, Position position) {
|
||
|
6 years ago
|
boost::system::error_code ec;
|
||
|
|
if(file_path_.empty() || (boost::filesystem::exists(file_path_, ec) && !boost::filesystem::is_regular_file(file_path_, ec))) {
|
||
|
|
Terminal::get().print("Error: could not open " + file_path_.string() + "\n", true);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
8 years ago
|
auto file_path = filesystem::get_normal_path(file_path_);
|
||
|
|
|
||
|
6 years ago
|
if((position == Position::right || position == Position::split) && !split)
|
||
|
10 years ago
|
toggle_split();
|
||
|
8 years ago
|
|
||
|
8 years ago
|
// Use canonical path to follow symbolic links
|
||
|
6 years ago
|
if(position == Position::infer) {
|
||
|
6 years ago
|
auto canonical_file_path = filesystem::get_canonical_path(file_path);
|
||
|
6 years ago
|
for(size_t c = 0; c < size(); c++) {
|
||
|
|
bool equal;
|
||
|
|
{
|
||
|
|
LockGuard lock(source_views[c]->canonical_file_path_mutex);
|
||
|
|
equal = canonical_file_path == source_views[c]->canonical_file_path;
|
||
|
|
}
|
||
|
|
if(equal) {
|
||
|
|
auto notebook_page = get_notebook_page(c);
|
||
|
|
notebooks[notebook_page.first].set_current_page(notebook_page.second);
|
||
|
|
focus_view(source_views[c]);
|
||
|
|
return;
|
||
|
|
}
|
||
|
11 years ago
|
}
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
6 years ago
|
if(boost::filesystem::exists(file_path, ec)) {
|
||
|
10 years ago
|
std::ifstream can_read(file_path.string());
|
||
|
|
if(!can_read) {
|
||
|
8 years ago
|
Terminal::get().print("Error: could not open " + file_path.string() + "\n", true);
|
||
|
10 years ago
|
return;
|
||
|
|
}
|
||
|
|
can_read.close();
|
||
|
11 years ago
|
}
|
||
|
8 years ago
|
|
||
|
|
auto last_view = get_current_view();
|
||
|
|
|
||
|
|
auto language = Source::guess_language(file_path);
|
||
|
|
|
||
|
8 years ago
|
std::string language_protocol_language_id;
|
||
|
|
if(language) {
|
||
|
8 years ago
|
language_protocol_language_id = language->get_id();
|
||
|
|
if(language_protocol_language_id == "js") {
|
||
|
|
if(file_path.extension() == ".ts")
|
||
|
|
language_protocol_language_id = "typescript";
|
||
|
6 years ago
|
else if(file_path.extension() == ".tsx")
|
||
|
|
language_protocol_language_id = "typescriptreact";
|
||
|
8 years ago
|
else
|
||
|
8 years ago
|
language_protocol_language_id = "javascript";
|
||
|
8 years ago
|
}
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
|
if(language && (language->get_id() == "chdr" || language->get_id() == "cpphdr" || language->get_id() == "c" || language->get_id() == "cpp" || language->get_id() == "objc"))
|
||
|
10 years ago
|
source_views.emplace_back(new Source::ClangView(file_path, language));
|
||
|
8 years ago
|
else if(language && !language_protocol_language_id.empty() && !filesystem::find_executable(language_protocol_language_id + "-language-server").empty())
|
||
|
8 years ago
|
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id));
|
||
|
10 years ago
|
else
|
||
|
10 years ago
|
source_views.emplace_back(new Source::GenericView(file_path, language));
|
||
|
8 years ago
|
|
||
|
7 years ago
|
auto view = source_views.back();
|
||
|
6 years ago
|
|
||
|
6 years ago
|
if(position == Position::split) {
|
||
|
6 years ago
|
auto previous_view = get_current_view();
|
||
|
|
if(previous_view) {
|
||
|
|
view->replace_text(previous_view->get_buffer()->get_text());
|
||
|
6 years ago
|
position = get_notebook_page(get_index(previous_view)).first == 0 ? Position::right : Position::left;
|
||
|
6 years ago
|
}
|
||
|
|
}
|
||
|
|
|
||
|
7 years ago
|
view->configure();
|
||
|
8 years ago
|
|
||
|
7 years ago
|
view->update_status_location = [this](Source::BaseView *view) {
|
||
|
8 years ago
|
if(get_current_view() == view) {
|
||
|
|
auto iter = view->get_buffer()->get_insert()->get_iter();
|
||
|
7 years ago
|
auto status_location_text = ' ' + std::to_string(iter.get_line() + 1) + ':' + std::to_string(iter.get_line_offset() + 1);
|
||
|
|
|
||
|
|
if(view->get_buffer()->get_has_selection()) {
|
||
|
|
Gtk::TextIter start, end;
|
||
|
|
view->get_buffer()->get_selection_bounds(start, end);
|
||
|
|
if(start > end)
|
||
|
|
std::swap(start, end);
|
||
|
|
int lines = end.get_line() - start.get_line() + (end.starts_line() ? 0 : 1); // Do not count lines where the end iter is at start of line
|
||
|
|
int words = 0;
|
||
|
|
bool in_word = false;
|
||
|
|
auto iter = start;
|
||
|
|
do {
|
||
|
|
if(*iter <= ' ')
|
||
|
|
in_word = false;
|
||
|
|
else if(!in_word) {
|
||
|
|
++words;
|
||
|
|
in_word = true;
|
||
|
|
}
|
||
|
|
} while(iter.forward_char() && iter < end);
|
||
|
|
int chars = end.get_offset() - start.get_offset();
|
||
|
|
status_location_text += " (" + std::to_string(lines) + ':' + std::to_string(words) + ':' + std::to_string(chars) + ')';
|
||
|
|
}
|
||
|
|
|
||
|
|
status_location.set_text(status_location_text);
|
||
|
9 years ago
|
}
|
||
|
|
};
|
||
|
7 years ago
|
view->update_status_file_path = [this](Source::BaseView *view) {
|
||
|
8 years ago
|
if(get_current_view() == view)
|
||
|
|
status_file_path.set_text(' ' + filesystem::get_short_path(view->file_path).string());
|
||
|
9 years ago
|
};
|
||
|
7 years ago
|
view->update_status_branch = [this](Source::BaseView *view) {
|
||
|
8 years ago
|
if(get_current_view() == view) {
|
||
|
9 years ago
|
if(!view->status_branch.empty())
|
||
|
8 years ago
|
status_branch.set_text(" (" + view->status_branch + ")");
|
||
|
9 years ago
|
else
|
||
|
|
status_branch.set_text("");
|
||
|
|
}
|
||
|
|
};
|
||
|
7 years ago
|
view->update_status_diagnostics = [this](Source::BaseView *view) {
|
||
|
8 years ago
|
if(get_current_view() == view) {
|
||
|
9 years ago
|
std::string diagnostic_info;
|
||
|
8 years ago
|
|
||
|
|
auto num_warnings = std::get<0>(view->status_diagnostics);
|
||
|
|
auto num_errors = std::get<1>(view->status_diagnostics);
|
||
|
|
auto num_fix_its = std::get<2>(view->status_diagnostics);
|
||
|
|
if(num_warnings > 0 || num_errors > 0 || num_fix_its > 0) {
|
||
|
6 years ago
|
auto normal_color = status_diagnostics.get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL);
|
||
|
6 years ago
|
auto light_theme = (normal_color.get_red() + normal_color.get_green() + normal_color.get_blue()) / 3 < 0.5;
|
||
|
|
|
||
|
9 years ago
|
Gdk::RGBA yellow;
|
||
|
|
yellow.set_rgba(1.0, 1.0, 0.2);
|
||
|
8 years ago
|
double factor = 0.5;
|
||
|
|
yellow.set_red(normal_color.get_red() + factor * (yellow.get_red() - normal_color.get_red()));
|
||
|
|
yellow.set_green(normal_color.get_green() + factor * (yellow.get_green() - normal_color.get_green()));
|
||
|
|
yellow.set_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue()));
|
||
|
9 years ago
|
Gdk::RGBA red;
|
||
|
|
red.set_rgba(1.0, 0.0, 0.0);
|
||
|
6 years ago
|
factor = light_theme ? 0.5 : 0.35;
|
||
|
8 years ago
|
red.set_red(normal_color.get_red() + factor * (red.get_red() - normal_color.get_red()));
|
||
|
|
red.set_green(normal_color.get_green() + factor * (red.get_green() - normal_color.get_green()));
|
||
|
|
red.set_blue(normal_color.get_blue() + factor * (red.get_blue() - normal_color.get_blue()));
|
||
|
9 years ago
|
Gdk::RGBA green;
|
||
|
|
green.set_rgba(0.0, 1.0, 0.0);
|
||
|
8 years ago
|
factor = 0.4;
|
||
|
|
green.set_red(normal_color.get_red() + factor * (green.get_red() - normal_color.get_red()));
|
||
|
|
green.set_green(normal_color.get_green() + factor * (green.get_green() - normal_color.get_green()));
|
||
|
|
green.set_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue()));
|
||
|
|
|
||
|
9 years ago
|
std::stringstream yellow_ss, red_ss, green_ss;
|
||
|
8 years ago
|
yellow_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(yellow.get_red_u() >> 8) << std::setw(2) << (int)(yellow.get_green_u() >> 8) << std::setw(2) << (int)(yellow.get_blue_u() >> 8);
|
||
|
|
red_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(red.get_red_u() >> 8) << std::setw(2) << (int)(red.get_green_u() >> 8) << std::setw(2) << (int)(red.get_blue_u() >> 8);
|
||
|
|
green_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(green.get_red_u() >> 8) << std::setw(2) << (int)(green.get_green_u() >> 8) << std::setw(2) << (int)(green.get_blue_u() >> 8);
|
||
|
|
if(num_warnings > 0) {
|
||
|
|
diagnostic_info += "<span color='#" + yellow_ss.str() + "'>";
|
||
|
|
diagnostic_info += std::to_string(num_warnings) + " warning";
|
||
|
|
if(num_warnings > 1)
|
||
|
|
diagnostic_info += 's';
|
||
|
|
diagnostic_info += "</span>";
|
||
|
9 years ago
|
}
|
||
|
8 years ago
|
if(num_errors > 0) {
|
||
|
|
if(num_warnings > 0)
|
||
|
|
diagnostic_info += ", ";
|
||
|
|
diagnostic_info += "<span color='#" + red_ss.str() + "'>";
|
||
|
|
diagnostic_info += std::to_string(num_errors) + " error";
|
||
|
|
if(num_errors > 1)
|
||
|
|
diagnostic_info += 's';
|
||
|
|
diagnostic_info += "</span>";
|
||
|
9 years ago
|
}
|
||
|
8 years ago
|
if(num_fix_its > 0) {
|
||
|
|
if(num_warnings > 0 || num_errors > 0)
|
||
|
|
diagnostic_info += ", ";
|
||
|
|
diagnostic_info += "<span color='#" + green_ss.str() + "'>";
|
||
|
|
diagnostic_info += std::to_string(num_fix_its) + " fix it";
|
||
|
|
if(num_fix_its > 1)
|
||
|
|
diagnostic_info += 's';
|
||
|
|
diagnostic_info += "</span>";
|
||
|
9 years ago
|
}
|
||
|
|
}
|
||
|
|
status_diagnostics.set_markup(diagnostic_info);
|
||
|
|
}
|
||
|
|
};
|
||
|
7 years ago
|
view->update_status_state = [this](Source::BaseView *view) {
|
||
|
8 years ago
|
if(get_current_view() == view)
|
||
|
|
status_state.set_text(view->status_state + " ");
|
||
|
10 years ago
|
};
|
||
|
8 years ago
|
|
||
|
11 years ago
|
scrolled_windows.emplace_back(new Gtk::ScrolledWindow());
|
||
|
9 years ago
|
hboxes.emplace_back(new Gtk::Box());
|
||
|
7 years ago
|
scrolled_windows.back()->add(*view);
|
||
|
10 years ago
|
hboxes.back()->pack_start(*scrolled_windows.back());
|
||
|
|
|
||
|
|
source_maps.emplace_back(Glib::wrap(gtk_source_map_new()));
|
||
|
7 years ago
|
gtk_source_map_set_view(GTK_SOURCE_MAP(source_maps.back()->gobj()), view->gobj());
|
||
|
6 years ago
|
source_maps.back()->get_style_context()->add_class("juci_source_map");
|
||
|
8 years ago
|
|
||
|
8 years ago
|
configure(source_views.size() - 1);
|
||
|
|
|
||
|
10 years ago
|
//Set up tab label
|
||
|
7 years ago
|
tab_labels.emplace_back(new TabLabel([this, view]() {
|
||
|
|
auto index = get_index(view);
|
||
|
8 years ago
|
if(index != static_cast<size_t>(-1))
|
||
|
10 years ago
|
close(index);
|
||
|
10 years ago
|
}));
|
||
|
7 years ago
|
view->update_tab_label = [this](Source::BaseView *view) {
|
||
|
8 years ago
|
std::string title = view->file_path.filename().string();
|
||
|
8 years ago
|
if(view->get_buffer()->get_modified())
|
||
|
8 years ago
|
title += '*';
|
||
|
8 years ago
|
else
|
||
|
8 years ago
|
title += ' ';
|
||
|
|
for(size_t c = 0; c < size(); ++c) {
|
||
|
|
if(source_views[c] == view) {
|
||
|
|
auto &tab_label = tab_labels.at(c);
|
||
|
8 years ago
|
tab_label->label.set_text(title);
|
||
|
|
tab_label->set_tooltip_text(filesystem::get_short_path(view->file_path).string());
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
7 years ago
|
view->update_tab_label(view);
|
||
|
8 years ago
|
|
||
|
11 years ago
|
//Add star on tab label when the page is not saved:
|
||
|
7 years ago
|
view->get_buffer()->signal_modified_changed().connect([view]() {
|
||
|
|
if(view->update_tab_label)
|
||
|
|
view->update_tab_label(view);
|
||
|
10 years ago
|
});
|
||
|
8 years ago
|
|
||
|
9 years ago
|
//Cursor history
|
||
|
7 years ago
|
auto update_cursor_locations = [this, view](const Gtk::TextBuffer::iterator &iter) {
|
||
|
8 years ago
|
bool mark_moved = false;
|
||
|
|
if(current_cursor_location != static_cast<size_t>(-1)) {
|
||
|
|
auto &cursor_location = cursor_locations.at(current_cursor_location);
|
||
|
7 years ago
|
if(cursor_location.view == view && abs(cursor_location.mark->get_iter().get_line() - iter.get_line()) <= 2) {
|
||
|
|
view->get_buffer()->move_mark(cursor_location.mark, iter);
|
||
|
8 years ago
|
mark_moved = true;
|
||
|
9 years ago
|
}
|
||
|
|
}
|
||
|
|
if(!mark_moved) {
|
||
|
8 years ago
|
if(current_cursor_location != static_cast<size_t>(-1)) {
|
||
|
|
for(auto it = cursor_locations.begin() + current_cursor_location + 1; it != cursor_locations.end();) {
|
||
|
9 years ago
|
it->view->get_buffer()->delete_mark(it->mark);
|
||
|
8 years ago
|
it = cursor_locations.erase(it);
|
||
|
9 years ago
|
}
|
||
|
|
}
|
||
|
7 years ago
|
cursor_locations.emplace_back(view, view->get_buffer()->create_mark(iter));
|
||
|
8 years ago
|
current_cursor_location = cursor_locations.size() - 1;
|
||
|
9 years ago
|
}
|
||
|
8 years ago
|
|
||
|
9 years ago
|
// Combine adjacent cursor histories that are similar
|
||
|
|
if(!cursor_locations.empty()) {
|
||
|
8 years ago
|
size_t cursor_locations_index = 1;
|
||
|
|
auto last_it = cursor_locations.begin();
|
||
|
|
for(auto it = cursor_locations.begin() + 1; it != cursor_locations.end();) {
|
||
|
|
if(last_it->view == it->view && abs(last_it->mark->get_iter().get_line() - it->mark->get_iter().get_line()) <= 2) {
|
||
|
9 years ago
|
last_it->view->get_buffer()->delete_mark(last_it->mark);
|
||
|
8 years ago
|
last_it->mark = it->mark;
|
||
|
|
it = cursor_locations.erase(it);
|
||
|
|
if(current_cursor_location != static_cast<size_t>(-1) && current_cursor_location > cursor_locations_index)
|
||
|
9 years ago
|
--current_cursor_location;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
++it;
|
||
|
|
++last_it;
|
||
|
|
++cursor_locations_index;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
9 years ago
|
// Remove start of cache if cache limit is exceeded
|
||
|
8 years ago
|
while(cursor_locations.size() > 10) {
|
||
|
9 years ago
|
cursor_locations.begin()->view->get_buffer()->delete_mark(cursor_locations.begin()->mark);
|
||
|
|
cursor_locations.erase(cursor_locations.begin());
|
||
|
8 years ago
|
if(current_cursor_location != static_cast<size_t>(-1))
|
||
|
9 years ago
|
--current_cursor_location;
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
|
if(current_cursor_location >= cursor_locations.size())
|
||
|
|
current_cursor_location = cursor_locations.size() - 1;
|
||
|
9 years ago
|
};
|
||
|
7 years ago
|
view->get_buffer()->signal_mark_set().connect([this, update_cursor_locations](const Gtk::TextBuffer::iterator &iter, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
|
||
|
8 years ago
|
if(mark->get_name() == "insert") {
|
||
|
9 years ago
|
if(disable_next_update_cursor_locations) {
|
||
|
8 years ago
|
disable_next_update_cursor_locations = false;
|
||
|
9 years ago
|
return;
|
||
|
|
}
|
||
|
|
update_cursor_locations(iter);
|
||
|
|
}
|
||
|
|
});
|
||
|
7 years ago
|
view->get_buffer()->signal_changed().connect([view, update_cursor_locations] {
|
||
|
|
update_cursor_locations(view->get_buffer()->get_insert()->get_iter());
|
||
|
11 years ago
|
});
|
||
|
8 years ago
|
|
||
|
9 years ago
|
#ifdef JUCI_ENABLE_DEBUG
|
||
|
7 years ago
|
if(dynamic_cast<Source::ClangView *>(view) || (view->language && view->language->get_id() == "rust")) {
|
||
|
|
view->toggle_breakpoint = [view](int line_nr) {
|
||
|
|
if(view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_breakpoint").size() > 0) {
|
||
|
|
auto start_iter = view->get_buffer()->get_iter_at_line(line_nr);
|
||
|
|
auto end_iter = view->get_iter_at_line_end(line_nr);
|
||
|
|
view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint");
|
||
|
|
view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint_and_stop");
|
||
|
9 years ago
|
if(Project::current && Project::debugging)
|
||
|
7 years ago
|
Project::current->debug_remove_breakpoint(view->file_path, line_nr + 1, view->get_buffer()->get_line_count() + 1);
|
||
|
9 years ago
|
}
|
||
|
|
else {
|
||
|
7 years ago
|
auto iter = view->get_buffer()->get_iter_at_line(line_nr);
|
||
|
|
gtk_source_buffer_create_source_mark(view->get_source_buffer()->gobj(), nullptr, "debug_breakpoint", iter.gobj()); // Gsv::Buffer::create_source_mark is bugged
|
||
|
|
if(view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_stop").size() > 0)
|
||
|
|
gtk_source_buffer_create_source_mark(view->get_source_buffer()->gobj(), nullptr, "debug_breakpoint_and_stop", iter.gobj()); // Gsv::Buffer::create_source_mark is bugged
|
||
|
9 years ago
|
if(Project::current && Project::debugging)
|
||
|
7 years ago
|
Project::current->debug_add_breakpoint(view->file_path, line_nr + 1);
|
||
|
9 years ago
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
#endif
|
||
|
8 years ago
|
|
||
|
7 years ago
|
view->signal_focus_in_event().connect([this, view](GdkEventFocus *) {
|
||
|
6 years ago
|
if(on_focus_page)
|
||
|
|
on_focus_page(view);
|
||
|
7 years ago
|
set_current_view(view);
|
||
|
9 years ago
|
return false;
|
||
|
|
});
|
||
|
8 years ago
|
|
||
|
6 years ago
|
if(position == Position::infer) {
|
||
|
10 years ago
|
if(!split)
|
||
|
6 years ago
|
position = Position::left;
|
||
|
8 years ago
|
else if(notebooks[0].get_n_pages() == 0)
|
||
|
6 years ago
|
position = Position::left;
|
||
|
8 years ago
|
else if(notebooks[1].get_n_pages() == 0)
|
||
|
6 years ago
|
position = Position::right;
|
||
|
10 years ago
|
else if(last_view)
|
||
|
6 years ago
|
position = get_notebook_page(get_index(last_view)).first == 0 ? Position::left : Position::right;
|
||
|
10 years ago
|
}
|
||
|
6 years ago
|
size_t notebook_index = position == Position::right ? 1 : 0;
|
||
|
8 years ago
|
auto ¬ebook = notebooks[notebook_index];
|
||
|
|
|
||
|
10 years ago
|
notebook.append_page(*hboxes.back(), *tab_labels.back());
|
||
|
8 years ago
|
|
||
|
10 years ago
|
notebook.set_tab_reorderable(*hboxes.back(), true);
|
||
|
|
notebook.set_tab_detachable(*hboxes.back(), true);
|
||
|
|
show_all_children();
|
||
|
8 years ago
|
|
||
|
|
notebook.set_current_page(notebook.get_n_pages() - 1);
|
||
|
|
last_index = -1;
|
||
|
10 years ago
|
if(last_view) {
|
||
|
8 years ago
|
auto index = get_index(last_view);
|
||
|
|
auto notebook_page = get_notebook_page(index);
|
||
|
|
if(notebook_page.first == notebook_index)
|
||
|
|
last_index = index;
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
|
||
|
10 years ago
|
set_focus_child(*source_views.back());
|
||
|
7 years ago
|
focus_view(view);
|
||
|
11 years ago
|
}
|
||
|
11 years ago
|
|
||
|
7 years ago
|
void Notebook::open_uri(const std::string &uri) {
|
||
|
|
#ifdef __APPLE__
|
||
|
|
Terminal::get().process("open " + filesystem::escape_argument(uri));
|
||
|
|
#else
|
||
|
|
GError *error = nullptr;
|
||
|
|
#if GTK_VERSION_GE(3, 22)
|
||
|
|
gtk_show_uri_on_window(nullptr, uri.c_str(), GDK_CURRENT_TIME, &error);
|
||
|
|
#else
|
||
|
|
gtk_show_uri(nullptr, uri.c_str(), GDK_CURRENT_TIME, &error);
|
||
|
|
#endif
|
||
|
|
g_clear_error(&error);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
10 years ago
|
void Notebook::configure(size_t index) {
|
||
|
10 years ago
|
if(Config::get().source.show_map) {
|
||
|
8 years ago
|
if(hboxes.at(index)->get_children().size() == 1)
|
||
|
10 years ago
|
hboxes.at(index)->pack_end(*source_maps.at(index), Gtk::PACK_SHRINK);
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
else if(hboxes.at(index)->get_children().size() == 2)
|
||
|
10 years ago
|
hboxes.at(index)->remove(*source_maps.at(index));
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
10 years ago
|
bool Notebook::save(size_t index) {
|
||
|
8 years ago
|
if(!source_views[index]->save())
|
||
|
10 years ago
|
return false;
|
||
|
10 years ago
|
Project::on_save(index);
|
||
|
10 years ago
|
return true;
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
11 years ago
|
bool Notebook::save_current() {
|
||
|
8 years ago
|
if(auto view = get_current_view())
|
||
|
10 years ago
|
return save(get_index(view));
|
||
|
|
return false;
|
||
|
11 years ago
|
}
|
||
|
|
|
||
|
10 years ago
|
bool Notebook::close(size_t index) {
|
||
|
8 years ago
|
if(auto view = get_view(index)) {
|
||
|
|
if(view->get_buffer()->get_modified()) {
|
||
|
10 years ago
|
if(!save_modified_dialog(index))
|
||
|
11 years ago
|
return false;
|
||
|
11 years ago
|
}
|
||
|
8 years ago
|
if(view == get_current_view()) {
|
||
|
|
bool focused = false;
|
||
|
|
if(last_index != static_cast<size_t>(-1)) {
|
||
|
|
auto notebook_page = get_notebook_page(last_index);
|
||
|
|
if(notebook_page.first == get_notebook_page(get_index(view)).first) {
|
||
|
10 years ago
|
focus_view(source_views[last_index]);
|
||
|
|
notebooks[notebook_page.first].set_current_page(notebook_page.second);
|
||
|
8 years ago
|
last_index = -1;
|
||
|
|
focused = true;
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
if(!focused) {
|
||
|
8 years ago
|
auto notebook_page = get_notebook_page(get_index(view));
|
||
|
|
if(notebook_page.second > 0)
|
||
|
|
focus_view(get_view(notebook_page.first, notebook_page.second - 1));
|
||
|
10 years ago
|
else {
|
||
|
8 years ago
|
size_t notebook_index = notebook_page.first == 0 ? 1 : 0;
|
||
|
|
if(notebooks[notebook_index].get_n_pages() > 0)
|
||
|
10 years ago
|
focus_view(get_view(notebook_index, notebooks[notebook_index].get_current_page()));
|
||
|
10 years ago
|
else
|
||
|
|
set_current_view(nullptr);
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
else if(index == last_index)
|
||
|
|
last_index = -1;
|
||
|
|
else if(index < last_index && last_index != static_cast<size_t>(-1))
|
||
|
10 years ago
|
last_index--;
|
||
|
8 years ago
|
|
||
|
|
auto notebook_page = get_notebook_page(index);
|
||
|
10 years ago
|
notebooks[notebook_page.first].remove_page(notebook_page.second);
|
||
|
8 years ago
|
source_maps.erase(source_maps.begin() + index);
|
||
|
10 years ago
|
|
||
|
10 years ago
|
if(on_close_page)
|
||
|
|
on_close_page(view);
|
||
|
8 years ago
|
|
||
|
9 years ago
|
delete_cursor_locations(view);
|
||
|
8 years ago
|
|
||
|
|
SelectionDialog::get() = nullptr;
|
||
|
|
CompletionDialog::get() = nullptr;
|
||
|
|
|
||
|
|
if(auto clang_view = dynamic_cast<Source::ClangView *>(view))
|
||
|
10 years ago
|
clang_view->async_delete();
|
||
|
10 years ago
|
else
|
||
|
10 years ago
|
delete view;
|
||
|
8 years ago
|
source_views.erase(source_views.begin() + index);
|
||
|
|
scrolled_windows.erase(scrolled_windows.begin() + index);
|
||
|
|
hboxes.erase(hboxes.begin() + index);
|
||
|
|
tab_labels.erase(tab_labels.begin() + index);
|
||
|
11 years ago
|
}
|
||
|
11 years ago
|
return true;
|
||
|
11 years ago
|
}
|
||
|
11 years ago
|
|
||
|
9 years ago
|
void Notebook::delete_cursor_locations(Source::View *view) {
|
||
|
8 years ago
|
size_t cursor_locations_index = 0;
|
||
|
|
for(auto it = cursor_locations.begin(); it != cursor_locations.end();) {
|
||
|
|
if(it->view == view) {
|
||
|
9 years ago
|
view->get_buffer()->delete_mark(it->mark);
|
||
|
8 years ago
|
it = cursor_locations.erase(it);
|
||
|
|
if(current_cursor_location != static_cast<size_t>(-1) && current_cursor_location > cursor_locations_index)
|
||
|
9 years ago
|
--current_cursor_location;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
++it;
|
||
|
|
++cursor_locations_index;
|
||
|
|
}
|
||
|
|
}
|
||
|
8 years ago
|
if(current_cursor_location >= cursor_locations.size())
|
||
|
|
current_cursor_location = cursor_locations.size() - 1;
|
||
|
9 years ago
|
}
|
||
|
|
|
||
|
10 years ago
|
bool Notebook::close_current() {
|
||
|
|
return close(get_index(get_current_view()));
|
||
|
|
}
|
||
|
|
|
||
|
|
void Notebook::next() {
|
||
|
8 years ago
|
if(auto view = get_current_view()) {
|
||
|
|
auto notebook_page = get_notebook_page(get_index(view));
|
||
|
|
int page = notebook_page.second + 1;
|
||
|
|
if(page >= notebooks[notebook_page.first].get_n_pages())
|
||
|
10 years ago
|
notebooks[notebook_page.first].set_current_page(0);
|
||
|
|
else
|
||
|
|
notebooks[notebook_page.first].set_current_page(page);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Notebook::previous() {
|
||
|
8 years ago
|
if(auto view = get_current_view()) {
|
||
|
|
auto notebook_page = get_notebook_page(get_index(view));
|
||
|
|
int page = notebook_page.second - 1;
|
||
|
|
if(page < 0)
|
||
|
|
notebooks[notebook_page.first].set_current_page(notebooks[notebook_page.first].get_n_pages() - 1);
|
||
|
10 years ago
|
else
|
||
|
|
notebooks[notebook_page.first].set_current_page(page);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Notebook::toggle_split() {
|
||
|
|
if(!split) {
|
||
|
|
pack2(notebooks[1], true, true);
|
||
|
8 years ago
|
set_position(get_width() / 2);
|
||
|
10 years ago
|
show_all();
|
||
|
10 years ago
|
//Make sure the position is correct
|
||
|
|
//TODO: report bug to gtk if it is not fixed in gtk3.22
|
||
|
10 years ago
|
Glib::signal_timeout().connect([this] {
|
||
|
8 years ago
|
set_position(get_width() / 2);
|
||
|
10 years ago
|
return false;
|
||
|
10 years ago
|
}, 200);
|
||
|
10 years ago
|
}
|
||
|
|
else {
|
||
|
8 years ago
|
for(size_t c = size() - 1; c != static_cast<size_t>(-1); --c) {
|
||
|
|
auto notebook_index = get_notebook_page(c).first;
|
||
|
|
if(notebook_index == 1 && !close(c))
|
||
|
10 years ago
|
return;
|
||
|
|
}
|
||
|
|
remove(notebooks[1]);
|
||
|
|
}
|
||
|
8 years ago
|
split = !split;
|
||
|
10 years ago
|
}
|
||
|
9 years ago
|
void Notebook::toggle_tabs() {
|
||
|
9 years ago
|
//Show / Hide tabs for each notebook.
|
||
|
9 years ago
|
for(auto ¬ebook : Notebook::notebooks)
|
||
|
9 years ago
|
notebook.set_show_tabs(!notebook.get_show_tabs());
|
||
|
9 years ago
|
}
|
||
|
10 years ago
|
|
||
|
8 years ago
|
std::vector<std::pair<size_t, Source::View *>> Notebook::get_notebook_views() {
|
||
|
|
std::vector<std::pair<size_t, Source::View *>> notebook_views;
|
||
|
8 years ago
|
for(size_t notebook_index = 0; notebook_index < notebooks.size(); ++notebook_index) {
|
||
|
|
for(int page = 0; page < notebooks[notebook_index].get_n_pages(); ++page) {
|
||
|
|
if(auto view = get_view(notebook_index, page))
|
||
|
8 years ago
|
notebook_views.emplace_back(notebook_index, view);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return notebook_views;
|
||
|
|
}
|
||
|
|
|
||
|
8 years ago
|
void Notebook::update_status(Source::BaseView *view) {
|
||
|
9 years ago
|
if(view->update_status_location)
|
||
|
|
view->update_status_location(view);
|
||
|
|
if(view->update_status_file_path)
|
||
|
|
view->update_status_file_path(view);
|
||
|
|
if(view->update_status_branch)
|
||
|
|
view->update_status_branch(view);
|
||
|
|
if(view->update_status_diagnostics)
|
||
|
|
view->update_status_diagnostics(view);
|
||
|
|
if(view->update_status_state)
|
||
|
|
view->update_status_state(view);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Notebook::clear_status() {
|
||
|
|
status_location.set_text("");
|
||
|
|
status_file_path.set_text("");
|
||
|
|
status_branch.set_text("");
|
||
|
|
status_diagnostics.set_text("");
|
||
|
|
status_state.set_text("");
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
size_t Notebook::get_index(Source::View *view) {
|
||
|
8 years ago
|
for(size_t c = 0; c < size(); ++c) {
|
||
|
|
if(source_views[c] == view)
|
||
|
10 years ago
|
return c;
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
Source::View *Notebook::get_view(size_t notebook_index, int page) {
|
||
|
8 years ago
|
if(notebook_index == static_cast<size_t>(-1) || notebook_index >= notebooks.size() ||
|
||
|
|
page < 0 || page >= notebooks[notebook_index].get_n_pages())
|
||
|
10 years ago
|
return nullptr;
|
||
|
8 years ago
|
auto hbox = dynamic_cast<Gtk::Box *>(notebooks[notebook_index].get_nth_page(page));
|
||
|
|
auto scrolled_window = dynamic_cast<Gtk::ScrolledWindow *>(hbox->get_children()[0]);
|
||
|
|
return dynamic_cast<Source::View *>(scrolled_window->get_children()[0]);
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
|
void Notebook::focus_view(Source::View *view) {
|
||
|
8 years ago
|
intermediate_view = view;
|
||
|
10 years ago
|
view->grab_focus();
|
||
|
|
}
|
||
|
|
|
||
|
|
std::pair<size_t, int> Notebook::get_notebook_page(size_t index) {
|
||
|
8 years ago
|
if(index >= hboxes.size())
|
||
|
10 years ago
|
return {-1, -1};
|
||
|
8 years ago
|
for(size_t c = 0; c < notebooks.size(); ++c) {
|
||
|
|
auto page_num = notebooks[c].page_num(*hboxes[index]);
|
||
|
|
if(page_num >= 0)
|
||
|
10 years ago
|
return {c, page_num};
|
||
|
|
}
|
||
|
|
return {-1, -1};
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
void Notebook::set_current_view(Source::View *view) {
|
||
|
8 years ago
|
intermediate_view = nullptr;
|
||
|
|
if(current_view != view) {
|
||
|
|
if(auto view = get_current_view()) {
|
||
|
10 years ago
|
view->hide_tooltips();
|
||
|
10 years ago
|
view->hide_dialogs();
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
current_view = view;
|
||
|
10 years ago
|
if(view && on_change_page)
|
||
|
10 years ago
|
on_change_page(view);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
bool Notebook::save_modified_dialog(size_t index) {
|
||
|
8 years ago
|
Gtk::MessageDialog dialog(*static_cast<Gtk::Window *>(get_toplevel()), "Save file!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
|
||
|
10 years ago
|
dialog.set_default_response(Gtk::RESPONSE_YES);
|
||
|
8 years ago
|
dialog.set_secondary_text("Do you want to save: " + get_view(index)->file_path.string() + " ?");
|
||
|
11 years ago
|
int result = dialog.run();
|
||
|
8 years ago
|
if(result == Gtk::RESPONSE_YES) {
|
||
|
9 years ago
|
return save(index);
|
||
|
11 years ago
|
}
|
||
|
8 years ago
|
else if(result == Gtk::RESPONSE_NO) {
|
||
|
11 years ago
|
return true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return false;
|
||
|
11 years ago
|
}
|
||
|
|
}
|