include/svx/strings.hrc                     |    1 
 svx/source/dialog/docrecovery.cxx           |  140 +++++++++++++++++++++++++---
 svx/source/inc/docrecovery.hxx              |   12 ++
 svx/uiconfig/ui/docrecoveryrecoverdialog.ui |   83 +++++++++-------
 4 files changed, 185 insertions(+), 51 deletions(-)

New commits:
commit a9c8ac0605fd1d19e1d79b54804b610a64a8a056
Author:     Heiko Tietze <tietze.he...@gmail.com>
AuthorDate: Wed Jul 6 15:11:28 2022 +0200
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Fri Aug 5 16:25:46 2022 +0200

    Resolves tdf#114508: Individual selection in recovery dialog
    
    Based on work by Danie Truter at I397119a6e50c256e87e3a484a6c17a252f191981
    
    Change-Id: I75fd321cbfeb7a92bd14e94b0c1ae0bb25be73cd
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136845
    Tested-by: Jenkins
    Tested-by: Heiko Tietze <heiko.tie...@documentfoundation.org>
    Reviewed-by: Heiko Tietze <heiko.tie...@documentfoundation.org>
    Tested-by: Caolán McNamara <caol...@redhat.com>
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/include/svx/strings.hrc b/include/svx/strings.hrc
index f800d8365cf5..f46663ccf669 100644
--- a/include/svx/strings.hrc
+++ b/include/svx/strings.hrc
@@ -1020,6 +1020,7 @@
 #define RID_SVXSTR_RECOVFAILED                              
NC_("RID_SVXSTR_RECOVFAILED", "Recovery failed")
 #define RID_SVXSTR_RECOVINPROGR                             
NC_("RID_SVXSTR_RECOVINPROGR", "Recovery in progress")
 #define RID_SVXSTR_NOTRECOVYET                              
NC_("RID_SVXSTR_NOTRECOVYET", "Not recovered yet")
+#define RID_SVXSTR_WILLDISCARD                              
NC_("RID_SVXSTR_WILLDISCARD", "Will be discarded")
 #define RID_SVXSTR_RECOVERY_INPROGRESS                      
NC_("RID_SVXSTR_RECOVERY_INPROGRESS", "%PRODUCTNAME %PRODUCTVERSION has begun 
recovering your documents. Depending on the size of the documents this process 
can take some time.")
 #define RID_SVXSTR_RECOVERYONLY_FINISH_DESCR                
NC_("RID_SVXSTR_RECOVERYONLY_FINISH_DESCR", "Recovery of your documents was 
finished. Click 'Finish' to see your documents.")
 #define RID_SVXSTR_RECOVERYONLY_FINISH                      
NC_("RID_SVXSTR_RECOVERYONLY_FINISH", "~Finish")
diff --git a/svx/source/dialog/docrecovery.cxx 
b/svx/source/dialog/docrecovery.cxx
index 61a2fedb4f8a..6675a05c64ea 100644
--- a/svx/source/dialog/docrecovery.cxx
+++ b/svx/source/dialog/docrecovery.cxx
@@ -47,6 +47,11 @@ namespace svx::DocRecovery
 
 using namespace ::osl;
 
+#define COLUMN_STANDARDIMAGE -1
+#define COLUMN_DISPLAYNAME 0
+#define COLUMN_STATUSIMAGE 1
+#define COLUMN_STATUSTEXT 2
+
 RecoveryCore::RecoveryCore(css::uno::Reference< css::uno::XComponentContext > 
xContext,
                                  bool                                          
  bUsedForSaving)
     : m_xContext        (std::move( xContext    ))
@@ -190,6 +195,33 @@ void RecoveryCore::forgetBrokenTempEntries()
     }
 }
 
