From 9146ef2f6140d183c18523b07368b6b7710e5a60 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 1 Jun 2020 11:11:38 +0200
Subject: [PATCH] Copy/Paste for the Settings and Layers in the ObjectList

---
 src/slic3r/GUI/GUI_ObjectList.cpp | 108 +++++++++++++++++++++++++-----
 src/slic3r/GUI/GUI_ObjectList.hpp |  31 ++++++++-
 src/slic3r/GUI/Plater.cpp         |  14 +++-
 3 files changed, 131 insertions(+), 22 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 441f30816..b8f596e6b 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -695,33 +695,41 @@ void ObjectList::selection_changed()
     part_selection_changed();
 }
 
-void ObjectList::fill_layer_config_ranges_cache()
+void ObjectList::copy_layers_to_clipboard()
 {
     wxDataViewItemArray sel_layers;
     GetSelections(sel_layers);
 
-    const int obj_idx = m_objects_model->GetObjectIdByItem(sel_layers[0]);
+    const int obj_idx = m_objects_model->GetObjectIdByItem(sel_layers.front());
     if (obj_idx < 0 || (int)m_objects->size() <= obj_idx)
         return;
 
     const t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
-    m_layer_config_ranges_cache.clear();
+    t_layer_config_ranges& cache_ranges = m_clipboard.get_ranges_cache();
+
+    if (sel_layers.Count() == 1 && m_objects_model->GetItemType(sel_layers.front()) & itLayerRoot)
+    {
+        cache_ranges.clear();
+        cache_ranges = ranges;
+        return;
+    }
 
     for (const auto layer_item : sel_layers)
         if (m_objects_model->GetItemType(layer_item) & itLayer) {
             auto range = m_objects_model->GetLayerRangeByItem(layer_item);
             auto it = ranges.find(range);
             if (it != ranges.end())
-                m_layer_config_ranges_cache[it->first] = it->second;
+                cache_ranges[it->first] = it->second;
         }
 }
 
 void ObjectList::paste_layers_into_list()
 {
     const int obj_idx = m_objects_model->GetObjectIdByItem(GetSelection());
+    t_layer_config_ranges& cache_ranges = m_clipboard.get_ranges_cache();
 
     if (obj_idx < 0 || (int)m_objects->size() <= obj_idx || 
-        m_layer_config_ranges_cache.empty() || printer_technology() == ptSLA)
+        cache_ranges.empty() || printer_technology() == ptSLA)
         return;
 
     const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx);
@@ -732,7 +740,7 @@ void ObjectList::paste_layers_into_list()
     t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
 
     // and create Layer item(s) according to the layer_config_ranges
-    for (const auto range : m_layer_config_ranges_cache)
+    for (const auto range : cache_ranges)
         ranges.emplace(range);
 
     layers_item = add_layer_root_item(object_item);
@@ -745,6 +753,48 @@ void ObjectList::paste_layers_into_list()
 #endif //no __WXOSX__
 }
 
+void ObjectList::copy_settings_to_clipboard()
+{
+    wxDataViewItem item = GetSelection();
+    assert(item.IsOk());
+    if (m_objects_model->GetItemType(item) & itSettings)
+        item = m_objects_model->GetParent(item);
+
+    DynamicPrintConfig& config_cache = m_clipboard.get_config_cache();
+    config_cache = get_item_config(item);
+}
+
+void ObjectList::paste_settings_into_list()
+{
+    wxDataViewItem item = GetSelection();
+    assert(item.IsOk());
+    if (m_objects_model->GetItemType(item) & itSettings)
+        item = m_objects_model->GetParent(item);
+
+    ItemType item_type = m_objects_model->GetItemType(item);
+    if(!(item_type & (itObject | itVolume |itLayer)))
+        return;
+
+    DynamicPrintConfig& config_cache = m_clipboard.get_config_cache();
+    assert(!config_cache.empty());
+
+    auto keys = config_cache.keys();
+    auto part_options = get_options(true);
+
+    for (const std::string& opt_key: keys) {
+        if (item_type & (itVolume | itLayer) &&
+            std::find(part_options.begin(), part_options.end(), opt_key) == part_options.end())
+            continue; // we can't to add object specific options for the part's(itVolume | itLayer) config 
+
+        const ConfigOption* option = config_cache.option(opt_key);
+        if (option)
+            m_config->set_key_value(opt_key, option->clone());
+    }
+
+    // Add settings item for object/sub-object and show them 
+    show_settings(add_settings_item(item, m_config));
+}
+
 void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
 {
     if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx))
