ClipboardHistoryModel.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*
  2. * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
  3. * Copyright (c) 2021, Mustafa Quraish <mustafa@cs.toronto.edu>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "ClipboardHistoryModel.h"
  8. #include <AK/NumberFormat.h>
  9. #include <AK/StringBuilder.h>
  10. #include <LibConfig/Client.h>
  11. NonnullRefPtr<ClipboardHistoryModel> ClipboardHistoryModel::create()
  12. {
  13. return adopt_ref(*new ClipboardHistoryModel());
  14. }
  15. ClipboardHistoryModel::ClipboardHistoryModel()
  16. : m_history_limit(Config::read_i32("ClipboardHistory", "ClipboardHistory", "NumHistoryItems", 20))
  17. {
  18. }
  19. ClipboardHistoryModel::~ClipboardHistoryModel()
  20. {
  21. }
  22. String ClipboardHistoryModel::column_name(int column) const
  23. {
  24. switch (column) {
  25. case Column::Data:
  26. return "Data";
  27. case Column::Type:
  28. return "Type";
  29. case Column::Size:
  30. return "Size";
  31. default:
  32. VERIFY_NOT_REACHED();
  33. }
  34. }
  35. static const char* bpp_for_format_resilient(String format)
  36. {
  37. unsigned format_uint = format.to_uint().value_or(static_cast<unsigned>(Gfx::BitmapFormat::Invalid));
  38. // Cannot use Gfx::Bitmap::bpp_for_format here, as we have to accept invalid enum values.
  39. switch (static_cast<Gfx::BitmapFormat>(format_uint)) {
  40. case Gfx::BitmapFormat::Indexed1:
  41. return "1";
  42. case Gfx::BitmapFormat::Indexed2:
  43. return "2";
  44. case Gfx::BitmapFormat::Indexed4:
  45. return "4";
  46. case Gfx::BitmapFormat::Indexed8:
  47. return "8";
  48. case Gfx::BitmapFormat::BGRx8888:
  49. case Gfx::BitmapFormat::BGRA8888:
  50. return "32";
  51. case Gfx::BitmapFormat::Invalid:
  52. /* fall-through */
  53. default:
  54. return "?";
  55. }
  56. }
  57. GUI::Variant ClipboardHistoryModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
  58. {
  59. if (role != GUI::ModelRole::Display)
  60. return {};
  61. auto& data_and_type = m_history_items[index.row()];
  62. switch (index.column()) {
  63. case Column::Data:
  64. if (data_and_type.mime_type.starts_with("text/"))
  65. return String::copy(data_and_type.data);
  66. if (data_and_type.mime_type == "image/x-serenityos") {
  67. StringBuilder builder;
  68. builder.append("[");
  69. builder.append(data_and_type.metadata.get("width").value_or("?"));
  70. builder.append('x');
  71. builder.append(data_and_type.metadata.get("height").value_or("?"));
  72. builder.append('x');
  73. builder.append(bpp_for_format_resilient(data_and_type.metadata.get("height").value_or("0")));
  74. builder.append(" bitmap");
  75. builder.append("]");
  76. return builder.to_string();
  77. }
  78. if (data_and_type.mime_type.starts_with("glyph/")) {
  79. StringBuilder builder;
  80. builder.append("[");
  81. builder.append(data_and_type.metadata.get("width").value_or("?"));
  82. builder.append("x");
  83. builder.append(data_and_type.metadata.get("height").value_or("?"));
  84. builder.append("] ");
  85. builder.append("(");
  86. builder.append(data_and_type.metadata.get("char").value_or(""));
  87. builder.append(")");
  88. return builder.to_string();
  89. }
  90. return "<...>";
  91. case Column::Type:
  92. return data_and_type.mime_type;
  93. case Column::Size:
  94. return AK::human_readable_size(data_and_type.data.size());
  95. default:
  96. VERIFY_NOT_REACHED();
  97. }
  98. }
  99. void ClipboardHistoryModel::add_item(const GUI::Clipboard::DataAndType& item)
  100. {
  101. m_history_items.remove_first_matching([&](GUI::Clipboard::DataAndType& existing) {
  102. return existing.data == item.data && existing.mime_type == item.mime_type;
  103. });
  104. if (m_history_items.size() == m_history_limit)
  105. m_history_items.take_last();
  106. m_history_items.prepend(item);
  107. invalidate();
  108. }
  109. void ClipboardHistoryModel::remove_item(int index)
  110. {
  111. m_history_items.remove(index);
  112. }
  113. void ClipboardHistoryModel::config_string_did_change(String const& domain, String const& group, String const& key, String const& value_string)
  114. {
  115. if (domain != "ClipboardHistory" || group != "ClipboardHistory")
  116. return;
  117. // FIXME: Once we can get notified for `i32` changes, we can use that instead of this hack.
  118. if (key == "NumHistoryItems") {
  119. auto value_or_error = value_string.to_int();
  120. if (!value_or_error.has_value())
  121. return;
  122. auto value = value_or_error.value();
  123. if (value < (int)m_history_items.size()) {
  124. m_history_items.remove(value, m_history_items.size() - value);
  125. invalidate();
  126. }
  127. m_history_limit = value;
  128. return;
  129. }
  130. }