+// should only be called with valid m_xRealCore
+void RecoveryCore::forgetAllRecoveryEntriesMarkedForDiscard()
+{
+    assert(m_xRealCore);
+
+    // potential to move in a separate function
+    css::util::URL aRemoveURL = 
impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
+    css::uno::Sequence<css::beans::PropertyValue> lRemoveArgs(2);
+    auto plRemoveArgs = lRemoveArgs.getArray();
+    plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
+    plRemoveArgs[0].Value <<= false;
+    plRemoveArgs[1].Name = PROP_ENTRYID;
+
+    // work on a copied list only ...
+    // Reason: We will get notifications from the core for every
+    // changed or removed element. And that will change our m_lURLs list.
+    // That's not a good idea, if we use a stl iterator inbetween .-)
+    TURLList lURLs = m_lURLs;
+    for (const TURLInfo& rInfo : lURLs)
+    {
+        if (!rInfo.ShouldDiscard)
+            continue;
+
+        plRemoveArgs[1].Value <<= rInfo.ID;
+        m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
+    }
+}
 
 void RecoveryCore::forgetAllRecoveryEntries()
 {
@@ -293,6 +325,8 @@ void RecoveryCore::doRecovery()
     if (!m_xRealCore.is())
         return;
 
+    forgetAllRecoveryEntriesMarkedForDiscard();
+
     css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_RECOVERY);
 
     css::uno::Sequence< css::beans::PropertyValue > lArgs{
@@ -646,29 +680,31 @@ RecoveryDialog::RecoveryDialog(weld::Window* pParent, 
RecoveryCore* pCore)
     , m_eRecoveryState(RecoveryDialog::E_RECOVERY_PREPARED)
     , m_bWaitForCore(false)
     , m_bWasRecoveryStarted(false)
+//    , m_aColumnOffset(0)
+    , m_aToggleCount(0)
     , m_aSuccessRecovStr(SvxResId(RID_SVXSTR_SUCCESSRECOV))
     , m_aOrigDocRecovStr(SvxResId(RID_SVXSTR_ORIGDOCRECOV))
     , m_aRecovFailedStr(SvxResId(RID_SVXSTR_RECOVFAILED))
     , m_aRecovInProgrStr(SvxResId(RID_SVXSTR_RECOVINPROGR))
     , m_aNotRecovYetStr(SvxResId(RID_SVXSTR_NOTRECOVYET))
+    , m_aWillBeDiscStr(SvxResId(RID_SVXSTR_WILLDISCARD))
     , m_xDescrFT(m_xBuilder->weld_label("desc"))
     , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
     , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
     , m_xNextBtn(m_xBuilder->weld_button("next"))
     , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
 {
-    const auto nWidth = m_xFileListLB->get_approximate_digit_width() * 70;
+    const auto nWidth = m_xFileListLB->get_approximate_digit_width() * 80;
     m_xFileListLB->set_size_request(nWidth, 
m_xFileListLB->get_height_rows(10));
     
m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() 
* 50, -1);
     m_xProgress = new PluginProgress(m_xProgressBar.get());
 
-    std::vector<int> aWidths
-    {
-        o3tl::narrowing<int>(m_xFileListLB->get_checkbox_column_width()),
-        o3tl::narrowing<int>(60 * nWidth / 100),
-        o3tl::narrowing<int>(m_xFileListLB->get_checkbox_column_width())
-    };
+    std::vector<int> aWidths;
+    aWidths.push_back(60 * nWidth / 100);
+    aWidths.push_back(5 * nWidth / 100);
     m_xFileListLB->set_column_fixed_widths(aWidths);
+    m_xFileListLB->enable_toggle_buttons(weld::ColumnToggleType::Check);
+    m_xFileListLB->connect_toggled( LINK(this, RecoveryDialog, ToggleRowHdl) );
 
     m_xNextBtn->set_sensitive(true);
     m_xNextBtn->connect_clicked( LINK( this, RecoveryDialog, NextButtonHdl ) );
@@ -680,11 +716,13 @@ RecoveryDialog::RecoveryDialog(weld::Window* pParent, 
RecoveryCore* pCore)
     {
         const TURLInfo& rInfo = rURLList[i];
         m_xFileListLB->append();
+        m_xFileListLB->set_toggle(i, TRISTATE_TRUE);
         m_xFileListLB->set_id(i, weld::toId(&rInfo));
-        m_xFileListLB->set_image(i, rInfo.StandardImageId, 0);
-        m_xFileListLB->set_text(i, rInfo.DisplayName, 1);
-        m_xFileListLB->set_image(i, impl_getStatusImage(rInfo), 2);
-        m_xFileListLB->set_text(i, impl_getStatusString(rInfo), 3);
+        m_xFileListLB->set_image(i, rInfo.StandardImageId, 
COLUMN_STANDARDIMAGE);
+        m_xFileListLB->set_text(i, rInfo.DisplayName, COLUMN_DISPLAYNAME);
+        m_xFileListLB->set_image(i, impl_getStatusImage(rInfo), 
COLUMN_STATUSIMAGE);
+        m_xFileListLB->set_text(i, impl_getStatusString(rInfo), 
COLUMN_STATUSTEXT);
+        m_aToggleCount++;
     }
 
     // mark first item
@@ -876,10 +914,10 @@ void RecoveryDialog::updateItems()
         if ( !pInfo )
             continue;
 
-        m_xFileListLB->set_image(i, impl_getStatusImage(*pInfo), 2);
+        m_xFileListLB->set_image(i, impl_getStatusImage(*pInfo), 
COLUMN_STATUSIMAGE);
         OUString sStatus = impl_getStatusString( *pInfo );
         if (!sStatus.isEmpty())
-            m_xFileListLB->set_text(i, sStatus, 3);
+            m_xFileListLB->set_text(i, sStatus, COLUMN_STATUSTEXT);
     }
 }
 
