SpreadsheetModel.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. * Copyright (c) 2020-2022, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "SpreadsheetModel.h"
  7. #include "ConditionalFormatting.h"
  8. #include <LibGUI/AbstractView.h>
  9. #include <LibJS/Runtime/Error.h>
  10. #include <LibJS/Runtime/Object.h>
  11. #include <LibURL/URL.h>
  12. namespace Spreadsheet {
  13. GUI::Variant SheetModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
  14. {
  15. if (!index.is_valid())
  16. return {};
  17. if (role == GUI::ModelRole::Display) {
  18. auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() });
  19. if (!cell)
  20. return ByteString::empty();
  21. Function<ByteString(JS::Value)> to_byte_string_as_exception = [&](JS::Value value) {
  22. auto& vm = cell->sheet().global_object().vm();
  23. StringBuilder builder;
  24. builder.append("Error: "sv);
  25. if (value.is_object()) {
  26. auto& object = value.as_object();
  27. if (is<JS::Error>(object)) {
  28. auto message = object.get_without_side_effects("message");
  29. auto error = message.to_byte_string(vm);
  30. if (error.is_throw_completion())
  31. builder.append(message.to_string_without_side_effects());
  32. else
  33. builder.append(error.release_value());
  34. return builder.to_byte_string();
  35. }
  36. }
  37. auto error_message = value.to_byte_string(vm);
  38. if (error_message.is_throw_completion())
  39. return to_byte_string_as_exception(*error_message.release_error().value());
  40. builder.append(error_message.release_value());
  41. return builder.to_byte_string();
  42. };
  43. if (cell->kind() == Spreadsheet::Cell::Formula) {
  44. if (auto opt_throw_value = cell->thrown_value(); opt_throw_value.has_value())
  45. return to_byte_string_as_exception(*opt_throw_value);
  46. }
  47. auto display = cell->typed_display();
  48. if (display.is_error())
  49. return to_byte_string_as_exception(*display.release_error().value());
  50. return display.release_value();
  51. }
  52. if (role == GUI::ModelRole::MimeData)
  53. return Position { (size_t)index.column(), (size_t)index.row() }.to_url(m_sheet).to_byte_string();
  54. if (role == GUI::ModelRole::TextAlignment) {
  55. auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() });
  56. if (!cell)
  57. return {};
  58. return cell->type_metadata().alignment;
  59. }
  60. if (role == GUI::ModelRole::ForegroundColor) {
  61. auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() });
  62. if (!cell)
  63. return {};
  64. if (cell->kind() == Spreadsheet::Cell::Formula) {
  65. if (cell->thrown_value().has_value())
  66. return Color(Color::Red);
  67. }
  68. if (cell->evaluated_formats().foreground_color.has_value())
  69. return cell->evaluated_formats().foreground_color.value();
  70. if (auto color = cell->type_metadata().static_format.foreground_color; color.has_value())
  71. return color.value();
  72. return {};
  73. }
  74. if (role == GUI::ModelRole::BackgroundColor) {
  75. auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() });
  76. if (!cell)
  77. return {};
  78. if (cell->evaluated_formats().background_color.has_value())
  79. return cell->evaluated_formats().background_color.value();
  80. if (auto color = cell->type_metadata().static_format.background_color; color.has_value())
  81. return color.value();
  82. return {};
  83. }
  84. if (to_underlying(role) == to_underlying(Role::Tooltip)) {
  85. auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() });
  86. if (!cell || !cell->thrown_value().has_value())
  87. return {};
  88. auto value = cell->thrown_value().value();
  89. if (!value.is_object())
  90. return {};
  91. auto& object = value.as_object();
  92. if (!is<JS::Error>(object))
  93. return {};
  94. auto& error = static_cast<JS::Error&>(object);
  95. auto const& trace = error.traceback();
  96. StringBuilder builder;
  97. builder.appendff("{}\n", error.get_without_side_effects(object.vm().names.message).to_string_without_side_effects());
  98. for (auto const& frame : trace.in_reverse()) {
  99. if (frame.source_range().filename().contains("runtime.js"sv)) {
  100. if (frame.function_name == "<unknown>")
  101. builder.appendff(" in a builtin function at line {}, column {}\n", frame.source_range().start.line, frame.source_range().start.column);
  102. else
  103. builder.appendff(" while evaluating builtin '{}'\n", frame.function_name);
  104. } else if (frame.source_range().filename().starts_with("cell "sv)) {
  105. auto filename = frame.source_range().filename();
  106. builder.appendff(" in cell '{}', at line {}, column {}\n", filename.substring_view(5), frame.source_range().start.line, frame.source_range().start.column);
  107. }
  108. }
  109. return builder.to_byte_string();
  110. }
  111. return {};
  112. }
  113. RefPtr<Core::MimeData> SheetModel::mime_data(const GUI::ModelSelection& selection) const
  114. {
  115. auto mime_data = GUI::Model::mime_data(selection);
  116. bool first = true;
  117. const GUI::ModelIndex* cursor = nullptr;
  118. const_cast<SheetModel*>(this)->for_each_view([&](const GUI::AbstractView& view) {
  119. if (!first)
  120. return;
  121. cursor = &view.cursor_index();
  122. first = false;
  123. });
  124. VERIFY(cursor);
  125. Position cursor_position { (size_t)cursor->column(), (size_t)cursor->row() };
  126. auto mime_data_buffer = mime_data->data("text/x-spreadsheet-data"sv);
  127. auto new_data = ByteString::formatted("{}\n{}",
  128. cursor_position.to_url(m_sheet).to_byte_string(),
  129. StringView(mime_data_buffer));
  130. mime_data->set_data("text/x-spreadsheet-data"_string, new_data.to_byte_buffer());
  131. return mime_data;
  132. }
  133. ErrorOr<String> SheetModel::column_name(int index) const
  134. {
  135. if (index < 0)
  136. return String {};
  137. return TRY(String::from_byte_string(m_sheet->column(index)));
  138. }
  139. bool SheetModel::is_editable(const GUI::ModelIndex& index) const
  140. {
  141. if (!index.is_valid())
  142. return false;
  143. return true;
  144. }
  145. void SheetModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& value)
  146. {
  147. if (!index.is_valid())
  148. return;
  149. auto& cell = m_sheet->ensure({ (size_t)index.column(), (size_t)index.row() });
  150. auto previous_data = cell.data();
  151. cell.set_data(value.to_byte_string());
  152. if (on_cell_data_change)
  153. on_cell_data_change(cell, previous_data);
  154. did_update(UpdateFlag::DontInvalidateIndices);
  155. }
  156. void SheetModel::update()
  157. {
  158. m_sheet->update();
  159. did_update(UpdateFlag::DontInvalidateIndices | Model::UpdateFlag::DontResizeColumns);
  160. }
  161. CellsUndoCommand::CellsUndoCommand(Vector<CellChange> cell_changes)
  162. {
  163. m_cell_changes = cell_changes;
  164. }
  165. CellsUndoCommand::CellsUndoCommand(Cell& cell, ByteString const& previous_data)
  166. {
  167. m_cell_changes.append(CellChange(cell, previous_data));
  168. }
  169. void CellsUndoCommand::undo()
  170. {
  171. for (size_t i = 0; i < m_cell_changes.size(); ++i) {
  172. m_cell_changes[i].cell().set_data(m_cell_changes[i].previous_data());
  173. }
  174. }
  175. void CellsUndoCommand::redo()
  176. {
  177. for (size_t i = 0; i < m_cell_changes.size(); ++i) {
  178. m_cell_changes[i].cell().set_data(m_cell_changes[i].new_data());
  179. }
  180. }
  181. CellsUndoMetadataCommand::CellsUndoMetadataCommand(Vector<CellChange> cell_changes)
  182. {
  183. m_cell_changes = move(cell_changes);
  184. }
  185. void CellsUndoMetadataCommand::undo()
  186. {
  187. for (size_t i = 0; i < m_cell_changes.size(); ++i) {
  188. m_cell_changes[i].cell().set_type_metadata(m_cell_changes[i].previous_type_metadata());
  189. }
  190. }
  191. void CellsUndoMetadataCommand::redo()
  192. {
  193. for (size_t i = 0; i < m_cell_changes.size(); ++i) {
  194. m_cell_changes[i].cell().set_type_metadata(m_cell_changes[i].new_type_metadata());
  195. }
  196. }
  197. }