314 lines
9.1 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|
|
}
|