@@ -946,6 +984,52 @@ IMPL_LINK_NOARG(RecoveryDialog, CancelButtonHdl, 
weld::Button&, void)
     }
 }
 
+IMPL_LINK_NOARG(RecoveryDialog, ToggleRowHdl, const weld::TreeView::iter_col&, 
void)
+{
+    int aIndex = m_xFileListLB->get_selected_index();
+    TriState eState = m_xFileListLB->get_toggle(aIndex);
+
+    if (m_bWasRecoveryStarted)
+    {
+        switch (eState)
+        {
+            case TRISTATE_FALSE:
+                 eState = TRISTATE_TRUE;
+                break;
+            case TRISTATE_TRUE:
+                eState = TRISTATE_FALSE;
+                break;
+            default:
+                // should never happen
+                assert(false);
+                break;
+        }
+
+        // revert toggle
+        m_xFileListLB->set_toggle(aIndex, eState);
+    }
+    else
+    {
+        impl_updateItemDescription(aIndex, eState);
+
+        switch (eState)
+        {
+            case TRISTATE_FALSE:
+                m_aToggleCount--;
+                break;
+            case TRISTATE_TRUE:
+                m_aToggleCount++;
+                break;
+            default:
+                // should never happen
+                assert(false);
+                break;
+        }
+
+        m_xNextBtn->set_sensitive(m_aToggleCount != 0);
+    }
+}
+
 OUString RecoveryDialog::impl_getStatusString( const TURLInfo& rInfo ) const
 {
     OUString sStatus;
@@ -966,6 +1050,9 @@ OUString RecoveryDialog::impl_getStatusString( const 
TURLInfo& rInfo ) const
         case E_NOT_RECOVERED_YET :
             sStatus = m_aNotRecovYetStr;
             break;
+        case E_WILL_BE_DISCARDED:
+            sStatus = m_aWillBeDiscStr;
+            break;
         default:
             break;
     }
