Serenity Operating System
at master 137 lines 4.1 kB view raw
1/* 2 * Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "OutlineModel.h" 8#include <AK/Assertions.h> 9#include <LibGUI/ModelRole.h> 10#include <LibGfx/Font/FontDatabase.h> 11#include <LibGfx/TextAlignment.h> 12#include <LibPDF/Document.h> 13 14enum Columns { 15 Page, 16 Title, 17 _Count 18}; 19 20ErrorOr<NonnullRefPtr<OutlineModel>> OutlineModel::create(NonnullRefPtr<PDF::OutlineDict> const& outline) 21{ 22 auto outline_model = adopt_ref(*new OutlineModel(outline)); 23 outline_model->m_closed_item_icon.set_bitmap_for_size(16, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/book.png"sv))); 24 outline_model->m_open_item_icon.set_bitmap_for_size(16, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/book-open.png"sv))); 25 return outline_model; 26} 27 28OutlineModel::OutlineModel(NonnullRefPtr<PDF::OutlineDict> const& outline) 29 : m_outline(outline) 30{ 31} 32 33void OutlineModel::set_index_open_state(const GUI::ModelIndex& index, bool is_open) 34{ 35 VERIFY(index.is_valid()); 36 auto* outline_item = static_cast<PDF::OutlineItem*>(index.internal_data()); 37 38 if (is_open) { 39 m_open_outline_items.set(outline_item); 40 } else { 41 m_open_outline_items.remove(outline_item); 42 } 43} 44 45int OutlineModel::row_count(const GUI::ModelIndex& index) const 46{ 47 if (!index.is_valid()) 48 return m_outline->children.size(); 49 auto outline_item = static_cast<PDF::OutlineItem*>(index.internal_data()); 50 return static_cast<int>(outline_item->children.size()); 51} 52 53int OutlineModel::tree_column() const 54{ 55 return Columns::Title; 56} 57 58int OutlineModel::column_count(const GUI::ModelIndex&) const 59{ 60 return Columns::_Count; 61} 62 63PDF::Destination const& OutlineModel::get_destination(GUI::ModelIndex const& index) 64{ 65 auto* outline_item = static_cast<PDF::OutlineItem*>(index.internal_data()); 66 return outline_item->dest; 67} 68 69GUI::Variant OutlineModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const 70{ 71 VERIFY(index.is_valid()); 72 auto outline_item = static_cast<PDF::OutlineItem*>(index.internal_data()); 73 74 switch (role) { 75 case GUI::ModelRole::Display: 76 switch (index.column()) { 77 case Columns::Title: 78 return outline_item->title; 79 case Columns::Page: { 80 auto maybe_page_number = outline_item->dest.page; 81 if (maybe_page_number.has_value()) { 82 return maybe_page_number.release_value(); 83 } 84 return {}; 85 } 86 default: 87 VERIFY_NOT_REACHED(); 88 } 89 90 case GUI::ModelRole::Icon: 91 if (m_open_outline_items.contains(outline_item)) 92 return m_open_item_icon; 93 return m_closed_item_icon; 94 default: 95 return {}; 96 97 case GUI::ModelRole::TextAlignment: 98 switch (index.column()) { 99 case Columns::Title: 100 return Gfx::TextAlignment::CenterLeft; 101 case Columns::Page: 102 return Gfx::TextAlignment::CenterRight; 103 default: 104 VERIFY_NOT_REACHED(); 105 } 106 } 107} 108 109GUI::ModelIndex OutlineModel::parent_index(const GUI::ModelIndex& index) const 110{ 111 if (!index.is_valid()) 112 return {}; 113 114 auto* outline_item = static_cast<PDF::OutlineItem*>(index.internal_data()); 115 auto& parent = outline_item->parent; 116 117 if (!parent) 118 return {}; 119 120 Vector<NonnullRefPtr<PDF::OutlineItem>> parent_siblings = (parent->parent ? parent->parent->children : m_outline->children); 121 for (size_t i = 0; i < parent_siblings.size(); i++) { 122 auto* parent_sibling = parent_siblings[i].ptr(); 123 if (parent_sibling == parent.ptr()) 124 return create_index(static_cast<int>(i), index.column(), parent.ptr()); 125 } 126 127 VERIFY_NOT_REACHED(); 128} 129 130GUI::ModelIndex OutlineModel::index(int row, int column, const GUI::ModelIndex& parent) const 131{ 132 if (!parent.is_valid()) 133 return create_index(row, column, &m_outline->children[row]); 134 135 auto parent_outline_item = static_cast<PDF::OutlineItem*>(parent.internal_data()); 136 return create_index(row, column, &parent_outline_item->children[row]); 137}