@@ -984,20 +1034,46 @@ void ObjectList::extruder_editing()
 
 void ObjectList::copy()
 {
-    // if (m_selection_mode & smLayer)
-    //     fill_layer_config_ranges_cache();
-    // else {
-    //     m_layer_config_ranges_cache.clear();
-        wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY));
-    // }
+    wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY));
 }
 
 void ObjectList::paste()
 {
-    // if (!m_layer_config_ranges_cache.empty())
-    //     paste_layers_into_list();
-    // else
-        wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
+    wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
+}
+
+bool ObjectList::copy_to_clipboard()
+{
+    wxDataViewItemArray sels;
+    GetSelections(sels);
+    ItemType type = m_objects_model->GetItemType(sels.front());
+    if (!(type & (itSettings | itLayer | itLayerRoot))) {
+        m_clipboard.reset();
+        return false;
+    }
+
+    if (type & itSettings)
+        copy_settings_to_clipboard();
+    if (type & (itLayer | itLayerRoot))
+        copy_layers_to_clipboard();
+
+    m_clipboard.set_type(type);
+    return true;
+}
+
+bool ObjectList::paste_from_clipboard()
+{
+    if (!(m_clipboard.get_type() & (itSettings | itLayer | itLayerRoot))) {
+        m_clipboard.reset();
+        return false;
+    }
+
+    if (m_clipboard.get_type() & itSettings)
+        paste_settings_into_list();
+    if (m_clipboard.get_type() & (itLayer | itLayerRoot))
+        paste_layers_into_list();
+
+    return true;
 }
 
 void ObjectList::undo()
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index 4490ec5e9..ad222a4a8 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -81,10 +81,32 @@ public:
         smLayerRoot = 16, // used for undo/redo
     };
 
+    struct Clipboard
+    {
+        void reset() {
+            m_type = itUndef; 
+            m_layer_config_ranges_cache .clear();
+            m_config_cache.clear();
+        }
+        bool        empty()    const { return m_type == itUndef; }
+        ItemType    get_type() const { return m_type; }
+        void        set_type(ItemType type) { m_type = type; }
+
+        t_layer_config_ranges&  get_ranges_cache() { return m_layer_config_ranges_cache; }
+        DynamicPrintConfig&     get_config_cache() { return m_config_cache; }
+
+    private:
+        ItemType                m_type {itUndef};
+        t_layer_config_ranges   m_layer_config_ranges_cache;
+        DynamicPrintConfig      m_config_cache;
+    };
+
 private:
     SELECTION_MODE  m_selection_mode {smUndef};
     int             m_selected_layers_range_idx;
 
+    Clipboard       m_clipboard;
+
     struct dragged_item_data
     {
         void init(const int obj_idx, const int subobj_idx, const ItemType type) {
@@ -148,8 +170,6 @@ private:
 
     std::vector<wxBitmap*>      m_bmp_vector;
 
-    t_layer_config_ranges       m_layer_config_ranges_cache;
-
     int			m_selected_object_id = -1;
     bool		m_prevent_list_events = false;		// We use this flag to avoid circular event handling Select() 
                                                     // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler 
@@ -230,6 +250,8 @@ public:
 
     void                copy();
     void                paste();
+    bool                copy_to_clipboard();
+    bool                paste_from_clipboard();
     void                undo();
     void                redo();
 
@@ -385,8 +407,11 @@ public:
     void fix_through_netfabb();
     void update_item_error_icon(const int obj_idx, int vol_idx) const ;
 
-    void fill_layer_config_ranges_cache();
+    void copy_layers_to_clipboard();
     void paste_layers_into_list();
+    void copy_settings_to_clipboard();
+    void paste_settings_into_list();
+    bool clipboard_is_empty() const { return m_clipboard.empty(); } 
     void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
     void paste_objects_into_list(const std::vector<size_t>& object_idxs);
 
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 03bbab796..34b151a93 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -5456,7 +5456,10 @@ void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_acti
 
 void Plater::copy_selection_to_clipboard()
 {
-    if (can_copy_to_clipboard())
+    // At first try to copy selected values to the ObjectList's clipboard
+    // to check if Settings or Layers are selected in the list
+    // and then copy to 3DCanvas's clipboard if not
+    if (can_copy_to_clipboard() && !p->sidebar->obj_list()->copy_to_clipboard())
         p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
 }
 
@@ -5466,7 +5469,12 @@ void Plater::paste_from_clipboard()
         return;
 
     Plater::TakeSnapshot snapshot(this, _L("Paste From Clipboard"));
-    p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
+
+    // At first try to paste values from the ObjectList's clipboard
+    // to check if Settings or Layers were copied
+    // and then paste from the 3DCanvas's clipboard if not
+    if (!p->sidebar->obj_list()->paste_from_clipboard())
+        p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
 }
 
 void Plater::search(bool plater_is_active)
@@ -5591,7 +5599,7 @@ bool Plater::can_paste_from_clipboard() const
     const Selection& selection = p->view3D->get_canvas3d()->get_selection();
     const Selection::Clipboard& clipboard = selection.get_clipboard();
 
-    if (clipboard.is_empty())
+    if (clipboard.is_empty() && p->sidebar->obj_list()->clipboard_is_empty())
         return false;
 
     if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) && !clipboard.is_sla_compliant())