@@ -992,6 +1079,33 @@ OUString RecoveryDialog::impl_getStatusImage( const 
TURLInfo& rInfo )
     return sStatus;
 }
 
+void RecoveryDialog::impl_updateItemDescription(int row, const TriState& 
rState)
+{
+    TURLInfo* pInfo = 
reinterpret_cast<TURLInfo*>(m_xFileListLB->get_id(row).toInt64());
+    if (!pInfo)
+        return;
+
+    switch (rState)
+    {
+        case TRISTATE_FALSE:
+            pInfo->RecoveryState = ERecoveryState::E_WILL_BE_DISCARDED;
+            pInfo->ShouldDiscard = true;
+            break;
+        case TRISTATE_TRUE:
+            pInfo->RecoveryState = ERecoveryState::E_NOT_RECOVERED_YET;
+            pInfo->ShouldDiscard = false;
+            break;
+        default:
+            // should never happen
+            assert(false);
+            break;
+    }
+
+    OUString sStatus = impl_getStatusString(*pInfo);
+    if (!sStatus.isEmpty())
+        m_xFileListLB->set_text(row, sStatus, COLUMN_STATUSTEXT);
+}
+
 BrokenRecoveryDialog::BrokenRecoveryDialog(weld::Window* pParent,
                                            RecoveryCore* pCore,
                                            bool bBeforeRecovery)
diff --git a/svx/source/inc/docrecovery.hxx b/svx/source/inc/docrecovery.hxx
index df90e5354f56..d7442d2c7ab1 100644
--- a/svx/source/inc/docrecovery.hxx
+++ b/svx/source/inc/docrecovery.hxx
@@ -101,7 +101,8 @@ enum ERecoveryState
     E_ORIGINAL_DOCUMENT_RECOVERED,
     E_RECOVERY_FAILED,
     E_RECOVERY_IS_IN_PROGRESS,
-    E_NOT_RECOVERED_YET
+    E_NOT_RECOVERED_YET,
+    E_WILL_BE_DISCARDED,
 };
 
 
@@ -139,12 +140,16 @@ struct TURLInfo
     /// standard icon
     OUString StandardImageId;
 
+    /// user choice to discard
+    bool ShouldDiscard;
+
     public:
 
     TURLInfo()
         : ID           (-1                 )
         , DocState     (EDocStates::Unknown)
         , RecoveryState(E_NOT_RECOVERED_YET)
+        , ShouldDiscard(false)
     {}
 };
 
@@ -233,6 +238,7 @@ class RecoveryCore final : public ::cppu::WeakImplHelper< 
css::frame::XStatusLis
         void forgetBrokenTempEntries();
         void forgetAllRecoveryEntries();
         void forgetBrokenRecoveryEntries();
+        void forgetAllRecoveryEntriesMarkedForDiscard();
 
 
         /** @short  TODO */
@@ -404,12 +410,14 @@ class RecoveryDialog final : public 
weld::GenericDialogController
         sal_Int32 m_eRecoveryState;
         bool  m_bWaitForCore;
         bool  m_bWasRecoveryStarted;
+        int   m_aToggleCount;
 
         OUString m_aSuccessRecovStr;
         OUString m_aOrigDocRecovStr;
         OUString m_aRecovFailedStr;
         OUString m_aRecovInProgrStr;
         OUString m_aNotRecovYetStr;
+        OUString m_aWillBeDiscStr;
 
         std::unique_ptr<weld::Label> m_xDescrFT;
         std::unique_ptr<weld::ProgressBar> m_xProgressBar;
@@ -436,9 +444,11 @@ class RecoveryDialog final : public 
weld::GenericDialogController
     private:
         DECL_LINK(NextButtonHdl, weld::Button&, void);
         DECL_LINK(CancelButtonHdl, weld::Button&, void);
+        DECL_LINK(ToggleRowHdl, const weld::TreeView::iter_col&, void);
 
         OUString impl_getStatusString( const TURLInfo& rInfo ) const;
         static OUString impl_getStatusImage( const TURLInfo& rInfo );
+        void impl_updateItemDescription(int row, const TriState& rState);
 };
 
 
diff --git a/svx/uiconfig/ui/docrecoveryrecoverdialog.ui 
b/svx/uiconfig/ui/docrecoveryrecoverdialog.ui
index a7dcc775269d..70a1ccbb7550 100644
--- a/svx/uiconfig/ui/docrecoveryrecoverdialog.ui
+++ b/svx/uiconfig/ui/docrecoveryrecoverdialog.ui
@@ -4,22 +4,29 @@
   <requires lib="gtk+" version="3.20"/>
   <object class="GtkTreeStore" id="liststore1">
     <columns>
-      <!-- column-name image -->
+      <!-- column-name checkbox -->
+      <column type="gboolean"/>
+      <!-- column-name standardimage -->
       <column type="GdkPixbuf"/>
-      <!-- column-name text -->
+      <!-- column-name displayname -->
       <column type="gchararray"/>
-      <!-- column-name image2 -->
+      <!-- column-name statustimage -->
       <column type="GdkPixbuf"/>
-      <!-- column-name text1 -->
+      <!-- column-name statustext -->
       <column type="gchararray"/>
       <!-- column-name id -->
       <column type="gchararray"/>
+      <!-- column-name chkVis -->
+      <column type="gboolean"/>
+      <!-- column-name chkTri -->
+      <column type="gboolean"/>
     </columns>
   </object>
   <object class="GtkDialog" id="DocRecoveryRecoverDialog">
     <property name="can-focus">False</property>
     <property name="border-width">6</property>
     <property name="title" translatable="yes" 
context="docrecoveryrecoverdialog|DocRecoveryRecoverDialog">%PRODUCTNAME 
%PRODUCTVERSION Document Recovery</property>
+    <property name="resizable">False</property>
     <property name="modal">True</property>
     <property name="default-width">0</property>
     <property name="default-height">0</property>
@@ -35,7 +42,7 @@
             <property name="layout-style">end</property>
             <child>
               <object class="GtkButton" id="cancel">
-                <property name="label" translatable="yes" 
context="docrecoveryrecoverdialog|cancel">_Discard</property>
+                <property name="label" translatable="yes" 
context="docrecoveryrecoverdialog|cancel">_Discard All</property>
                 <property name="visible">True</property>
                 <property name="can-focus">True</property>
                 <property name="can-default">True</property>
@@ -50,7 +57,7 @@
             </child>
             <child>
               <object class="GtkButton" id="next">
-                <property name="label" translatable="yes" 
context="docrecoveryrecoverdialog|next">_Start</property>
+                <property name="label" translatable="yes" 
context="docrecoveryrecoverdialog|next">_Recover Selected</property>
                 <property name="visible">True</property>
                 <property name="can-focus">True</property>
                 <property name="can-default">True</property>
@@ -69,11 +76,11 @@
             <property name="expand">False</property>
             <property name="fill">True</property>
             <property name="pack-type">end</property>
-            <property name="position">0</property>
+            <property name="position">2</property>
           </packing>
         </child>
         <child>
-          <!-- n-columns=1 n-rows=4 -->
+          <!-- n-columns=1 n-rows=3 -->
           <object class="GtkGrid" id="grid1">
             <property name="visible">True</property>
             <property name="can-focus">False</property>
@@ -84,7 +91,7 @@
               <object class="GtkLabel" id="desc">
                 <property name="visible">True</property>
                 <property name="can-focus">False</property>
