diff options
Diffstat (limited to 'Applications/Spreadsheet')
-rw-r--r-- | Applications/Spreadsheet/SpreadsheetModel.cpp | 7 | ||||
-rw-r--r-- | Applications/Spreadsheet/SpreadsheetModel.h | 1 | ||||
-rw-r--r-- | Applications/Spreadsheet/SpreadsheetView.cpp | 46 | ||||
-rw-r--r-- | Applications/Spreadsheet/SpreadsheetWidget.h | 1 | ||||
-rw-r--r-- | Applications/Spreadsheet/main.cpp | 68 |
5 files changed, 123 insertions, 0 deletions
diff --git a/Applications/Spreadsheet/SpreadsheetModel.cpp b/Applications/Spreadsheet/SpreadsheetModel.cpp index 34925c0780..cf07b0c6af 100644 --- a/Applications/Spreadsheet/SpreadsheetModel.cpp +++ b/Applications/Spreadsheet/SpreadsheetModel.cpp @@ -26,6 +26,7 @@ #include "SpreadsheetModel.h" #include "ConditionalFormatting.h" +#include <AK/URL.h> #include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/Object.h> @@ -68,6 +69,12 @@ GUI::Variant SheetModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) return cell->typed_display(); } + if (role == GUI::ModelRole::DragData) { + // FIXME: It would be really nice if we could send out a URL *and* some extra data, + // The Event already has support for this, but the user-facing API does not. + return Position { m_sheet->column(index.column()), (size_t)index.row() }.to_url().to_string(); + } + if (role == GUI::ModelRole::TextAlignment) { const auto* cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); if (!cell) diff --git a/Applications/Spreadsheet/SpreadsheetModel.h b/Applications/Spreadsheet/SpreadsheetModel.h index 740d2cef00..8edf46ab1e 100644 --- a/Applications/Spreadsheet/SpreadsheetModel.h +++ b/Applications/Spreadsheet/SpreadsheetModel.h @@ -44,6 +44,7 @@ public: virtual void set_data(const GUI::ModelIndex&, const GUI::Variant&) override; virtual void update() override; virtual bool is_column_sortable(int) const override { return false; } + virtual StringView drag_data_type() const override { return "text/x-spreadsheet-data"; } private: explicit SheetModel(Sheet& sheet) diff --git a/Applications/Spreadsheet/SpreadsheetView.cpp b/Applications/Spreadsheet/SpreadsheetView.cpp index f1023a5427..681d99736f 100644 --- a/Applications/Spreadsheet/SpreadsheetView.cpp +++ b/Applications/Spreadsheet/SpreadsheetView.cpp @@ -27,6 +27,9 @@ #include "SpreadsheetView.h" #include "CellTypeDialog.h" #include "SpreadsheetModel.h" +#include <AK/ScopeGuard.h> +#include <AK/URL.h> +#include <LibCore/MimeData.h> #include <LibGUI/BoxLayout.h> #include <LibGUI/HeaderView.h> #include <LibGUI/Menu.h> @@ -144,6 +147,49 @@ SpreadsheetView::SpreadsheetView(Sheet& sheet) m_table_view->update(); } })); + + m_table_view->on_drop = [&](const GUI::ModelIndex& index, const GUI::DropEvent& event) { + if (!index.is_valid()) + return; + + ScopeGuard update_after_drop { [this] { update(); } }; + + if (event.mime_data().has_format("text/x-spreadsheet-data")) { + auto data = event.mime_data().data("text/x-spreadsheet-data"); + StringView urls { data.data(), data.size() }; + bool first = true; + for (auto url : urls.lines(false)) { + if (!first) { // FIXME: Allow d&d from many cells to many cells, somehow. + dbg() << "Ignored '" << url << "'"; + continue; + } + + first = false; + + auto& target_cell = m_sheet->ensure({ m_sheet->column(index.column()), (size_t)index.row() }); + auto* source_cell = m_sheet->from_url(url); + + if (!source_cell) { + target_cell.set_data(""); + return; + } + + auto ref_cells = target_cell.referencing_cells; + target_cell = *source_cell; + target_cell.dirty = true; + target_cell.referencing_cells = move(ref_cells); + } + return; + } + + if (event.mime_data().has_text()) { + auto* target_cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); + ASSERT(target_cell); + + target_cell->set_data(event.text()); + return; + } + }; } void SpreadsheetView::hide_event(GUI::HideEvent&) diff --git a/Applications/Spreadsheet/SpreadsheetWidget.h b/Applications/Spreadsheet/SpreadsheetWidget.h index 908cef183e..ae1121a120 100644 --- a/Applications/Spreadsheet/SpreadsheetWidget.h +++ b/Applications/Spreadsheet/SpreadsheetWidget.h @@ -46,6 +46,7 @@ public: const String& current_filename() const { return m_workbook->current_filename(); } const Sheet& current_worksheet() const { return m_selected_view->sheet(); } + Sheet& current_worksheet() { return m_selected_view->sheet(); } void set_filename(const String& filename); Workbook& workbook() { return *m_workbook; } diff --git a/Applications/Spreadsheet/main.cpp b/Applications/Spreadsheet/main.cpp index 0acf43513b..0cf7e8a748 100644 --- a/Applications/Spreadsheet/main.cpp +++ b/Applications/Spreadsheet/main.cpp @@ -31,6 +31,7 @@ #include <LibCore/File.h> #include <LibGUI/AboutDialog.h> #include <LibGUI/Application.h> +#include <LibGUI/Clipboard.h> #include <LibGUI/FilePicker.h> #include <LibGUI/Forward.h> #include <LibGUI/Icon.h> @@ -150,6 +151,73 @@ int main(int argc, char* argv[]) spreadsheet_widget.set_filename(current_filename); })); + auto& edit_menu = menubar->add_menu("Edit"); + edit_menu.add_action(GUI::CommonActions::make_copy_action([&](auto&) { + auto& cells = spreadsheet_widget.current_worksheet().selected_cells(); + ASSERT(!cells.is_empty()); + StringBuilder text_builder, url_builder; + bool first = true; + for (auto& cell : cells) { + url_builder.append(cell.to_url().to_string()); + url_builder.append('\n'); + + auto cell_data = spreadsheet_widget.current_worksheet().at(cell); + if (!first) + text_builder.append('\t'); + if (cell_data) + text_builder.append(cell_data->data); + first = false; + } + HashMap<String, String> metadata; + metadata.set("text/x-spreadsheet-data", url_builder.to_string()); + + GUI::Clipboard::the().set_data(text_builder.string_view().bytes(), "text/plain", move(metadata)); + }, + window)); + edit_menu.add_action(GUI::CommonActions::make_paste_action([&](auto&) { + auto& cells = spreadsheet_widget.current_worksheet().selected_cells(); + ASSERT(!cells.is_empty()); + const auto& data = GUI::Clipboard::the().data_and_type(); + if (auto spreadsheet_data = data.metadata.get("text/x-spreadsheet-data"); spreadsheet_data.has_value()) { + Vector<URL> urls; + for (auto line : spreadsheet_data.value().split_view('\n')) { + if (line.is_empty()) + continue; + URL url { line }; + if (!url.is_valid()) + continue; + urls.append(move(url)); + } + + if (urls.size() == 1 && cells.size() == 1) { + auto& cell = *cells.begin(); + auto& url = urls.first(); + + auto* source_cell = spreadsheet_widget.current_worksheet().from_url(url); + if (source_cell) { + auto& target_cell = spreadsheet_widget.current_worksheet().ensure(cell); + auto references = target_cell.referencing_cells; + target_cell = *source_cell; + target_cell.referencing_cells = move(references); + target_cell.dirty = true; + spreadsheet_widget.update(); + } + + return; + } + + if (urls.size() != cells.size()) { + // FIXME: Somehow copy a bunch of cells into another bunch of cells. + TODO(); + } + } else { + for (auto& cell : spreadsheet_widget.current_worksheet().selected_cells()) + spreadsheet_widget.current_worksheet().ensure(cell).set_data(StringView { data.data.data(), data.data.size() }); + spreadsheet_widget.update(); + } + }, + window)); + auto& help_menu = menubar->add_menu("Help"); help_menu.add_action(GUI::Action::create( |