#include "BufferView.h" #include #include #include BufferView::BufferView(std::shared_ptr buffer, std::shared_ptr iterator, CharacterWidthDeterminer & character_width_determiner) : m_buffer(buffer), m_iterator(iterator), m_character_width_determiner(character_width_determiner) { m_width = 1; m_height = 1; m_scroll_offset = 0; m_cursor_screen_row = 0; m_cursor_screen_column = 0; m_cursor_virtual_column = 0; m_cursor_row_offset = 0; m_target_column = 0; m_update_target_column = false; } void BufferView::resize(int width, int height) { m_width = std::max(1, width); m_height = std::max(1, height); } void BufferView::set_scroll_offset(int scroll_offset) { m_scroll_offset = std::max(0, scroll_offset); } BufferView::Iterator BufferView::vert_iter() { return Iterator(*this); } void BufferView::update() { /* Calculate number of rows in the cursor line and update cursor row offset * and cursor column values. */ auto start_of_line = std::make_shared(*m_iterator); start_of_line->go_start_of_line(); int rows_in_cursor_line = calculate_rows_in_cursor_line(start_of_line); /* Limit the cursor screen row taking into account view dimensions, * available buffer contents above and below the current line, and scroll * offset. */ std::list>> backward_lines; int so = effective_scroll_offset(); int rows_above = screen_rows_above_line(start_of_line, backward_lines) + m_cursor_row_offset; int rows_below = screen_rows_below_line(start_of_line) + std::max(0, rows_in_cursor_line - m_cursor_row_offset - 1); int min_rows_to_leave_above = std::min(rows_above, so); int min_rows_to_leave_below = std::min(rows_below, so); m_cursor_screen_row = std::min(m_height - min_rows_to_leave_below - 1, m_cursor_screen_row); m_cursor_screen_row = std::max(min_rows_to_leave_above, m_cursor_screen_row); m_cursor_screen_row = std::max(m_height - rows_below - 1, m_cursor_screen_row); m_cursor_screen_row = std::min(rows_above, m_cursor_screen_row); /* Determine the first line that is visible in this view. */ auto line_iterator = std::make_shared(*m_iterator); int row_offset = m_cursor_screen_row - m_cursor_row_offset; if (row_offset <= 0) { line_iterator->go_start_of_line(); } else for (auto rows_iterator_pair : backward_lines) { row_offset -= rows_iterator_pair.first; line_iterator = rows_iterator_pair.second; if (row_offset <= 0) { break; } } /* Now start with first visible line and build up all lines visible in the view. */ m_lines.clear(); while ((row_offset < m_height) && (line_iterator->valid())) { LineDescriptor ld; ld.row_offset = row_offset; ld.n_rows = calculate_rows_in_line(line_iterator); ld.line = std::make_shared(*line_iterator); m_lines.push_back(ld); row_offset += ld.n_rows; line_iterator->go_next_line(); } /* Reset some fields if buffer becomes empty. */ if (!m_iterator->valid()) { m_cursor_screen_row = 0; m_cursor_screen_column = 0; m_cursor_virtual_column = 0; } } bool BufferView::cursor_move(CursorMovement which, bool allow_eol) { bool moved = false; switch (which) { case CursorMovement::LEFT: moved = m_iterator->go_left_in_line(); m_update_target_column = true; break; case CursorMovement::RIGHT: moved = m_iterator->go_right_in_line(allow_eol); m_update_target_column = true; break; case CursorMovement::UP: moved = m_iterator->go_previous_line(); break; case CursorMovement::DOWN: moved = m_iterator->go_next_line(); break; case CursorMovement::SOL: moved = m_iterator->go_start_of_line(); m_target_column = 0; break; case CursorMovement::EOL: moved = m_iterator->go_end_of_line(allow_eol); m_target_column = INT_MAX; break; case CursorMovement::FIRST_LINE: { auto it = m_buffer->begin(); if (it != *m_iterator) { *m_iterator = it; moved = true; } } m_target_column = 0; break; case CursorMovement::LAST_LINE: { auto it = m_buffer->end(); it.go_back(); it.go_start_of_line(); if (it != *m_iterator) { *m_iterator = it; moved = true; } } m_target_column = 0; break; case CursorMovement::SCREEN_ROW_UP: /* TODO */ #if 0 moved = move_cursor_screen_row_up(); #endif break; case CursorMovement::SCREEN_ROW_DOWN: /* TODO */ #if 0 moved = move_cursor_screen_row_down(); #endif break; } if (moved) { switch (which) { case CursorMovement::LEFT: case CursorMovement::RIGHT: determine_new_cursor_screen_row(); break; case CursorMovement::UP: case CursorMovement::DOWN: move_forward_to_column(m_target_column, allow_eol); determine_new_cursor_screen_row(); break; case CursorMovement::SOL: case CursorMovement::EOL: determine_new_cursor_screen_row(); break; case CursorMovement::FIRST_LINE: m_cursor_screen_row = 0; break; case CursorMovement::LAST_LINE: m_cursor_screen_row = m_height - 1; break; } } return moved; } /************************************************************************** * Internal functions *************************************************************************/ void BufferView::determine_new_cursor_screen_row() { if (m_lines.size() > 0) { if (m_iterator->line() < m_lines.begin()->line->line()) { m_cursor_screen_row = 0; return; } if (m_iterator->line() > m_lines.rbegin()->line->line()) { m_cursor_screen_row = m_height - 1; return; } for (const LineDescriptor & screen_line : m_lines) { if (screen_line.line->line() == m_iterator->line()) { m_cursor_screen_row = screen_line.row_offset + m_cursor_row_offset; return; } } } } int BufferView::calculate_rows_in_line(std::shared_ptr start_of_line) { int saved_row_offset = 0; for (auto it = horiz_iter(start_of_line); it.is_valid(); it++) { if (!it.is_eol()) { saved_row_offset = it.row_offset(); } else { break; } } return saved_row_offset + 1; } int BufferView::calculate_rows_in_cursor_line(std::shared_ptr start_of_line) { int saved_row_offset = 0; for (auto it = horiz_iter(start_of_line); it.is_valid(); it++) { if (*it.iterator() == *m_iterator) { m_cursor_virtual_column = it.virtual_column(); m_cursor_screen_column = it.screen_column(); if (m_update_target_column) { m_target_column = m_cursor_virtual_column; } m_cursor_row_offset = it.row_offset(); } if (!it.is_eol()) { saved_row_offset = it.row_offset(); } else { break; } } m_update_target_column = false; return saved_row_offset + 1; } int BufferView::screen_rows_below_line(std::shared_ptr line) { auto i = std::make_shared(*line); int rows = 0; while ((rows < m_height) && i->go_next_line()) { rows += calculate_rows_in_line(i); } return rows; } int BufferView::screen_rows_above_line( std::shared_ptr line, std::list>> & backward_lines) { auto i = std::make_shared(*line); int rows = 0; while ((rows < m_height) && i->go_previous_line()) { int rows_in_this_line = calculate_rows_in_line(i); rows += rows_in_this_line; backward_lines.push_back(std::pair>(rows_in_this_line, std::make_shared(*i))); } return rows; } void BufferView::move_forward_to_column(int to_column, bool allow_eol) { auto start_of_line = std::make_shared(*m_iterator); start_of_line->go_start_of_line(); for (auto it = horiz_iter(start_of_line); it.is_valid(); it++) { if (allow_eol || (!it.is_eol())) { if (it.virtual_column() <= to_column) { *m_iterator = *it.iterator(); } else { break; } } } }