-                <property name="label" translatable="yes" 
context="docrecoveryrecoverdialog|desc">%PRODUCTNAME will attempt to recover 
the state of the files you were working on before it crashed. Click 'Start' to 
begin the process, or click 'Discard' to cancel the recovery.</property>
+                <property name="label" translatable="yes" 
context="docrecoveryrecoverdialog|desc">%PRODUCTNAME will attempt to recover 
the state of the files you were working on before it crashed. Click 'Recover 
Selected' to begin the process, or click 'Discard' to cancel the 
recovery.</property>
                 <property name="wrap">True</property>
                 <property name="width-chars">70</property>
                 <property name="max-width-chars">70</property>
@@ -95,17 +102,6 @@
                 <property name="top-attach">0</property>
               </packing>
             </child>
-            <child>
-              <object class="GtkProgressBar" id="progress">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
-                <property name="hexpand">True</property>
-              </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">1</property>
-              </packing>
-            </child>
             <child>
               <object class="GtkLabel" id="label2">
                 <property name="visible">True</property>
@@ -117,7 +113,7 @@
               </object>
               <packing>
                 <property name="left-attach">0</property>
-                <property name="top-attach">2</property>
+                <property name="top-attach">1</property>
               </packing>
             </child>
             <child>
@@ -142,43 +138,45 @@
                       <object class="GtkTreeSelection" 
id="treeview-selection2"/>
                     </child>
                     <child>
-                      <object class="GtkTreeViewColumn" id="treeviewcolumn0">
+                      <object class="GtkTreeViewColumn" id="colRecover">
+                        <property name="title" translatable="yes" 
context="docrecoveryrecoverdialog|recoverft">Recover Document</property>
                         <child>
-                          <object class="GtkCellRendererPixbuf" 
id="cellrenderertext9"/>
+                          <object class="GtkCellRendererToggle" 
id="cellrenderertoggle"/>
                           <attributes>
-                            <attribute name="pixbuf">0</attribute>
+                            <attribute name="active">0</attribute>
                           </attributes>
                         </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkTreeViewColumn" id="treeviewcolumn1">
-                        <property name="title" translatable="yes" 
context="docrecoveryrecoverdialog|nameft">Document Name</property>
                         <child>
-                          <object class="GtkCellRendererText" 
id="cellrenderertext2"/>
+                          <object class="GtkCellRendererPixbuf" 
id="cellrendererstandardimage"/>
                           <attributes>
-                            <attribute name="text">1</attribute>
+                            <attribute name="pixbuf">1</attribute>
+                          </attributes>
+                        </child>
+                        <child>
+                          <object class="GtkCellRendererText" 
id="cellrendererdisplayname"/>
+                          <attributes>
+                            <attribute name="text">2</attribute>
                           </attributes>
                         </child>
                       </object>
                     </child>
                     <child>
-                      <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+                      <object class="GtkTreeViewColumn" id="colStatusImage">
                         <child>
-                          <object class="GtkCellRendererPixbuf" 
id="cellrenderertext4"/>
+                          <object class="GtkCellRendererPixbuf" 
id="cellrendererstatusimage"/>
                           <attributes>
-                            <attribute name="pixbuf">2</attribute>
+                            <attribute name="pixbuf">3</attribute>
                           </attributes>
                         </child>
                       </object>
                     </child>
                     <child>
-                      <object class="GtkTreeViewColumn" id="treeviewcolumn3">
+                      <object class="GtkTreeViewColumn" id="colStatusText">
                         <property name="title" translatable="yes" 
context="docrecoveryrecoverdialog|statusft">Status</property>
                         <child>
                           <object class="GtkCellRendererText" 
id="cellrenderertext3"/>
                           <attributes>
-                            <attribute name="text">3</attribute>
+                            <attribute name="text">4</attribute>
                           </attributes>
                         </child>
                       </object>
@@ -188,10 +186,21 @@
               </object>
               <packing>
                 <property name="left-attach">0</property>
-                <property name="top-attach">3</property>
+                <property name="top-attach">2</property>
               </packing>
             </child>
           </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkProgressBar" id="progress">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+          </object>
           <packing>
             <property name="expand">False</property>
             <property name="fill">True</property>

Reply via email to