[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1109180: marked as done (unblock: lifeograph/3.0.3-2)



Your message dated Thu, 14 Aug 2025 13:52:43 +0200
with message-id <906e549b-f761-4c21-b196-54e704c4bee0@debian.org>
and subject line close all open unblock requests, the trixie release happened
has caused the Debian Bug report #1109180,
regarding unblock: lifeograph/3.0.3-2
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1109180: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1109180
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
Control: affects -1 + src:lifeograph

Hi RMs,

Please approve unblocking of package lifeograph, which is a personal
journal application with encryption support.

[ Reason ]
Bookworm has a 2.0.3 version of this software and 3.0 is a major
upgrade. While Trixie has the 3.0.1 version, it contains some early
bugs. These contain UI issues, diary use and crashes involving ones
that happen when the format is upgraded from the old major version.

[ Impact ]
Small as there's minor UI improvements, but the changes are for major
bug fixing. With this update users get several bug fixes that
otherwise corrupt their diary or simply the application would crash.

[ Tests ]
I've tested it personally and reported found bugs upstream. He is
always polite and quick to fix any issues arise. Previous updates were
in the archive for several days. Ubuntu is quick to include lifeograph
updates in their archives, as such it is also tested there without
issues.

[ Risks ]
I think there's no risk, all bugs found is checked as resolved now.
Upstream is responsive and quick with fixes if there might be any
update still necessary.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

Regards,
Laszlo/GCS
diff -Nru lifeograph-3.0.1/NEWS lifeograph-3.0.3/NEWS
--- lifeograph-3.0.1/NEWS	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/NEWS	2025-06-14 22:47:00.000000000 +0000
@@ -7,6 +7,57 @@
 
 ================================================================================
 
+3.0.3 - 2025-06-15
+
+    • Generalize list type changes of paragraphs to the sibling paragraphs that
+      already have the same list type
+    • Added keyboard shortcuts for adding child and sibling entries that use th
+      events to determine the relative entry.
+    • Added keyboard shortcuts for going up and down in the entry list without
+      visiting filtered-out entries
+    • Bug fixes, mainly:
+        • Fixed opening non-writable diaries
+        • Fixed dropping charts and tables into entry text
+        • Fixed subheading suggestions in the smart add dialogs
+        • Fixed and improved behavior of backspace in lists and indented
+          paragraphs
+        • Disabled dropping onto the text editor in read-only mode
+        • Fixed dropping multi-line text
+        • Fixed clearing filtered states from parents of non-filtered entries
+        • While creating new entries, do not inherit themes from relative
+          entries if they don't have any theme set
+        • Fixed issues related to the password popover
+
+
+3.0.2 - 2025-05-18
+
+    • Bug fixes, mainly:
+        • Fixed crash when collapsing entries in the list
+        • Redraw entry list upon completion of drag operations to remove the
+          overlays immediately
+        • Fixed setting status of multiple paragraphs at once
+        • Fixed joining paragraphs
+        • Fixed background image of the main text editor theme leaking to its
+          child entry widgets
+        • Fixed background color of the main text editor theme leaking to its
+          child entry widgets
+        • Fixed selecting a collapsed entry from the list when the current entry
+          is a child of it
+        • Fixed crash in upgrading old diaries
+        • Some improvements in drag and drop in the entry list especially
+          prevention of dropping an entry onto itself
+        • Many more
+    • Reenabled clear icon in EntryClear
+    • Use native controls for Gtk 4.18 and above
+    • When duplicating, insert the copy after the last child of a collapsed
+      paragraph
+    • Add space after the cursor on Shift+Space
+    • Scale up list number text for subheaders
+    • Do not indent subheading paragraphs when converting them into lists
+    • Implemented cycling through siblings of a tag via Alt +/-
+    • Lifeograph can run on MacOS now
+    • Some cleanup
+
 
 3.0.1 - 2025-03-02
 
diff -Nru lifeograph-3.0.1/debian/changelog lifeograph-3.0.3/debian/changelog
--- lifeograph-3.0.1/debian/changelog	2025-03-03 18:43:18.000000000 +0000
+++ lifeograph-3.0.3/debian/changelog	2025-07-10 14:41:35.000000000 +0000
@@ -1,3 +1,62 @@
+lifeograph (3.0.3-2) unstable; urgency=medium
+
+  * Backport upstream fix to loading theme icons that don't have a path.
+
+ -- Laszlo Boszormenyi (GCS) <gcs@debian.org>  Thu, 10 Jul 2025 16:41:35 +0200
+
+lifeograph (3.0.3-1) unstable; urgency=medium
+
+  * New upstream release:
+    - generalize list type changes of paragraphs to the sibling paragraphs
+      that already have the same list type,
+    - added keyboard shortcuts for adding child and sibling entries that use
+      the events to determine the relative entry,
+    - added keyboard shortcuts for going up and down in the entry list without
+      visiting filtered-out entries,
+    - fixed opening non-writable diaries,
+    - fixed dropping charts and tables into entry text,
+    - fixed subheading suggestions in the smart add dialogs,
+    - fixed and improved behavior of backspace in lists and indented
+      paragraphs,
+    - disabled dropping onto the text editor in read-only mode,
+    - fixed dropping multi-line text,
+    - fixed clearing filtered states from parents of non-filtered entries,
+    - while creating new entries, do not inherit themes from relative entries,
+      if they don't have any theme set,
+    - fixed issues related to the password popover.
+
+ -- Laszlo Boszormenyi (GCS) <gcs@debian.org>  Sun, 22 Jun 2025 07:56:16 +0200
+
+lifeograph (3.0.2-1) unstable; urgency=medium
+
+  * New upstream release:
+    - fixed crash when collapsing entries in the list,
+    - redraw entry list upon completion of drag operations to remove the
+      overlays immediately,
+    - fixed setting status of multiple paragraphs at once,
+    - fixed joining paragraphs,
+    - fixed background image of the main text editor theme leaking to its
+      child entry widgets,
+    - fixed background color of the main text editor theme leaking to its
+      child entry widgets,
+    - fixed selecting a collapsed entry from the list when the current entry
+      is a child of it,
+    - fixed crash in upgrading old diaries,
+    - some improvements in drag and drop in the entry list especially
+      prevention of dropping an entry onto itself,
+    - reenabled clear icon in EntryClear,
+    - use native controls for Gtk 4.18 and above,
+    - when duplicating, insert the copy after the last child of a collapsed
+      paragraph,
+    - add space after the cursor on Shift+Space,
+    - scale up list number text for subheaders,
+    - do not indent subheading paragraphs when converting them into lists,
+    - implemented cycling through siblings of a tag via Alt +/-,
+    - Lifeograph can run on MacOS now.
+  * Refer online version of Free Software Foundation licence.
+
+ -- Laszlo Boszormenyi (GCS) <gcs@debian.org>  Thu, 22 May 2025 19:29:57 +0200
+
 lifeograph (3.0.1-1) unstable; urgency=medium
 
   * New upstream release.
diff -Nru lifeograph-3.0.1/debian/copyright lifeograph-3.0.3/debian/copyright
--- lifeograph-3.0.1/debian/copyright	2024-11-09 23:24:30.000000000 +0000
+++ lifeograph-3.0.3/debian/copyright	2025-05-20 20:52:50.000000000 +0000
@@ -23,9 +23,8 @@
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  .
- You should have received a copy of the GNU General Public License
- along with this package; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ You should have received a copy of the GNU General Public License 3
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
  .
  On Debian systems, the complete text of the GNU General Public License
  version 3 can be found in `/usr/share/common-licenses/GPL-3'.
diff -Nru lifeograph-3.0.1/debian/patches/fixed_loading_theme_icons.patch lifeograph-3.0.3/debian/patches/fixed_loading_theme_icons.patch
--- lifeograph-3.0.1/debian/patches/fixed_loading_theme_icons.patch	1970-01-01 00:00:00.000000000 +0000
+++ lifeograph-3.0.3/debian/patches/fixed_loading_theme_icons.patch	2025-07-10 14:38:26.000000000 +0000
@@ -0,0 +1,97 @@
+From 504220fb7d062a80e4eb65cdf706c100fb766f48 Mon Sep 17 00:00:00 2001
+From: Ahmet Ozturk <aoz_2@yahoo.com>
+Date: Thu, 10 Jul 2025 02:31:36 +0300
+Subject: fixed loading theme icons that don't have a path. some cleanup.
+
+---
+ src/app_window.cpp | 16 ++++++++--------
+ src/helpers.cpp    | 20 +++++++++++++++-----
+ src/helpers.hpp    |  3 ++-
+ 3 files changed, 25 insertions(+), 14 deletions(-)
+
+diff --git a/src/app_window.cpp b/src/app_window.cpp
+index 1dd04f4f..2f190888 100644
+--- a/src/app_window.cpp
++++ b/src/app_window.cpp
+@@ -25,7 +25,6 @@
+ #endif
+ 
+ #include <string>
+-#include <cstdlib>
+ 
+ #include <gtkmm/eventcontrollerlegacy.h>
+ 
+@@ -485,15 +484,16 @@ AppWindow::handle_login()
+             Lifeograph::icons->todo_canceled_32 =   Gdk::Pixbuf::create_from_file(
+                     Lifeograph::get_icon_dir() + "/todo_canceled-32.png" );
+ 
+-            Lifeograph::icons->filter = lookup_default_icon_pixbuf( "filter-16-symbolic", 16 );
+-            Lifeograph::icons->table = lookup_default_icon_pixbuf( "table-16-symbolic", 16 );
+-            Lifeograph::icons->chart = lookup_default_icon_pixbuf( "chart-16-symbolic", 16 );
++            Lifeograph::icons->filter = lookup_theme_icon_pixbuf( theme, "filter-16-symbolic", 16 );
++            Lifeograph::icons->table = lookup_theme_icon_pixbuf( theme, "table-16-symbolic", 16 );
++            Lifeograph::icons->chart = lookup_theme_icon_pixbuf( theme, "chart-16-symbolic", 16 );
+ 
+-            Lifeograph::icons->map_point = lookup_default_icon_pixbuf( "map-point", 16 );
++            Lifeograph::icons->map_point = lookup_theme_icon_pixbuf( theme, "map-point", 16 );
+ 
+-            Lifeograph::icons->go_prev = lookup_default_icon_pixbuf( "go-previous-symbolic", 16 );
+-            Lifeograph::icons->go_next = lookup_default_icon_pixbuf( "go-next-symbolic", 16 );
+-            Lifeograph::icons->go_home = lookup_default_icon_pixbuf( "go-home-symbolic", 16 );
++            Lifeograph::icons->go_prev = lookup_theme_icon_pixbuf( theme, "go-previous-symbolic",
++                                                                   16 );
++            Lifeograph::icons->go_next = lookup_theme_icon_pixbuf( theme, "go-next-symbolic", 16 );
++            Lifeograph::icons->go_home = lookup_theme_icon_pixbuf( theme, "go-home-symbolic", 16 );
+ 
+             // PANELS & VIEWS
+             UI_diary = new UIDiary;
+diff --git a/src/helpers.cpp b/src/helpers.cpp
+index 79e4e95f..ba03c6ad 100644
+--- a/src/helpers.cpp
++++ b/src/helpers.cpp
+@@ -2032,13 +2032,23 @@ select_LB_item_next( Gtk::ListBox* LB )
+ }
+ 
+ R2Pixbuf
+-lookup_default_icon_pixbuf( const Ustring& name, int size )
++lookup_theme_icon_pixbuf( const Glib::RefPtr< Gtk::IconTheme >& theme, const Ustring& name,
++                          int size )
+ {
+-    auto icon { Gtk::IconTheme::get_for_display( Gdk::Display::get_default() )->
+-                        lookup_icon( name, size ) };
+-    auto path { icon->get_file()->get_path() };
++    auto icon { theme->lookup_icon( name, size ) };
++    auto file { icon->get_file() };
++    if( !file ) return {};
+ 
+-    return Gdk::Pixbuf::create_from_file( path );
++    try
++    {
++        auto stream { file->read() }; // open Gio::InputStream from resource or file
++        return Gdk::Pixbuf::create_from_stream( stream );
++    }
++    catch( const Glib::Error& ex )
++    {
++        print_error( "Failed to load icon pixbuf: ", ex.what() );
++        return {};
++    }
+ }
+ 
+ } // end of name space
+diff --git a/src/helpers.hpp b/src/helpers.hpp
+index 0e12daa3..f3e53050 100644
+--- a/src/helpers.hpp
++++ b/src/helpers.hpp
+@@ -1114,7 +1114,8 @@ void                scroll_to_selected_LB_row( Gtk::ListBox* LB );
+ void                select_LB_item_prev( Gtk::ListBox* );
+ void                select_LB_item_next( Gtk::ListBox* );
+ 
+-R2Pixbuf            lookup_default_icon_pixbuf( const Ustring& name, int size );
++R2Pixbuf            lookup_theme_icon_pixbuf( const Glib::RefPtr< Gtk::IconTheme >&,
++                                              const Ustring& name, int size );
+ 
+ inline R2Icon
+ lookup_default_icon( const Ustring& name, int size )
+-- 
+cgit v1.2.3
+
diff -Nru lifeograph-3.0.1/debian/patches/series lifeograph-3.0.3/debian/patches/series
--- lifeograph-3.0.1/debian/patches/series	2024-11-09 23:24:30.000000000 +0000
+++ lifeograph-3.0.3/debian/patches/series	2025-07-10 14:39:44.000000000 +0000
@@ -1 +1,2 @@
 #no_build_time.patch
+fixed_loading_theme_icons.patch
diff -Nru "/tmp/MYiMVA9Z8l/lifeograph-3.0.1/diaries/Lifeograph Manual" "/tmp/68sqrft6KA/lifeograph-3.0.3/diaries/Lifeograph Manual"
--- "/tmp/MYiMVA9Z8l/lifeograph-3.0.1/diaries/Lifeograph Manual"	2025-03-02 05:12:04.000000000 +0000
+++ "/tmp/68sqrft6KA/lifeograph-3.0.3/diaries/Lifeograph Manual"	2025-06-14 22:47:00.000000000 +0000
@@ -7,7 +7,7 @@
 Dp0
 Dsen_US
 Df10039
-Dl10039
+Dl10008
 
 ID6160733
 T ~Lifeograph
@@ -434,7 +434,7 @@
 ID10008
 E __n_BN_~~~~~~~~201109130002457
 Ec201109130002457
-Ee202411280225243
+Ee202506050222555
 Eha10014
 EpDKeyboard Shortcuts
 EpD
@@ -538,6 +538,9 @@
 Ep<_-E_1_____~~~~~Insert horizontal rule: Alt +Shift + R
 EfB0|23|
 EfH24|38|
+Ep<_-E_1_____~~~~~Insert space after the cursor: Shift + Space
+EfB0|30|
+EfH31|44|
 Ep<B_E_0_____~~~~~Paragraph Formatting
 Ep<_-E_1_____~~~~~Indent: Alt + I
 EfB0|7|
@@ -614,10 +617,14 @@
 EfH0|11|
 EpD
 Ep<S_E_0_____~~~~~Navigation
-Ep<_-E_1_____~~~~~Go to the previous entry in the list: Ctrl + Page Down
-EfB0|37|
-Ep<_-E_1_____~~~~~Go to the next entry in the list: Ctrl + Page Up
-EfB0|33|
+Ep<_-E_1_____~~~~~Go to the previous entry in the list order: Ctrl + Page Down
+EfB0|43|
+Ep<_-E_1_____~~~~~Go to the next entry in the list order: Ctrl + Page Up
+EfB0|39|
+Ep<_-E_1_____~~~~~Go to the previous entry in the list omitting filtered entries: Ctrl + Shift + Page Down
+EfB0|63|
+Ep<_-E_1_____~~~~~Go to the next entry in the list omitting filtered entries: Ctrl + Shift + Page Up
+EfB0|59|
 Ep<_-E_1_____~~~~~Go to the previous entry in browsing history: Alt + Left
 EfB0|45|
 Ep<_-E_1_____~~~~~Go to the next entry in browsing history: Alt + Right
@@ -636,6 +643,10 @@
 EfB0|16|
 Ep<_-E_1_____~~~~~Duplicate Entry: Ctrl + Shift + R
 EfB0|16|
+Ep<_-E_1_____~~~~~Add Child Entry: Ctrl + Shift + C
+EfB0|16|
+Ep<_-E_1_____~~~~~Add Sibling Entry: Ctrl + Shift + S
+EfB0|18|
 
 ID10011
 E __nEBN_~~~~~~~~202008090140423
diff -Nru lifeograph-3.0.1/diaries/example.diary lifeograph-3.0.3/diaries/example.diary
--- lifeograph-3.0.1/diaries/example.diary	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/diaries/example.diary	2025-06-14 22:47:00.000000000 +0000
@@ -4,10 +4,10 @@
 Id3078619
 
 Do~~~
-Dp4
+Dp2
 Dsen_US
 Df10034
-Dl84939979
+Dl66753828
 
 ID1760047
 T ~Lifeograph
@@ -89,7 +89,7 @@
 ID10034
 E __n_BN_~~~~~~~~201206090125329
 Ec201206090125329
-Ee202503020072534
+Ee202506090082600
 Ehu10032
 EpDWelcome to Lifeograph
 EpD
@@ -98,8 +98,8 @@
 EpDEntry Basics (example entry)
 EfD0|28|10033
 EpD
-EpDWhat is new in this version (3.0.1)
-EfD0|35|84939979
+EpDWhat is new in this version (3.0.3)
+EfD0|35|66753828
 
 ID10033
 E F_P_BN_~~~~~~~~201203050000000
@@ -155,11 +155,74 @@
 EmLifeograph
 EpDRelease History
 
+ID66753828
+E __n_DN_~~~~~~~~202506150000000
+Ec202506080063506
+Ee202506150011024
+Ehu63869137
+Emstable
+EpD3.0.3 / Madleen
+EpD2025-06-15 
+Ep<S_E_0_____~~~~~What is new?
+Ep<_-E_1_____~~~~~Generalize list type changes of paragraphs to the sibling paragraphs that already have the same list type
+Ep<_-E_1_____~~~~~Added keyboard shortcuts for adding child and sibling entries that use the events to determine the relative entry
+Ep<_-E_1_____~~~~~Added keyboard shortcuts for going up and down in the entry list without visiting filtered-out entries
+Ep<_-E_1_____~~~~~Bug fixes, mainly:
+Ep<_-E_2_____~~~~~Fixed opening non-writable diaries
+Ep<_-E_2_____~~~~~Fixed dropping charts and tables into entry text
+Ep<_-E_2_____~~~~~Fixed subheading suggestions in the smart add dialogs
+Ep<_-E_2_____~~~~~Fixed and improved behavior of backspace in lists and indented paragraphs
+Ep<_-E_2_____~~~~~Disabled dropping onto the text editor in read-only mode
+Ep<_-E_2_____~~~~~Fixed dropping multi-line text
+Ep<_-E_2_____~~~~~Fixed clearing filtered states from parents of non-filtered entries
+Ep<_-E_2_____~~~~~While creating new entries, do not inherit themes from relative entries if they don't have any theme set
+Ep<_-E_2_____~~~~~Fixed issues related to the password popover
+EpD
+EpDminor
+EfT0|5|10005
+EpDstable
+EfT0|6|10031
+
+ID76769090
+E __n_DN_~~~~~~~~202505180000000
+Ec202505120221117
+Ee202505120225712
+Eha66753828
+Emstable
+EpD3.0.2 / in memory of Aaron Bushnell
+EpD2025-05-18
+Ep<S_E_0_____~~~~~What is new?
+Ep<_-E_1_____~~~~~Bug fixes, mainly:
+Ep<_-E_2_____~~~~~Fixed crash when collapsing entries in the list
+Ep<_-E_2_____~~~~~Redraw entry list upon completion of drag operations to remove the overlays immediately
+Ep<_-E_2_____~~~~~Fixed setting status of multiple paragraphs at once
+Ep<_-E_2_____~~~~~Fixed joining paragraphs
+Ep<_-E_2_____~~~~~Fixed background image of the main text editor theme leaking to its child entry widgets
+Ep<_-E_2_____~~~~~Fixed background color of the main text editor theme leaking to its child entry widgets
+Ep<_-E_2_____~~~~~Fixed selecting a collapsed entry from the list when the current entry is a child of it
+Ep<_-E_2_____~~~~~Fixed crash in upgrading old diaries
+Ep<_-E_2_____~~~~~Some improvements in drag and drop in the entry list especially prevention of dropping an entry onto itself
+Ep<_-E_2_____~~~~~Many more
+Ep<_-E_1_____~~~~~Reenabled clear icon in EntryClear
+Ep<_-E_1_____~~~~~Use native controls for Gtk 4.18 and above
+Ep<_-E_1_____~~~~~When duplicating, insert the copy after the last child of a collapsed paragraph
+Ep<_-E_1_____~~~~~Add space after the cursor on Shift+Space
+Ep<_-E_1_____~~~~~Scale up list number text for subheaders
+Ep<_-E_1_____~~~~~Do not indent subheading paragraphs when converting them into lists
+Ep<_-E_1_____~~~~~Implemented cycling through siblings of a tag via Alt +/-
+Ep<_-E_1_____~~~~~Lifeograph can run on MacOS now
+Ep<_-E_1_____~~~~~Some cleanup
+EpD
+EpDminor
+EfT0|5|10005
+EpDstable
+EfT0|6|10031
+
 ID84939979
 E __n_DN_~~~~~~~~202503020000000
 Ec202502230190039
 Ee202503020071805
-Ehu63869137
+Eha76769090
 Emstable
 EpD3.0.1 / Be silent when children sleep not when they are massacred
 EpD2025-03-02 
@@ -1694,4 +1757,4 @@
 Ec202008030134827
 Ee202008030134827
 Eha1245112
-EpDexample tag
\ No newline at end of file
+EpDexample tag
diff -Nru lifeograph-3.0.1/net.sourceforge.Lifeograph.metainfo.xml.in lifeograph-3.0.3/net.sourceforge.Lifeograph.metainfo.xml.in
--- lifeograph-3.0.1/net.sourceforge.Lifeograph.metainfo.xml.in	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/net.sourceforge.Lifeograph.metainfo.xml.in	2025-06-14 22:47:00.000000000 +0000
@@ -66,82 +66,111 @@
     <content_rating type="oars-1.1" />
 
     <releases>
-        <release version="3.0.1" date="2025-03-02">
+      <release version="3.0.3" date="2025-06-15">
+        <description>
+          <p>Generalize list type changes of paragraphs to the sibling paragraphs that already have the same list type</p>
+          <p>Added keyboard shortcuts for adding child and sibling entries that use the events to determine the relative entry</p>
+          <p>Added keyboard shortcuts for going up and down in the entry list without visiting filtered-out entries</p>
+          <p>Fixed opening non-writable diaries</p>
+          <p>Fixed dropping charts and tables into entry text</p>
+          <p>Fixed subheading suggestions in the smart add dialogs</p>
+          <p>Fixed and improved behavior of backspace in lists and indented paragraphs</p>
+          <p>Disabled dropping onto the text editor in read-only mode</p>
+          <p>Fixed issues related to the password popover</p>
+          <p>Many more</p>
+        </description>
+      </release>
+      <release version="3.0.2" date="2025-05-18">
+        <description>
+          <p>Fixed crash when collapsing entries in the list</p>
+          <p>Fixed setting status of multiple paragraphs at once</p>
+          <p>Fixed joining paragraphs</p>
+          <p>Fixed crash in upgrading old diaries</p>
+          <p>Some improvements in drag and drop in the entry list especially</p>
+          <p>Reenabled clear icon in related Entry Widgets</p>
+          <p>Add space after the cursor with Shift+Space</p>
+          <p>Implemented cycling through siblings of a tag via Alt +/-</p>
+          <p>Lifeograph can run on MacOS now</p>
+          <p>Some cleanup</p>
+          <p>Many more</p>
+        </description>
+      </release>
+      <release version="3.0.1" date="2025-03-02">
+        <description>
+          <p>Various fixes related to undo/redo</p>
+          <p>Fixed and improved upgrading old diaries</p>
+          <p>Scroll the text to ensure that the cursor line is visible while moving paragraphs</p>
+          <p>Fixes and fine tunings in gantt columns in tables</p>
+          <p>Fixes in tagging system</p>
+          <p>Fixes in filtering system</p>
+          <p>Include link URIs (if any) in the text columns in tables when the field is URI</p>
+        </description>
+      </release>
+      <release version="3.0.0" date="2024-12-29">
+        <description>
+          <p>New main icon that reflects the improved functionality as a second brain</p>
+          <p>UI upgraded to GTK4 replacing the deprecated UI elements with new ones</p>
+          <p>All file I/O is moved to Gio for compatibility with network addresses and virtual drives, etc...</p>
+          <p>Wiki style formatting markup is largely omitted in favor of a WYSIWYG style formatting system [[inline comments are an exception]]</p>
+          <p>Paragraph folding that creates folds automatically based on heading and indentation levels</p>
+          <p>New references button that pops up a list of referencing entries/paragraphs for the current entry (a.k.a back-links)</p>
+          <p>Context-sensitive right-click menu that displays specific options for tags, images, dates, etc. based on the cursor location</p>
+          <p>Tables can also be inserted into the entries now</p>
+          <p>Added buttons for visiting link items to the right-click popover menu</p>
+          <p>Added horizontal rules which can also be combined with text</p>
+          <p>Entries can now be ordered, grouped, and arranged freely, irrespective of the their types</p>
+          <p>Added an option to merge selected entries</p>
+          <p>Added many new filterers</p>
+          <p>All filters can be negated now</p>
+          <p>Table columns are resizable now</p>
+          <p>Many new column types added</p>
+          <p>Group lines in a hierarchical view by any column</p>
+          <p>Conditional coloring of cell values</p>
+          <p>Completion columns show a progress bar in the background</p>
+          <p>Option to combine identical consequent cells in a column to get cleaner tables</p>
+          <p>Added a new vertical bars style to charts</p>
+          <p>A basic inline table was added to allow creation of basic charts without creating a table first</p>
+          <p>Added navigation icons to the calendar widget</p>
+          <p>Added the ability to mark custom days as holidays</p>
+          <p>Added the ability to change the first day of the week</p>
+          <p>Searching is multi-threaded now for speed gains and responsiveness</p>
+          <p>Migrated from libchamplain to libshumate which also works under Windows</p>
+          <p>Many many more</p>
+        </description>
+      </release>
+      <release version="2.0.3" date="2022-04-24">
           <description>
-            <p>Various fixes related to undo/redo</p>
-            <p>Fixed and improved upgrading old diaries</p>
-            <p>Scroll the text to ensure that the cursor line is visible while moving paragraphs</p>
-            <p>Fixes and fine tunings in gantt columns in tables</p>
-            <p>Fixes in tagging system</p>
-            <p>Fixes in filtering system</p>
-            <p>Include link URIs (if any) in the text columns in tables when the field is URI</p>
+              <p>Bug fixes</p>
+              <p>Added Danish translation</p>
+              <p>Updated Simplified Chinese, Dutch, French, and Italian translations</p>
           </description>
-        </release>
-        <release version="3.0.0" date="2024-12-29">
+      </release>
+      <release version="2.0.2" date="2021-06-29">
           <description>
-            <p>New main icon that reflects the improved functionality as a second brain</p>
-            <p>UI upgraded to GTK4 replacing the deprecated UI elements with new ones</p>
-            <p>All file I/O is moved to Gio for compatibility with network addresses and virtual drives, etc...</p>
-            <p>Wiki style formatting markup is largely omitted in favor of a WYSIWYG style formatting system [[inline comments are an exception]]</p>
-            <p>Paragraph folding that creates folds automatically based on heading and indentation levels</p>
-            <p>New references button that pops up a list of referencing entries/paragraphs for the current entry (a.k.a back-links)</p>
-            <p>Context-sensitive right-click menu that displays specific options for tags, images, dates, etc. based on the cursor location</p>
-            <p>Tables can also be inserted into the entries now</p>
-            <p>Added buttons for visiting link items to the right-click popover menu</p>
-            <p>Added horizontal rules which can also be combined with text</p>
-            <p>Entries can now be ordered, grouped, and arranged freely, irrespective of the their types</p>
-            <p>Added an option to merge selected entries</p>
-            <p>Added many new filterers</p>
-            <p>All filters can be negated now</p>
-            <p>Table columns are resizable now</p>
-            <p>Many new column types added</p>
-            <p>Group lines in a hierarchical view by any column</p>
-            <p>Conditional coloring of cell values</p>
-            <p>Completion columns show a progress bar in the background</p>
-            <p>Option to combine identical consequent cells in a column to get cleaner tables</p>
-            <p>Added a new vertical bars style to charts</p>
-            <p>A basic inline table was added to allow creation of basic charts without creating a table first</p>
-            <p>Added navigation icons to the calendar widget</p>
-            <p>Added the ability to mark custom days as holidays</p>
-            <p>Added the ability to change the first day of the week</p>
-            <p>Searching is multi-threaded now for speed gains and responsiveness</p>
-            <p>Migrated from libchamplain to libshumate which also works under Windows</p>
-            <p>Many many more</p>
+              <p>Bug fixes</p>
+              <p>Updated Italian translation</p>
+          </description>
+      </release>
+      <release version="2.0.1" date="2021-04-09">
+          <description>
+              <p>Bug fixes</p>
+          </description>
+      </release>
+      <release version="2.0.0" date="2020-12-06">
+          <description>
+              <p>Unified different elements such as chapters and tags into entry</p>
+              <p>New inline tags that are assigned to the paragraphs as well as entries</p>
+              <p>New integrated map to assign locations and travel paths to entries</p>
+              <p>New custom calendar widget that integrates with the program more nicely</p>
+              <p>New table view for very basic spreadsheet like functionality</p>
+              <p>Completion percentages for entries</p>
+              <p>Support for background images in the entry editor</p>
+              <p>a new toolbar-like right-click popover that replaced the toolbar</p>
+              <p>Vastly more capable text search and entry list filtering</p>
+              <p>Printing system improvements</p>
+              <p>Both functionally and visually improved charts</p>
+              <p>Many many more</p>
           </description>
-        </release>
-        <release version="2.0.3" date="2022-04-24">
-            <description>
-                <p>Bug fixes</p>
-                <p>Added Danish translation</p>
-                <p>Updated Simplified Chinese, Dutch, French, and Italian translations</p>
-            </description>
-        </release>
-        <release version="2.0.2" date="2021-06-29">
-            <description>
-                <p>Bug fixes</p>
-                <p>Updated Italian translation</p>
-            </description>
-        </release>
-        <release version="2.0.1" date="2021-04-09">
-            <description>
-                <p>Bug fixes</p>
-            </description>
-        </release>
-        <release version="2.0.0" date="2020-12-06">
-            <description>
-                <p>Unified different elements such as chapters and tags into entry</p>
-                <p>New inline tags that are assigned to the paragraphs as well as entries</p>
-                <p>New integrated map to assign locations and travel paths to entries</p>
-                <p>New custom calendar widget that integrates with the program more nicely</p>
-                <p>New table view for very basic spreadsheet like functionality</p>
-                <p>Completion percentages for entries</p>
-                <p>Support for background images in the entry editor</p>
-                <p>a new toolbar-like right-click popover that replaced the toolbar</p>
-                <p>Vastly more capable text search and entry list filtering</p>
-                <p>Printing system improvements</p>
-                <p>Both functionally and visually improved charts</p>
-                <p>Many many more</p>
-            </description>
         </release>
     </releases>
 
diff -Nru lifeograph-3.0.1/po/ar.po lifeograph-3.0.3/po/ar.po
--- lifeograph-3.0.1/po/ar.po	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/po/ar.po	2025-06-14 22:47:00.000000000 +0000
@@ -1625,7 +1625,7 @@
 msgid "Char Count"
 msgstr "%1 إدخال في"
 
-#. <item translatable="yes">Word Count</item> TODO: v3.1 or later
+#. <item translatable="yes">Word Count</item> TODO: 3.1 or later
 #: ui/col_opt_size.ui:23
 msgid "Paragraph Count"
 msgstr ""
diff -Nru lifeograph-3.0.1/po/da.po lifeograph-3.0.3/po/da.po
--- lifeograph-3.0.1/po/da.po	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/po/da.po	2025-06-14 22:47:00.000000000 +0000
@@ -1586,7 +1586,7 @@
 msgid "Char Count"
 msgstr "Diagram"
 
-#. <item translatable="yes">Word Count</item> TODO: v3.1 or later
+#. <item translatable="yes">Word Count</item> TODO: 3.1 or later
 #: ui/col_opt_size.ui:23
 #, fuzzy
 msgid "Paragraph Count"
diff -Nru lifeograph-3.0.1/po/he.po lifeograph-3.0.3/po/he.po
--- lifeograph-3.0.1/po/he.po	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/po/he.po	2025-06-14 22:47:00.000000000 +0000
@@ -1650,7 +1650,7 @@
 "\n"
 "%2 רשומות"
 
-#. <item translatable="yes">Word Count</item> TODO: v3.1 or later
+#. <item translatable="yes">Word Count</item> TODO: 3.1 or later
 #: ui/col_opt_size.ui:23
 msgid "Paragraph Count"
 msgstr ""
diff -Nru lifeograph-3.0.1/po/ja.po lifeograph-3.0.3/po/ja.po
--- lifeograph-3.0.1/po/ja.po	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/po/ja.po	2025-06-14 22:47:00.000000000 +0000
@@ -1619,7 +1619,7 @@
 msgid "Char Count"
 msgstr "%1のエントリー"
 
-#. <item translatable="yes">Word Count</item> TODO: v3.1 or later
+#. <item translatable="yes">Word Count</item> TODO: 3.1 or later
 #: ui/col_opt_size.ui:23
 #, fuzzy
 msgid "Paragraph Count"
diff -Nru lifeograph-3.0.1/po/sr.po lifeograph-3.0.3/po/sr.po
--- lifeograph-3.0.1/po/sr.po	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/po/sr.po	2025-06-14 22:47:00.000000000 +0000
@@ -1644,7 +1644,7 @@
 msgid "Char Count"
 msgstr "Унос"
 
-#. <item translatable="yes">Word Count</item> TODO: v3.1 or later
+#. <item translatable="yes">Word Count</item> TODO: 3.1 or later
 #: ui/col_opt_size.ui:23
 msgid "Paragraph Count"
 msgstr ""
diff -Nru lifeograph-3.0.1/po/uk.po lifeograph-3.0.3/po/uk.po
--- lifeograph-3.0.1/po/uk.po	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/po/uk.po	2025-06-14 22:47:00.000000000 +0000
@@ -1606,7 +1606,7 @@
 msgid "Char Count"
 msgstr "Запис"
 
-#. <item translatable="yes">Word Count</item> TODO: v3.1 or later
+#. <item translatable="yes">Word Count</item> TODO: 3.1 or later
 #: ui/col_opt_size.ui:23
 msgid "Paragraph Count"
 msgstr ""
diff -Nru lifeograph-3.0.1/src/app_window.cpp lifeograph-3.0.3/src/app_window.cpp
--- lifeograph-3.0.1/src/app_window.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/app_window.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -92,6 +92,8 @@
 
 #ifdef _WIN32
     m_Hb_main->set_decoration_layout( ":minimize,maximize,close" );
+#elif GTK_CHECK_VERSION(4, 18, 0)
+    m_Hb_main->set_use_native_controls( true );
 #endif
 
     Lifeograph::icons->diary_32 = Gdk::Pixbuf::create_from_file(
@@ -338,24 +340,32 @@
 }
 
 void
-AppWindow::go_to_prev()
+AppWindow::go_to_prev( bool F_include_invisibles )
 {
-    // get_prev_straight is not very meaningful here
-    Entry* entry_prev{ UI_entry->get_cur_entry()->get_prev() };
-
-    if( !entry_prev )
-        entry_prev = UI_entry->get_cur_entry()->get_parent();
-
-    if( entry_prev )
-        show_entry( entry_prev );
+    for( Entry* entry_prev = UI_entry->get_cur_entry()->get_prev_straight();
+         entry_prev;
+         entry_prev = entry_prev->get_prev_straight() )
+    {
+        if( F_include_invisibles || !entry_prev->is_filtered_out() )
+        {
+            show_entry( entry_prev );
+            break;
+        }
+    }
 }
 void
-AppWindow::go_to_next()
+AppWindow::go_to_next( bool F_include_invisibles )
 {
-    Entry* entry_next{ UI_entry->get_cur_entry()->get_next_straight() };
-
-    if( entry_next )
-        show_entry( entry_next );
+    for( Entry* entry_next = UI_entry->get_cur_entry()->get_next_straight();
+         entry_next;
+         entry_next = entry_next->get_next_straight() )
+    {
+        if( F_include_invisibles || !entry_next->is_filtered_out() )
+        {
+            show_entry( entry_next );
+            break;
+        }
+    }
 }
 
 void
@@ -514,19 +524,19 @@
         m_Pd_extra->set_position( Lifeograph::settings.position_paned_extra );
 
         // ACTIONS
-        using VInt = Glib::Variant< int >;
+        // using VInt = Glib::Variant< int >;
         Lifeograph::p->add_action( "undo", [ this ](){ handle_undo(); } );
         Lifeograph::p->set_accel_for_action( "app.undo", "<Ctrl>Z" );
         Lifeograph::p->add_action( "redo", [ this ](){ handle_redo(); } );
         Lifeograph::p->set_accel_for_action( "app.redo", "<Ctrl><Shift>Z" );
-        Lifeograph::p->add_action_with_parameter(
-                "show_elem",
-                VInt::variant_type(),
-	            []( const Glib::VariantBase& value )
-	            {
-                    const DEID id{ Glib::VariantBase::cast_dynamic< VInt >( value ).get() };
-                    AppWindow::p->show_elem( id );
-                } );
+        // Lifeograph::p->add_action_with_parameter(
+        //         "show_elem",
+        //         VInt::variant_type(),
+	       //      []( const Glib::VariantBase& value )
+	       //      {
+        //             const DEID id{ Glib::VariantBase::cast_dynamic< VInt >( value ).get() };
+        //             AppWindow::p->show_elem( id );
+        //         } );
     }
 
     init_textview_theme_classes();
diff -Nru lifeograph-3.0.1/src/app_window.hpp lifeograph-3.0.3/src/app_window.hpp
--- lifeograph-3.0.1/src/app_window.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/app_window.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -109,8 +109,8 @@
         void                        show_elem( DEID );
         void                        show_entry( Entry* );
 
-        void                        go_to_prev();
-        void                        go_to_next();
+        void                        go_to_prev( bool = false );
+        void                        go_to_next( bool = false );
 
         // AUTO LOGOUT FUNCTIONALITY
         bool                        handle_idle();
diff -Nru lifeograph-3.0.1/src/dialogs/dialog_export.hpp lifeograph-3.0.3/src/dialogs/dialog_export.hpp
--- lifeograph-3.0.1/src/dialogs/dialog_export.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/dialogs/dialog_export.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -23,7 +23,7 @@
 #ifndef LIFEOGRAPH_DIALOG_EXPORT_HEADER
 #define LIFEOGRAPH_DIALOG_EXPORT_HEADER
 
-// TODO: rename this file to cover all of its current functions
+// TODO: 3.1: rename this file to cover all of its current functions
 
 
 #include <gtkmm.h>
diff -Nru lifeograph-3.0.1/src/dialogs/dialog_password.cpp lifeograph-3.0.3/src/dialogs/dialog_password.cpp
--- lifeograph-3.0.1/src/dialogs/dialog_password.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/dialogs/dialog_password.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -23,8 +23,6 @@
 #include <gtkmm/label.h>
 
 #include "../lifeograph.hpp"
-#include "../app_window.hpp"
-#include "../diaryelements/diary.hpp"    // for LIFEO::PASSPHRASE_MIN_SIZE
 #include "dialog_password.hpp"
 
 
@@ -35,12 +33,11 @@
 DialogPassword* DialogPassword::ptr = nullptr;
 
 DialogPassword::DialogPassword( BaseObjectType* cobject,
-                                const Glib::RefPtr< Gtk::Builder >& refbuilder )
+                                const Glib::RefPtr< Gtk::Builder >& builder )
 :   Gtk::Popover( cobject ), m_ptr2diary( nullptr )
 {
     Gtk::Button* B_cancel;
 
-    auto builder{ Lifeograph::get_builder() };
     m_B_go      = builder->get_widget< Gtk::Button >( "B_password_go" );
     B_cancel    = builder->get_widget< Gtk::Button >( "B_password_cancel" );
     m_E_current = Gtk::Builder::get_widget_derived< EntryClear >( builder, "E_password_current" );
@@ -64,7 +61,7 @@
     m_E_confirm->signal_changed().connect(
             sigc::mem_fun( *this, &DialogPassword::check_match ) );
 
-    B_cancel->signal_clicked().connect( [ this ]{ hide(); m_handler_cancel(); } );
+    B_cancel->signal_clicked().connect( [ this ]{ popdown(); m_handler_cancel(); } );
     m_B_go->signal_clicked().connect( sigc::mem_fun( *this, &DialogPassword::handle_go ) );
 }
 
@@ -74,25 +71,20 @@
         const FuncVoid& handler_go,
         const FuncVoid& handler_cancel )
 {
-    if( ptr == nullptr )
-        ptr = Gtk::Builder::get_widget_derived< DialogPassword >(
-                Lifeograph::get_builder(), "Po_password" );
+    if( ptr ) delete ptr;
+
+    auto&& builder { Lifeograph::create_gui( Lifeograph::SHAREDIR + "/ui/po_password.ui" ) };
+    ptr = Gtk::Builder::get_widget_derived< DialogPassword >( builder, "Po_password" );
 
     ptr->m_ptr2diary = diary;
     ptr->m_ot = ot;
     ptr->m_handler_go = handler_go;
     ptr->m_handler_cancel = handler_cancel;
 
-    if( ptr->get_parent() != parent )
-        ptr->set_parent( *parent );
+    ptr->set_parent( *parent );
 
     if( rect )
         ptr->set_pointing_to( *rect );
-    else
-    {
-        Gdk::Rectangle rect2 { parent->get_allocation() };
-        ptr->set_pointing_to( rect2 );
-    }
 
     ptr->launch_internal();
 }
@@ -130,25 +122,20 @@
             break;
     }
 
-    if( m_ptr2diary->get_uri() == m_path_prev )
-        process_wrong_password();
-    else
+    switch( m_ot )
     {
-        switch( m_ot )
-        {
-            case OT_AUTHENTICATE:
-            case OT_OPEN:
-                m_L_msg->set_text( _( "Please enter password" ) );
-                break;
-            case OT_ADD:
-                m_L_msg->set_text( _( "Please enter a new password" ) );
-                break;
-            case OT_CHANGE:
-                m_L_msg->set_text( _( "Please enter the current and new passwords" ) );
-                break;
-            default:
-                break;
-        }
+        case OT_AUTHENTICATE:
+        case OT_OPEN:
+            m_L_msg->set_text( _( "Please enter password" ) );
+            break;
+        case OT_ADD:
+            m_L_msg->set_text( _( "Please enter a new password" ) );
+            break;
+        case OT_CHANGE:
+            m_L_msg->set_text( _( "Please enter the current and new passwords" ) );
+            break;
+        default:
+            break;
     }
 
     m_E_current->set_text( "" );
@@ -167,18 +154,12 @@
 DialogPassword::finish( const std::string& path )
 {
     if( ptr )
-    {
-        if( path == ptr->m_path_prev )
-            ptr->m_path_prev.clear();
         ptr->popdown();
-    }
 }
 
 void
 DialogPassword::handle_go()
 {
-    m_path_prev = m_ptr2diary->get_uri();
-
     if( m_ot & OTC_CHECK )
     {
         if( ! m_ptr2diary->compare_passphrase( ptr->m_E_current->get_text() ) )
@@ -186,8 +167,6 @@
             process_wrong_password();
             return;     // do not call go handler
         }
-        else
-            m_path_prev.clear();
     }
 
     if( m_ot == OT_OPEN )
@@ -235,7 +214,6 @@
         m_E_current->set_progress_fraction( 0 );
         m_E_current->set_sensitive( true );
         m_E_current->grab_focus();
-        m_path_prev.clear();
     }
 }
 
diff -Nru lifeograph-3.0.1/src/dialogs/dialog_password.hpp lifeograph-3.0.3/src/dialogs/dialog_password.hpp
--- lifeograph-3.0.1/src/dialogs/dialog_password.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/dialogs/dialog_password.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -28,6 +28,7 @@
 #include <gtkmm/entry.h>
 
 #include "../helpers.hpp"
+#include "../diaryelements/diary.hpp"
 
 
 namespace LIFEO
@@ -60,6 +61,8 @@
                                             const HELPERS::FuncVoid&,
                                             const HELPERS::FuncVoid& );
         static void                 finish( const String& path );
+        static void                 handle_wrong_password()
+        { ptr->process_wrong_password(); }
 
     protected:
         void                        launch_internal();
@@ -84,7 +87,6 @@
 
         Diary*                      m_ptr2diary;
         OperationType               m_ot;
-        std::string                 m_path_prev;
 
         static DialogPassword*      ptr;
 
diff -Nru lifeograph-3.0.1/src/dialogs/dialog_smartadd.cpp lifeograph-3.0.3/src/dialogs/dialog_smartadd.cpp
--- lifeograph-3.0.1/src/dialogs/dialog_smartadd.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/dialogs/dialog_smartadd.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -223,7 +223,7 @@
              ph && subh_count < MAX_SUGGESTIONS;
              ph = ph->get_next() )
         {
-            if( ph->get_heading_level() > 0 )
+            if( ph->get_heading_level() < VT::PS_NOTHDR )
             {
                 subh_count++;
                 add_suggestion( SuggestionType::AS_A_NEW_PARA,
@@ -378,7 +378,7 @@
 DialogSmartAdd::add_as_a_new_para()
 {
     Paragraph* p{ m_p2entry_parent->add_paragraph_after( new Paragraph( m_p2para ),
-                                                         m_p2para_parent ) };
+                                                         m_p2para_parent->get_sub_last() ) };
 
     AppWindow::p->UI_entry->refresh(); // a bit costly! try to refreshh the changed part only
     //AppWindow::p->show_entry( entry_new );
diff -Nru lifeograph-3.0.1/src/dialogs/dialog_sync.cpp lifeograph-3.0.3/src/dialogs/dialog_sync.cpp
--- lifeograph-3.0.1/src/dialogs/dialog_sync.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/dialogs/dialog_sync.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -22,8 +22,8 @@
 
 #include "../lifeograph.hpp"
 #include "../app_window.hpp"
-#include "dialog_export.hpp"
 #include "dialog_sync.hpp"
+#include "dialog_password.hpp"
 
 
 using namespace LIFEO;
@@ -54,6 +54,7 @@
         m_TS_content = TreeStoreImport::create();
         m_B_go              = builder->get_widget< Gtk::Button >( "B_sync_go" );
         m_B_cancel          = builder->get_widget< Gtk::Button >( "B_sync_cancel" );
+        m_B_unlock          = builder->get_widget< Gtk::Button >( "B_sync_unlock" );
         m_IB_sync           = builder->get_widget< Gtk::InfoBar >( "IB_sync" );
         m_L_info            = builder->get_widget< Gtk::Label >( "L_sync_info" );
         m_FCB_diary         = Gtk::Builder::get_widget_derived< FileChooserButton >(
@@ -229,8 +230,10 @@
     m_B_ow->signal_clicked().connect(
             sigc::bind( sigc::mem_fun( *this, &DialogSync::change_action ), SI::OVERWRITE ) );
 
+    m_FCB_diary->signal_clicked().connect( [&](){ set_not_ready(); } );
     m_FCB_diary->m_signal_file_set.connect(
             sigc::mem_fun( *this, &DialogSync::open_remote_diary ) );
+    m_B_unlock->signal_clicked().connect( [&](){ ask_for_password(); } );
 
     m_B_go->signal_clicked().connect(
             [ this ]()
@@ -248,11 +251,12 @@
 DialogSync::on_show()
 {
     Gtk::Window::on_show();
-    set_not_ready( _( "Select a diary file for synchronizing" ) );
+    set_not_ready();
 
     m_Bx_operations->set_visible( false );
 
     m_FCB_diary->set_uri( "" );
+    m_B_unlock->hide();
     m_WEP_widget->clear();
 
     m_FCB_diary->grab_focus();
@@ -271,7 +275,12 @@
         if( m_diary_r->read_header() == SUCCESS )
         {
             if( m_diary_r->is_encrypted() )
-                ask_for_password();
+                // Glib::signal_idle().connect_once(
+                //     sigc::mem_fun( *this, &DialogSync::ask_for_password ) );
+                // ask_for_password();
+                // TODO: 3.2: popover cannot be shown in a Filechooser event any more...
+                // ...so we use an intermediary unlock button to tringger the password popover
+                m_B_unlock->set_visible();
             else
                 open_remote_diary2();
         }
@@ -305,6 +314,7 @@
 DialogSync::open_remote_diary3()
 {
     DialogPassword::finish( m_diary_r->get_uri() );
+    m_B_unlock->hide();
     m_P_contents->set_position( get_height() * 0.55 );
     m_RB_changed_ignore->set_active( true );
     m_RB_new_ignore->set_active( true );
@@ -330,10 +340,13 @@
 void
 DialogSync::ask_for_password()
 {
+    auto            gphrect { m_B_unlock->compute_bounds( *this ) };
+    Gdk::Rectangle  rect    { int( gphrect->get_x() ),      int( gphrect->get_y() ),
+                              int( gphrect->get_width() ),  int( gphrect->get_height() ) };
     DialogPassword::launch( DialogPassword::OT_OPEN,
-                            m_diary_r, nullptr, m_FCB_diary,
+                            m_diary_r, &rect, this,
                             sigc::mem_fun( *this, &DialogSync::open_remote_diary2 ),
-                            [ this ]() { m_FCB_diary->set_uri( "" ); } );
+                            [ this ]() { m_B_unlock->hide(); m_FCB_diary->set_uri( "" ); } );
 }
 
 void
@@ -906,7 +919,7 @@
     if( m_action_map[ m_diary_r->get_id() ] != SI::SI_IGNORE )
         Diary::d->synchronize_options( m_diary_r );
 
-    set_not_ready( "" );
+    set_not_ready();
 
     hide();
 }
diff -Nru lifeograph-3.0.1/src/dialogs/dialog_sync.hpp lifeograph-3.0.3/src/dialogs/dialog_sync.hpp
--- lifeograph-3.0.1/src/dialogs/dialog_sync.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/dialogs/dialog_sync.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -27,7 +27,6 @@
 #include <gtkmm.h>
 
 #include "../diaryelements/diary.hpp"
-#include "dialog_password.hpp"
 #include "../widgets/widget_entrypicker.hpp"
 
 
@@ -132,7 +131,8 @@
 
         void                        populate_content();
         void                        set_ready();
-        void                        set_not_ready( const Ustring& );
+        void                        set_not_ready( const Ustring& =
+                                                   _( "Select a diary file for synchronizing" ) );
 
         bool                        is_ready();
 
@@ -197,12 +197,11 @@
         ColrecComp                  m_CR_compare;
         Glib::RefPtr< Gtk::ListStore >
                                     m_LS_compare;
+        Gtk::Button*                m_B_unlock;
         Gtk::Button*                m_B_add;
         Gtk::Button*                m_B_ow;
         Gtk::Switch*                m_Sw_similar;
 
-        // DialogPassword*             m_dialog_password;
-
         Diary*                      m_diary_r;
         std::map< DEID, SI >        m_action_map;
         std::map< DEID, SI >        m_comparison_map;
diff -Nru lifeograph-3.0.1/src/diaryelements/diary.cpp lifeograph-3.0.3/src/diaryelements/diary.cpp
--- lifeograph-3.0.1/src/diaryelements/diary.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/diary.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -216,13 +216,13 @@
     }
     m_matches.clear();
 
-    m_filters.clear(); // TODO: 3.0: implement custom clear() that deletes the allocated objects
+    m_filters.clear();
     m_p2filter_list = nullptr;
 
-    m_charts.clear(); // TODO: 3.0: implement custom clear() that deletes the allocated objects
+    m_charts.clear();
     m_p2chart_active = nullptr;
 
-    m_tables.clear(); // TODO: 3.0: implement custom clear() that deletes the allocated objects
+    m_tables.clear();
     m_p2table_active = nullptr;
 
     clear_themes();
@@ -373,16 +373,17 @@
             PRINT_DEBUG( "File is not readable" );
             return LIFEO::FILE_NOT_READABLE;
         }
-        else if( !finfo->get_attribute_boolean( G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE ) )
-        {
-            PRINT_DEBUG( "File is not writable" );
-            return LIFEO::FILE_NOT_WRITABLE;
-        }
+        // else if( type != SPT_READ_ONLY &&
+        //          !finfo->get_attribute_boolean( G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE ) )
+        // {
+        //     PRINT_DEBUG( "File is not writable" );
+        //     return LIFEO::FILE_NOT_WRITABLE;
+        // }
 
         // RESOLVE SYMBOLIC LINK
         if( finfo->get_file_type() == Gio::FileType::SYMBOLIC_LINK )
         {
-            uri = finfo->get_symlink_target(); // TODO: is this necessary with Gio::File?
+            uri = finfo->get_symlink_target(); // TODO: 3.1: is this necessary with Gio::File?
             print_info( "Symbolic link resolved to path: ", uri );
         }
     }
@@ -396,7 +397,7 @@
     m_F_read_only = ( type == SPT_READ_ONLY );
 
     // CHECK IF LOCKED
-    if( is_locked() )
+    if( !m_F_read_only && is_locked() )
         return LIFEO::FILE_LOCKED;
     else
         return LIFEO::SUCCESS;
@@ -782,7 +783,8 @@
         if( e_last_l1 )
         {
             m_p2entry_1st = e_last_l1;
-            m_p2entry_1st->get_sibling_last()->add_sibling_chain_after( e_first_temporal );
+            if( e_first_temporal )
+                m_p2entry_1st->get_sibling_last()->add_sibling_chain_after( e_first_temporal );
         }
         else if( e_first_temporal )
         {
@@ -1441,7 +1443,7 @@
                             if( p2para->is_image() )
                             {
                                 p2para->set_image_size( std::stoi( line.substr( 9, 1 ) ) );
-                                // TODO: the below steps are dumb, try to set text to uri at first
+                                // TODO: 3.2: below steps are dumb, try to set text to uri at first
                                 p2para->m_uri = p2para->m_text;
                                 p2para->m_text.clear();
                             }
@@ -1717,7 +1719,7 @@
 //                        case 'd': sorting_criteria = SoCr_DATE_ASC; break;
 //                        case 's': sorting_criteria = SoCr_SIZE_C_ASC; break;
 //                        case 'c': sorting_criteria = SoCr_CHANGE_ASC; break;
-                        // TODO: find a way to pass this to the filters
+                        // TODO: 3.1: find a way to pass this to the filters
 //                    }
                     if( m_read_version == 1050 )
                     {
@@ -1800,8 +1802,8 @@
                     case 'h':
                         p2entry->m_date_edited = std::stoul( line.substr( 2 ) );
                         break;
-                    case 's': // TODO: make use of this
-                        //p2entry->m_date_finish = std::stoul( line.substr( 2 ) );
+                    case 's':
+                        p2entry->m_date_finish = std::stoul( line.substr( 2 ) );
                         //p2entry->set_date_finish_explicit( true );
                         break;
                 }
@@ -1865,7 +1867,6 @@
     {
         auto  giofile   { Gio::File::create_for_uri( m_F_continue_from_lock ? m_uri + LOCK_SUFFIX
                                                                             : m_uri ) };
-        // TODO: check if PATH() is necessary on windows
         auto  ifstream  { giofile->read() };
         auto  finfo     { giofile->query_info() };
         gsize bytes_read;
@@ -1997,7 +1998,7 @@
         if( buf.buffer[ 0 ] != m_passphrase[ 0 ] || buf.buffer[ 1 ] != '\n' )
         {
             buf.clear();
-            clear();
+            // do not clear the diary as it will be reused
             return LIFEO::WRONG_PASSWORD;
         }
     }
@@ -2498,8 +2499,6 @@
     if( m_uri.empty() )
         return false;
 
-    if( m_F_read_only )
-        return true;
     try
     {
         auto file_lock  { Gio::File::create_for_uri( m_uri + LOCK_SUFFIX ) };
@@ -2892,7 +2891,7 @@
     else
         entry->set_title_style( style );
 
-    if( entry_rel )
+    if( entry_rel && entry_rel->is_theme_set() )
     {
         entry->set_theme( entry_rel->get_theme() );
     }
@@ -3093,12 +3092,6 @@
 void
 Diary::move_entry( Entry* p2entry2move, Entry* p2entry_target, const DropPosition& position )
 {
-    // CAUTION: tampers with the entry list order. list orders have to be updated after this func
-    if( p2entry2move->get_list_order() == -400 ) return;
-
-    p2entry2move->set_list_order( -400 );
-    p2entry2move->do_for_each_descendant( []( Entry* e ){ e->set_list_order( -400 ); } );
-
     remove_entry_from_hierarchy_with_descendants( p2entry2move );
 
     switch( position )
@@ -3605,7 +3598,7 @@
             }
         }
     }
-    // TODO: if filter referring to the other filter is itself unused we can detect it...
+    // TODO: 3.2: if filter referring to the other filter is itself unused we can detect it...
     // ...and reset the user count here
 
     // CHARTS (not now)
diff -Nru lifeograph-3.0.1/src/diaryelements/diary.hpp lifeograph-3.0.3/src/diaryelements/diary.hpp
--- lifeograph-3.0.1/src/diaryelements/diary.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/diary.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -576,7 +576,7 @@
         MapUstringTableElem     m_tables;
         TableElem*              m_p2table_active{ nullptr };
 
-        // TODO: sync the following, too:
+        // TODO: 3.2: sync the following, too:
         bool                    m_weekends[ 7 ] = { true, false, false, false, false, false, true };
         std::set< DateV >       m_holidays;
 
diff -Nru lifeograph-3.0.1/src/diaryelements/diarydata.cpp lifeograph-3.0.3/src/diaryelements/diarydata.cpp
--- lifeograph-3.0.1/src/diaryelements/diarydata.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/diarydata.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -397,7 +397,7 @@
     color_link =          contrast2( color_base, s_color_link1, s_color_link2 );
     color_link_broken =   contrast2( color_base, s_color_broken1, s_color_broken2 );
 
-    // TODO: we may change the coefficients below depending on the difference between the...
+    // TODO: 3.2: we may change the coefficients below depending on the difference between the...
     // ... contrasting colors using get_color_diff( Theme::s_color_done, theme->color_base )...
     // ... generally, when get_color_diff is < 1.0 contrast is not satisfactory
     color_open =          midtone( s_color_todo, color_text );
@@ -424,23 +424,27 @@
                                            : STR::compose( "url(\"", image_bg, "\")" ) };
 
     return( STR::compose(
-            "textview.", name, " { "
+            "textview#", name, " { "
                 "color: ",              c_text, "; "
                 "font-family: ",        font_f, "; "
                 "font-size: ",          size,   "pt; "
                 "caret-color: ",        c_text, "; }\n"
-            "textview.", name, " text selection { "
+            "textview#", name, " text selection { "
                 "color: ",              c_base, "; "
                 "background: ",   color_heading.to_string(), "; }\n"
-            "textview.", name, " text:selected { "
+            "textview#", name, " text:selected { "
                 "color: ",              c_base, "; "
                 "background: ",   color_heading.to_string(), "; }\n"
-            "textview.", name, " text { "
+            "textview#", name, " text { "
                 "background-color: ",   c_base, "; "
                 "background-image: ",   bg_img, "; }\n"
             "button.", name, " { "
                 "background-color: ",   c_base, "; "
-                "background-image: ",   bg_img, "; }" ) );
+                "background-image: ",   bg_img, "; }\n"
+            "entry#noncustom text { background-color: initial; background-image: initial; }\n"
+            "combobox#noncustom entry text { background-color: initial; "
+                                            "background-image: initial; }" ) );
+    // noncustom specialization is to revert children of the main textview to defaults
 }
 
 // THEMESYSTEM =====================================================================================
@@ -606,7 +610,7 @@
 void
 ChartData::set_from_string_old( const Ustring& chart_def )
 {
-    // TODO: to be improved but it may not worth to effort to implement upgrading all sorts of...
+    // TODO: 3.1: to be improved but it may not worth to effort to implement upgrading all sorts of...
     //       ...older charts
     String      line;
     StringSize  line_offset{ 0 };
@@ -660,7 +664,7 @@
                 switch( line[ 3 ] )
                 {
                     case 'Y': m_tcidu = COLUMN_PREV_YEAR; break;
-                    case 'P':  break; // planned - TODO: 3.0
+                    case 'P':  break; // planned - TODO: 3.1
                 }
                 switch( line[ 4 ] )
                 {
@@ -833,7 +837,7 @@
     {
         case ChartData::PERIOD_DAILY:
             //offset = Date::get_days_in_year( Date::get_year( d ) - 1 );
-            offset = 365; // TODO: incorporate leap years
+            offset = 365; // TODO: 3.1: incorporate leap years
             break;
         case ChartData::PERIOD_WEEKLY: offset = 52; break;
         // case ChartData::PERIOD_MONTHLY:
@@ -965,7 +969,7 @@
             case ChartData::TYPE_DATE:
                 if( col->get_type() == TableColumn::TCT_GANTT )
                 {
-                    // TODO: subdivide the value when necessary
+                    // TODO: 3.1: subdivide the value when necessary
                     // if( m_tcidy != ChartData::COLUMN_COUNT )
                     // {
                     //     const Value vy_sub{ vy / period };
diff -Nru lifeograph-3.0.1/src/diaryelements/diarydata.hpp lifeograph-3.0.3/src/diaryelements/diarydata.hpp
--- lifeograph-3.0.1/src/diaryelements/diarydata.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/diarydata.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -269,7 +269,7 @@
     REG_PROP_ALIAS( DEFAULT, CHAR_COUNT );
     REG_PROP_XTRA( FILTER, 0x0F );
     REG_PROP_END;
-    // TODO: v3.1 or later:  REG_PROP_INTR( 1, 0, 'W', 0x02, WORD_COUNT, N_( "Word Count" ) );
+    // TODO: 3.1 or later:  REG_PROP_INTR( 1, 0, 'W', 0x02, WORD_COUNT, N_( "Word Count" ) );
 
     // TABLE COLUMN OPTIONS: DURATION
     static constexpr char TCD_CHARS[]           = "_SCHFN12";
@@ -463,7 +463,7 @@
                          HFT_FOLD_COLLAPSED = 25 | HFT_F_LINK | HFT_F_ALWAYS | HFT_F_NOT_SAVED,
                          HFT_DATE_ELLIPSIS  = 61 | HFT_F_ONTHEFLY,
                          HFT_UNSET          = 99;
-                        // TODO: combine ON_THE_FLY and NOT_SAVED?
+                        // TODO: 3.2: combine ON_THE_FLY and NOT_SAVED?
 }
 
 typedef int SortCriterion;
@@ -499,6 +499,7 @@
                                                   // all standard ids have to be less than this
 static const DEID DEID_MIN_ACTUAL    = 10000000;  // ids have to be greater than this
 static const DEID DEID_UNSET         = 404;       // :)
+static const DEID DEID_MULTIPLE      = 606;
 static const DEID HOME_CURRENT_ENTRY = 1;         // entry shown at startup
 static const DEID HOME_LAST_ENTRY    = 2;         // entry shown at startup
 // NOTE: when HOME is fixed element, elements ID is used
@@ -756,6 +757,22 @@
 using FuncVoidElem              = std::function< void( DiaryElement* ) >;
 
 template< class T >
+class MapUstringDiaryElem : public std::map< Ustring, T*, FuncCmpStrings >
+{
+public:
+    ~MapUstringDiaryElem() { clear(); }
+
+    void
+    clear()
+    {
+        for( auto kv : *this )
+            delete kv.second;
+
+        std::map< Ustring, T*, FuncCmpStrings >::clear();
+    }
+};
+
+template< class T >
 Ustring
 create_unique_name_for_map( const std::map< Ustring, T, FuncCmpStrings >& map,
                             const Ustring& name0 )
@@ -1187,6 +1204,8 @@
 class ChartElem : public StringDefElem
 {
     public:
+        using GValue = Glib::Value< ChartElem* >;
+
         static const Ustring DEFINITION_DEFAULT;
         static const Ustring DEFINITION_DEFAULT_Y; // temporary: needed for diary v1050 support
 
@@ -1199,7 +1218,7 @@
         const R2Pixbuf&         get_icon() const override;
 };
 
-typedef std::map< Ustring, ChartElem*, FuncCmpStrings > MapUstringChartElem;
+using MapUstringChartElem = MapUstringDiaryElem< ChartElem >;
 
 } // end of namespace LIFEO
 
diff -Nru lifeograph-3.0.1/src/diaryelements/entry.cpp lifeograph-3.0.3/src/diaryelements/entry.cpp
--- lifeograph-3.0.1/src/diaryelements/entry.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/entry.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -207,6 +207,16 @@
 }
 
 Entry*
+Entry::get_prev_straight( bool F_go_up ) const
+{
+    if ( m_p2prev )
+    {
+        auto&& descendants { m_p2prev->get_descendants() };
+        return( descendants.empty() ? m_p2prev : descendants.back() );
+    }
+    else return m_p2parent;
+}
+Entry*
 Entry::get_next_straight( bool F_go_down ) const
 {
     if     ( F_go_down && m_p2child_1st ) return m_p2child_1st;
@@ -992,8 +1002,15 @@
                                         F_inherit_style && !para_inherit );
             pos_split = 0;
 
-            if( F_inherit_style && para_inherit && !para_inherit->is_empty() )
-                para->inherit_style_from( para_inherit, false );
+            if( F_inherit_style && para_inherit )
+                para->inherit_style_from( para_inherit,
+                                          para_inherit->is_empty() && !para->is_empty() );
+            // remove heading and hrule properties from empty paragraphs:
+            if( para_inherit->is_empty() )
+            {
+                para_inherit->set_hrule( false );
+                para_inherit->set_heading_level( VT::PS_NOTHDR );
+            }
 
             pos_para_bgn += ( para->get_size() + 1 );   // +1 for the \n
             pos_in_para = 0;
@@ -1140,7 +1157,7 @@
                                1, pos_end, pos_bgn + para_new_1st->get_chain_length() );
 
     // INSERT THE NEW TEXT
-    if( pos_split > 0 )
+    if( pos_split >= 0 )
         para_split = para->split_at( pos_split );
     para->insert_text( pos_split, para_new_1st, nullptr );  // first para is inserted
     // we normally don't access the parser directly, but here it makes sense:
@@ -1532,7 +1549,7 @@
 
     // move collapsed paragraphs together with their children:
     if( !para_end->is_expanded() )
-        para_end = para_end->get_sub_last();
+        para_end = para_end->get_sub_last_invisible();
 
     remove_paragraphs( para_bgn, para_end );
     add_paragraph_after( para_bgn, para_prev->m_p2prev );
@@ -1556,7 +1573,7 @@
 
     // move collapsed paragraphs together with their children:
     if( !para_end->is_expanded() )
-        para_end = para_end->get_sub_last();
+        para_end = para_end->get_sub_last_invisible();
 
     // skip hidden sub-paragraphs:
     if( !para_next->is_expanded() && para_next->has_subs() )
@@ -1751,7 +1768,7 @@
             {
                 if( sub_tag_lowest )
                 {
-                    if( sub_tag->get_list_order() < sub_tag_lowest->get_list_order() )
+                    if( sub_tag->get_sibling_order() < sub_tag_lowest->get_sibling_order() )
                         sub_tag_lowest = sub_tag;
                 }
                 else
@@ -1777,7 +1794,7 @@
             {
                 if( sub_tag_highest )
                 {
-                    if( sub_tag->get_list_order() > sub_tag_highest->get_list_order() )
+                    if( sub_tag->get_sibling_order() > sub_tag_highest->get_sibling_order() )
                         sub_tag_highest = sub_tag;
                 }
                 else
diff -Nru lifeograph-3.0.1/src/diaryelements/entry.hpp lifeograph-3.0.3/src/diaryelements/entry.hpp
--- lifeograph-3.0.1/src/diaryelements/entry.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/entry.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -100,10 +100,15 @@
         Entry*                  get_parent_unfiltered( FiltererContainer* fc ) const;
         Entry*                  get_child_1st() const { return m_p2child_1st; }
         Entry*                  get_prev() const      { return m_p2prev; }
+        Entry*                  get_prev_cyclic()
+        { return( m_p2prev ? m_p2prev : ( m_p2next ? get_sibling_last() : nullptr ) ); }
         Entry*                  get_prev_or_up() const
         { return( m_p2prev ? m_p2prev : m_p2parent ); }
         Entry*                  get_next() const      { return m_p2next; }
+        Entry*                  get_next_cyclic()
+        { return( m_p2next ? m_p2next : ( m_p2prev ? get_sibling_1st() : nullptr ) ); }
         Entry*                  get_prev_unfiltered( FiltererContainer* fc ) const;
+        Entry*                  get_prev_straight( bool = true ) const;
         Entry*                  get_next_straight( bool = true ) const;
         Entry*                  get_next_straight( const Entry*, bool = true ) const;
         Entry*                  get_sibling_1st();
@@ -172,10 +177,12 @@
         }
         void                    set_filtered_out( bool filteredout )
         {
+            // for the following to work always call this function in a parent-to-child order:
+            m_status &= ~ES::HAS_VSBL_DESCENDANT;
+
             if( filteredout )
             {
                 m_status |= ES::FILTERED_OUT;
-                m_status &= ~ES::HAS_VSBL_DESCENDANT;
             }
             else
             {
@@ -295,10 +302,6 @@
             }
             return number;
         }
-        int                     get_list_order() const
-        { return m_list_order; }
-        void                    set_list_order( int order )
-        { m_list_order = order; }
         void                    update_sibling_orders();
         int                     get_sibling_order() const
         { return m_sibling_order; }
@@ -500,7 +503,6 @@
 
         DateV                   m_date_created;
         DateV                   m_date_edited;
-        int                     m_list_order    { 0 };  // starts from 0
         int                     m_sibling_order { 1 };  // starts from 1
         Paragraph*              m_p2para_1st    { nullptr };
         Paragraph*              m_p2para_last   { nullptr };
@@ -526,7 +528,7 @@
 
         UndoStack               m_session_edits;
 
-    friend class Diary; // TODO: remove this friendship too??
+    friend class Diary;
 };
 
 // MAIN ENTRY POOL =================================================================================
diff -Nru lifeograph-3.0.1/src/diaryelements/filter.cpp lifeograph-3.0.3/src/diaryelements/filter.cpp
--- lifeograph-3.0.1/src/diaryelements/filter.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/filter.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -852,7 +852,6 @@
 FiltererTitleStyle::filter( const Entry* entry ) const
 {
     return( entry->get_title_style() == VT::get_v< VT::ETS, int, char >( m_title_style ) );
-    // TODO: consider storing the title style as number to speed up
 }
 
 void
diff -Nru lifeograph-3.0.1/src/diaryelements/filter.hpp lifeograph-3.0.3/src/diaryelements/filter.hpp
--- lifeograph-3.0.1/src/diaryelements/filter.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/filter.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -693,7 +693,7 @@
         int                     m_num_users{ 0 };
 };
 
-typedef std::map< Ustring, Filter*, FuncCmpStrings > MapUstringFilter;
+using MapUstringFilter = MapUstringDiaryElem< Filter >;
 
 } // end of namespace LIFEO
 
diff -Nru lifeograph-3.0.1/src/diaryelements/paragraph.cpp lifeograph-3.0.3/src/diaryelements/paragraph.cpp
--- lifeograph-3.0.1/src/diaryelements/paragraph.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/paragraph.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -72,9 +72,12 @@
 {
     for( auto prev = m_p2prev; prev; prev = prev->m_p2prev )
     {
-        if( prev->get_indent_level() < get_indent_level() )
+        if( prev->get_indent_level() < get_indent_level() ||
+            prev->get_heading_level() < get_heading_level() ||
+            prev->is_empty_completely() )
             break;
-        if( prev->get_indent_level() == get_indent_level() )
+        if( prev->get_indent_level() == get_indent_level() &&
+            prev->get_heading_level() == get_heading_level() )
             return prev;
     }
 
@@ -85,15 +88,30 @@
 {
     for( auto next = m_p2next; next; next = next->m_p2next )
     {
-        if( next->get_indent_level() < get_indent_level() )
+        if( next->get_indent_level() < get_indent_level() ||
+            next->get_heading_level() < get_heading_level() ||
+            next->is_empty_completely() )
             break;
-        if( next->get_indent_level() == get_indent_level() )
+        if( next->get_indent_level() == get_indent_level() &&
+            next->get_heading_level() == get_heading_level() )
             return next;
     }
 
     return nullptr;
 }
 
+ListParagraphs
+Paragraph::get_siblings() const
+{
+    ListParagraphs siblings{ const_cast< Paragraph* >( this ) };
+    for( auto prev = get_prev_sibling(); prev; prev = prev->get_prev_sibling() )
+        siblings.push_front( prev );
+    for( auto next = get_next_sibling(); next; next = next->get_next_sibling() )
+        siblings.push_back( next );
+
+    return siblings;
+}
+
 Paragraph*
 Paragraph::get_nth_next( int n )
 {
@@ -127,7 +145,7 @@
             if( prev->is_header() ) // entry title is not a parent
                 break;
 
-            if( prev->is_sub( this ) )
+            if( prev->is_parent_of( this ) )
                 return prev;
         }
     }
@@ -158,7 +176,7 @@
 
     for( Paragraph* p = m_p2next; p; p = p->m_p2next )
     {
-        if( is_sub( p ) )
+        if( this->is_parent_of( p ) )
             p_sub = p;
         else
             break;
@@ -175,7 +193,7 @@
 
     for( Paragraph* p = m_p2next; p; p = p->m_p2next )
     {
-        if( is_sub( p ) )
+        if( this->is_parent_of( p ) )
         {
             if( p->is_visible() && ( !F_force_1st_gen || p->get_parent() == this ) )
                 p_sub = p;
@@ -188,6 +206,27 @@
 
     return p_sub;
 }
+Paragraph*
+Paragraph::get_sub_last_invisible() const
+// Caution: returns itself in the absence of any visible sub
+{
+    Paragraph* p_sub{ const_cast< Paragraph* >( this ) };
+
+    for( Paragraph* p = m_p2next; p; p = p->m_p2next )
+    {
+        if( is_parent_of( p ) )
+        {
+            if( !p->is_visible() )
+                p_sub = p;
+            else
+                continue;
+        }
+        else
+            break;
+    }
+
+    return p_sub;
+}
 
 bool
 Paragraph::is_last_in_host() const
@@ -277,7 +316,7 @@
 Paragraph::get_name() const
 {
     //return STR::compose( m_host->get_name(), ":", m_order_in_host );
-    return get_text(); // TODO: find a better way of describing a paragraph
+    return get_text(); // TODO: 3.2: find a better way of describing a paragraph
 }
 
 int
@@ -505,7 +544,10 @@
     set_indent_level( para->get_indent_level() );
 
     if( F_heading_level_too )
+    {
+        set_hrule( para->is_hrule() );
         set_heading_level( para->get_heading_level() );
+    }
 }
 
 void
@@ -515,7 +557,7 @@
 
     for( Paragraph* p = m_p2next; p; p = p->m_p2next )
     {
-        if( is_sub( p ) )
+        if( is_parent_of( p ) )
             p->reset_visibility();
         else
             break;
@@ -689,7 +731,7 @@
             {
                 if( sub_tag_lowest )
                 {
-                    if( sub_tag->get_list_order() < sub_tag_lowest->get_list_order() )
+                    if( sub_tag->get_sibling_order() < sub_tag_lowest->get_sibling_order() )
                         sub_tag_lowest = sub_tag;
                 }
                 else
@@ -714,7 +756,7 @@
             {
                 if( sub_tag_highest )
                 {
-                    if( sub_tag->get_list_order() > sub_tag_highest->get_list_order() )
+                    if( sub_tag->get_sibling_order() > sub_tag_highest->get_sibling_order() )
                         sub_tag_highest = sub_tag;
                 }
                 else
@@ -868,19 +910,6 @@
     }
 }
 
-void
-Paragraph::change_ordered_list_type()
-{
-    const int lt{ get_list_type() };
-    if( !( lt & VT::PS_ORDERED_GEN ) || lt == VT::PS_SROMAN )
-        set_list_type( VT::PS_NUMBER );
-    else
-        set_list_type( lt + 0x10 );
-
-    if( get_indent_level() == 0 )
-        set_indent_level( 1 );
-}
-
 bool
 Paragraph::set_indent_level( int lvl )
 {
@@ -907,17 +936,25 @@
             p->set_list_type( ns->get_list_type() );
     }
 }
-void
+bool
 Paragraph::indent()
 {
     if( set_indent_level( get_indent_level() + 1 ) )
+    {
         update_para_style_after_after_indentation_change( this );
+        return true;
+    }
+    return false;
 }
-void
+bool
 Paragraph::unindent()
 {
     if( set_indent_level( get_indent_level() - 1 ) )
+    {
         update_para_style_after_after_indentation_change( this );
+        return true;
+    }
+    return false;
 }
 
 void
@@ -1214,7 +1251,7 @@
 {
     for( auto& format : m_formats )
     {
-        if( format->type == type && format->pos_bgn < pos_end && format->pos_end > pos_bgn )
+        if( format->type == type && format->pos_bgn < pos_end && format->pos_end >= pos_bgn )
             return format;
     }
     return nullptr;
@@ -1295,16 +1332,16 @@
     // match the indent level from previous paras of the same type:
     for( Paragraph* p = m_p2prev; p && p->m_order_in_host < m_order_in_host; p = p->m_p2prev )
     {
-        const auto pt{ p->get_para_type() };
-
-        if( pt == get_para_type() )
+        if( p->get_para_type() == get_para_type() )
         {
             indent_level = p->get_indent_level();
             break;
         }
-        // else   // TODO: it is unclear what this was for
-        // if( p->is_list() && p->get_indent_level() >= indent_level )
-        //     indent_level = ( p->get_indent_level() + 1 );
+        // indent if different than the previous list paragraph
+        // TODO: 3.1: we may as well need to outdent based on the parents of p
+        else
+        if( p->is_list() && p->get_indent_level() >= indent_level )
+            indent_level = ( p->get_indent_level() + 1 );
     }
 
     // it is not beautiful the lists to hace 0 indent (questionable):
diff -Nru lifeograph-3.0.1/src/diaryelements/paragraph.hpp lifeograph-3.0.3/src/diaryelements/paragraph.hpp
--- lifeograph-3.0.1/src/diaryelements/paragraph.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/paragraph.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -164,12 +164,14 @@
         { return m_p2next; }
         Paragraph*                  get_next_visible() const;
         Paragraph*                  get_next_sibling() const;
+        std::list< Paragraph* >     get_siblings() const;
         Paragraph*                  get_parent() const;
         Paragraph*                  get_nth_next( int );
         Paragraph*                  get_last() const;
         Paragraph*                  get_sub_last() const;
         Paragraph*                  get_sub_last_visible( bool = false ) const;
-        bool                        is_sub( const Paragraph* p_sub ) const
+        Paragraph*                  get_sub_last_invisible() const;
+        bool                        is_parent_of( const Paragraph* p_sub ) const
         {
             if( p_sub )
             {
@@ -192,7 +194,7 @@
             return false;
         }
         bool                        has_subs() const
-        { return( !is_header() && is_sub( m_p2next ) ); }
+        { return( !is_header() && is_parent_of( m_p2next ) ); }
 
         int                         get_para_no() const
         { return m_order_in_host; }
@@ -228,6 +230,8 @@
         // TEXTUAL CONTENTS
         bool                        is_empty() const
         { return m_text.empty(); }
+        bool                        is_empty_completely() const
+        { return( m_text.empty() && !is_list() ); }
         gunichar                    get_char( UstringSize i ) const
         { return m_text[ i ]; }
         const Ustring&              get_text() const
@@ -349,6 +353,11 @@
                 default:            return ES::NOT_TODO;
             }
         }
+        int                         get_todo_status_ps() const
+        {
+            const auto style { m_style & VT::PS_FLT_LIST };
+            return( ( style & VT::PS_TODO_GEN ) ? style : 0 );
+        }
         bool                        is_todo_status_forced() const
         { return( m_style & VT::PS_TODO_FORCED ); }
         void                        set_todo_status_forced( bool F_forced )
@@ -534,15 +543,13 @@
         { m_style = ( ( m_style & ~VT::PS_FLT_TYPE ) | ( type & VT::PS_FLT_TYPE ) ); }
         void                        set_para_type2( int type );
 
-        void                        change_ordered_list_type();
-
         // INDENTATION
         int                         get_indent_level() const
         { return( m_style & VT::PS_FLT_INDENT ) >> 24; }
 
         bool                        set_indent_level( int );
-        void                        indent();
-        void                        unindent();
+        bool                        indent();
+        bool                        unindent();
 
         int                         m_style{ VT::PS_DEFAULT };
         Entry*                      m_host;
diff -Nru lifeograph-3.0.1/src/diaryelements/tableelem.cpp lifeograph-3.0.3/src/diaryelements/tableelem.cpp
--- lifeograph-3.0.1/src/diaryelements/tableelem.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/tableelem.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -849,7 +849,7 @@
         }
     }
 
-    // TODO: handle dates differently
+    // TODO: 3.1: handle dates differently
     switch( m_opt_int & VT::TCAo::FILTER )
     {
         default:               return( vA + vB );
@@ -1243,13 +1243,13 @@
         else
             continue;
 
-        // TODO: the ones below require recalculation:
+        // TODO: 3.1: the ones below require recalculation:
         // case TableColumn::TCT_TODO_STATUS:
         // case TableColumn::TCT_GANTT:
         // case TableColumn::TCT_BOOL:
         // case TableColumn::TCT_SUB:
 
-        // TODO: min and max values needs recalculation
+        // TODO: 3.1: min and max values needs recalculation
 
         col->format_number( this ); // use set_total_value_strings?
     }
@@ -1720,38 +1720,10 @@
                                 col->set_count_filter( m_p2diary->get_filter(
                                         std::stoul( line.substr( 4 ) ) ) );
                                 break;
-                              // TODO: below are needed to support db v2000:
-//                            case 'l':
-//                                switch( line[ 4 ] )
-//                                {
-//                                    case '<':
-//                                        col->set_condition_rel_lo( TableColumn::C_LESS );
-//                                        break;
-//                                    case '[':
-//                                        col->set_condition_rel_lo( TableColumn::C_LESS_OR_EQUAL );
-//                                        break;
-//                                }
-//                                if( line[ 4 ] != '_' )
-//                                    col->set_condition_num_lo( STR::get_d( line.substr( 5 ) ) );
-//                                break;
-//                            case 'h':
-//                                switch( line[ 4 ] )
-//                                {
-//                                    case '<':
-//                                        col->set_condition_rel_hi( TableColumn::C_LESS );
-//                                        break;
-//                                    case '[':
-//                                        col->set_condition_rel_hi( TableColumn::C_LESS_OR_EQUAL );
-//                                        break;
-//                                    case '=':
-//                                        col->set_condition_rel_hi( TableColumn::C_EQUAL );
-//                                        break;
-//                                    default:
-//                                        col->set_condition_rel_hi( TableColumn::C_IGNORE );
-//                                }
-//                                if( line[ 4 ] != '_' )
-//                                    col->set_condition_num_hi( STR::get_d( line.substr( 5 ) ) );
-//                                break;
+                           // NOTE: numeric limits were removed in favor of filters...
+                           // ... these have to be set manually after update:
+                           // case 'l':
+                           // case 'h':
                         }
                         break;
                 }
@@ -1763,10 +1735,9 @@
             case 'l':   // filter
                 m_filter_para = m_p2diary->get_filter( std::stoul( line.substr( 2 ) ) );
                 break;
-                // TODO: below are needed to support db v2000:
-//            case 't':
-//                m_tag_filter = m_p2diary->get_entry_by_id( std::stoul( line.substr( 2 ) ) );
-//                break;
+            // NOTE: tag filters were removed in favor of generic filters...
+            // ... these have to be set manually after update:
+            // case 't':
             case 'o':
                 if( line[ 2 ] == 'G' || line[ 2 ] == '_' ) // to support pre v2016
                     m_grouping_depth = 0;
diff -Nru lifeograph-3.0.1/src/diaryelements/tableelem.hpp lifeograph-3.0.3/src/diaryelements/tableelem.hpp
--- lifeograph-3.0.1/src/diaryelements/tableelem.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/diaryelements/tableelem.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -437,7 +437,7 @@
         void                        resort_lines();
         void                        sort_group_lines( TableLine* );
 
-        // TODO: move the below methods to TableLine at he earliest conveninece:
+        // TODO: 3.1: consider moving the below methods to TableLine:
         double                      get_value( unsigned int, unsigned int ) const;
         double                      get_value_pure( unsigned int, unsigned int ) const;
         double                      get_weight( unsigned int, unsigned int ) const;
@@ -547,6 +547,8 @@
 class TableElem : public StringDefElem
 {
     public:
+        using GValue = Glib::Value< TableElem* >;
+
         static const Ustring DEFINITION_DEFAULT;
         static const Ustring DEFINITION_REPORT;
 
@@ -559,7 +561,7 @@
         const R2Pixbuf&         get_icon() const override;
 };
 
-typedef std::map< Ustring, TableElem*, FuncCmpStrings > MapUstringTableElem;
+using MapUstringTableElem = MapUstringDiaryElem< TableElem >;
 
 } // end of namespace LIFEO
 
diff -Nru lifeograph-3.0.1/src/helpers.cpp lifeograph-3.0.3/src/helpers.cpp
--- lifeograph-3.0.1/src/helpers.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/helpers.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -1311,7 +1311,7 @@
     // http://www.gnupg.org/documentation/manuals/gcrypt/Initializing-the-library.html
 
     // initialize subsystems:
-    if( ! gcry_check_version( NULL ) )  // TODO: check version
+    if( ! gcry_check_version( NULL ) )  // TODO: 3.2 check version
     {
         print_error( "Libgcrypt version mismatch" );
         return false;
@@ -1591,24 +1591,24 @@
 void
 EntryClear::on_changed()
 {
-    // TODO: setting icon of an entry hosted in a popopver creates problems in gtkmm4
-    // if( get_text().empty() )
-    // {
-    //     unset_icon( Gtk::Entry::IconPosition::SECONDARY );
-    //     set_icon_activatable( false, Gtk::Entry::IconPosition::SECONDARY );
-    // }
-    // else
-    // {
-    //     try // may not work!
-    //     {
-    //         set_icon_from_icon_name( "edit-clear-symbolic", Gtk::Entry::IconPosition::SECONDARY );
-    //     }
-    //     catch( ... )
-    //     {
-    //         set_icon_from_icon_name( "edit-clear", Gtk::Entry::IconPosition::SECONDARY );
-    //     }
-    //     set_icon_activatable( true, Gtk::Entry::IconPosition::SECONDARY );
-    // }
+    // be watchful: setting icon of an entry hosted in a popopver used to creates problems in gtkmm4
+    if( get_text().empty() )
+    {
+        unset_icon( Gtk::Entry::IconPosition::SECONDARY );
+        set_icon_activatable( false, Gtk::Entry::IconPosition::SECONDARY );
+    }
+    else
+    {
+        try // may not work!
+        {
+            set_icon_from_icon_name( "edit-clear-symbolic", Gtk::Entry::IconPosition::SECONDARY );
+        }
+        catch( ... )
+        {
+            set_icon_from_icon_name( "edit-clear", Gtk::Entry::IconPosition::SECONDARY );
+        }
+        set_icon_activatable( true, Gtk::Entry::IconPosition::SECONDARY );
+    }
 
     Gtk::Entry::on_changed();
 }
diff -Nru lifeograph-3.0.1/src/helpers.hpp lifeograph-3.0.3/src/helpers.hpp
--- lifeograph-3.0.1/src/helpers.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/helpers.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -616,6 +616,114 @@
     return( date_l > date_r );
 }
 
+// CUSTOM CONTAINER OR POLLING OBJECTS =============================================================
+template< typename KeyType >
+class PollMap
+{
+    public:
+        // Insert or update
+        void
+        set( const KeyType& key, const int value )
+        {
+            auto it { key_to_value.find( key ) };
+            if( it != key_to_value.end() )
+            {
+                int   old_value { it->second };
+                auto  range     { value_to_key.equal_range( old_value ) };
+                for( auto v_it = range.first; v_it != range.second; ++v_it )
+                {
+                    if( v_it->second == key )
+                    {
+                        value_to_key.erase( v_it );
+                        break;
+                    }
+                }
+            }
+            key_to_value[ key ] = value;
+            value_to_key.insert( { value, key } );
+        }
+
+        void
+        increase( const KeyType& key )
+        {
+            auto  it    { key_to_value.find( key ) };
+            int   value { 0 };
+            if( it != key_to_value.end() )
+            {
+                value = it->second;
+                auto range { value_to_key.equal_range( value ) };
+                for( auto v_it = range.first; v_it != range.second; ++v_it )
+                {
+                    if( v_it->second == key )
+                    {
+                        value_to_key.erase( v_it );
+                        break;
+                    }
+                }
+            }
+            ++value;
+            key_to_value[ key ] = value;
+            value_to_key.insert( { value, key } );
+        }
+
+        // Get value by key
+        int
+        get_value( const KeyType& key ) const
+        {
+            auto it { key_to_value.find( key ) };
+            if( it == key_to_value.end() )
+                throw std::out_of_range( "Key not found" );
+            return it->second;
+        }
+
+        // Get the key with the lowest value
+        KeyType
+        get_key_min() const
+        {
+            if( value_to_key.empty() )
+                throw std::runtime_error( "Map is empty" );
+            return value_to_key.begin()->second;
+        }
+        KeyType
+        get_key_max() const
+        {
+            if( value_to_key.empty() )
+                throw std::runtime_error( "Map is empty" );
+            return value_to_key.rbegin()->second;
+        }
+
+        // Remove a key
+        void
+        remove( const KeyType& key )
+        {
+            auto it { key_to_value.find( key ) };
+            if( it != key_to_value.end() )
+            {
+                int   value { it->second };
+                auto  range { value_to_key.equal_range(value) };
+                for( auto v_it = range.first; v_it != range.second; ++v_it )
+                {
+                    if( v_it->second == key )
+                    {
+                        value_to_key.erase( v_it );
+                        break;
+                    }
+                }
+                key_to_value.erase( it );
+            }
+        }
+
+        // Size of the container
+        size_t size() const { return key_to_value.size(); }
+        // Check if empty
+        bool empty() const { return key_to_value.empty(); }
+
+    private:
+        std::map< KeyType, int >      key_to_value;
+        std::multimap< int, KeyType > value_to_key;
+};
+
+
 // CONSOLE MESSAGES ================================================================================
 class Console
 {
diff -Nru lifeograph-3.0.1/src/lifeograph.cpp lifeograph-3.0.3/src/lifeograph.cpp
--- lifeograph-3.0.1/src/lifeograph.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/lifeograph.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -26,6 +26,7 @@
 #include "app_window.hpp"
 #include "lifeograph.hpp"
 #include "dialogs/dialog_preferences.hpp"
+#include "dialogs/dialog_password.hpp"
 
 #if LIFEOGRAPH_DEBUG_BUILD || defined( LIFEOGRAPH_BETA_STAGE )
 #include <build_time.h>
@@ -35,11 +36,11 @@
 using namespace LIFEO;
 
 // STATIC MEMBERS
-const char                      Lifeograph::PROGRAM_VERSION_STRING[] = "3.0.1"
+const char                      Lifeograph::PROGRAM_VERSION_STRING[] = "3.0.3"
 #if LIFEOGRAPH_DEBUG_BUILD || defined( LIFEOGRAPH_BETA_STAGE )
         "\n" BUILD_TIMESTAMP
 #endif
-        "\n\"Be silent when children sleep not when they are massacred\"";
+        "\n\"Madleen\"";
 
 
 Lifeograph*                     Lifeograph::p;
@@ -383,7 +384,7 @@
     {
         // the attempt below does not work. we admit defeat, temporarily:
         s_color_insensitive = "gray";
-        // TODO: 3.1: find a way for gtkmm4:
+        // TODO: 3.2: find a way for gtkmm4:
         // note that the subtitle label is always insensitive:
         // Gtk::Widget* widget { AppWindow::p->m_L_subtitle };
         // s_color_insensitive = convert_gdkcolor_to_html( widget->get_style_context()->get_color() );
diff -Nru lifeograph-3.0.1/src/parsers/parser_stripper.cpp lifeograph-3.0.3/src/parsers/parser_stripper.cpp
--- lifeograph-3.0.1/src/parsers/parser_stripper.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/parsers/parser_stripper.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -39,6 +39,9 @@
 
     for( auto format : para->m_formats )
     {
+        // ignore formats already in a processed area (for formats within comments):
+        if( format->pos_bgn < unsigned( m_i_last ) ) continue;
+
         switch( format->type )
         {
             case VT::HFT_COMMENT:
diff -Nru lifeograph-3.0.1/src/parsers/parser_stripper.hpp lifeograph-3.0.3/src/parsers/parser_stripper.hpp
--- lifeograph-3.0.1/src/parsers/parser_stripper.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/parsers/parser_stripper.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -40,11 +40,13 @@
                                                         UstringSize pos_end,
                                                         int component )
         {
+            // if any, add the plain text remnants first:
             if( m_flags & VT::TCT_FILTER_COMPONENT & VT::TCT_CMPNT_PLAIN )
             {
-                int offset{ ( m_stripped_text.empty() ||
-                              m_stripped_text[ m_stripped_text.size() - 1 ] == ' ' ) &&
-                            m_p2para->get_char( m_i_last ) == ' ' ? 1 : 0 };
+                int offset
+                { ( m_stripped_text.empty() ||
+                    STR::is_char_space( m_stripped_text[ m_stripped_text.size() - 1 ] ) ) &&
+                  STR::is_char_space( m_p2para->get_char( m_i_last ) ) ? 1 : 0 };
                 m_stripped_text += m_p2para->get_substr( m_i_last + offset, pos_bgn );
             }
 
diff -Nru lifeograph-3.0.1/src/printing.cpp lifeograph-3.0.3/src/printing.cpp
--- lifeograph-3.0.1/src/printing.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/printing.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -449,7 +449,7 @@
 
                 if( ( m_format_cur & CF_FILTER ) == CF_TAG )
                 {
-                    // TODO: draw tag form around it (this is not so easy)
+                    // TODO: 3.1: draw tag form around it (this is not so easy)
                     break;
                 }
             } // end of if( format_cur != format_prev )
diff -Nru lifeograph-3.0.1/src/settings.cpp lifeograph-3.0.3/src/settings.cpp
--- lifeograph-3.0.1/src/settings.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/settings.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -49,7 +49,7 @@
     {
         PRINT_DEBUG( "Configuration file does not exist, maiden voyage!" );
         Lifeograph::settings.rtflag_force_welcome = true;
-        recentfiles.insert( Lifeograph::EXAMPLEDIARYPATH );
+        recentfiles.insert( Glib::filename_to_uri( Lifeograph::EXAMPLEDIARYPATH ) );
 
         return false;
     }
@@ -96,8 +96,8 @@
                     if( diary_extension[ 0 ] != '.' )
                         diary_extension.insert( 0, "." );
                     break;
-                // TODO: position & size settings may go to the diary
-                // TODO: check if these are within screen boundaries
+                // TODO: 3.2: position & size settings may go to the diary
+                // TODO: 3.2: check if these are within screen boundaries
                 case 'H':
                     height = std::stoi( line.substr( 2 ) );
                     break;
diff -Nru lifeograph-3.0.1/src/ui_diary.cpp lifeograph-3.0.3/src/ui_diary.cpp
--- lifeograph-3.0.1/src/ui_diary.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/ui_diary.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -30,10 +30,12 @@
 #include "ui_entry.hpp"
 #include "widgets/widget_textview.hpp"
 #include "dialogs/dialog_smartadd.hpp"
+#include "dialogs/dialog_password.hpp"
 
 
 using namespace LIFEO;
 
+using VStr = Glib::Variant< Ustring >;
 
 UIDiary::UIDiary()
 {
@@ -183,16 +185,9 @@
             "update_date_separators",
             [ this ](){ update_date_separators(); } );
 
-    m_A_add_entry_dated = Lifeograph::p->add_action(
-            "add_entry_dated",
-            [ this ]() { add_entry_dated( false ); } );
-
-    m_A_add_milestone = Lifeograph::p->add_action(
-            "add_milestone",
-            [ this ]() { add_entry_dated( true ); } );
-
-    m_A_add_entry_todo = Lifeograph::p->add_action(
-            "add_entry_todo",
+    Lifeograph::p->add_action( "add_entry_dated",     [ this ]() { add_entry_dated( false ); } );
+    Lifeograph::p->add_action( "add_milestone",       [ this ]() { add_entry_dated( true ); } );
+    Lifeograph::p->add_action( "add_entry_todo",
             [ this ]()
             {
                 if( Diary::d->is_in_edit_mode() )
@@ -203,40 +198,50 @@
                     update_for_added_entry( entry );
                 }
             } );
-
-    m_A_add_entry_free = Lifeograph::p->add_action(
-            "add_entry_free",
-            [ this ]() { add_entry_free(); } );
-
-    m_A_go_today = Lifeograph::p->add_action(
-            "go_to_today",
-            [ this ](){ show_today(); } );
-    m_A_smart_add = Lifeograph::p->add_action(
-            "smart_add",
-            [](){ DialogSmartAdd::create(); } );
-    m_A_go_list_prev = Lifeograph::p->add_action(
-            "go_list_prev",
-            [](){ AppWindow::p->go_to_prev(); } );
-    m_A_go_list_next = Lifeograph::p->add_action(
-            "go_list_next",
-            [](){ AppWindow::p->go_to_next(); } );
-    m_A_jump_to_entry = Lifeograph::p->add_action(
-            "jump_to_current_entry",
+    Lifeograph::p->add_action( "add_entry_free",      [ this ](){ add_entry_free(); } );
+    Lifeograph::p->add_action( "go_to_today",         [ this ](){ show_today(); } );
+    Lifeograph::p->add_action( "smart_add",           [](){ DialogSmartAdd::create(); } );
+    Lifeograph::p->add_action( "go_list_prev_all",    [](){ AppWindow::p->go_to_prev( true ); } );
+    Lifeograph::p->add_action( "go_list_next_all",    [](){ AppWindow::p->go_to_next( true ); } );
+    Lifeograph::p->add_action( "go_list_prev",        [](){ AppWindow::p->go_to_prev( false ); } );
+    Lifeograph::p->add_action( "go_list_next",        [](){ AppWindow::p->go_to_next( false ); } );
+    Lifeograph::p->add_action( "jump_to_current_entry",
             [ this ](){ show_in_list( AppWindow::p->UI_entry->get_cur_entry(), true ); } );
-    m_A_go_prev_sess_entry = Lifeograph::p->add_action(
-            "go_to_prev_sess_entry",
+    Lifeograph::p->add_action( "go_to_prev_sess_entry",
             [ this ](){ show_prev_session_elem(); } );
 
+    // ENTRY CREATION ACTIONS
+    // Lifeograph::p->add_action_with_parameter( "entry_duplicate",
     Lifeograph::p->add_action( "entry_duplicate",
             [ this ]()
             {
-                if( Diary::d->is_in_edit_mode() )
-                {
-                    auto p2entry { AppWindow::p->UI_entry->get_cur_entry() };
-                    update_for_added_entry( Diary::d->duplicate_entry( p2entry ) );
-                }
+                if( !Diary::d->is_in_edit_mode() ) return;
+                update_for_added_entry( Diary::d->duplicate_entry( get_entry_for_action() ) );
+            } );
+
+    Lifeograph::p->add_action( "entry_add_child",
+            [ this ]()
+            {
+                if( !Diary::d->is_in_edit_mode() ) return;
+
+                Entry* p2entry { get_entry_for_action() };
+                p2entry->set_expanded( true );
+                update_for_added_entry( Diary::d->create_entry( p2entry,
+                                                                true,
+                                                                Date::get_today(),
+                                                                "",
+                                                                VT::ETS::INHERIT::I ) );
+            } );
+    Lifeograph::p->add_action( "entry_add_sibling",
+            [ this ]()
+            {
+                if( !Diary::d->is_in_edit_mode() ) return;
+                update_for_added_entry( Diary::d->create_entry( get_entry_for_action(),
+                                                                false,
+                                                                Date::get_today(),
+                                                                "",
+                                                                VT::ETS::INHERIT::I ) );
             } );
-    // duplicate action is in "app" group to allow shortcut assignment
 
     // ENTRY ACTIONS
     m_AG_entry = Gio::SimpleActionGroup::create();
@@ -256,26 +261,7 @@
 
     m_AG_entry->add_action( "add" )
                     ->set_enabled( false );
-    m_AG_entry->add_action( "add_parent",   [ this ](){ add_parent_to_entries(); } );
-    m_AG_entry->add_action( "add_child",
-            [ this ]()
-            {
-                m_CPo_entry.m_p2entry->set_expanded( true );
-                update_for_added_entry( Diary::d->create_entry( m_CPo_entry.m_p2entry,
-                                                                true,
-                                                                Date::get_today(),
-                                                                "",
-                                                                VT::ETS::INHERIT::I ) );
-            } );
-    m_AG_entry->add_action( "add_sibling",
-            [ this ]()
-            {
-                update_for_added_entry( Diary::d->create_entry( m_CPo_entry.m_p2entry,
-                                                                false,
-                                                                Date::get_today(),
-                                                                "",
-                                                                VT::ETS::INHERIT::I ) );
-            } );
+    m_AG_entry->add_action( "add_parent", [ this ](){ add_parent_to_entries(); } );
 
     m_AG_entry->add_action( "merge", [ this ](){ merge_entries(); } )
                     ->set_enabled( false );
@@ -321,15 +307,21 @@
 
     AppWindow::p->insert_action_group( "entry", m_AG_entry );
 
-    Lifeograph::p->set_accel_for_action( "app.enable_editing",              "F2" );
-    Lifeograph::p->set_accel_for_action( "app.go_to_today",                 "<Ctrl>T" );
-    Lifeograph::p->set_accel_for_action( "app.smart_add",                   "F12" );
-    Lifeograph::p->set_accel_for_action( "app.add_entry_todo",              "<Ctrl>D" );
-    Lifeograph::p->set_accel_for_action( "app.go_list_prev",                "<Ctrl>Page_Up" );
-    Lifeograph::p->set_accel_for_action( "app.go_list_next",                "<Ctrl>Page_Down" );
-    Lifeograph::p->set_accel_for_action( "app.jump_to_current_entry",       "<Ctrl>J" );
-    Lifeograph::p->set_accel_for_action( "app.go_to_prev_sess_entry",       "<Ctrl>O" );
-    Lifeograph::p->set_accel_for_action( "app.entry_duplicate",             "<Ctrl><Shift>R" );
+    Lifeograph::p->set_accel_for_action( "app.enable_editing",          "F2" );
+    Lifeograph::p->set_accel_for_action( "app.go_to_today",             "<Ctrl>T" );
+    Lifeograph::p->set_accel_for_action( "app.smart_add",               "F12" );
+    Lifeograph::p->set_accel_for_action( "app.add_entry_todo",          "<Ctrl>D" );
+    // all goes to next prev/next entry in the diary even if it is filtered out of the list atm:
+    Lifeograph::p->set_accel_for_action( "app.go_list_prev_all",        "<Ctrl>Page_Up" );
+    Lifeograph::p->set_accel_for_action( "app.go_list_next_all",        "<Ctrl>Page_Down" );
+    Lifeograph::p->set_accel_for_action( "app.go_list_prev",            "<Ctrl><Shift>Page_Up" );
+    Lifeograph::p->set_accel_for_action( "app.go_list_next",            "<Ctrl><Shift>Page_Down" );
+    Lifeograph::p->set_accel_for_action( "app.jump_to_current_entry",   "<Ctrl>J" );
+    Lifeograph::p->set_accel_for_action( "app.go_to_prev_sess_entry",   "<Ctrl>O" );
+    // Lifeograph::p->set_accel_for_action( "app.entry_duplicate::KB",     "<Ctrl><Shift>R" );
+    Lifeograph::p->set_accel_for_action( "app.entry_duplicate",         "<Ctrl><Shift>R" );
+    Lifeograph::p->set_accel_for_action( "app.entry_add_child",         "<Ctrl><Shift>C" );
+    Lifeograph::p->set_accel_for_action( "app.entry_add_sibling",       "<Ctrl><Shift>S" );
 }
 UIDiary::~UIDiary()
 {
@@ -398,7 +390,7 @@
     // store the current scroll position
     // Gtk::TreePath path_bgn, path_end;
     // m_TV_entries->get_visible_range( path_bgn, path_end );
-    // TODO: an equal of this is not available in Gtk4 now.
+    // TODO: 4.0: an equal of this is not available in Gtk4 now.
     // see: https://gitlab.gnome.org/GNOME/gtk/-/issues/4688
 
     // entries:
@@ -665,13 +657,25 @@
 }
 
 Entry*
+UIDiary::get_entry_for_action()
+{
+    // const Ustring source { Glib::VariantBase::cast_dynamic< VStr >( value ).get() };
+    // PRINT_DEBUG( "parameter:", source );
+    // return ( source == "MENU" ? m_CPo_entry.m_p2entry
+    return( AppWindow::p->UI_entry->get_textview()->has_focus() ?
+            AppWindow::p->UI_entry->get_cur_entry() :
+            m_CPo_entry.m_p2entry );
+}
+
+Entry*
 UIDiary::add_entry_dated( bool F_milestone )
 {
     if( ! Diary::d->is_in_edit_mode() ) return nullptr;
 
-    return update_for_added_entry( Diary::d->create_entry_dated( AppWindow::p->UI_entry->get_cur_entry()->get_parent(),
-                                                              AppWindow::p->UI_extra->get_selected_date(),
-                                                              F_milestone ) );
+    return update_for_added_entry(
+            Diary::d->create_entry_dated( AppWindow::p->UI_entry->get_cur_entry()->get_parent(),
+                                          AppWindow::p->UI_extra->get_selected_date(),
+                                          F_milestone ) );
 
 }
 Entry*
@@ -707,24 +711,16 @@
 void
 UIDiary::add_parent_to_entries()
 {
-    auto&&    entries       { get_selected_entries() };
-
+    auto&&  entries       { get_selected_entries() };
     if( entries.empty() ) return;
+    // apparently we can trust that the selected entries are ordered properly:
+    Entry*  p2entry_first { *entries.begin() };
 
-    Entry*    p2entry_first { *entries.begin() };
-
-    // detect the first one (can we trust that the selected entries are ordered properly?)
-    for( Entry* e : entries )
-    {
-        if( e->get_list_order() < p2entry_first->get_list_order() )
-            p2entry_first = e;
-    }
-
-    auto entry_parent { Diary::d->create_entry( p2entry_first->get_prev_or_up(),
-                                                !p2entry_first->get_prev(),
-                                                Date::get_today(),
-                                                "",
-                                                VT::ETS::INHERIT::I ) };
+    auto    entry_parent  { Diary::d->create_entry( p2entry_first->get_prev_or_up(),
+                                                    !p2entry_first->get_prev(),
+                                                    Date::get_today(),
+                                                    "",
+                                                    VT::ETS::INHERIT::I ) };
 
     Diary::d->move_entries( &entries, entry_parent, DropPosition::INTO );
 
diff -Nru lifeograph-3.0.1/src/ui_diary.hpp lifeograph-3.0.3/src/ui_diary.hpp
--- lifeograph-3.0.1/src/ui_diary.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/ui_diary.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -47,7 +47,7 @@
         void                    handle_login();
         void                    handle_diary_ready()
         {
-            if( Diary::d->is_old() )  // TODO: fix to v3000 when ready
+            if( Diary::d->m_read_version < 3000 )
                 update_all_entries_filter_status();
             update_entry_list();
         }
@@ -86,6 +86,7 @@
         void                    update_startup_elem();
 
         Entry*                  update_for_added_entry( Entry* );
+        Entry*                  get_entry_for_action();
         Entry*                  add_entry_dated( bool );
         Entry*                  add_entry_free();
         void                    merge_entries();
@@ -158,16 +159,6 @@
         R2Action                m_A_change_password;
         R2Action                m_A_empty_trash;
         R2Action                m_A_update_date_seps;
-        R2Action                m_A_add_entry_dated;
-        R2Action                m_A_add_milestone;
-        R2Action                m_A_add_entry_todo;
-        R2Action                m_A_add_entry_free;
-        R2Action                m_A_go_today;
-        R2Action                m_A_smart_add;
-        R2Action                m_A_go_list_prev;
-        R2Action                m_A_go_list_next;
-        R2Action                m_A_jump_to_entry;
-        R2Action                m_A_go_prev_sess_entry;
 
         // DIALOGS
         DialogSync*             m_D_sync   { nullptr };
diff -Nru lifeograph-3.0.1/src/ui_extra.cpp lifeograph-3.0.3/src/ui_extra.cpp
--- lifeograph-3.0.1/src/ui_extra.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/ui_extra.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -126,9 +126,12 @@
     // CHART VIEW
     m_W_chart->set_helper_widgets( MB_chart_properties, Sc_chart_zoom );
 
+    ChartElem::GValue value_chart;
+    value_chart.init( value_chart.value_type() );
+    // value_chart.set( nullptr );
+    m_WP_chart->set_icon_drag_source( Gdk::ContentProvider::create( value_chart ) );
     m_WP_chart->set_show_add_button( false );
-    m_WP_chart->set_icon_drag_source(
-            Gdk::ContentProvider::create( MIME_THEME, Glib::Bytes::create( "", 0 ) ) );
+
     m_B_chart_new->signal_clicked().connect(
             [ this ]()
             { add_new_chart( _( "New Chart" ), ChartElem::DEFINITION_DEFAULT, true ); } );
@@ -149,7 +152,12 @@
     // TABLE VIEW
     m_W_table->set_helper_widgets( MB_table_tools );
 
+    TableElem::GValue value_table;
+    value_table.init( value_table.value_type() );
+    // value_table.set( nullptr );
+    m_WP_table->set_icon_drag_source( Gdk::ContentProvider::create( value_table ) );
     m_WP_table->set_show_add_button( false );
+
     m_B_table_new->signal_clicked().connect(
             [ this ]()
             { add_new_table( STR0/SI::NEW_TABLE, TableElem::DEFINITION_DEFAULT, true ); } );
diff -Nru lifeograph-3.0.1/src/ui_login.cpp lifeograph-3.0.3/src/ui_login.cpp
--- lifeograph-3.0.1/src/ui_login.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/ui_login.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -139,7 +139,7 @@
 
     // CONTROLLERS
     auto drop_target_file = Gtk::DropTarget::create( G_TYPE_FILE, Gdk::DragAction::COPY );
-    // TODO: check the file type:
+    // TODO: 3.1: check the file type:
     // m_drop_target->signal_accept().connect(
     //         sigc::mem_fun( *this, UILogin::handle_drop_file_accept ), true );
     drop_target_file->signal_drop().connect(
@@ -378,7 +378,7 @@
 
     // FORCE ACCESS TIME UPDATE
     // for performance reasons atime update policy on linux is usually once per day. see: relatime
-    // TODO: 3.0: reimplement for uris or get rid of it
+    // TODO: 3.1: reimplement for uris or get rid of it
 // #ifndef _WIN32
 //     struct stat fst;    // file date stat
 //     struct utimbuf utb;
@@ -456,7 +456,8 @@
             AppWindow::p->login();
             break;
         case WRONG_PASSWORD:
-            open_selected_diary( 0, false );
+            DialogPassword::handle_wrong_password();
+            Diary::d->read_header(); // make it ready for read_body() again
             break;
         case CORRUPT_FILE:
             AppWindow::p->show_info( _( STRING::CORRUPT_DIARY ) );
@@ -609,10 +610,6 @@
         auto gfile = ( GFile* ) g_value_get_object( value.gobj() );
         uri = g_file_get_uri( gfile );
         file = Gio::File::create_for_uri( uri );
-        // TODO: there might be a more direct way of doing this like below:
-        // Glib::Value< Glib::RefPtr< Gio::File > > value_file;
-        // value_file.init( value.gobj() );
-        // file = value_file.get();
     }
     // else if( G_VALUE_HOLDS( value.gobj(), Glib::Value< String >::value_type() ) )
     // {
diff -Nru lifeograph-3.0.1/src/widgets/po_entry.cpp lifeograph-3.0.3/src/widgets/po_entry.cpp
--- lifeograph-3.0.1/src/widgets/po_entry.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/po_entry.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -68,6 +68,8 @@
         throw LIFEO::Error( "Failed to create the entry popover" );
     }
 
+    m_CB_unit->set_name( "noncustom" );
+
     if( m_F_list_mode )
         Bx_comment_style->hide();
 
diff -Nru lifeograph-3.0.1/src/widgets/po_entry.hpp lifeograph-3.0.3/src/widgets/po_entry.hpp
--- lifeograph-3.0.1/src/widgets/po_entry.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/po_entry.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -52,7 +52,7 @@
         void                        update_size()
         {
             if( m_F_update_size > 0 && m_F_update_size < 3 )
-            // TODO: this is a very hackish solution to ensure the popover is sized properly
+            // TODO: 3.1: this is a very hackish solution to ensure the popover is sized properly
             {
                 m_Po_entry->present();
                 PRINT_DEBUG( "po size updated" );
diff -Nru lifeograph-3.0.1/src/widgets/widget_chart.cpp lifeograph-3.0.3/src/widgets/widget_chart.cpp
--- lifeograph-3.0.1/src/widgets/widget_chart.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_chart.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -399,7 +399,8 @@
 
     m_CB_col_filter->append( std::to_string( ChartData::COLUMN_NONE ),
                              STR::compose( "<", _( "None" ), ">" ) );
-    // TODO: splitting functionality for v3.1: m_CB_col_filter_v->append( STR::compose( "<", _( "Split" ), ">" ) );
+    // TODO: 3.1: splitting functionality:
+    // m_CB_col_filter_v->append( STR::compose( "<", _( "Split" ), ">" ) );
 
     // COLUMN VALUES
     for( const auto col : m_data.m_td->m_columns )
@@ -534,7 +535,7 @@
     else if( hovered_step != m_hovered_step )
     {
         m_hovered_step = hovered_step;
-        refresh();  // TODO: limit to tooltip area only
+        refresh();  // TODO: 3.1: limit to tooltip area only
     }
 
     m_F_widget_hovered = true;
diff -Nru lifeograph-3.0.1/src/widgets/widget_datepicker.cpp lifeograph-3.0.3/src/widgets/widget_datepicker.cpp
--- lifeograph-3.0.1/src/widgets/widget_datepicker.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_datepicker.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -157,7 +157,7 @@
 
 Gdk::DragAction
 WidgetDatePicker::handle_drop_motion( double x, double y )
-// TODO: gtkmm4: this function is probably no longer needed
+// TODO: 3.1: gtkmm4: this function is probably no longer needed
 {
     // if( Lifeograph::get_dragged_elem() != nullptr )
     //     if( Lifeograph::get_dragged_elem()->get_type() >= DiaryElement::ET_ENTRY )
diff -Nru lifeograph-3.0.1/src/widgets/widget_entrylist.cpp lifeograph-3.0.3/src/widgets/widget_entrylist.cpp
--- lifeograph-3.0.1/src/widgets/widget_entrylist.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_entrylist.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -80,7 +80,7 @@
     auto label    = Gtk::make_managed< Gtk::Label >();
     auto I_fav    = Gtk::make_managed< Gtk::Image >( Lifeograph::icons->favorite_16 );
 
-    // TODO: we may add this as an option on v3.1:
+    // TODO: 3.1: we may add this as an option on v3.1:
     // Pango::AttrList attrs;
     // auto&&          attrscale{ Pango::Attribute::create_attr_scale( 0.9 ) };
     // attrs.insert( attrscale );
@@ -113,7 +113,7 @@
     expander->set_list_row( row );
     // expander->set_indent_for_icon( true ); // this is the default it seems
 
-    gentry->m_p2entry->set_list_order( row->get_position() );
+    m_map_rows[ gentry->m_p2entry->get_id() ] = row;
 
     // note that when an entry is collapsed, its sub-entries are also collapsed automatically
     if( gentry->m_p2entry->get_child_1st() )
@@ -152,16 +152,17 @@
         sigc::bind( sigc::mem_fun( *this, &WidgetEntryList::handle_drag_prepare ),
                     drag_source, gentry ),
         false );
-    // drag_source->signal_drag_end().connect(
-    //     sigc::mem_fun( *this, &WidgetEntryList::handle_drag_end ), false );
+    drag_source->signal_drag_end().connect(
+            [ this ]( const Glib::RefPtr< Gdk::Drag >&, bool )
+            {
+                m_id_dragged_entry = DEID_UNSET;
+                m_drop_position = DropPosition::NONE;
+                queue_draw();
+            } );
     expander->add_controller( drag_source );
 
     auto drop_target { Gtk::DropTarget::create( G_TYPE_INVALID,
-#ifdef _WIN32
-                                                Gdk::DragAction::COPY ) };
-#else
                                                 Gdk::DragAction::COPY|Gdk::DragAction::MOVE ) };
-#endif // _WIN32
 
     drop_target->set_gtypes( { Glib::Value< Ustring >::value_type(),
                                Theme::GValue::value_type(),
@@ -169,18 +170,8 @@
                                EntrySelection::GValue::value_type() } );
     drop_target->signal_motion().connect(
             sigc::bind( sigc::mem_fun( *this, &WidgetEntryList::handle_drop_motion ),
-                        drop_target, expander ),
+                        drop_target, expander, gentry->m_p2entry ),
             false );
-    drop_target->signal_enter().connect(
-            [ this ]( double, double )
-            {
-                m_drop_coord_x = -1.0;
-                m_drop_position = DropPosition::NONE;
-                return Gdk::DragAction::COPY;
-            },
-            false );
-    // drop_target->signal_leave().connect(
-    //         [ this ]() { m_drop_coord_x = -1.0; m_drop_position = DropPosition::NONE; } );
     drop_target->signal_drop().connect(
             sigc::bind( sigc::mem_fun( *this, &WidgetEntryList::handle_drop ),
                         gentry->m_p2entry ),
@@ -195,7 +186,11 @@
     auto gentry { std::dynamic_pointer_cast< GObjectEntry >( row->get_item() ) };
 
     if( gentry )
+    {
         gentry->m_connection_expand.disconnect();
+        m_map_rows.erase( gentry->m_p2entry->get_id() );
+        PRINT_DEBUG( "handle_UNbind:", gentry->m_p2entry->get_name() );
+    }
 }
 
 void
@@ -259,8 +254,18 @@
         }
     }
 
-    m_selection_model->signal_selection_changed().connect(
-            sigc::mem_fun( *this, &WidgetEntryList::handle_selection_changed ) );
+    // the following is the standard way but does not fire when the active entry...
+    // ...is a child of a collapsed parent and the parent is selected:
+    // m_selection_model->signal_selection_changed().connect(
+    //         sigc::mem_fun( *this, &WidgetEntryList::handle_selection_changed
+    //         ) );
+
+    auto gesture_click { Gtk::GestureClick::create() };
+    gesture_click->set_button( 1 );
+    gesture_click->signal_released().connect(
+                [ this ]( int, double, double ) { handle_selection_changed(); } );
+
+    add_controller( gesture_click );
 
     set_model( m_selection_model );
 
@@ -273,21 +278,27 @@
     m_selected_entries.clear();
 
     Lifeograph::START_INTERNAL_OPERATIONS();
+
     if( p2entry )
     {
         p2entry = p2entry->get_first_exposed_upwards();
-        get_model()->select_item( p2entry->get_list_order(), true );
-        m_selected_entries.insert( p2entry );
+        const auto order { get_entry_order( p2entry ) };
+        if( order >= 0 )
+        {
+            get_model()->select_item( order, true );
+            m_selected_entries.insert( p2entry );
+        }
     }
     else
         get_model()->unselect_all();
+
     Lifeograph::FINISH_INTERNAL_OPERATIONS();
 }
 
 void
 WidgetEntryList::scroll_to_entry( const Entry* p2entry )
 {
-    const int order { p2entry->get_list_order() };
+    const auto order { get_entry_order( p2entry ) };
 
     Lifeograph::START_INTERNAL_OPERATIONS();
     scroll_to( order < 6 ? 0 : ( order - 5 ), Gtk::ListScrollFlags::NONE, {} );
@@ -324,20 +335,12 @@
 }
 
 void
-WidgetEntryList::expand_entry( const Entry* p2entry ) // this function does not do much
-{
-    Lifeograph::START_INTERNAL_OPERATIONS();
-    m_TLM->get_row( p2entry->get_list_order() )->set_expanded( true );
-    Lifeograph::FINISH_INTERNAL_OPERATIONS();
-}
-
-void
 WidgetEntryList::refresh_row( const Entry* entry )
 {
     if( Lifeograph::is_internal_operations_ongoing() || !m_TLM || !entry->is_exposed_in_list() )
         return;
 
-    auto row    { m_TLM->get_row( entry->get_list_order() ) };
+    auto row    { get_entry_row( entry ) };
     if( !row ) return;
     auto gentry { std::dynamic_pointer_cast< GObjectEntry >( row->get_item() ) };
     if( !gentry ) return;
@@ -354,7 +357,7 @@
     if( Lifeograph::is_internal_operations_ongoing() || !m_TLM || !entry->is_exposed_in_list() )
         return;
 
-    auto row    { m_TLM->get_row( entry->get_list_order() ) };
+    auto row    { get_entry_row( entry ) };
     if( !row ) return;
     auto gentry { std::dynamic_pointer_cast< GObjectEntry >( row->get_item() ) };
     if( !gentry ) return;
@@ -364,45 +367,13 @@
 }
 
 void
-WidgetEntryList::go_up()
-{
-    if( m_selected_entries.empty() ) return;
-
-    Entry* entry { *m_selected_entries.begin() };
-
-    if( entry == nullptr || entry->get_list_order() < 1 )
-        return;
-
-    auto row    { m_TLM->get_row( entry->get_list_order() - 1 ) };
-    auto gentry { std::dynamic_pointer_cast< GObjectEntry >( row->get_item() ) };
-
-    if( gentry )
-        select( gentry->m_p2entry );
-}
-
-void
-WidgetEntryList::go_down()
-{
-    if( m_selected_entries.empty() ) return;
-
-    Entry* entry { *m_selected_entries.begin() };
-
-    if( entry == nullptr || entry->get_list_order() > int( m_TLM->property_n_items() - 2 ) )
-        return;
-
-    auto row    { m_TLM->get_row( entry->get_list_order() + 1 ) };
-    auto gentry { std::dynamic_pointer_cast< GObjectEntry >( row->get_item() ) };
-
-    if( gentry )
-        select( gentry->m_p2entry );
-}
-
-void
 WidgetEntryList::toggle_selected_collapsed()
 {
     if( m_selected_entries.empty() ) return;
 
-    auto row { m_TLM->get_row( ( *m_selected_entries.begin() )->get_list_order() ) };
+    Entry*  entry  { *m_selected_entries.begin() };
+    if( !entry ) return;
+    auto    row    { get_entry_row( entry ) };
     if( !row ) return;
 
     row->set_expanded( !row->get_expanded() );
@@ -421,10 +392,12 @@
 
     // if( !F_ctrl_or_shift_pressed )
 
-    auto pt { gentry->m_Bx->compute_point( *AppWindow::p, Gdk::Graphene::Point( x, y ) ) };
+    auto pt   { gentry->m_Bx->compute_point( *AppWindow::p, Gdk::Graphene::Point( x, y ) ) };
+    auto row  { get_entry_row( gentry->m_p2entry ) };
+    if( !row ) return;
 
-    if( !get_model()->is_selected( gentry->m_p2entry->get_list_order() ) )
-        get_model()->select_item( gentry->m_p2entry->get_list_order(), true );
+    if( !get_model()->is_selected( row->get_position() ) )
+        get_model()->select_item( row->get_position(), true );
 
     AppWindow::p->UI_diary->show_Po_entry( gentry->m_p2entry,
                                            Gdk::Rectangle( pt->get_x(), pt->get_y(), 1, 1 ) );
@@ -519,6 +492,7 @@
         value_multi.set( &m_selected_entries );
 
         drag_source->set_icon( lookup_default_icon( "entry_plus-16", 16 ), 0, 0 );
+        m_id_dragged_entry = DEID_MULTIPLE;
 
         return Gdk::ContentProvider::create( value_multi );
     }
@@ -530,6 +504,7 @@
 
         auto widget_paintable { Gtk::WidgetPaintable::create( *gentry->m_Bx ) };
         drag_source->set_icon( widget_paintable, 0, 0 );
+        m_id_dragged_entry = gentry->m_p2entry->get_id();
 
         return Gdk::ContentProvider::create( value_entry );
     }
@@ -544,27 +519,37 @@
 Gdk::DragAction
 WidgetEntryList::handle_drop_motion( double x, double y,
                                      Glib::RefPtr< Gtk::DropTarget >& drop_target,
-                                     Gtk::TreeExpander* expander )
+                                     Gtk::TreeExpander* expander,
+                                     Entry* target_entry )
 {
-    // if( m_line_height < 1 && m_TLM->get_n_items() > 0 )
-    // {
-    //     auto gentry
-    //     { std::dynamic_pointer_cast< GObjectEntry >( m_TLM->get_row( 0 )->get_item() ) };
-
-    //     if( gentry )
-    //         m_line_height = gentry->m_Bx->get_height();
-    // }
+    auto pt       { expander->compute_point( *this, Gdk::Graphene::Point( x, 0.0 ) ) };
+    auto pt_2     { expander->compute_point( *this, Gdk::Graphene::Point( 0.0, 0.0 ) ) };
+    auto dp_new   { DropPosition::NONE };
+    auto formats  { drop_target->get_formats() };
+    auto action   { Gdk::DragAction::COPY };
+
+    // disable dropping onto itself:
+    if( m_id_dragged_entry == target_entry->get_id() )
+    {
+        PRINT_DEBUG( "contains entry");
+        action = Gdk::DragAction::MOVE;
+        return Gdk::DragAction( 0 );
+    }
+    else if( m_id_dragged_entry == DEID_MULTIPLE )
+    {
+        PRINT_DEBUG( "contains multiple entries");
+        action = Gdk::DragAction::MOVE;
+        if( m_selected_entries.find( target_entry ) != m_selected_entries.end() )
+            return Gdk::DragAction( 0 );
+    }
+
     if( m_line_height < 1 )
         m_line_height = expander->get_height();
 
-    auto pt     { expander->compute_point( *this, Gdk::Graphene::Point( x, 0.0 ) ) };
-    auto dp_new { DropPosition::NONE };
-
-    m_drop_coord_x = pt->get_x() - x;
+    m_drop_coord_x = pt_2->get_x();
 
     // EVALUATE THE HOVERED ELEMENT
-    // TODO: disable dropping an element onto itself
-    if     ( pt->get_x() < OFFSET_DROP_INTO )
+    if     ( pt->get_x() < OFFSET_DROP_INTO || m_id_dragged_entry == DEID_UNSET )
     {
         dp_new = DropPosition::INTO;
         m_drop_coord_y = pt->get_y();
@@ -589,20 +574,8 @@
         queue_draw();
     }
 
-    // ACTION TYPE --Windows does not support DragAction::MOVE for some reason
-#ifdef _WIN32
-    return Gdk::DragAction::COPY;
-#else
-    auto value { drop_target->get_value() };
-
-    if( G_VALUE_HOLDS( value.gobj(), Glib::Value< Ustring >::value_type() ) ||
-        G_VALUE_HOLDS( value.gobj(), Theme::GValue::value_type() ) )
-    {
-        return Gdk::DragAction::COPY;
-    }
-    else
-        return Gdk::DragAction::MOVE;
-#endif // _WIN32
+    // ACTION TYPE
+    return action;
 }
 
 void
@@ -697,12 +670,12 @@
     }
 
     m_drop_position = DropPosition::NONE;
-    m_drop_coord_x = -1;
+    m_id_dragged_entry = DEID_UNSET;
     return F_ret_val;
 }
 
 void
-WidgetEntryList::handle_selection_changed( guint pos, guint n_items )
+WidgetEntryList::handle_selection_changed( /*guint pos, guint n_items*/ )
 {
     // NOTE: we did not use the pos & n_items as it turned out to be quite hard
     // to keep an uptodate list of selected items
@@ -739,7 +712,7 @@
 
     Gtk::ListView::snapshot_vfunc( snapshot );
 
-    if( m_drop_position != DropPosition::NONE || m_drop_coord_x >= 0 )
+    if( m_drop_position != DropPosition::NONE || m_id_dragged_entry != DEID_UNSET )
     {
         const int       w       { get_width() };
         const int       h       { get_height() };
@@ -755,7 +728,7 @@
             cr->fill();
         }
 
-        if( m_drop_coord_x >= 0 )
+        if( m_id_dragged_entry != DEID_UNSET ) // only true for entries
         {
             Gdk::Cairo::set_source_rgba( cr, Gdk::RGBA( "#CC3333AA" ) );
             cr->set_line_width( 2.0 );
@@ -785,7 +758,11 @@
     if( !gentry ) return;
     gentry->m_p2entry->set_expanded( row->get_expanded() );
     if( !row->get_expanded() )
-        gentry->m_p2entry->do_for_each_descendant( []( Entry* e ) { e->set_list_order( -1 ); } );
+    {
+        if( AppWindow::p->UI_entry->get_cur_entry()->is_descendant_of( gentry->m_p2entry ) )
+            Glib::signal_idle().connect_once(
+                    [ this ](){ select( AppWindow::p->UI_entry->get_cur_entry() ); } );
+    }
 
     PRINT_DEBUG( "entry [", gentry->m_p2entry->get_name(),
                  "] new expand state: ", row->get_expanded() );
diff -Nru lifeograph-3.0.1/src/widgets/widget_entrylist.hpp lifeograph-3.0.3/src/widgets/widget_entrylist.hpp
--- lifeograph-3.0.1/src/widgets/widget_entrylist.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_entrylist.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -65,6 +65,18 @@
         void                        clear_and_disable();
         void                        populate();
 
+        int                         get_entry_order( const Entry* p2entry ) const
+        {
+            auto kv_row = m_map_rows.find( p2entry->get_id() );
+            return( kv_row != m_map_rows.end() ? kv_row->second->get_position() : -1 );
+        }
+        Glib::RefPtr< Gtk::TreeListRow >
+        get_entry_row( const Entry* p2entry ) const
+        {
+            auto kv_row = m_map_rows.find( p2entry->get_id() );
+            return( kv_row != m_map_rows.end() ? kv_row->second : nullptr );
+        }
+
         void                        select( Entry* );
         int                         get_selected_count() const
         { return m_selection_model->get_selection()->get_size(); }
@@ -76,13 +88,9 @@
         }
         void                        present_entry_hard( const Entry* );
         void                        present_entry( const Entry* );
-        void                        expand_entry( const Entry* );
         void                        refresh_row( const Entry* );
         void                        refresh_row_label( const Entry* );
 
-        void                        go_up();
-        void                        go_down();
-
         // Gdk::Rectangle              get_item_rect( const Entry* ) const;
 
         void                        toggle_selected_collapsed();
@@ -109,10 +117,10 @@
         // void                        handle_drag_end( const Glib::RefPtr< Gdk::Drag >&, bool );
         Gdk::DragAction             handle_drop_motion( double, double,
                                                         Glib::RefPtr< Gtk::DropTarget >&,
-                                                        Gtk::TreeExpander* );
+                                                        Gtk::TreeExpander*, Entry* );
         bool                        handle_drop( const Glib::ValueBase&, double, double, Entry* );
 
-        void                        handle_selection_changed( guint, guint );
+        void                        handle_selection_changed(/* guint, guint */);
 
         void                        handle_expand_changed( const Glib::RefPtr< Gtk::TreeListRow >& );
 // tried to use it for filtering by tagged but did not work well:
@@ -121,7 +129,11 @@
         void                        size_allocate_vfunc( int, int, int ) override;
         void                        snapshot_vfunc( const Glib::RefPtr< Gtk::Snapshot >& ) override;
 
+        using MapRowEntries = std::map< DEID, Glib::RefPtr< Gtk::TreeListRow > >;
+
+        MapRowEntries               m_map_rows;
         EntrySelection              m_selected_entries; // multiple entry selection
+        DEID                        m_id_dragged_entry { DEID_UNSET }; // single entry case
         EntrySelection              m_cut_entries;
 
         void                        handle_setup_row( const Glib::RefPtr< Gtk::ListItem >& );
diff -Nru lifeograph-3.0.1/src/widgets/widget_entrypicker.cpp lifeograph-3.0.3/src/widgets/widget_entrypicker.cpp
--- lifeograph-3.0.1/src/widgets/widget_entrypicker.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_entrypicker.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -69,6 +69,7 @@
     s_F_offer_new = false; // blocks infinite recursion
     m_WEP_new_under = Gtk::Builder::get_widget_derived< WidgetEntryPicker >( m_builder,
                                                                              "E_new_under" );
+    m_WEP_new_under->set_name( "noncustom" );
     B_add           = m_builder->get_widget< Gtk::Button >( "B_add" );
     B_add->signal_clicked().connect( std::bind( &EntryPickerCompletion::create_new_entry, this ) );
 }
@@ -153,7 +154,7 @@
 {
     update_list( filter );
 
-    if( !m_LB->get_first_child() ) // means: if empty TODO: is this still meaningful?
+    if( !m_LB->get_first_child() ) // means: if empty
         return;
 
     m_LB->select_row( * m_LB->get_row_at_index( 0 ) );
@@ -231,12 +232,7 @@
         return false;
     else if( F_ctrl )
     {
-        if( keyval == GDK_KEY_Right && get_selected() ) // TODO: what is this doing?
-        {
-            update_list( get_selected()->get_name() );
-            return true;
-        }
-        else if( keyval == GDK_KEY_Return )
+        if( keyval == GDK_KEY_Return )
         {
             create_new_entry();
             return true;
@@ -262,6 +258,8 @@
                     m_Sg_entry_activated.emit( e );
                     popdown();
                 }
+                else // i.e. create new entry is secected
+                    m_WEP_new_under->grab_focus();
                 return true;
             }
             case GDK_KEY_Escape:
@@ -459,7 +457,7 @@
 
 Gdk::DragAction
 WidgetEntryPicker::handle_drop_motion( double x, double y )
-// TODO: gtkmm4: this function is probably no longer needed
+// TODO: 3.1: gtkmm4: this function is probably no longer needed
 {
     // if( Lifeograph::get_dragged_elem() != nullptr &&
     //     Lifeograph::get_dragged_elem()->get_type() >= DiaryElement::ET_ENTRY )
diff -Nru lifeograph-3.0.1/src/widgets/widget_filter.cpp lifeograph-3.0.3/src/widgets/widget_filter.cpp
--- lifeograph-3.0.1/src/widgets/widget_filter.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_filter.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -877,8 +877,8 @@
     add_filterer_to_menu< FiltererTitleStyleUI, FiltererTitleStyle >();
     add_filterer_to_menu< FiltererHeadingLevelUI, FiltererHeadingLevel >();
     add_filterer_to_menu< FiltererEqualsUI, FiltererEquals >();
-    // TODO: check for cyclic references: if( m_p2container == nullptr )
-        add_filterer_to_menu< FiltererChildFilterUI, FiltererChildFilter >();
+    // TODO: 3.1: check for cyclic references:
+    add_filterer_to_menu< FiltererChildFilterUI, FiltererChildFilter >();
     if( m_p2filterer->m_p2container == nullptr ) // top level
         add_filterer_to_menu< FiltererContainerUI, FiltererContainer >();
 }
diff -Nru lifeograph-3.0.1/src/widgets/widget_filter.hpp lifeograph-3.0.3/src/widgets/widget_filter.hpp
--- lifeograph-3.0.1/src/widgets/widget_filter.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_filter.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -65,7 +65,7 @@
                 append( *m_B_remove );
                 append( *m_TB_not );
 
-                // TODO: v3.1: somehow indicate that the filterer is inactive
+                // TODO: 3.1: somehow indicate that the filterer is inactive
                 // ...if it does not match the filter widget's classes
             }
         }
@@ -454,6 +454,8 @@
             {
                 TU* filterer_ui { add_filterer< TU, TB >( m_p2filterer->add< TB >() ) };
 
+                m_p2WF->emit_changed();
+
                 if( typeid( TU ) == typeid( FiltererContainerUI ) )
                 {
                     FiltererContainerUI* filterer_ctr_ui { ( FiltererContainerUI* ) filterer_ui };
@@ -479,7 +481,6 @@
     filterer_ui->m_TB_not->signal_toggled().connect(
             sigc::mem_fun( * ( FiltererUI* ) filterer_ui, &FiltererUI::toggle_not ) );
 
-    m_p2WF->emit_changed();
     return filterer_ui;
 }
 
diff -Nru lifeograph-3.0.1/src/widgets/widget_filterpicker.hpp lifeograph-3.0.3/src/widgets/widget_filterpicker.hpp
--- lifeograph-3.0.1/src/widgets/widget_filterpicker.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_filterpicker.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -111,7 +111,7 @@
         void                        clear_list();
 
         Ustring                     get_all_items_text() const
-        // TODO: we need to change the name to ALL PARAGRAPHS for paragraph-only situations
+        // TODO: 3.1: we need to change the name to ALL PARAGRAPHS for paragraph-only situations
         { return STR::compose( '<', STR0/SI::ALL_ENTRIES, '>' ); }
 
         void                        refresh_text()
diff -Nru lifeograph-3.0.1/src/widgets/widget_map.cpp lifeograph-3.0.3/src/widgets/widget_map.cpp
--- lifeograph-3.0.1/src/widgets/widget_map.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_map.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -193,8 +193,6 @@
             true );
 
     m_Po_actions->insert_action_group( "map", m_AG );
-
-    // TODO: doeasn't work:   m_W_map->set_cursor( Gdk::Cursor::create( "crosshair" ) );
 }
 
 void
diff -Nru lifeograph-3.0.1/src/widgets/widget_table.cpp lifeograph-3.0.3/src/widgets/widget_table.cpp
--- lifeograph-3.0.1/src/widgets/widget_table.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_table.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -1540,7 +1540,7 @@
 {
     if( !m_data.populate_lines( false, entry ) && !m_F_needs_update ) return;
 
-    // TODO: try to maintain the expanded statuses
+    // TODO: 3.2: try to maintain the expanded statuses
 
     update_line_c_vis();
     update_scrollbar();
diff -Nru lifeograph-3.0.1/src/widgets/widget_textview.cpp lifeograph-3.0.3/src/widgets/widget_textview.cpp
--- lifeograph-3.0.1/src/widgets/widget_textview.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_textview.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -470,13 +470,11 @@
 void
 TextviewDiary::set_theme( const Theme* theme )
 {
-    if( m_p2theme )
-        remove_css_class( m_p2theme->get_css_class_name() );
-
     // when theme == nullptr, it works in refresh mode:
     m_p2theme = theme ? theme : m_p2entry->get_theme();
 
-    add_css_class( m_p2theme->get_css_class_name() );
+    // set theme by changing the name:
+    set_name( m_p2theme->get_css_class_name() );
 
     m_tag_heading->property_foreground_rgba() = m_p2theme->color_heading;
     m_tag_subheading->property_foreground_rgba() = m_p2theme->color_subheading;
@@ -672,7 +670,7 @@
         }
 
         // INDENTATION
-        // TODO: v3.0: for empty paragraphs this breaks the indentation of the following paragraph
+        // TODO: 3.1: for empty paragraphs this breaks the indentation of the following paragraph
         switch( para->get_indent_level() )
         {
             case 1: m_r2buffer->apply_tag( m_tag_indent_1, it_bgn, it_end ); break;
@@ -917,7 +915,7 @@
     if( !get_clipboard()->set_content( content_provider_combined ) )
         print_error( "Copying to clipboard failed!" );
 
-    // TODO: implement rich text for external applications
+    // TODO: 3.2: implement rich text for external applications
 
     if( F_cut )
         m_r2buffer->erase( it_bgn, it_end );
@@ -1141,7 +1139,7 @@
             };
             break;
         default:
-            m_p2hflink_hovered = nullptr;
+            set_hovered_link( nullptr );
             break;
     }
 }
@@ -1410,6 +1408,9 @@
         int             y_hover_top{ 0 }, y_quot_top{ 0 }, y_quot_bottom{ 0 }, h_line{ 0 };
         Paragraph*      p, * p_last{ nullptr };
         auto&&          textlayout{ Pango::Layout::create( cr ) };
+        auto&&          textlayout_header{ Pango::Layout::create( cr ) };
+        Pango::AttrList attrlist;
+        auto&&          attscale { Pango::Attribute::create_attr_scale( 1.4 ) };
 
         auto draw_icon = [ & ]( R2Pixbuf& buf )
         {
@@ -1481,13 +1482,14 @@
         auto draw_number = [ & ]()
         {
             int textw, texth;
+            auto& tl { p->get_heading_level() == VT::PS_SUBHDR ? textlayout_header : textlayout };
             Gdk::Cairo::set_source_rgba( cr, m_p2theme->color_subsubheading );
-            textlayout->set_text( p->get_list_order_str() );
-            textlayout->get_pixel_size( textw, texth );
+            tl->set_text( p->get_list_order_str() );
+            tl->get_pixel_size( textw, texth );
             cr->move_to( PAGE_MARGIN + EXPANDER_WIDTH + 27 +
                          p->get_indent_level() * INDENT_WIDTH - textw,
                          y_para );
-            textlayout->show_in_cairo_context( cr );
+            tl->show_in_cairo_context( cr );
         };
         auto draw_quotation_end = [ & ]()
         {
@@ -1546,6 +1548,9 @@
         };
 
         textlayout->set_font_description( m_p2theme->font );
+        textlayout_header->set_font_description( m_p2theme->font );
+        attrlist.insert( attscale );
+        textlayout_header->set_attributes( attrlist );
 
         // PARAGRAPHS
         for( p = para_bgn; p; p = p->get_next_visible() )
diff -Nru lifeograph-3.0.1/src/widgets/widget_textview.hpp lifeograph-3.0.3/src/widgets/widget_textview.hpp
--- lifeograph-3.0.1/src/widgets/widget_textview.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_textview.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -163,10 +163,10 @@
         void                        set_hovered_link( const HiddenFormat* link )
         {
             m_hflink_internal.type = VT::HFT_UNSET;
-            m_p2hflink_hovered = link;
+            m_p2hflink_hovered = ( link ? link : &m_hflink_internal );
         }
         int                         get_hovered_link_type()
-        { return( m_p2hflink_hovered ? m_p2hflink_hovered->type : m_hflink_internal.type ); }
+        { return( m_p2hflink_hovered ? m_p2hflink_hovered->type : VT::HFT_UNSET ); }
 
         Glib::RefPtr< TextbufferDiary >
                                     m_r2buffer;
@@ -174,7 +174,7 @@
         const Glib::RefPtr< Gdk::Cursor >*
                                     m_ptr2cursor_last   { nullptr };
         HiddenFormat                m_hflink_internal   { VT::HFT_UNSET, "", 0, 0 };
-        const HiddenFormat*         m_p2hflink_hovered  { nullptr };
+        const HiddenFormat*         m_p2hflink_hovered  { &m_hflink_internal };
         std::function< void() >     m_link_hovered_go   { [](){} };
         Paragraph*                  m_para_menu         { nullptr };
         Paragraph*                  m_para_sel_bgn      { nullptr };
diff -Nru lifeograph-3.0.1/src/widgets/widget_textviewedit.cpp lifeograph-3.0.3/src/widgets/widget_textviewedit.cpp
--- lifeograph-3.0.1/src/widgets/widget_textviewedit.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_textviewedit.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -20,6 +20,7 @@
 ***********************************************************************************/
 
 
+#include "gdk/gdkkeysyms.h"
 #include "src/diaryelements/diarydata.hpp"
 #ifdef HAVE_CONFIG_H
 #include <config.h>
@@ -42,17 +43,24 @@
     auto&&      iter        { m_r2buffer->get_iter_at_mark( m_r2buffer->get_insert() ) };
     const int   offset_cur  { iter.get_offset() };
     Paragraph*  para        { m_p2entry->get_paragraph( offset_cur, true ) };
+    const int   indent_lvl  { para ? para->get_indent_level() : -1 };
+    bool        ret_value   { false };
 
-    if( para && para->get_indent_level() > 0 &&
-        offset_cur == para->get_bgn_offset_in_host() &&
+    if( para && offset_cur == para->get_bgn_offset_in_host() &&
         m_para_sel_end == nullptr ) // no selection
     {
-        if( para->is_list() )
+        if( indent_lvl == 0 && para->is_list() )
+        {
             para->clear_list_type();
-        para->unindent();
+            ret_value = true;
+        }
+        if( para->unindent() )
+            ret_value = true;
 
-        update_text_formatting( para, para );
-        return true;
+        if ( ret_value )
+            update_text_formatting( para, para );
+
+        return ret_value;
     }
 
     return false;
@@ -147,13 +155,13 @@
     PRINT_DEBUG( "TextbufferDiary::on_erase()" );
 
     const UstringSize offset_bgn { ( UstringSize ) it_bgn.get_offset() };
-    const int         pos { m_pos_cursor_in_para > 0 ? m_pos_cursor_in_para - 1 : 0 };
 
     // needs to be calculated before erase_text():
     m_F_show_Po_completion = (
             m_completion && ( m_completion->is_on_display() ||
                               m_para_sel_bgn->get_format_at( VT::HFT_TAG,
-                                                             pos, m_pos_cursor_in_para ) ) );
+                                                              it_bgn.get_line_offset() + 1,
+                                                              it_end.get_line_offset() ) ) );
 
     m_Sg_changed_before_parse.emit();
 
@@ -326,6 +334,7 @@
 
     m_p2diary->m_parser_bg.parse( para );
 
+    update_entry_name_if_needed( para );
     update_para_region_cur( pos_end_p + m_pos_para_sel_bgn + int( F_add_equal ) );
 
     AppWindow::p->UI_diary->update_entry_list();
@@ -372,6 +381,7 @@
 
     m_p2entry->update_inline_dates();
 
+    update_entry_name_if_needed( m_para_sel_bgn );
     update_para_region_cur( pos_end );
 }
 
@@ -381,7 +391,8 @@
     Gtk::TextIter it_bgn;
     Gtk::TextIter it_end;
     const auto    pos_cur_p { m_pos_cursor - m_pos_para_sel_bgn };
-    const auto    format    { m_para_sel_bgn->get_format_oneof_at( VT::HFT_F_NUMERIC, pos_cur_p ) };
+    const auto    format    { m_para_sel_bgn->get_format_oneof_at( VT::HFT_F_NUMERIC|VT::HFT_F_LINK,
+                                                                   pos_cur_p ) };
     // bool          F_date    { false };
     String        str_new;
 
@@ -391,7 +402,7 @@
 
     switch( format->type )
     {
-        // TODO: on v3.1 or later
+        // TODO: 3.1 or later:
         // case VT::HFT_TAG_VALUE:
         // {
         //     double value { double( format->var_i ) };
@@ -416,8 +427,16 @@
 
             break;
         }
+        case VT::HFT_TAG:
+        {
+            Entry* tag  { m_p2diary->get_entry_by_id( format->ref_id ) };
+            if( !tag ) return;
+            Entry* tag2 { offset > 0 ? tag->get_next_cyclic() : tag->get_prev_cyclic() };
+            if( tag2 ) replace_tag_at_cursor( tag2 );
+            return;
+        }
         case VT::HFT_TIME:
-            // TODO: on v3.1 or later
+            // TODO: 3.1 or later:
             //break;
         default:
             return;
@@ -436,6 +455,7 @@
     // if( F_date )
         m_p2entry->update_inline_dates();
 
+    update_entry_name_if_needed( m_para_sel_bgn );
     update_para_region_cur();
 }
 
@@ -455,7 +475,7 @@
 
     update_para_region( pos_bgn, pos_end, para, m_para_cursor, m_pos_cursor + 1 );
 
-    // TODO: check if everything is OK if( m_para_cursor->m_p2prev->is_visible() )
+    // TODO: 3.1: check if everything is OK if( m_para_cursor->m_p2prev->is_visible() )
 }
 
 void
@@ -526,10 +546,14 @@
 
         // erase \n char
         const auto pos_end{ para_bgn->get_end_offset_in_host() };
-        m_p2entry->erase_text( pos_end, pos_end + 1, true );
+        m_p2entry->erase_text( pos_end, pos_end + 1, false );
         ++i;
     }
 
+    update_entry_name_if_needed( para_bgn );
+    m_p2entry->set_date_edited( Date::get_now() );
+    m_p2entry->update_inline_dates();
+
     // forward to the last newly visible paragraph:
     for( para_end = para_bgn;
          para_end->m_p2next && !para_end->m_p2next->is_visible()
@@ -574,7 +598,7 @@
 TextviewDiaryEdit::move_paragraphs_down()
 {
     Paragraph* para_bgn, * para_end;
-    calculate_sel_bounding_paras( para_bgn, para_end );  // TODO: use m_para_sel_bgn ?
+    calculate_sel_bounding_paras( para_bgn, para_end );
 
     if( !para_end ) return;
 
@@ -653,7 +677,8 @@
     int           offset    { 0 };
 
     calculate_sel_bounding_paras( para_bgn, para_end );
-    para_before = para_end;
+    para_before = ( !para_end->is_expanded() && para_end->has_subs() ) ? para_end->get_sub_last()
+                                                                       : para_end;
 
     const int     n_paras   { para_end->m_order_in_host - para_bgn->m_order_in_host + 1 };
     auto          undo_edit { m_p2entry->add_undo_action( UndoableType::MODIFY_TEXT,
@@ -722,13 +747,8 @@
 }
 
 void
-TextviewDiaryEdit::do_for_each_sel_para( const FuncParagraph& process_para, bool F_recursive )
+TextviewDiaryEdit::calculate_selection_range( Paragraph*& para_bgn, Paragraph*& para_end )
 {
-    if( Lifeograph::is_internal_operations_ongoing() )
-        return;
-
-    Paragraph* para_bgn, * para_end, * para_end_process;
-
     if( m_para_menu && m_Po_paragraph->get_visible() && // only when the para po is open
         ( !m_para_sel_end || // no selection
           m_para_menu->m_order_in_host < m_para_sel_bgn->m_order_in_host ||
@@ -741,6 +761,16 @@
         para_bgn = m_para_sel_bgn;
         para_end = ( m_para_sel_end ? m_para_sel_end : para_bgn );
     }
+}
+
+void
+TextviewDiaryEdit::do_for_each_sel_para( const FuncParagraph& process_para, bool F_recursive )
+{
+    if( Lifeograph::is_internal_operations_ongoing() ) return;
+
+    Paragraph* para_bgn, * para_end, * para_end_process;
+
+    calculate_selection_range( para_bgn, para_end );
 
     if( F_recursive && para_end->is_foldable() && !para_end->is_expanded() )
         para_end_process = para_end->get_sub_last();
@@ -763,7 +793,32 @@
     }
 
     p2undo->m_n_paras_after = n_paras;
-    // TODO: rather than pointing to end, combine with save_selection()
+
+    save_selection();
+    update_para_region( pos_erase_bgn, pos_erase_end, para_bgn, para_end );
+    restore_selection();
+}
+
+void
+TextviewDiaryEdit::do_for_paras( const ListParagraphs& paras, const FuncParagraph& process_para )
+{
+    if( Lifeograph::is_internal_operations_ongoing() ) return;
+
+    Paragraph*  para_bgn      { paras.front() };
+    Paragraph*  para_end      { paras.back() };
+    const int   pos_erase_bgn { para_bgn->get_bgn_offset_in_host() };
+    const int   pos_erase_end { para_end->get_end_offset_in_host() };
+    const int   n_paras       { para_end->m_order_in_host - para_bgn->m_order_in_host + 1 };
+    auto        p2undo        { m_p2entry->add_undo_action( UndoableType::MODIFY_TEXT,
+                                                            para_bgn, n_paras,
+                                                            m_pos_cursor, m_pos_cursor ) };
+
+    m_Sg_changed_before_parse.emit();
+
+    for( Paragraph* p : paras )
+        process_para( p );
+
+    p2undo->m_n_paras_after = n_paras;
 
     save_selection();
     update_para_region( pos_erase_bgn, pos_erase_end, para_bgn, para_end );
@@ -866,7 +921,7 @@
     update_text_formatting( para_bgn, para_end );
 
     m_B_insert_link->set_visible( false );
-    update_hidden_link_options( m_pos_cursor );
+    update_hidden_link_options();
     m_Po_context->present(); // TODO: 3.1: this is a very hackish way of resizing the popover
     m_E_edit_link_uri->grab_focus();
 }
@@ -1224,6 +1279,7 @@
         if( p == m_para_sel_end ) break;
     }
 
+    update_entry_name_if_needed( m_para_sel_bgn );
     update_para_region_cur();
 
     // in some corner cases char count of the selection may change but ignore for now:
@@ -1275,7 +1331,7 @@
     CopyFile( PATH( fname_src ).c_str(), PATH( path_dest ).c_str(), true );
 #endif*/
 
-    // TODO: should we add an undo here?
+    // TODO: 3.1: add an undo here
     m_para_sel_bgn->set_uri( "rel://" + rel_folder_name + "/" + file_dest->get_basename() );
 }
 
@@ -1589,6 +1645,8 @@
     m_drop_target = Gtk::DropTarget::create( G_TYPE_INVALID, Gdk::DragAction::COPY );
     m_drop_target->set_gtypes( { Glib::Value< Ustring >::value_type(),
                                  Theme::GValue::value_type(),
+                                 ChartElem::GValue::value_type(),
+                                 TableElem::GValue::value_type(),
                                  Entry::GValue::value_type() } );
     // m_drop_target->signal_accept().connect(
     //         sigc::mem_fun( *this, &TextviewDiaryEdit::handle_drop_accept ), false );
@@ -1596,7 +1654,6 @@
             sigc::mem_fun( *this, &TextviewDiaryEdit::handle_drop_motion ), false );
     m_drop_target->signal_drop().connect(
             sigc::mem_fun( *this, &TextviewDiaryEdit::handle_drop ), true );
-    add_controller( m_drop_target );
 
     m_gesture_click->set_propagation_phase( Gtk::PropagationPhase::CAPTURE );
     m_gesture_click->signal_pressed().connect(
@@ -1697,33 +1754,33 @@
             [ this ]() { change_para_indentation( false ); } );
 
     m_RB_para_hoff->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_hoff, VT::PS_HEADER_GEN ); } );
+            [ this ]() { change_para_type( VT::PS_HEADER_GEN, m_RB_para_hoff ); } );
     m_RB_para_subh->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_subh, VT::PS_SUBHDR ); } );
+            [ this ]() { change_para_type( VT::PS_SUBHDR, m_RB_para_subh ); } );
     m_RB_para_ssbh->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_ssbh, VT::PS_SSBHDR ); } );
+            [ this ]() { change_para_type( VT::PS_SSBHDR, m_RB_para_ssbh ); } );
 
     m_RB_para_todo->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_todo, VT::PS_TODO ); } );
+            [ this ]() { change_para_type( VT::PS_TODO, m_RB_para_todo ); } );
     m_RB_para_prog->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_prog, VT::PS_PROGRS ); } );
+            [ this ]() { change_para_type( VT::PS_PROGRS, m_RB_para_prog ); } );
     m_RB_para_done->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_done, VT::PS_DONE ); } );
+            [ this ]() { change_para_type( VT::PS_DONE, m_RB_para_done ); } );
     m_RB_para_cncl->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_cncl, VT::PS_CANCLD ); } );
+            [ this ]() { change_para_type( VT::PS_CANCLD, m_RB_para_cncl ); } );
 
     m_RB_para_plai->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_plai, VT::PS_LIST_GEN ); } );
+            [ this ]() { change_para_type( VT::PS_LIST_GEN, m_RB_para_plai ); } );
     m_RB_para_bllt->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_bllt, VT::PS_BULLET ); } );
+            [ this ]() { change_para_type( VT::PS_BULLET, m_RB_para_bllt ); } );
     m_RB_para_nmbr->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_nmbr, VT::PS_NUMBER ); } );
+            [ this ]() { change_para_type( VT::PS_NUMBER, m_RB_para_nmbr ); } );
     m_RB_para_cltr->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_cltr, get_para_letter_type() ); } );
+            [ this ]() { change_para_type( get_para_letter_type(), m_RB_para_cltr ); } );
     m_RB_para_crmn->signal_toggled().connect(
-            [ this ]() { handle_para_type_changed( m_RB_para_crmn, get_para_letter_type() ); } );
+            [ this ]() { change_para_type( get_para_letter_type(), m_RB_para_crmn ); } );
     m_Sw_para_sltr->property_active().signal_changed().connect(
-            [ this ]() { handle_para_type_changed( nullptr, get_para_letter_type() ); } );
+            [ this ]() { change_para_type( get_para_letter_type() ); } );
 
     m_Sw_para_hrule->property_active().signal_changed().connect(
             [ this ]() { do_for_each_sel_para(
@@ -1779,6 +1836,11 @@
 {
     Gtk::TextView::set_editable( F_editable ); // must come first
 
+    if( F_editable )
+        add_controller( m_drop_target );
+    else
+        remove_controller( m_drop_target );
+
     m_Bx_context_edit_1->set_visible( F_editable );
     m_Bx_context_edit_2->set_visible( F_editable );
     m_B_context_extras->set_visible( F_editable );
@@ -2016,15 +2078,9 @@
 }
 
 bool
-TextviewDiaryEdit::update_hidden_link_options( const UstringSize pos )
+TextviewDiaryEdit::update_hidden_link_options()
 {
-    auto para   { m_p2entry->get_paragraph( pos, true ) };
-    m_p2hflink_hovered = m_para_sel_bgn->get_format_oneof_at(
-                                VT::HFT_F_LINK, pos - para->get_bgn_offset_in_host() );
-
-    if( !m_p2hflink_hovered ) return false;
-
-    const auto link_type { m_p2hflink_hovered->type };
+    const auto link_type { get_hovered_link_type() };
 
     // do not add another one if Bx_edit_special is already set:
     if( !m_p2Bx_edit_special && ( ( link_type & VT::HFT_F_LINK_MANUAL ) == VT::HFT_F_LINK_MANUAL ) )
@@ -2043,6 +2099,8 @@
         B_go_to                 = builder->get_widget< Gtk::Button >( "B_go_to" );
 
         m_WEP_edit_link_deid->set_diary( m_p2diary );
+        m_WEP_edit_link_deid->set_name( "noncustom" );
+        m_E_edit_link_uri->set_name( "noncustom" );
 
         if     ( link_type == VT::HFT_LINK_ID )
         {
@@ -2136,8 +2194,8 @@
     // do not add another one if Bx_edit_special is already set:
     if( !m_p2Bx_edit_special && get_hovered_link_type() == VT::HFT_TAG )
     {
-        auto&&       builder{ Lifeograph::create_gui( Lifeograph::SHAREDIR +
-                                                      "/ui/tv_diary_edit_tag.ui" ) };
+        auto&&       builder { Lifeograph::create_gui( Lifeograph::SHAREDIR +
+                                                       "/ui/tv_diary_edit_tag.ui" ) };
         Gtk::Button* B_remove, * B_go_to;
 
         m_Bx_edit_tag   = builder->get_widget< Gtk::Box >( "Bx_edit_tag" );
@@ -2150,7 +2208,7 @@
         if( e_sib_1st )
             for( const Entry* e = e_sib_1st->get_sibling_1st(); e; e = e->get_next() )
                 m_CB_edit_tag->append( std::to_string( e->get_id() ), e->get_name() );
-                // TODO: ellipsize entry name as some of them might be too long
+                // TODO: 3.1: ellipsize entry name as some of them might be too long
 
         m_CB_edit_tag->set_active_id( std::to_string( m_p2hflink_hovered->ref_id ) );
 
@@ -2180,24 +2238,55 @@
 }
 
 void
-TextviewDiaryEdit::handle_para_type_changed( Gtk::ToggleButton* RB, int ps )
+TextviewDiaryEdit::change_para_type( int ps, Gtk::ToggleButton* RB )
 {
     if( Lifeograph::is_internal_operations_ongoing() ) return;
 
     if( !RB || RB->get_active() )
     {
-        do_for_each_sel_para(
+        auto process_para =
                 [ & ]( Paragraph* para )
                 {
                     const bool was_list{ para->is_list() };
                     para->set_para_type2( ps );
-                    if( !was_list && ( ps & VT::PS_LIST_GEN ) && para->get_indent_level() == 0 )
+                    if( !was_list && ( ps & VT::PS_LIST_GEN ) && para->get_indent_level() == 0 &&
+                        para->get_heading_level() != VT::PS_SUBHDR ) // do not indent subheadings
                     {
                         para->set_indent_level( 1 );
                         //para->set_visible( true );
                     }
-                },
-                false );
+                };
+
+        if( m_r2buffer->get_has_selection() ||
+            !( ps & VT::PS_LIST_GEN ) || ( ps & VT::PS_TODO_GEN )  )
+            // do not generalize todo statuses
+            do_for_each_sel_para( process_para, false );
+        else
+        {
+            Paragraph*        para_bgn;
+            calculate_selection_range( para_bgn, para_bgn ); // as there is no range in this case
+            ListParagraphs&&  siblings            { para_bgn->get_siblings() };
+            const int         ps_bgn              { para_bgn->get_list_type() };
+            bool              F_homogeneous_block { ps_bgn && !( ps_bgn & VT::PS_TODO_GEN ) };
+            // do not generalize to siblings if they are not already non-todo list items
+
+            if( F_homogeneous_block )
+            {
+                for( Paragraph* p : siblings )
+                {
+                    if( p->get_list_type() != ps_bgn )
+                    {
+                        F_homogeneous_block = false;
+                        break;
+                    }
+                }
+            }
+
+            if( F_homogeneous_block )
+                do_for_paras( siblings, process_para );
+            else
+                do_for_paras( { para_bgn }, process_para );
+        }
 
         if( RB )
         {
@@ -2234,24 +2323,49 @@
 void
 TextviewDiaryEdit::change_para_todo_status()
 {
-    auto new_status { VT::PS_TODO };
+    PollMap< int >  poll;
+    int             next_status;
+    Paragraph*      para_bgn, * para_end;
 
-    do_for_each_sel_para(
-            [ & ]( Paragraph* p )
-            {
-                // TODO: try to find a common value
-                switch( p->get_todo_status() )
-                {
-                    case ES::NOT_TODO:    new_status = VT::PS_TODO; break;
-                    case ES::TODO:        new_status = VT::PS_PROGRS; break;
-                    case ES::PROGRESSED:  new_status = VT::PS_DONE; break;
-                    case ES::DONE:        new_status = VT::PS_CANCLD; break;
-                    default:              new_status = VT::PS_LIST_GEN; break; // means clear
-                }
-            },
-            false );
+    calculate_selection_range( para_bgn, para_end );
+
+    for( Paragraph* p = para_bgn; p; p = p->get_next() )
+    {
+        poll.increase( p->get_todo_status_ps() );
+        if( p == para_end ) break;
+    }
+
+    switch( poll.get_key_max() )
+    {
+        case VT::PS_TODO:   next_status = VT::PS_PROGRS; break;
+        case VT::PS_PROGRS: next_status = VT::PS_DONE; break;
+        case VT::PS_DONE:   next_status = VT::PS_CANCLD; break;
+        // case VT::PS_CANCLD: next_status = VT::PS_LIST_GEN; break; // no need to clear
+        default:            next_status = VT::PS_TODO; break;
+    }
+    change_para_type( next_status );
+}
+
+void
+TextviewDiaryEdit::change_para_ordered_list_type()
+{
+    PollMap< int >  poll;
+    Paragraph*      para_bgn, * para_end;
+
+    calculate_selection_range( para_bgn, para_end );
+
+    for( Paragraph* p = para_bgn; p; p = p->get_next() )
+    {
+        poll.increase( p->get_list_type() );
+        if( p == para_end ) break;
+    }
 
-    handle_para_type_changed( nullptr, new_status );
+    const int       lt { poll.get_key_max() };
+
+    if( !( lt & VT::PS_ORDERED_GEN ) || lt == VT::PS_SROMAN )
+        change_para_type( VT::PS_NUMBER );
+    else
+        change_para_type( lt + 0x10 );
 }
 
 void
@@ -2343,11 +2457,15 @@
         m_TB_superscript->set_active( toggle_format( VT::HFT_SUPERSCRIPT, true ) );
         Lifeograph::FINISH_INTERNAL_OPERATIONS();
 
+        const auto pos  { iter.get_offset() };
+        auto para       { m_p2entry->get_paragraph( pos, true ) };
+        set_hovered_link( para->get_format_oneof_at(
+                                VT::HFT_F_LINK, pos - para->get_bgn_offset_in_host() ) );
         update_image_options();
         update_chart_options();
         update_table_options();
         update_date_options();
-        F_has_link = update_hidden_link_options( iter.get_offset() );
+        F_has_link = update_hidden_link_options();
         update_tag_options();
 
         m_Bx_context_edit_1->set_visible( true );
@@ -2605,7 +2723,7 @@
                     case GDK_KEY_greater: add_link_to_sel_interactive(); return true;
                 }
                 break;
-           case int( Gdk::ModifierType::SHIFT_MASK ):
+            case int( Gdk::ModifierType::SHIFT_MASK ):
                 if( m_completion && m_completion->handle_key( keyval, false, true ) )
                     return true;
 
@@ -2614,6 +2732,13 @@
                     case GDK_KEY_greater:
                         if( handle_replace_str( ">" ) ) return true;
                         break;
+                    case GDK_KEY_space:
+                    {
+                        auto iter { m_r2buffer->get_iter_at_offset( m_pos_cursor ) };
+                        m_r2buffer->insert( iter, " " );
+                        set_cursor_position( m_pos_cursor - 1 );
+                        return true;
+                    }
                 }
                 break;
 #ifdef __APPLE__
@@ -2628,10 +2753,7 @@
                         do_for_each_sel_para(
                             []( Paragraph* p ){ p->clear_para_type(); }, true );
                         return true;
-                    case GDK_KEY_1:
-                        do_for_each_sel_para(
-                            []( Paragraph* p ){ p->change_ordered_list_type(); }, true );
-                        return true;
+                    case GDK_KEY_1:             change_para_ordered_list_type(); return true;
                     case GDK_KEY_2:             duplicate_paragraphs(); return true;
                     case GDK_KEY_C:
                     case GDK_KEY_c:             insert_comment(); return true;
@@ -2651,9 +2773,7 @@
                     case GDK_KEY_Iabovedot: // Turkish İ
                     case GDK_KEY_idotless:  // Turkish ı
                     case GDK_KEY_I:
-                    case GDK_KEY_i:
-                        change_para_indentation( true );
-                        return true;
+                    case GDK_KEY_i:             change_para_indentation( true ); return true;
                     case GDK_KEY_J:
                     case GDK_KEY_j:             join_paragraphs(); return true;
                     case GDK_KEY_L:
@@ -2677,11 +2797,11 @@
                         return true;
                     case GDK_KEY_Up:            move_paragraphs_up(); return true;
                     case GDK_KEY_Down:          move_paragraphs_down(); return true;
-                    case GDK_KEY_asterisk:
-                        handle_para_type_changed( nullptr, VT::PS_BULLET );
-                        return true;
-                    case GDK_KEY_minus:         modify_numeric_field( -1 ); return true;
-                    case GDK_KEY_plus:          modify_numeric_field( 1 ); return true;
+                    case GDK_KEY_asterisk:      change_para_type( VT::PS_BULLET ); return true;
+                    case GDK_KEY_minus:
+                    case GDK_KEY_KP_Subtract:   modify_numeric_field( -1 ); return true;
+                    case GDK_KEY_plus:
+                    case GDK_KEY_KP_Add:        modify_numeric_field( 1 ); return true;
                     default: PRINT_DEBUG( "keyval: ",  keyval ); break;
                 }
                 break;
@@ -2708,9 +2828,7 @@
                     case GDK_KEY_R:
                     case GDK_KEY_r:             insert_hrule(); return true;
                     // to support keyboard layouts where a Shift is needed for these characters:
-                    case GDK_KEY_asterisk:
-                        handle_para_type_changed( nullptr, VT::PS_BULLET );
-                        return true;
+                    case GDK_KEY_asterisk:      change_para_type( VT::PS_BULLET ); return true;
                     // case GDK_KEY_underscore: use Alt + T
                     //     handle_para_type_changed( nullptr, VT::PS_TODO );
                     //     break;
@@ -2807,7 +2925,8 @@
 
     if     ( formats->contain_gtype( Glib::Value< Ustring >::value_type() ) )
         return Gdk::DragAction::MOVE;
-    else if( formats->contain_gtype( Entry::GValue::value_type() ) )
+    // else if( formats->contain_gtype( Entry::GValue::value_type() ) ||
+    //          formats->contain_gtype( ChartElem::GValue::value_type() ) )
     {
         int             buf_x, buf_y;
         Gtk::TextIter   iter;
@@ -2851,6 +2970,21 @@
 
         AppWindow::p->UI_entry->set_theme( value_theme.get() );
     }
+    else if( G_VALUE_HOLDS( value.gobj(), ChartElem::GValue::value_type() ) )
+    {
+        // the following is not needed as there is only one source for charts:
+        // ChartElem::GValue value_chart;
+        // value_chart.init( value.gobj() );
+        // const ChartElem* chart { value_chart.get() };
+
+        insert_image2( std::to_string( Diary::d->get_chart_active()->get_id() ),
+                                       VT::PS_IMAGE_CHART );
+    }
+    else if( G_VALUE_HOLDS( value.gobj(), TableElem::GValue::value_type() ) )
+    {
+        insert_image2( std::to_string( Diary::d->get_table_active()->get_id() ),
+                                       VT::PS_IMAGE_TABLE );
+    }
     else if( G_VALUE_HOLDS( value.gobj(), Glib::Value< Ustring >::value_type() ) )
     {
         int                     buf_x, buf_y;
@@ -2863,21 +2997,23 @@
         window_to_buffer_coords( Gtk::TextWindowType::TEXT, x, y, buf_x, buf_y );
         get_iter_at_location( iter, buf_x, buf_y );
 
-        const auto  offset_cur  { iter.get_offset() };
-        Paragraph*  p2para      { m_p2entry->get_paragraph( offset_cur, true ) };
-
-        if( !p2para ) return false;
-
-        const int   size_prev   { p2para->get_size() };
+        const auto  offset_cur    { iter.get_offset() };
+        Paragraph*  p2para        { m_p2entry->get_paragraph( offset_cur, true ) };
+        const auto  pos_erase_end { p2para->get_end_offset_in_host() };
 
-        p2para->insert_text_with_spaces( iter.get_line_offset(), text, &m_p2diary->m_parser_bg );
+        // TODO: 3.2: separate with spaces if needed:
+        m_p2entry->insert_text( offset_cur, text, nullptr, true, true );
 
-        const int   ins_len     { p2para->get_size() - size_prev };
+        auto      undo_last { m_p2entry->get_undo_stack()->get_undo_cur() };
+        const int p_count   { dynamic_cast< UndoEdit* >( undo_last )->m_n_paras_after - 1 };
 
-        update_para_region( ins_len, p2para, offset_cur + ins_len );
+        update_para_region( p2para->get_bgn_offset_in_host(), pos_erase_end,
+                            p2para, p2para->get_nth_next( p_count ),
+                            text.size() );
     }
+// TODO: 3.0 check necessity on gtkmm4:
 // #ifdef _WIN32
-//     else if( drag_dest_find_target( dc ) == "text/uri-list" ) // TODO: gtkmm4
+//     else if( drag_dest_find_target( dc ) == "text/uri-list" )
 //     {
 //         int                     buf_x, buf_y;
 //         Gtk::TextIter           iter;
diff -Nru lifeograph-3.0.1/src/widgets/widget_textviewedit.hpp lifeograph-3.0.3/src/widgets/widget_textviewedit.hpp
--- lifeograph-3.0.1/src/widgets/widget_textviewedit.hpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_textviewedit.hpp	2025-06-14 22:47:00.000000000 +0000
@@ -67,9 +67,10 @@
 
         void                        update_highlight_button();
 
-        void                        handle_para_type_changed( Gtk::ToggleButton*, int );
+        void                        change_para_type( int, Gtk::ToggleButton* = nullptr );
         int                         get_para_letter_type() const;
         void                        change_para_todo_status();
+        void                        change_para_ordered_list_type();
         void                        change_para_hrule();
         void                        change_para_indentation( bool );
 
@@ -101,7 +102,7 @@
         void                        update_table_options();
         void                        update_date_options();
         void                        update_evaluated_link_label( const HiddenFormat* );
-        bool                        update_hidden_link_options( const UstringSize );
+        bool                        update_hidden_link_options();
         void                        update_tag_options();
 
         void                        remove_Po_context_special()
@@ -217,7 +218,9 @@
         void                        delete_paragraphs( Paragraph*, Paragraph* );
         void                        duplicate_paragraphs();
         void                        beautify_text( bool );
+        void                        calculate_selection_range( Paragraph*&, Paragraph*& );
         void                        do_for_each_sel_para( const FuncParagraph&, bool );
+        void                        do_for_paras( const ListParagraphs&, const FuncParagraph& );
         void                        save_selection( int = 0 );
         void                        restore_selection();
 
@@ -293,6 +296,11 @@
                                 m_para_sel_bgn, m_para_sel_end,
                                 pos == -1 ? m_pos_cursor : pos );
         }
+        void                        update_entry_name_if_needed( const Paragraph* p2para )
+        {
+            if( p2para->is_header() )
+                m_p2entry->update_name();
+        }
 
         void                        copy_image_file_to_rel();
 
diff -Nru lifeograph-3.0.1/src/widgets/widget_textviewsearch.cpp lifeograph-3.0.3/src/widgets/widget_textviewsearch.cpp
--- lifeograph-3.0.1/src/widgets/widget_textviewsearch.cpp	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/src/widgets/widget_textviewsearch.cpp	2025-06-14 22:47:00.000000000 +0000
@@ -259,9 +259,9 @@
 void
 TextviewDiarySearch::process_link_uri( const Paragraph* para_hovered, int offset )
 {
-    m_p2hflink_hovered = para_hovered->get_format_at( VT::HFT_MATCH, offset );
+    set_hovered_link( para_hovered->get_format_at( VT::HFT_MATCH, offset ) );
 
-    if( m_p2hflink_hovered )
+    if( get_hovered_link_type() != VT::HFT_UNSET )
         m_link_hovered_go = [ & ](){ handle_link_match(); };
 }
 
diff -Nru lifeograph-3.0.1/ui/chart.ui lifeograph-3.0.3/ui/chart.ui
--- lifeograph-3.0.1/ui/chart.ui	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/ui/chart.ui	2025-06-14 22:47:00.000000000 +0000
@@ -243,7 +243,7 @@
                 <property name="margin-start">5</property>
                 <property name="orientation">vertical</property>
                 <property name="spacing">5</property>
-                <!--child> TODO: v3.1+
+                <!--child> TODO: 3.1+
                   <object class="GtkBox">
                     <property name="spacing">5</property>
                     <child>
diff -Nru lifeograph-3.0.1/ui/dlg_sync.ui lifeograph-3.0.3/ui/dlg_sync.ui
--- lifeograph-3.0.1/ui/dlg_sync.ui	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/ui/dlg_sync.ui	2025-06-14 22:47:00.000000000 +0000
@@ -72,6 +72,17 @@
             </child>
           </object>
         </child>
+        <child type="end">
+          <object class="GtkButton" id="B_sync_unlock">
+            <!-- <property name="label" translatable="yes">Cancel</property> -->
+            <property name="css-classes">suggested-action</property>
+            <child>
+              <object class="GtkImage" >
+                <property name="icon-name">system-lock-screen-symbolic</property>
+              </object>
+            </child>
+          </object>
+        </child>
       </object>
     </property>
     <child>
diff -Nru lifeograph-3.0.1/ui/lifeograph.ui lifeograph-3.0.3/ui/lifeograph.ui
--- lifeograph-3.0.1/ui/lifeograph.ui	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/ui/lifeograph.ui	2025-06-14 22:47:00.000000000 +0000
@@ -889,132 +889,6 @@
     </child>
   </object>
 
-  <object class="GtkPopover" id="Po_password">
-    <property name="child">
-      <object class="GtkBox">
-        <property name="margin-start">5</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">5</property>
-        <child>
-          <object class="GtkLabel" id="L_password_msg">
-            <property name="label" translatable="yes">Please enter password</property>
-            <attributes>
-              <attribute name="scale" value="1.2"></attribute>
-            </attributes>
-          </object>
-        </child>
-        <child>
-          <object class="GtkEntry" id="E_password_current">
-            <property name="visibility">0</property>
-            <property name="activates-default">1</property>
-            <property name="width-chars">30</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkGrid" id="G_password_new">
-            <property name="hexpand">1</property>
-            <property name="margin-start">5</property>
-            <property name="row-spacing">2</property>
-            <property name="column-spacing">8</property>
-            <child>
-              <object class="GtkEntry" id="E_password_new">
-                <property name="hexpand">1</property>
-                <property name="visibility">0</property>
-                <property name="width-chars">30</property>
-                <layout>
-                  <property name="column">1</property>
-                  <property name="row">1</property>
-                </layout>
-              </object>
-            </child>
-            <child>
-              <object class="GtkEntry" id="E_password_confirm">
-                <property name="hexpand">1</property>
-                <property name="visibility">0</property>
-                <property name="activates-default">1</property>
-                <property name="width-chars">30</property>
-                <layout>
-                  <property name="column">1</property>
-                  <property name="row">2</property>
-                </layout>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel">
-                <property name="sensitive">0</property>
-                <property name="halign">start</property>
-                <property name="label" translatable="yes">Confirm</property>
-                <layout>
-                  <property name="column">0</property>
-                  <property name="row">2</property>
-                </layout>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel">
-                <property name="sensitive">0</property>
-                <property name="label" translatable="yes">New Password</property>
-                <layout>
-                  <property name="column">0</property>
-                  <property name="row">1</property>
-                </layout>
-              </object>
-            </child>
-            <child>
-              <object class="GtkBox" id="Bx_password_authenticate">
-                <property name="margin-bottom">10</property>
-                <property name="spacing">5</property>
-                <child>
-                  <object class="GtkSeparator">
-                    <property name="valign">center</property>
-                    <property name="hexpand">1</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkButton" id="B_password_authenticate">
-                    <property name="label" translatable="yes">Authenticate</property>
-                    <property name="sensitive">0</property>
-                    <property name="receives-default">1</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkSeparator">
-                    <property name="valign">center</property>
-                    <property name="hexpand">1</property>
-                  </object>
-                </child>
-                <layout>
-                  <property name="column">0</property>
-                  <property name="row">0</property>
-                  <property name="column-span">2</property>
-                </layout>
-              </object>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="GtkBox">
-            <property name="margin-top">5</property>
-            <property name="spacing">10</property>
-            <property name="homogeneous">1</property>
-            <child>
-              <object class="GtkButton" id="B_password_cancel">
-                <property name="label">Cancel</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton" id="B_password_go">
-                <property name="label">OK</property>
-                <property name="sensitive">0</property>
-                <property name="css-classes">suggested-action</property>
-              </object>
-            </child>
-          </object>
-        </child>
-      </object>
-    </property>
-  </object>
-
   <object class="GtkPopover" id="Po_sorting">
     <property name="child">
       <object class="GtkBox" id="Bx_diaries_sort_by">
diff -Nru lifeograph-3.0.1/ui/po_entry.ui lifeograph-3.0.3/ui/po_entry.ui
--- lifeograph-3.0.1/ui/po_entry.ui	2025-03-02 05:12:04.000000000 +0000
+++ lifeograph-3.0.3/ui/po_entry.ui	2025-06-14 22:47:00.000000000 +0000
@@ -14,15 +14,18 @@
           </item>
           <item>
             <attribute name="label" translatable="yes">Sibling</attribute>
-            <attribute name="action">entry.add_sibling</attribute>
+            <attribute name="action">app.entry_add_sibling</attribute>
+            <!-- <attribute name="target">MENU</attribute> -->
           </item>
           <item>
             <attribute name="label" translatable="yes">Child</attribute>
-            <attribute name="action">entry.add_child</attribute>
+            <attribute name="action">app.entry_add_child</attribute>
+            <!-- <attribute name="target">MENU</attribute> -->
           </item>
           <item>
             <attribute name="label" translatable="yes">Duplicate</attribute>
             <attribute name="action">app.entry_duplicate</attribute>
+            <!-- <attribute name="target">MENU</attribute> -->
           </item>
         </section>
       </submenu>
diff -Nru lifeograph-3.0.1/ui/po_password.ui lifeograph-3.0.3/ui/po_password.ui
--- lifeograph-3.0.1/ui/po_password.ui	1970-01-01 00:00:00.000000000 +0000
+++ lifeograph-3.0.3/ui/po_password.ui	2025-06-14 22:47:00.000000000 +0000
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Copyright (C) 2007-2025
+
+This file is part of Lifeograph.
+
+Lifeograph is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Lifeograph is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Lifeograph.  If not, see <http://www.gnu.org/licenses/>.
+
+Author: Ahmet Öztürk
+
+-->
+<interface>
+  <requires lib="gtk" version="4.0"/>
+
+  <object class="GtkPopover" id="Po_password">
+    <property name="child">
+      <object class="GtkBox">
+        <property name="margin-start">5</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">5</property>
+        <child>
+          <object class="GtkLabel" id="L_password_msg">
+            <property name="label" translatable="yes">Please enter password</property>
+            <attributes>
+              <attribute name="scale" value="1.2"></attribute>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkEntry" id="E_password_current">
+            <property name="visibility">0</property>
+            <property name="activates-default">1</property>
+            <property name="width-chars">30</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkGrid" id="G_password_new">
+            <property name="hexpand">1</property>
+            <property name="margin-start">5</property>
+            <property name="row-spacing">2</property>
+            <property name="column-spacing">8</property>
+            <child>
+              <object class="GtkEntry" id="E_password_new">
+                <property name="hexpand">1</property>
+                <property name="visibility">0</property>
+                <property name="width-chars">30</property>
+                <layout>
+                  <property name="column">1</property>
+                  <property name="row">1</property>
+                </layout>
+              </object>
+            </child>
+            <child>
+              <object class="GtkEntry" id="E_password_confirm">
+                <property name="hexpand">1</property>
+                <property name="visibility">0</property>
+                <property name="activates-default">1</property>
+                <property name="width-chars">30</property>
+                <layout>
+                  <property name="column">1</property>
+                  <property name="row">2</property>
+                </layout>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="sensitive">0</property>
+                <property name="halign">start</property>
+                <property name="label" translatable="yes">Confirm</property>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">2</property>
+                </layout>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="sensitive">0</property>
+                <property name="label" translatable="yes">New Password</property>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">1</property>
+                </layout>
+              </object>
+            </child>
+            <child>
+              <object class="GtkBox" id="Bx_password_authenticate">
+                <property name="margin-bottom">10</property>
+                <property name="spacing">5</property>
+                <child>
+                  <object class="GtkSeparator">
+                    <property name="valign">center</property>
+                    <property name="hexpand">1</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="B_password_authenticate">
+                    <property name="label" translatable="yes">Authenticate</property>
+                    <property name="sensitive">0</property>
+                    <property name="receives-default">1</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkSeparator">
+                    <property name="valign">center</property>
+                    <property name="hexpand">1</property>
+                  </object>
+                </child>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">0</property>
+                  <property name="column-span">2</property>
+                </layout>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="margin-top">5</property>
+            <property name="spacing">10</property>
+            <property name="homogeneous">1</property>
+            <child>
+              <object class="GtkButton" id="B_password_cancel">
+                <property name="label">Cancel</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="B_password_go">
+                <property name="label">OK</property>
+                <property name="sensitive">0</property>
+                <property name="css-classes">suggested-action</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </property>
+  </object>
+</interface>

--- End Message ---
--- Begin Message ---
Hi,

I'm sorry we couldn't unblock your request in time for trixie. I'm closing all open unblock requests in one go, so I'm not going into details of the particular request, but reasons are typically as follow:
- the request came after the deadline of 2025-07-30
- the request came late and we just didn't have the time to deal with it
- the request was waiting for action from the submitter (moreinfo tag)
- the request didn't appear to be in line with the freeze policy and we
  didn't have the energy to engage (sorry for that, see our FAQ [1])
- there was discussion in the unblock request but no agreement was
  reached in time for the release.

Paul

[1] https://release.debian.org/trixie/FAQ.html

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature


--- End Message ---

Reply to: