/* * Copyright (c) 2021-2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include class UndoSelection : public RefCounted { public: explicit UndoSelection(int const start, int const size, u32 const active_glyph, Gfx::BitmapFont const& font, NonnullRefPtr glyph_map_widget) : m_start(start) , m_size(size) , m_active_glyph(active_glyph) , m_font(font) , m_glyph_map_widget(move(glyph_map_widget)) { } ErrorOr> save_state() { auto state = TRY(try_make_ref_counted(m_start, m_size, m_active_glyph, *m_font, m_glyph_map_widget)); size_t bytes_per_glyph = Gfx::GlyphBitmap::bytes_per_row() * font().glyph_height(); auto* rows = font().rows() + m_start * bytes_per_glyph; auto* widths = font().widths() + m_start; TRY(state->m_data.try_append(&rows[0], bytes_per_glyph * m_size)); TRY(state->m_data.try_append(&widths[0], m_size)); TRY(state->m_restored_modified_state.try_ensure_capacity(m_size)); for (int glyph = m_start; glyph < m_start + m_size; ++glyph) TRY(state->m_restored_modified_state.try_append(m_glyph_map_widget->glyph_is_modified(glyph))); return state; } void restore_state(UndoSelection const& state) { size_t bytes_per_glyph = Gfx::GlyphBitmap::bytes_per_row() * font().glyph_height(); auto* rows = font().rows() + state.m_start * bytes_per_glyph; auto* widths = font().widths() + state.m_start; memcpy(rows, &state.m_data[0], bytes_per_glyph * state.m_size); memcpy(widths, &state.m_data[bytes_per_glyph * state.m_size], state.m_size); for (int i = 0; i < state.m_size; ++i) m_glyph_map_widget->set_glyph_modified(state.m_start + i, state.m_restored_modified_state[i]); m_restored_active_glyph = state.m_active_glyph; m_restored_start = state.m_start; m_restored_size = state.m_size; } void set_start(int start) { m_start = start; } void set_size(int size) { m_size = size; } void set_active_glyph(u32 code_point) { m_active_glyph = code_point; } Gfx::BitmapFont& font() { return *m_font; } u32 restored_active_glyph() const { return m_restored_active_glyph; } int restored_start() const { return m_restored_start; } int restored_size() const { return m_restored_size; } private: int m_start { 0 }; int m_size { 0 }; u32 m_active_glyph { 0 }; int m_restored_start { 0 }; int m_restored_size { 0 }; u32 m_restored_active_glyph { 0 }; Vector m_restored_modified_state; RefPtr m_font; NonnullRefPtr m_glyph_map_widget; ByteBuffer m_data; }; class SelectionUndoCommand : public GUI::Command { public: SelectionUndoCommand(UndoSelection& selection, NonnullRefPtr undo_state) : m_undo_state(undo_state) , m_undo_selection(selection) { } virtual void undo() override { if (!m_redo_state) { if (auto maybe_state = m_undo_state->save_state(); !maybe_state.is_error()) { auto state = maybe_state.release_value(); m_redo_state = move(state); } else { warnln("Failed to save redo state: {}", maybe_state.error()); } } m_undo_selection.restore_state(*m_undo_state); } virtual void redo() override { if (m_redo_state) m_undo_selection.restore_state(*m_redo_state); else warnln("Failed to restore state"); } private: NonnullRefPtr m_undo_state; RefPtr m_redo_state; UndoSelection& m_undo_selection; };