jes/src/core/BufferView.cc

314 lines
9.1 KiB
C++

#include "BufferView.h"
#include <list>
#include <utility>
#include <limits.h>
BufferView::BufferView(std::shared_ptr<Buffer> buffer,
std::shared_ptr<Buffer::Iterator> 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<Buffer::Iterator>(*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<std::pair<int, std::shared_ptr<Buffer::Iterator>>> 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<Buffer::Iterator>(*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<Buffer::Iterator>(*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<Buffer::Iterator> 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<Buffer::Iterator> 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<Buffer::Iterator> line)
{
auto i = std::make_shared<Buffer::Iterator>(*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<Buffer::Iterator> line,
std::list<std::pair<int, std::shared_ptr<Buffer::Iterator>>> & backward_lines)
{
auto i = std::make_shared<Buffer::Iterator>(*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<int, std::shared_ptr<Buffer::Iterator>>(rows_in_this_line, std::make_shared<Buffer::Iterator>(*i)));
}
return rows;
}
void BufferView::move_forward_to_column(int to_column, bool allow_eol)
{
auto start_of_line = std::make_shared<Buffer::Iterator>(*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;
}
}
}
}