about summary refs log tree commit diff
diff options
context:
space:
mode:
authorr3pll <106473215+r3pll@users.noreply.github.com>2022-06-07 14:14:59 +0300
committerGitHub <noreply@github.com>2022-06-07 14:14:59 +0300
commit02c76729565c06cfeaa0b1029bc9768cd3b46d7d (patch)
tree3642cd2c2da187250ef4708061d81208e57740b5
parent08a3a2416e6b5357911815d49e074928dcc5d3e6 (diff)
parent89955b21d2a048037822afb60f5200f9da76c487 (diff)
downloadFurtherance-02c76729565c06cfeaa0b1029bc9768cd3b46d7d.tar.zst
Merge pull request #1 from lakoliu/main
1
-rwxr-xr-xCargo.lock2
-rwxr-xr-xCargo.toml2
-rwxr-xr-xREADME.md9
-rwxr-xr-xdata/com.lakoliu.Furtherance.appdata.xml.in8
-rwxr-xr-xmeson.build2
-rw-r--r--po/Furtherance.pot205
-rwxr-xr-xpo/de.po181
-rwxr-xr-xpo/it.po332
-rwxr-xr-xsrc/application.rs9
-rwxr-xr-xsrc/database.rs26
-rwxr-xr-xsrc/furtherance.gresource.xml9
-rw-r--r--src/gtk/report.ui170
-rwxr-xr-xsrc/gtk/window.ui4
-rwxr-xr-xsrc/meson.build1
-rwxr-xr-xsrc/ui.rs18
-rw-r--r--src/ui/report.rs469
-rwxr-xr-xsrc/ui/task_details.rs18
-rwxr-xr-xsrc/ui/tasks_group.rs3
18 files changed, 1245 insertions, 223 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d44808f..a1b6112 100755
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -211,7 +211,7 @@ dependencies = [
 
 [[package]]
 name = "furtherance"
-version = "1.2.0"
+version = "1.4.0"
 dependencies = [
  "chrono",
  "dbus",
diff --git a/Cargo.toml b/Cargo.toml
index 2956773..6adf376 100755
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "furtherance"
-version = "1.3.0"
+version = "1.4.0"
 authors = ["Ricky Kresslein <rk@lakoliu.com>"]
 edition = "2018"
 
diff --git a/README.md b/README.md
index a425831..9da0310 100755
--- a/README.md
+++ b/README.md
@@ -55,4 +55,11 @@ This project is licensed under the GNU General Public License v3.0. See the [LIC
 This project is created and maintained by [Ricky Kresslein](https://kressle.in) under [lakoliu](https://lakoliu.com). More information at [Furtherance.app](https://furtherance.app).
 
 ### Tips
-Besides helping to pay the bills, tips make me feel all warm and fuzzy inside. If you've gotten value from Furtherance, you can tip me via [Ko-fi](https://ko-fi.com/lakoliu) or [PayPal](https://www.paypal.com/donate/?hosted_button_id=TLYY8YZ424VRL). Thank you so much!
+Besides helping to pay the bills, tips make me feel all warm and fuzzy inside. If you've gotten value from Furtherance, you can tip me via: 
+* [Ko-fi](https://ko-fi.com/lakoliu) 
+* [PayPal](https://www.paypal.com/donate/?hosted_button_id=TLYY8YZ424VRL)
+* **Bitcoin**: bc1q70czd5evhsxnjcd45cj2n4s3dr6qmhvrlljjlk
+* **Ethereum**: 0x1fe9C92693eFd9D2429eE3d265e8aB453AFc4FDb
+* **Dogecoin**: DCEHKYq6EnSjYnouAUB6kVTqcAfGrXCTkU
+
+Thank you so much!
diff --git a/data/com.lakoliu.Furtherance.appdata.xml.in b/data/com.lakoliu.Furtherance.appdata.xml.in
index 479e6ac..b66b439 100755
--- a/data/com.lakoliu.Furtherance.appdata.xml.in
+++ b/data/com.lakoliu.Furtherance.appdata.xml.in
@@ -39,6 +39,14 @@
   <content_rating type="oars-1.1" />
 
   <releases>
+    <release version="1.4.0" date="2022-06-05">
+      <description>
+        <ul>
+          <li>Generate reports of task history over a specified timeframe</li>
+          <li>Bug fixes and improvements</li>
+        </ul>
+      </description>
+    </release>
     <release version="1.3.0" date="2022-05-26">
       <description>
         <ul>
diff --git a/meson.build b/meson.build
index 04b8bed..50b19d9 100755
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
 project('furtherance', 'rust',
-          version: '1.3.0',
+          version: '1.4.0',
     meson_version: '>= 0.50.0',
   default_options: [ 'warning_level=2',
                    ],
diff --git a/po/Furtherance.pot b/po/Furtherance.pot
index c36daf6..154118e 100644
--- a/po/Furtherance.pot
+++ b/po/Furtherance.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: furtherance\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-05-28 14:09+0300\n"
+"POT-Creation-Date: 2022-06-05 12:46+0300\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:6 src/application.rs:166
+#: data/com.lakoliu.Furtherance.appdata.xml.in:6 src/application.rs:173
 msgid "Track your time without being tracked."
 msgstr ""
 
@@ -29,62 +29,70 @@ msgid ""
 msgstr ""
 
 #: data/com.lakoliu.Furtherance.appdata.xml.in:45
-msgid "Ability to add tasks manually"
+msgid "Generate reports of task history over a specified timeframe"
 msgstr ""
 
 #: data/com.lakoliu.Furtherance.appdata.xml.in:46
+msgid "Bug fixes and improvements"
+msgstr ""
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:53
+msgid "Ability to add tasks manually"
+msgstr ""
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:54
 msgid "Auto-save option"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:47
+#: data/com.lakoliu.Furtherance.appdata.xml.in:55
 msgid "Task names can now be changed for an entire group"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:54
+#: data/com.lakoliu.Furtherance.appdata.xml.in:62
 msgid "Added Pomodoro-style countdown timer!"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:55
+#: data/com.lakoliu.Furtherance.appdata.xml.in:63
 msgid "Bug fixes"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:62
+#: data/com.lakoliu.Furtherance.appdata.xml.in:70
 msgid "Ability to add tags!"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:63
+#: data/com.lakoliu.Furtherance.appdata.xml.in:71
 msgid "Fixed icon positioning"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:64
+#: data/com.lakoliu.Furtherance.appdata.xml.in:72
 msgid "Changed start button color and behavior"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:65
+#: data/com.lakoliu.Furtherance.appdata.xml.in:73
 msgid "Changed color of delete buttons"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:66
+#: data/com.lakoliu.Furtherance.appdata.xml.in:74
 msgid "Dutch, Brazilian Portuguese, and French translations"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:73
+#: data/com.lakoliu.Furtherance.appdata.xml.in:81
 msgid "Furtherance has a new icon! And lots of bug fixes and improvements."
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:82
+#: data/com.lakoliu.Furtherance.appdata.xml.in:90
 msgid "First stable release of Furtherance."
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:86
+#: data/com.lakoliu.Furtherance.appdata.xml.in:94
 msgid "Track time spent on tasks"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:87
+#: data/com.lakoliu.Furtherance.appdata.xml.in:95
 msgid "Change/delete recorded times"
 msgstr ""
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:88
+#: data/com.lakoliu.Furtherance.appdata.xml.in:96
 msgid "Idle detection"
 msgstr ""
 
@@ -212,15 +220,92 @@ msgstr ""
 msgid "Autosave every X _minutes"
 msgstr ""
 
-#: src/gtk/task_details.ui:8
-msgid "Task Details"
+#: src/gtk/report.ui:8
+msgid "Report"
 msgstr ""
 
-#: src/gtk/task_details.ui:88 src/ui/task_details.rs:190 src/ui/window.rs:331
+#: src/gtk/report.ui:33
+msgid "Past week"
+msgstr ""
+
+#: src/gtk/report.ui:34
+msgid "This month"
+msgstr ""
+
+#: src/gtk/report.ui:35
+msgid "Past 30 days"
+msgstr ""
+
+#: src/gtk/report.ui:36
+msgid "Past 180 days"
+msgstr ""
+
+#: src/gtk/report.ui:37
+msgid "Past year"
+msgstr ""
+
+#: src/gtk/report.ui:38
+msgid "Date range"
+msgstr ""
+
+#: src/gtk/report.ui:52 src/gtk/task_details.ui:88 src/ui/task_details.rs:189
+#: src/ui/window.rs:331
 msgid "Start"
 msgstr ""
 
-#: src/gtk/task_details.ui:96 src/application.rs:280 src/ui/task_details.rs:192
+#: src/gtk/report.ui:58 src/gtk/report.ui:74
+msgid "MM/DD/YYYY"
+msgstr ""
+
+#: src/gtk/report.ui:68
+msgid "End"
+msgstr ""
+
+#: src/gtk/report.ui:83
+msgid "Use the format MM/DD/YYYY"
+msgstr ""
+
+#: src/gtk/report.ui:92
+msgid "Start date must be before end date"
+msgstr ""
+
+#: src/gtk/report.ui:105
+msgid "Sort by:"
+msgstr ""
+
+#: src/gtk/report.ui:110 src/gtk/task_row.ui:22 src/ui/report.rs:155
+msgid "Task"
+msgstr ""
+
+#: src/gtk/report.ui:117
+msgid "Tag"
+msgstr ""
+
+#: src/gtk/report.ui:124
+msgid "Filter by task or tags"
+msgstr ""
+
+#: src/gtk/report.ui:136
+msgid "Tasks"
+msgstr ""
+
+#: src/gtk/report.ui:137 src/ui/task_details.rs:180
+msgid "Tags"
+msgstr ""
+
+#: src/gtk/report.ui:143 src/ui/report.rs:144
+msgid "Task, Task 2"
+msgstr ""
+
+#: src/gtk/report.ui:150
+msgid "Refresh"
+msgstr ""
+
+#: src/gtk/task_details.ui:8
+msgid "Task Details"
+msgstr ""
+
+#: src/gtk/task_details.ui:96 src/application.rs:287 src/ui/task_details.rs:191
 #: src/ui/window.rs:333 src/ui/window.rs:595
 msgid "Stop"
 msgstr ""
@@ -233,10 +318,6 @@ msgstr ""
 msgid "Delete all"
 msgstr ""
 
-#: src/gtk/task_row.ui:22
-msgid "Task"
-msgstr ""
-
 #: src/gtk/task_row.ui:45
 msgid "Time"
 msgstr ""
@@ -258,95 +339,123 @@ msgid "_Preferences"
 msgstr ""
 
 #: src/gtk/window.ui:96
-msgid "_Delete history"
+msgid "_Generate Report"
 msgstr ""
 
 #: src/gtk/window.ui:100
+msgid "_Delete history"
+msgstr ""
+
+#: src/gtk/window.ui:104
 msgid "_About Furtherance"
 msgstr ""
 
-#: src/application.rs:169
+#: src/application.rs:176
 msgid "translator-credits"
 msgstr ""
 
-#: src/application.rs:185
+#: src/application.rs:192
 msgid "Delete history?"
 msgstr ""
 
-#: src/application.rs:188 src/ui/task_details.rs:572 src/ui/window.rs:317
+#: src/application.rs:195 src/ui/task_details.rs:568 src/ui/window.rs:317
 msgid "Cancel"
 msgstr ""
 
-#: src/application.rs:189 src/ui/task_details.rs:573
+#: src/application.rs:196 src/ui/task_details.rs:569
 msgid "Delete"
 msgstr ""
 
-#: src/application.rs:196
+#: src/application.rs:203
 msgid "This will delete ALL of your task history."
 msgstr ""
 
-#: src/application.rs:198
+#: src/application.rs:205
 msgid "Type DELETE in the box below then click Delete to proceed."
 msgstr ""
 
-#: src/application.rs:207
+#: src/application.rs:214
 msgid "DELETE"
 msgstr ""
 
-#: src/application.rs:257 src/ui/window.rs:559
+#: src/application.rs:264 src/ui/window.rs:559
 msgid "Discard"
 msgstr ""
 
-#: src/application.rs:258 src/application.rs:279 src/ui/window.rs:560
+#: src/application.rs:265 src/application.rs:286 src/ui/window.rs:560
 #: src/ui/window.rs:594
 msgid "Continue"
 msgstr ""
 
-#: src/application.rs:269 src/ui/window.rs:591
+#: src/application.rs:276 src/ui/window.rs:591
 msgid "Time's up!"
 msgstr ""
 
-#: src/application.rs:270
+#: src/application.rs:277
 msgid "Your Furtherance timer ended."
 msgstr ""
 
-#: src/ui/task_details.rs:172 src/ui/task_details.rs:500
-msgid "Edit Task"
+#: src/ui/report.rs:146
+msgid "tag, tag 2"
 msgstr ""
 
-#: src/ui/task_details.rs:181
-msgid "Tags"
+#: src/ui/report.rs:158
+msgid "Duration"
+msgstr ""
+
+#: src/ui/report.rs:295
+msgid "No Results"
+msgstr ""
+
+#: src/ui/report.rs:303
+msgid "All Results"
+msgstr ""
+
+#: src/ui/report.rs:445
+msgid "no tags"
+msgstr ""
+
+#: src/ui/task_details.rs:171 src/ui/task_details.rs:497
+msgid "Edit Task"
 msgstr ""
 
-#: src/ui/task_details.rs:211 src/ui/window.rs:355
+#: src/ui/task_details.rs:210 src/ui/window.rs:355
 msgid "*Use the format MMM DD YYYY HH:MM:SS"
 msgstr ""
 
-#: src/ui/task_details.rs:213 src/ui/window.rs:357
+#: src/ui/task_details.rs:212 src/ui/window.rs:357
 msgid "*Use the format MMM DD YYYY HH:MM"
 msgstr ""
 
-#: src/ui/task_details.rs:219 src/ui/window.rs:363
+#: src/ui/task_details.rs:218 src/ui/window.rs:363
 msgid "*Start time cannot be later than stop time."
 msgstr ""
 
-#: src/ui/task_details.rs:224 src/ui/window.rs:368
+#: src/ui/task_details.rs:223 src/ui/window.rs:368
 msgid "*Time cannot be in the future."
 msgstr ""
 
-#: src/ui/task_details.rs:230
+#: src/ui/task_details.rs:229
 msgid "Delete task"
 msgstr ""
 
-#: src/ui/task_details.rs:257
+#: src/ui/task_details.rs:256
 msgid "Delete task?"
 msgstr ""
 
-#: src/ui/task_details.rs:568
+#: src/ui/task_details.rs:503
+msgid "New Name #tags"
+msgstr ""
+
+#: src/ui/task_details.rs:504
+msgid "Task name cannot be empty."
+msgstr ""
+
+#: src/ui/task_details.rs:564
 msgid "Delete All?"
 msgstr ""
 
-#: src/ui/task_details.rs:570
+#: src/ui/task_details.rs:566
 msgid "This will delete all occurrences of this task on this day."
 msgstr ""
 
diff --git a/po/de.po b/po/de.po
index c9673e1..ca41160 100755
--- a/po/de.po
+++ b/po/de.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Furtherance\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-05-03 22:38+0200\n"
-"PO-Revision-Date: 2022-05-04 15:55+0200\n"
+"POT-Creation-Date: 2022-05-29 18:16+0300\n"
+"PO-Revision-Date: 2022-06-03 15:11+0200\n"
 "Last-Translator: Gabriel Brand <gabr.brand@gmail.com>\n"
 "Language-Team: \n"
 "Language: de\n"
@@ -22,7 +22,7 @@ msgstr ""
 msgid "Track your time without being tracked."
 msgstr "Erfassen Sie Ihre Zeit ohne getrackt zu werden."
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:14
+#: data/com.lakoliu.Furtherance.appdata.xml.in:13
 msgid ""
 "Furtherance is a time tracking app built for GNOME with Rust and GTK4. In "
 "addition to tracking time spent on tasks, you can edit that time, delete "
@@ -34,70 +34,69 @@ msgstr ""
 "Einstellungen ändern. Es hat sogar eine Inaktivitätserkennung."
 
 #: data/com.lakoliu.Furtherance.appdata.xml.in:45
+msgid "Ability to add tasks manually"
+msgstr "Möglichkeit, Aufgaben manuell hinzuzufügen"
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:46
+msgid "Auto-save option"
+msgstr "Option zum automatischen Speichern"
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:47
+msgid "Task names can now be changed for an entire group"
+msgstr "Aufgabennamen können jetzt für eine gesamte Gruppe geändert werden"
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:54
 msgid "Added Pomodoro-style countdown timer!"
 msgstr "Pomodoro-Timer hinzugefügt!"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:46
+#: data/com.lakoliu.Furtherance.appdata.xml.in:55
 msgid "Bug fixes"
 msgstr "Fehlerbehebungen"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:53
+#: data/com.lakoliu.Furtherance.appdata.xml.in:62
 msgid "Ability to add tags!"
 msgstr "Möglichkeit, Tags hinzuzufügen!"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:54
+#: data/com.lakoliu.Furtherance.appdata.xml.in:63
 msgid "Fixed icon positioning"
 msgstr "Symbolpositionierung korrigiert"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:55
+#: data/com.lakoliu.Furtherance.appdata.xml.in:64
 msgid "Changed start button color and behavior"
 msgstr "Veränderte Farbe und Verhalten des Start-Knopfes"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:56
+#: data/com.lakoliu.Furtherance.appdata.xml.in:65
 msgid "Changed color of delete buttons"
 msgstr "Veränderte Farbe der Löschen-Knöpfe"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:57
+#: data/com.lakoliu.Furtherance.appdata.xml.in:66
 msgid "Dutch, Brazilian Portuguese, and French translations"
 msgstr ""
 "Übersetzungen ins Niederländische, Brasilianische, Portugiesische und "
 "Französische"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:64
+#: data/com.lakoliu.Furtherance.appdata.xml.in:73
 msgid "Furtherance has a new icon! And lots of bug fixes and improvements."
 msgstr ""
 "Furtherance hat ein neues Symbol! Und viele Fehlerbehebungen und "
 "Verbesserungen."
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:73
+#: data/com.lakoliu.Furtherance.appdata.xml.in:82
 msgid "First stable release of Furtherance."
 msgstr "Erste stabile Veröffentlichung von Furtherance."
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:77
+#: data/com.lakoliu.Furtherance.appdata.xml.in:86
 msgid "Track time spent on tasks"
 msgstr "Für Aufgaben aufgewendete Zeit erfassen"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:78
+#: data/com.lakoliu.Furtherance.appdata.xml.in:87
 msgid "Change/delete recorded times"
 msgstr "Erfasste Zeiten ändern/löschen"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:79
+#: data/com.lakoliu.Furtherance.appdata.xml.in:88
 msgid "Idle detection"
 msgstr "Inaktivitätserkennung"
 
-#: data/com.lakoliu.Furtherance.desktop.in:4
-msgid "Time Tracker"
-msgstr "Zeiterfasser"
-
-#: data/com.lakoliu.Furtherance.desktop.in:5
-msgid "Track your time without being tracked"
-msgstr "Erfassen Sie Ihre Zeit ohne getrackt zu werden"
-
-#. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
-#: data/com.lakoliu.Furtherance.desktop.in:12
-msgid "timer;tracker;clock;tasks;productivity;"
-msgstr "Timer;Tracker;Uhr;Aufgaben;Produktivität;"
-
 #: src/gtk/history_box.ui:25
 msgid "Start Tracking"
 msgstr "Fangen Sie das Erfassen an"
@@ -127,7 +126,7 @@ msgstr "Erscheinungsbild"
 msgid "_Dark theme"
 msgstr "_Dunkles Thema"
 
-#: src/gtk/preferences_window.ui:28 src/ui/window.rs:364
+#: src/gtk/preferences_window.ui:28 src/ui/window.rs:556
 msgid "Idle"
 msgstr "Inaktivität"
 
@@ -211,24 +210,38 @@ msgstr "Intervall"
 msgid "Start time in minutes"
 msgstr "Startzeit in Minuten"
 
+#: src/gtk/preferences_window.ui:181
+msgid "_Autosave"
+msgstr "_Automatisches Speichern"
+
+#: src/gtk/preferences_window.ui:182
+msgid "Prevent losing tracked time due to improper shutdown"
+msgstr ""
+"Verhindern Sie, dass erfasste Zeiten durch unsachgemäßes Herunterfahren "
+"verloren gehen"
+
+#: src/gtk/preferences_window.ui:187
+msgid "Autosave every X _minutes"
+msgstr "Alle X _Minuten automatisch speichern"
+
 #: src/gtk/task_details.ui:8
 msgid "Task Details"
 msgstr "Aufgabendetails"
 
-#: src/gtk/task_details.ui:73 src/ui/task_details.rs:188
+#: src/gtk/task_details.ui:88 src/ui/task_details.rs:190 src/ui/window.rs:331
 msgid "Start"
 msgstr "Start"
 
-#: src/gtk/task_details.ui:81 src/application.rs:280 src/ui/task_details.rs:190
-#: src/ui/window.rs:403
+#: src/gtk/task_details.ui:96 src/application.rs:280 src/ui/task_details.rs:192
+#: src/ui/window.rs:333 src/ui/window.rs:595
 msgid "Stop"
 msgstr "Stopp"
 
-#: src/gtk/task_details.ui:89
+#: src/gtk/task_details.ui:104
 msgid "Total"
 msgstr "Gesamt"
 
-#: src/gtk/task_details.ui:116
+#: src/gtk/task_details.ui:131
 msgid "Delete all"
 msgstr "Alle löschen"
 
@@ -241,26 +254,29 @@ msgid "Time"
 msgstr "Zeit"
 
 #: src/gtk/window.ui:20
+msgid "Add Task"
+msgstr "Aufgabe hinzufügen"
+
+#: src/gtk/window.ui:26
 msgid "Main Menu"
 msgstr "Hauptmenü"
 
-#: src/gtk/window.ui:58
-msgid "Task Name #Tags"
+#: src/gtk/window.ui:64
+msgid "Task Name #tags"
 msgstr "Aufgabenname #Tags"
 
-#: src/gtk/window.ui:86
+#: src/gtk/window.ui:92
 msgid "_Preferences"
 msgstr "_Einstellungen"
 
-#: src/gtk/window.ui:90
+#: src/gtk/window.ui:96
 msgid "_Delete history"
 msgstr "_Chronik löschen"
 
-#: src/gtk/window.ui:94
+#: src/gtk/window.ui:100
 msgid "_About Furtherance"
 msgstr "_Info zu Furtherance"
 
-#. TRANSLATORS: 'Name <email@domain.com>' or 'Name https://website.example'
 #: src/application.rs:169
 msgid "translator-credits"
 msgstr "Gabriel Brand <gabr.brand@gmail.com>"
@@ -269,11 +285,11 @@ msgstr "Gabriel Brand <gabr.brand@gmail.com>"
 msgid "Delete history?"
 msgstr "Chronik löschen?"
 
-#: src/application.rs:188 src/ui/task_details.rs:504
+#: src/application.rs:188 src/ui/task_details.rs:572 src/ui/window.rs:317
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: src/application.rs:189 src/ui/task_details.rs:505
+#: src/application.rs:189 src/ui/task_details.rs:573
 msgid "Delete"
 msgstr "Löschen"
 
@@ -287,21 +303,20 @@ msgstr ""
 "Geben Sie LÖSCHEN in das Feld unten ein und klicken Sie dann auf Löschen, um "
 "fortzufahren."
 
-#. TRANSLATORS: This must match the translation for "DELETE" in the other string
 #: src/application.rs:207
 msgid "DELETE"
 msgstr "LÖSCHEN"
 
-#: src/application.rs:257 src/ui/window.rs:367
+#: src/application.rs:257 src/ui/window.rs:559
 msgid "Discard"
 msgstr "Verwerfen"
 
-#: src/application.rs:258 src/application.rs:279 src/ui/window.rs:368
-#: src/ui/window.rs:402
+#: src/application.rs:258 src/application.rs:279 src/ui/window.rs:560
+#: src/ui/window.rs:594
 msgid "Continue"
 msgstr "Fortsetzen"
 
-#: src/application.rs:269 src/ui/window.rs:399
+#: src/application.rs:269 src/ui/window.rs:591
 msgid "Time's up!"
 msgstr "Zeit ist vorbei!"
 
@@ -309,43 +324,51 @@ msgstr "Zeit ist vorbei!"
 msgid "Your Furtherance timer ended."
 msgstr "Ihr Furtherance-Timer ist abgelaufen."
 
-#: src/ui/task_details.rs:170
+#: src/ui/task_details.rs:172 src/ui/task_details.rs:500
 msgid "Edit Task"
 msgstr "Aufgabe bearbeiten"
 
-#: src/ui/task_details.rs:179
+#: src/ui/task_details.rs:181
 msgid "Tags"
 msgstr "Tags"
 
-#: src/ui/task_details.rs:209
+#: src/ui/task_details.rs:211 src/ui/window.rs:355
 msgid "*Use the format MMM DD YYYY HH:MM:SS"
 msgstr "*Nutzen Sie das Format MMM DD YYYY HH:MM:SS"
 
-#: src/ui/task_details.rs:211
+#: src/ui/task_details.rs:213 src/ui/window.rs:357
 msgid "*Use the format MMM DD YYYY HH:MM"
 msgstr "*Nutzen Sie das Format MMM DD YYYY HH:MM"
 
-#: src/ui/task_details.rs:217
+#: src/ui/task_details.rs:219 src/ui/window.rs:363
 msgid "*Start time cannot be later than stop time."
 msgstr "*Startzeit darf nicht nach der Endzeit liegen."
 
-#: src/ui/task_details.rs:222
+#: src/ui/task_details.rs:224 src/ui/window.rs:368
 msgid "*Time cannot be in the future."
 msgstr "*Zeit darf nicht in der Zukunft liegen."
 
-#: src/ui/task_details.rs:228
+#: src/ui/task_details.rs:230
 msgid "Delete task"
 msgstr "Aufgabe löschen"
 
-#: src/ui/task_details.rs:255
+#: src/ui/task_details.rs:257
 msgid "Delete task?"
 msgstr "Aufgabe löschen?"
 
-#: src/ui/task_details.rs:500
+#: src/ui/task_details.rs:506
+msgid "New Name #tags"
+msgstr "Neuer Name #Tags"
+
+#: src/ui/task_details.rs:507
+msgid "Task name cannot be empty."
+msgstr "Aufgabenname darf nicht leer sein."
+
+#: src/ui/task_details.rs:568
 msgid "Delete All?"
 msgstr "Alle löschen?"
 
-#: src/ui/task_details.rs:502
+#: src/ui/task_details.rs:570
 msgid "This will delete all occurrences of this task on this day."
 msgstr "Dadurch werden alle Einträge dieser Aufgabe an diesem Tag gelöscht."
 
@@ -357,11 +380,31 @@ msgstr "Heute"
 msgid "Yesterday"
 msgstr "Gestern"
 
-#: src/ui/window.rs:355
+#: src/ui/window.rs:313
+msgid "New Task"
+msgstr "Neue Aufgabe"
+
+#: src/ui/window.rs:318
+msgid "Add"
+msgstr "Hinzufügen"
+
+#: src/ui/window.rs:324
+msgid "Task Name"
+msgstr "Aufgabenname"
+
+#: src/ui/window.rs:326
+msgid "tags"
+msgstr "Tags"
+
+#: src/ui/window.rs:373
+msgid "*Task name cannot be blank."
+msgstr "*Aufgabenname darf nicht leer sein."
+
+#: src/ui/window.rs:547
 msgid "You have been idle for "
 msgstr "Sie waren inaktiv für "
 
-#: src/ui/window.rs:356
+#: src/ui/window.rs:548
 msgid ""
 "\n"
 "Would you like to discard that time, or continue the clock?"
@@ -369,12 +412,28 @@ msgstr ""
 "\n"
 "Möchten Sie diese Zeit verwerfen oder die Uhr fortsetzen?"
 
-#: src/ui/window.rs:458
+#: src/ui/window.rs:689
+msgid "Autosave Restored"
+msgstr "Automatische Speicherung wiederhergestellt"
+
+#: src/ui/window.rs:692
+msgid "Furtherance shut down improperly. An autosave was restored."
+msgstr ""
+"Furtherance wurde nicht ordnungsgemäß heruntergefahren. Eine automatische "
+"Speicherung wurde wiederhergestellt."
+
+#: src/ui/window.rs:751
 msgid "Stop the timer to duplicate a task."
 msgstr "Stoppen Sie den Timer, um eine Aufgabe zu duplizieren."
 
-#~ msgid "No Task Name"
-#~ msgstr "Kein Aufgabenname"
+#~ msgid "Time Tracker"
+#~ msgstr "Zeiterfasser"
+
+#~ msgid "Track your time without being tracked"
+#~ msgstr "Erfassen Sie Ihre Zeit ohne getrackt zu werden"
+
+#~ msgid "timer;tracker;clock;tasks;productivity;"
+#~ msgstr "Timer;Tracker;Uhr;Aufgaben;Produktivität;"
 
 #~ msgid "Enter a task name to start the timer."
 #~ msgstr "Geben Sie einen Aufgabennamen ein, um den Timer zu starten."
diff --git a/po/it.po b/po/it.po
index 82df3ed..297a51d 100755
--- a/po/it.po
+++ b/po/it.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Furtherance\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-05-03 22:38+0200\n"
-"PO-Revision-Date: 2022-05-03 22:53+0200\n"
+"POT-Creation-Date: 2022-06-05 12:46+0300\n"
+"PO-Revision-Date: 2022-06-05 18:36+0200\n"
 "Last-Translator: musiclover <musiclover382@protonmail.com>\n"
 "Language-Team: \n"
 "Language: it\n"
@@ -18,11 +18,11 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Poedit 3.0.1\n"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:6 src/application.rs:166
+#: data/com.lakoliu.Furtherance.appdata.xml.in:6 src/application.rs:173
 msgid "Track your time without being tracked."
 msgstr "Traccia il tuo tempo senza essere tracciato."
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:14
+#: data/com.lakoliu.Furtherance.appdata.xml.in:13
 msgid ""
 "Furtherance is a time tracking app built for GNOME with Rust and GTK4. In "
 "addition to tracking time spent on tasks, you can edit that time, delete "
@@ -35,69 +35,75 @@ msgstr ""
 "l'inattività."
 
 #: data/com.lakoliu.Furtherance.appdata.xml.in:45
+msgid "Generate reports of task history over a specified timeframe"
+msgstr "Genera resoconti delle attività svolte in un determinato periodo"
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:46
+msgid "Bug fixes and improvements"
+msgstr "Correzioni di bug e miglioramenti"
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:53
+msgid "Ability to add tasks manually"
+msgstr "È possibile aggiungere attività manualmente"
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:54
+msgid "Auto-save option"
+msgstr "Opzione di salvataggio automatico"
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:55
+msgid "Task names can now be changed for an entire group"
+msgstr "Il nome delle attività può essere cambiato per un intero gruppo"
+
+#: data/com.lakoliu.Furtherance.appdata.xml.in:62
 msgid "Added Pomodoro-style countdown timer!"
 msgstr "Aggiunto conto alla rovescia in stile Pomodoro!"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:46
+#: data/com.lakoliu.Furtherance.appdata.xml.in:63
 msgid "Bug fixes"
 msgstr "Correzione di bug"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:53
+#: data/com.lakoliu.Furtherance.appdata.xml.in:70
 msgid "Ability to add tags!"
 msgstr "Aggiunti i tag"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:54
+#: data/com.lakoliu.Furtherance.appdata.xml.in:71
 msgid "Fixed icon positioning"
 msgstr "La posizione dell'icona è stata corretta"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:55
+#: data/com.lakoliu.Furtherance.appdata.xml.in:72
 msgid "Changed start button color and behavior"
 msgstr "Il colore e il funzionamento del pulsante avvia sono stati modificati"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:56
+#: data/com.lakoliu.Furtherance.appdata.xml.in:73
 msgid "Changed color of delete buttons"
 msgstr "Il colore del pulsante elimina è stato cambiato"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:57
+#: data/com.lakoliu.Furtherance.appdata.xml.in:74
 msgid "Dutch, Brazilian Portuguese, and French translations"
 msgstr "Traduzioni olandese, brasiliana e francese"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:64
+#: data/com.lakoliu.Furtherance.appdata.xml.in:81
 msgid "Furtherance has a new icon! And lots of bug fixes and improvements."
 msgstr ""
 "Furtherance ha una nuova icona! Oltre a diversi miglioramenti e correzioni "
 "di bug."
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:73
+#: data/com.lakoliu.Furtherance.appdata.xml.in:90
 msgid "First stable release of Furtherance."
 msgstr "La prima versione stabile di Furtherance."
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:77
+#: data/com.lakoliu.Furtherance.appdata.xml.in:94
 msgid "Track time spent on tasks"
 msgstr "Traccia il tempo speso compiendo delle attività"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:78
+#: data/com.lakoliu.Furtherance.appdata.xml.in:95
 msgid "Change/delete recorded times"
 msgstr "Cambia/elimina i tempi registrati"
 
-#: data/com.lakoliu.Furtherance.appdata.xml.in:79
+#: data/com.lakoliu.Furtherance.appdata.xml.in:96
 msgid "Idle detection"
 msgstr "Rilevamento di inattività"
 
-#: data/com.lakoliu.Furtherance.desktop.in:4
-msgid "Time Tracker"
-msgstr "Tracciamento del tempo"
-
-#: data/com.lakoliu.Furtherance.desktop.in:5
-msgid "Track your time without being tracked"
-msgstr "Traccia il tuo tempo senza essere tracciato"
-
-#: data/com.lakoliu.Furtherance.desktop.in:12
-msgid "timer;tracker;clock;tasks;productivity;"
-msgstr ""
-"timer;tracker;clock;tasks;productivity;tempo;tracciare;orologio;attività;"
-"compiti;produttività;"
-
 #: src/gtk/history_box.ui:25
 msgid "Start Tracking"
 msgstr "Inizia il tracciamento"
@@ -124,9 +130,9 @@ msgstr "Aspetto"
 
 #: src/gtk/preferences_window.ui:14
 msgid "_Dark theme"
-msgstr "_Tema scuro"
+msgstr "Tema scuro"
 
-#: src/gtk/preferences_window.ui:28 src/ui/window.rs:364
+#: src/gtk/preferences_window.ui:28 src/ui/window.rs:556
 msgid "Idle"
 msgstr "Inattività"
 
@@ -140,7 +146,7 @@ msgstr "(Solo per GNOME)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
-msgstr "_Minuti per l'inattività"
+msgstr "Minuti per l'inattività"
 
 #: src/gtk/preferences_window.ui:39
 msgid "Number of minutes before the user is considered idle"
@@ -152,7 +158,7 @@ msgstr "Lista delle attività"
 
 #: src/gtk/preferences_window.ui:67
 msgid "_Delete confirmation"
-msgstr "_Conferma eliminazioni"
+msgstr "Conferma eliminazioni"
 
 #: src/gtk/preferences_window.ui:79
 msgid "Limit tasks shown"
@@ -164,7 +170,7 @@ msgstr "Mostra solo un determinato numero di giorni nella lista delle attività"
 
 #: src/gtk/preferences_window.ui:85
 msgid "_Days ago"
-msgstr "_Giorni"
+msgstr "Giorni"
 
 #: src/gtk/preferences_window.ui:86
 msgid "Number of days to display in the list"
@@ -176,7 +182,7 @@ msgstr "Mostra somme giornaliere"
 
 #: src/gtk/preferences_window.ui:120
 msgid "_Show seconds"
-msgstr "_Mostra secondi"
+msgstr "Mostra secondi"
 
 #: src/gtk/preferences_window.ui:123
 msgid "Tasks list only. Seconds always show on timer"
@@ -197,7 +203,7 @@ msgstr "Timer"
 
 #: src/gtk/preferences_window.ui:152
 msgid "_Pomodoro"
-msgstr "_Pomodoro"
+msgstr "Pomodoro"
 
 #: src/gtk/preferences_window.ui:153
 msgid "Timer counts down instead of up"
@@ -211,139 +217,256 @@ msgstr "Intervallo"
 msgid "Start time in minutes"
 msgstr "Quanti minuti devono passare prima dell'interruzione"
 
-#: src/gtk/task_details.ui:8
-msgid "Task Details"
-msgstr "Dettagli attività"
+#: src/gtk/preferences_window.ui:181
+msgid "_Autosave"
+msgstr "Salvataggio automatico"
+
+#: src/gtk/preferences_window.ui:182
+msgid "Prevent losing tracked time due to improper shutdown"
+msgstr "Impedisce la perdita di dati in caso di una chiusura indesiderata"
+
+#: src/gtk/preferences_window.ui:187
+msgid "Autosave every X _minutes"
+msgstr "Salva automaticamente ogni X minuti"
+
+#: src/gtk/report.ui:8
+msgid "Report"
+msgstr "Resoconto"
+
+#: src/gtk/report.ui:33
+msgid "Past week"
+msgstr "Ultima settimana"
+
+#: src/gtk/report.ui:34
+msgid "This month"
+msgstr "Questo mese"
+
+#: src/gtk/report.ui:35
+msgid "Past 30 days"
+msgstr "Ultimi 30 giorni"
+
+#: src/gtk/report.ui:36
+msgid "Past 180 days"
+msgstr "Ultimi 180 giorni"
+
+#: src/gtk/report.ui:37
+msgid "Past year"
+msgstr "Ultimo anno"
+
+#: src/gtk/report.ui:38
+msgid "Date range"
+msgstr "Personalizzato"
 
-#: src/gtk/task_details.ui:73 src/ui/task_details.rs:188
+#: src/gtk/report.ui:52 src/gtk/task_details.ui:88 src/ui/task_details.rs:189
+#: src/ui/window.rs:331
 msgid "Start"
 msgstr "Inizio"
 
-#: src/gtk/task_details.ui:81 src/application.rs:280 src/ui/task_details.rs:190
-#: src/ui/window.rs:403
+#: src/gtk/report.ui:58 src/gtk/report.ui:74
+msgid "MM/DD/YYYY"
+msgstr "MM/DD/YYYY"
+
+#: src/gtk/report.ui:68
+msgid "End"
+msgstr "Fine"
+
+#: src/gtk/report.ui:83
+msgid "Use the format MM/DD/YYYY"
+msgstr "Usare il formato MM/GG/AAAA"
+
+#: src/gtk/report.ui:92
+msgid "Start date must be before end date"
+msgstr "La data iniziale dev'essere prima della data iniziale"
+
+#: src/gtk/report.ui:105
+msgid "Sort by:"
+msgstr "Ordina per:"
+
+#: src/gtk/report.ui:110 src/gtk/task_row.ui:22 src/ui/report.rs:155
+msgid "Task"
+msgstr "Attività"
+
+#: src/gtk/report.ui:117
+msgid "Tag"
+msgstr "Tag"
+
+#: src/gtk/report.ui:124
+msgid "Filter by task or tags"
+msgstr "Filtra per attività o tag"
+
+#: src/gtk/report.ui:136
+msgid "Tasks"
+msgstr "Attività"
+
+#: src/gtk/report.ui:137 src/ui/task_details.rs:180
+msgid "Tags"
+msgstr "Tag"
+
+#: src/gtk/report.ui:143 src/ui/report.rs:144
+msgid "Task, Task 2"
+msgstr "Attività, Attività 2"
+
+#: src/gtk/report.ui:150
+msgid "Refresh"
+msgstr "Aggiorna"
+
+#: src/gtk/task_details.ui:8
+msgid "Task Details"
+msgstr "Dettagli attività"
+
+#: src/gtk/task_details.ui:96 src/application.rs:287 src/ui/task_details.rs:191
+#: src/ui/window.rs:333 src/ui/window.rs:595
 msgid "Stop"
 msgstr "Fine"
 
-#: src/gtk/task_details.ui:89
+#: src/gtk/task_details.ui:104
 msgid "Total"
 msgstr "Totale"
 
-#: src/gtk/task_details.ui:116
+#: src/gtk/task_details.ui:131
 msgid "Delete all"
 msgstr "Elimina tutto"
 
-#: src/gtk/task_row.ui:22
-msgid "Task"
-msgstr "Attività"
-
 #: src/gtk/task_row.ui:45
 msgid "Time"
 msgstr "Tempo"
 
 #: src/gtk/window.ui:20
+msgid "Add Task"
+msgstr "Aggiungi attività"
+
+#: src/gtk/window.ui:26
 msgid "Main Menu"
 msgstr "Menu principale"
 
-#: src/gtk/window.ui:58
-msgid "Task Name #Tags"
+#: src/gtk/window.ui:64
+msgid "Task Name #tags"
 msgstr "Nome attività #Tag"
 
-#: src/gtk/window.ui:86
+#: src/gtk/window.ui:92
 msgid "_Preferences"
-msgstr "_Preferenze"
+msgstr "Preferenze"
+
+#: src/gtk/window.ui:96
+msgid "_Generate Report"
+msgstr "Resoconto"
 
-#: src/gtk/window.ui:90
+#: src/gtk/window.ui:100
 msgid "_Delete history"
-msgstr "_Cancella cronologia"
+msgstr "Cancella cronologia"
 
-#: src/gtk/window.ui:94
+#: src/gtk/window.ui:104
 msgid "_About Furtherance"
-msgstr "_Informazioni"
+msgstr "Informazioni"
 
-#: src/application.rs:169
+#: src/application.rs:176
 msgid "translator-credits"
 msgstr "Musiclover382 <musiclover382@protonmail.com>"
 
-#: src/application.rs:185
+#: src/application.rs:192
 msgid "Delete history?"
 msgstr "Cancellare la cronologia?"
 
-#: src/application.rs:188 src/ui/task_details.rs:504
+#: src/application.rs:195 src/ui/task_details.rs:568 src/ui/window.rs:317
 msgid "Cancel"
 msgstr "Annulla"
 
-#: src/application.rs:189 src/ui/task_details.rs:505
+#: src/application.rs:196 src/ui/task_details.rs:569
 msgid "Delete"
 msgstr "Elimina"
 
-#: src/application.rs:196
+#: src/application.rs:203
 msgid "This will delete ALL of your task history."
 msgstr "Verranno eliminate TUTTE le attività svolte."
 
-#: src/application.rs:198
+#: src/application.rs:205
 msgid "Type DELETE in the box below then click Delete to proceed."
 msgstr ""
 "Scrivere ELIMINA nella casella sottostante, poi cliccare Elimina per "
 "continuare."
 
-#: src/application.rs:207
+#: src/application.rs:214
 msgid "DELETE"
 msgstr "ELIMINA"
 
-#: src/application.rs:257 src/ui/window.rs:367
+#: src/application.rs:264 src/ui/window.rs:559
 msgid "Discard"
 msgstr "Scarta"
 
-#: src/application.rs:258 src/application.rs:279 src/ui/window.rs:368
-#: src/ui/window.rs:402
+#: src/application.rs:265 src/application.rs:286 src/ui/window.rs:560
+#: src/ui/window.rs:594
 msgid "Continue"
 msgstr "Continua"
 
-#: src/application.rs:269 src/ui/window.rs:399
+#: src/application.rs:276 src/ui/window.rs:591
 msgid "Time's up!"
 msgstr "Tempo scaduto!"
 
-#: src/application.rs:270
+#: src/application.rs:277
 msgid "Your Furtherance timer ended."
 msgstr "Il timer di Furtherance è concluso."
 
-#: src/ui/task_details.rs:170
+#: src/ui/report.rs:146
+msgid "tag, tag 2"
+msgstr "Tag, Tag 2"
+
+#: src/ui/report.rs:158
+msgid "Duration"
+msgstr "Tempo"
+
+#: src/ui/report.rs:295
+msgid "No Results"
+msgstr "Nessun risultato"
+
+#: src/ui/report.rs:303
+msgid "All Results"
+msgstr "Tutti i risultati"
+
+#: src/ui/report.rs:445
+msgid "no tags"
+msgstr "Nessun tag"
+
+#: src/ui/task_details.rs:171 src/ui/task_details.rs:497
 msgid "Edit Task"
 msgstr "Modifica attività"
 
-#: src/ui/task_details.rs:179
-msgid "Tags"
-msgstr "Tag"
-
-#: src/ui/task_details.rs:209
+#: src/ui/task_details.rs:210 src/ui/window.rs:355
 msgid "*Use the format MMM DD YYYY HH:MM:SS"
 msgstr "*Usare il formato MMM GG AAAA HH:MM:SS"
 
-#: src/ui/task_details.rs:211
+#: src/ui/task_details.rs:212 src/ui/window.rs:357
 msgid "*Use the format MMM DD YYYY HH:MM"
 msgstr "*Usare il formato MMM GG AAAA HH:MM"
 
-#: src/ui/task_details.rs:217
+#: src/ui/task_details.rs:218 src/ui/window.rs:363
 msgid "*Start time cannot be later than stop time."
 msgstr "*Il tempo iniziale non può essere successivo al tempo finale."
 
-#: src/ui/task_details.rs:222
+#: src/ui/task_details.rs:223 src/ui/window.rs:368
 msgid "*Time cannot be in the future."
 msgstr "*Il tempo non può essere futuro."
 
-#: src/ui/task_details.rs:228
+#: src/ui/task_details.rs:229
 msgid "Delete task"
 msgstr "Elimina attività"
 
-#: src/ui/task_details.rs:255
+#: src/ui/task_details.rs:256
 msgid "Delete task?"
 msgstr "Eliminare l'attività?"
 
-#: src/ui/task_details.rs:500
+#: src/ui/task_details.rs:503
+msgid "New Name #tags"
+msgstr "Nuovo nome #Tag"
+
+#: src/ui/task_details.rs:504
+msgid "Task name cannot be empty."
+msgstr "Il nome dell'attività non può essere vuoto."
+
+#: src/ui/task_details.rs:564
 msgid "Delete All?"
 msgstr "Eliminare tutto?"
 
-#: src/ui/task_details.rs:502
+#: src/ui/task_details.rs:566
 msgid "This will delete all occurrences of this task on this day."
 msgstr "Verranno eliminate tutte le attività con questo nome in questo giorno."
 
@@ -355,11 +478,31 @@ msgstr "Oggi"
 msgid "Yesterday"
 msgstr "Ieri"
 
-#: src/ui/window.rs:355
+#: src/ui/window.rs:313
+msgid "New Task"
+msgstr "Nuova attività"
+
+#: src/ui/window.rs:318
+msgid "Add"
+msgstr "Aggiungi"
+
+#: src/ui/window.rs:324
+msgid "Task Name"
+msgstr "Nome attività"
+
+#: src/ui/window.rs:326
+msgid "tags"
+msgstr "tag"
+
+#: src/ui/window.rs:373
+msgid "*Task name cannot be blank."
+msgstr "*Il nome dell'attività non può essere vuoto."
+
+#: src/ui/window.rs:547
 msgid "You have been idle for "
 msgstr "Sei stato inattivo per "
 
-#: src/ui/window.rs:356
+#: src/ui/window.rs:548
 msgid ""
 "\n"
 "Would you like to discard that time, or continue the clock?"
@@ -367,6 +510,27 @@ msgstr ""
 "\n"
 "Vuoi scartare tale lasso di tempo o continuare regolarmente?"
 
-#: src/ui/window.rs:458
+#: src/ui/window.rs:689
+msgid "Autosave Restored"
+msgstr "Salvataggio automatico ripristinato"
+
+#: src/ui/window.rs:692
+msgid "Furtherance shut down improperly. An autosave was restored."
+msgstr ""
+"Furtherance non si è chiuso correttamente. I dati salvati automaticamente "
+"sono stati ripristinati."
+
+#: src/ui/window.rs:751
 msgid "Stop the timer to duplicate a task."
 msgstr "Ferma il timer per duplicare un'attività."
+
+#~ msgid "Time Tracker"
+#~ msgstr "Tracciamento del tempo"
+
+#~ msgid "Track your time without being tracked"
+#~ msgstr "Traccia il tuo tempo senza essere tracciato"
+
+#~ msgid "timer;tracker;clock;tasks;productivity;"
+#~ msgstr ""
+#~ "timer;tracker;clock;tasks;productivity;tempo;tracciare;orologio;attività;"
+#~ "compiti;produttività;"
diff --git a/src/application.rs b/src/application.rs
index ce7a26f..6050af3 100755
--- a/src/application.rs
+++ b/src/application.rs
@@ -23,7 +23,7 @@ use log::debug;
 use std::sync::Mutex;
 
 use crate::config;
-use crate::ui::{FurtheranceWindow, FurPreferencesWindow};
+use crate::ui::{FurtheranceWindow, FurPreferencesWindow, FurReport};
 use crate::database;
 use crate::settings_manager;
 
@@ -111,6 +111,13 @@ impl FurtheranceApplication {
         self.set_accels_for_action("app.preferences", &["<primary>comma"]);
         self.add_action(&preferences_action);
 
+        let report_action = gio::SimpleAction::new("report", None);
+        report_action.connect_activate(clone!(@weak self as app => move |_, _| {
+            FurReport::new().show();
+        }));
+        self.set_accels_for_action("app.report", &["<primary>R"]);
+        self.add_action(&report_action);
+
         let about_action = gio::SimpleAction::new("about", None);
         about_action.connect_activate(clone!(@weak self as app => move |_, _| {
             app.show_about();
diff --git a/src/database.rs b/src/database.rs
index 69b56dc..a2485c6 100755
--- a/src/database.rs
+++ b/src/database.rs
@@ -120,6 +120,32 @@ pub fn retrieve() -> Result<Vec<Task>, rusqlite::Error> {
 
 }
 
+// pub fn retrieve_date_range() -> Result<Vec<Task>, rusqlite::Error> {
+    // Retrieve all tasks from the database
+//     let conn = Connection::open(get_directory())?;
+
+//     let mut query = conn.prepare("SELECT * FROM tasks ORDER BY start_time")?;
+//     let task_iter = query.query_map([], |row| {
+//         Ok(Task {
+//             id: row.get(0)?,
+//             task_name: row.get(1)?,
+//             start_time: row.get(2)?,
+//             stop_time: row.get(3)?,
+//             tags: row.get(4)?,
+//         })
+//     })?;
+
+//     let mut tasks_vec: Vec<Task> = Vec::new();
+//     for task_item in task_iter {
+//         let start = DateTime::parse_from_rfc3339(&task_item.start_time).unwrap();
+//         let stop = DateTime::parse_from_rfc3339(&task_item.stop_time).unwrap();
+//         tasks_vec.push(task_item.unwrap());
+//     }
+
+//     Ok(tasks_vec)
+
+// }
+
 pub fn update_start_time(id: i32, start_time: String) -> Result<()> {
     let conn = Connection::open(get_directory())?;
 
diff --git a/src/furtherance.gresource.xml b/src/furtherance.gresource.xml
index bfab2ff..bb1a9fb 100755
--- a/src/furtherance.gresource.xml
+++ b/src/furtherance.gresource.xml
@@ -1,13 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/com/lakoliu/Furtherance">
-    <file>gtk/window.ui</file>
     <file>gtk/history_box.ui</file>
+    <file>gtk/preferences_window.ui</file>
+    <file>gtk/report.ui</file>
     <file>gtk/style.css</file>
-    <file>gtk/tasks_page.ui</file>
+    <file>gtk/task_details.ui</file>
     <file>gtk/tasks_group.ui</file>
+    <file>gtk/tasks_page.ui</file>
     <file>gtk/task_row.ui</file>
-    <file>gtk/task_details.ui</file>
-    <file>gtk/preferences_window.ui</file>
+    <file>gtk/window.ui</file>
   </gresource>
 </gresources>
diff --git a/src/gtk/report.ui b/src/gtk/report.ui
new file mode 100644
index 0000000..9d4d5c8
--- /dev/null
+++ b/src/gtk/report.ui
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="FurReport" parent="AdwWindow">
+    <property name="width-request">450</property>
+    <property name="height-request">600</property>
+    <property name="default-width">450</property>
+    <property name="default-height">600</property>
+    <property name="title" translatable="yes">Report</property>
+    <property name="modal">True</property>
+    <style>
+      <class name="report"/>
+    </style>
+    <property name="content">
+    <object class="GtkBox">
+      <property name="orientation">vertical</property>
+      <child type="overlay">
+        <object class="GtkHeaderBar">
+          <style>
+            <class name="hidden"/>
+            <class name="flat-headerbar"/>
+          </style>
+        </object>
+      </child>
+      <child>
+        <object class="GtkBox">
+          <property name="orientation">vertical</property>
+          <property name="spacing">8</property>
+          <property name="margin-top">10</property>
+          <child>
+            <object class="GtkComboBoxText" id="range_combo">
+              <property name="halign">center</property>
+                <items>
+                  <item translatable="yes" id="week_item">Past week</item>
+                  <item translatable="yes" id="month_item">This month</item>
+                  <item translatable="yes" id="30_days_item">Past 30 days</item>
+                  <item translatable="yes" id="six_months_item">Past 180 days</item>
+                  <item translatable="yes" id="year_item">Past year</item>
+                  <item translatable="yes" id="date_range_item">Date range</item>
+                </items>
+            </object>
+          </child>
+          <child>
+            <object class="GtkBox" id="date_range_box">
+              <property name="spacing">8</property>
+              <property name="halign">center</property>
+              <property name="visible">False</property>
+              <child>
+                <object class="GtkBox">
+                  <property name="orientation">vertical</property>
+                  <child>
+                    <object class="GtkLabel">
+                      <property name="label" translatable="yes">Start</property>
+                      <property name="halign">start</property>
+                    </object>
+                  </child>
+                  <child>
+                    <object class="GtkEntry" id="start_date_entry">
+                      <property name="placeholder-text" translatable="yes">MM/DD/YYYY</property>
+                    </object>
+                  </child>
+                </object>
+              </child>
+              <child>
+                <object class="GtkBox">
+                  <property name="orientation">vertical</property>
+                  <child>
+                    <object class="GtkLabel">
+                      <property name="label" translatable="yes">End</property>
+                      <property name="halign">start</property>
+                    </object>
+                  </child>
+                  <child>
+                    <object class="GtkEntry" id="end_date_entry">
+                      <property name="placeholder-text" translatable="yes">MM/DD/YYYY</property>
+                    </object>
+                  </child>
+                </object>
+              </child>
+            </object>
+          </child>
+          <child>
+            <object class="GtkLabel" id="format_error">
+              <property name="label" translatable="yes">Use the format MM/DD/YYYY</property>
+              <property name="visible">False</property>
+              <style>
+                <class name="error_message"/>
+              </style>
+            </object>
+          </child>
+          <child>
+            <object class="GtkLabel" id="start_end_error">
+              <property name="label" translatable="yes">Start date must be before end date</property>
+              <property name="visible">False</property>
+              <style>
+                <class name="error_message"/>
+              </style>
+            </object>
+          </child>
+          <child>
+            <object class="GtkBox" id="sort_by_box">
+              <property name="spacing">6</property>
+              <property name="halign">center</property>
+              <child>
+                <object class="GtkLabel">
+                  <property name="label" translatable="yes">Sort by:</property>
+                </object>
+              </child>
+              <child>
+                <object class="GtkCheckButton" id="sort_by_task">
+                  <property name="label" translatable="yes">Task</property>
+                  <property name="active">True</property>
+                  <property name="group">sort_by_tag</property>
+                </object>
+              </child>
+              <child>
+                <object class="GtkCheckButton" id="sort_by_tag">
+                  <property name="label" translatable="yes">Tag</property>
+                </object>
+              </child>
+            </object>
+          </child>
+          <child>
+            <object class="GtkCheckButton" id="filter_check">
+              <property name="label" translatable="yes">Filter by task or tags</property>
+              <property name="halign">center</property>
+            </object>
+          </child>
+          <child>
+            <object class="GtkBox" id="filter_box">
+              <property name="spacing">6</property>
+              <property name="visible">False</property>
+              <property name="halign">center</property>
+              <child>
+                <object class="GtkComboBoxText" id="filter_combo">
+                    <items>
+                      <item translatable="yes" id="tasks_item">Tasks</item>
+                      <item translatable="yes" id="tags_item">Tags</item>
+                    </items>
+                </object>
+              </child>
+              <child>
+                <object class="GtkEntry" id="filter_entry">
+                  <property name="placeholder-text" translatable="yes">Task, Task 2</property>
+                </object>
+              </child>
+            </object>
+          </child>
+          <child>
+            <object class="GtkButton" id="refresh_btn">
+              <property name="label" translatable="yes">Refresh</property>
+              <property name="halign">center</property>
+            </object>
+          </child>
+          <child>
+            <object class="GtkScrolledWindow">
+              <property name="vexpand">true</property>
+              <child>
+                <object class="GtkTreeView" id="results_tree">
+
+                </object>
+              </child>
+            </object>
+          </child>
+
+        </object>
+      </child>
+    </object>
+    </property>
+  </template>
+</interface>
diff --git a/src/gtk/window.ui b/src/gtk/window.ui
index 963922a..174acc0 100755
--- a/src/gtk/window.ui
+++ b/src/gtk/window.ui
@@ -93,6 +93,10 @@
         <attribute name="action">app.preferences</attribute>
       </item>
       <item>
+        <attribute name="label" translatable="yes">_Generate Report</attribute>
+        <attribute name="action">app.report</attribute>
+      </item>
+      <item>
         <attribute name="label" translatable="yes">_Delete history</attribute>
         <attribute name="action">app.delete-history</attribute>
       </item>
diff --git a/src/meson.build b/src/meson.build
index c689689..3afd404 100755
--- a/src/meson.build
+++ b/src/meson.build
@@ -33,6 +33,7 @@ run_command(
 rust_sources = files(
   'ui.rs',
   'ui/preferences_window.rs',
+  'ui/report.rs',
   'ui/task_details.rs',
   'ui/task_row.rs',
   'ui/tasks_group.rs',
diff --git a/src/ui.rs b/src/ui.rs
index 1a3e3fc..36ba710 100755
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -14,18 +14,20 @@
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-pub mod window;
 mod history_box;
-mod tasks_page;
+mod preferences_window;
+mod report;
+mod task_details;
 mod tasks_group;
+mod tasks_page;
 mod task_row;
-mod task_details;
-mod preferences_window;
+pub mod window;
 
-pub use window::FurtheranceWindow;
 pub use history_box::FurHistoryBox;
-pub use tasks_page::FurTasksPage;
+pub use preferences_window::FurPreferencesWindow;
+pub use report::FurReport;
+pub use task_details::FurTaskDetails;
 pub use tasks_group::FurTasksGroup;
+pub use tasks_page::FurTasksPage;
 pub use task_row::FurTaskRow;
-pub use task_details::FurTaskDetails;
-pub use preferences_window::FurPreferencesWindow;
+pub use window::FurtheranceWindow;
diff --git a/src/ui/report.rs b/src/ui/report.rs
new file mode 100644
index 0000000..4fcfc51
--- /dev/null
+++ b/src/ui/report.rs
@@ -0,0 +1,469 @@
+// Furtherance - Track your time without being tracked
+// Copyright (C) 2022  Ricky Kresslein <rk@lakoliu.com>
+//
+// This program 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.
+//
+// This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use adw::subclass::prelude::*;
+use gettextrs::*;
+use glib::clone;
+use gtk::subclass::prelude::*;
+use gtk::{glib, prelude::*, CompositeTemplate};
+use chrono::{DateTime, NaiveDate, Local, Duration, Date, Datelike, offset::TimeZone};
+use itertools::Itertools;
+
+use crate::FurtheranceApplication;
+use crate::ui::FurtheranceWindow;
+use crate::database;
+
+mod imp {
+    use super::*;
+    use glib::subclass;
+
+    #[derive(Debug, Default, CompositeTemplate)]
+    #[template(resource = "/com/lakoliu/Furtherance/gtk/report.ui")]
+    pub struct FurReport {
+        #[template_child]
+        pub range_combo: TemplateChild<gtk::ComboBoxText>,
+        #[template_child]
+        pub date_range_box: TemplateChild<gtk::Box>,
+        #[template_child]
+        pub start_date_entry: TemplateChild<gtk::Entry>,
+        #[template_child]
+        pub end_date_entry: TemplateChild<gtk::Entry>,
+        #[template_child]
+        pub format_error: TemplateChild<gtk::Label>,
+        #[template_child]
+        pub start_end_error: TemplateChild<gtk::Label>,
+        #[template_child]
+        pub filter_check: TemplateChild<gtk::CheckButton>,
+        #[template_child]
+        pub filter_box: TemplateChild<gtk::Box>,
+        #[template_child]
+        pub filter_combo: TemplateChild<gtk::ComboBoxText>,
+        #[template_child]
+        pub filter_entry: TemplateChild<gtk::Entry>,
+        #[template_child]
+        pub results_tree: TemplateChild<gtk::TreeView>,
+        #[template_child]
+        pub sort_by_box: TemplateChild<gtk::Box>,
+        #[template_child]
+        pub sort_by_task: TemplateChild<gtk::CheckButton>,
+        #[template_child]
+        pub sort_by_tag: TemplateChild<gtk::CheckButton>,
+        #[template_child]
+        pub refresh_btn: TemplateChild<gtk::Button>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for FurReport {
+        const NAME: &'static str = "FurReport";
+        type ParentType = adw::Window;
+        type Type = super::FurReport;
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        fn instance_init(obj: &subclass::InitializingObject<Self>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for FurReport {
+
+        fn constructed(&self, obj: &Self::Type) {
+            obj.setup_widgets();
+            self.parent_constructed(obj);
+        }
+    }
+
+    impl WidgetImpl for FurReport {}
+
+    impl WindowImpl for FurReport {}
+
+    impl AdwWindowImpl for FurReport {}
+}
+
+glib::wrapper! {
+    pub struct FurReport(ObjectSubclass<imp::FurReport>)
+        @extends gtk::Widget, gtk::Window, adw::Window;
+}
+
+impl FurReport {
+    pub fn new() -> Self {
+        let dialog: Self = glib::Object::new(&[]).unwrap();
+
+        let window = FurtheranceWindow::default();
+        dialog.set_transient_for(Some(&window));
+
+        let app = FurtheranceApplication::default();
+        app.add_window(&window);
+
+        dialog
+    }
+
+    pub fn setup_widgets(&self) {
+        let imp = imp::FurReport::from_instance(self);
+
+        imp.range_combo.set_active_id(Some("week_item"));
+        imp.filter_combo.set_active_id(Some("tasks_item"));
+
+        imp.range_combo.connect_changed(clone!(@weak self as this => move |combo|{
+            let imp = imp::FurReport::from_instance(&this);
+            if combo.active_id().unwrap() != "date_range_item" {
+                imp.date_range_box.set_visible(false);
+                this.refresh_report();
+            } else {
+                imp.date_range_box.set_visible(true);
+            }
+        }));
+
+        imp.filter_check.connect_toggled(clone!(@weak self as this => move |_|{
+            let imp = imp::FurReport::from_instance(&this);
+            if imp.filter_box.get_visible() {
+                imp.filter_box.set_visible(false);
+            } else {
+                imp.filter_box.set_visible(true);
+            }
+        }));
+
+        imp.filter_combo.connect_changed(clone!(@weak self as this => move |combo|{
+            let imp = imp::FurReport::from_instance(&this);
+            if combo.active_id().unwrap() == "tasks_item" {
+                imp.filter_entry.set_placeholder_text(Some(&gettext("Task, Task 2")));
+            } else {
+                imp.filter_entry.set_placeholder_text(Some(&gettext("tag, tag 2")));
+            }
+        }));
+
+        imp.refresh_btn.connect_clicked(clone!(@weak self as this => move |_|{
+            this.refresh_report();
+        }));
+
+        let renderer = gtk::CellRendererText::new();
+        let task_column = gtk::TreeViewColumn::with_attributes(&gettext("Task"), &renderer, &[("text", 0)]);
+        task_column.set_expand(true);
+        task_column.set_resizable(true);
+        let duration_column = gtk::TreeViewColumn::with_attributes(&gettext("Duration"), &renderer, &[("text", 1)]);
+        duration_column.set_expand(false);
+        duration_column.set_resizable(true);
+        imp.results_tree.append_column(&task_column);
+        imp.results_tree.append_column(&duration_column);
+        imp.results_tree.set_enable_search(false);
+
+        self.refresh_report();
+    }
+
+    fn refresh_report(&self) {
+        let imp = imp::FurReport::from_instance(self);
+        imp.format_error.set_visible(false);
+        imp.start_end_error.set_visible(false);
+
+        let results_model = gtk::TreeStore::new(&[String::static_type(), String::static_type()]);
+
+        let mut task_list = database::retrieve().unwrap();
+        task_list.reverse();
+
+        // Get date range
+        let active_range = imp.range_combo.active_id().unwrap();
+        let today = Local::today();
+        let range_start_date: Date<Local>;
+        let mut range_end_date = today;
+        if active_range == "week_item" {
+            range_start_date = today - Duration::days(6);
+        } else if active_range == "month_item" {
+            let days_ago = today.day() - 1;
+            range_start_date = today - Duration::days(days_ago.into());
+        } else if active_range == "30_days_item" {
+            range_start_date = today - Duration::days(30);
+        } else if active_range == "six_months_item" {
+            range_start_date = today - Duration::days(180);
+        } else if active_range == "year_item" {
+            range_start_date = today - Duration::days(365);
+        } else {
+            let input_start_date = NaiveDate::parse_from_str(&imp.start_date_entry.text(), "%m/%d/%Y");
+            let input_end_date = NaiveDate::parse_from_str(&imp.end_date_entry.text(), "%m/%d/%Y");
+            // Check if user entered dates properly
+            if let Err(_) = input_start_date {
+                imp.format_error.set_visible(true);
+                results_model.clear();
+                imp.results_tree.set_model(Some(&results_model));
+                return
+            }
+            if let Err(_) = input_end_date {
+                imp.format_error.set_visible(true);
+                results_model.clear();
+                imp.results_tree.set_model(Some(&results_model));
+                return
+            }
+            // Start date cannot be after end date
+            if (input_end_date.unwrap() - input_start_date.unwrap()).num_days() < 0 {
+                imp.start_end_error.set_visible(true);
+                results_model.clear();
+                return
+            }
+            range_start_date = Local.from_local_date(&input_start_date.unwrap()).unwrap();
+            range_end_date = Local.from_local_date(&input_end_date.unwrap()).unwrap();
+        }
+
+        let mut total_time: i64 = 0;
+        let mut tasks_in_range: Vec<(database::Task, i64)> = Vec::new();
+        let mut user_chosen_tags: Vec<String> = Vec::new();
+        let mut only_this_tag = false;
+        for task in task_list {
+            let start = DateTime::parse_from_rfc3339(&task.start_time).unwrap().with_timezone(&Local);
+            let stop = DateTime::parse_from_rfc3339(&task.stop_time).unwrap().with_timezone(&Local);
+            // Check if start time is in date range and if not remove it from task_list
+            let start_date = start.date();
+            if start_date >= range_start_date && start_date <= range_end_date {
+                // Sort by only selected tasks or tags if filter is selected
+                if imp.filter_check.is_active() && !imp.filter_entry.text().trim().is_empty() {
+                    if imp.filter_combo.active_id().unwrap() == "tasks_item" {
+                        // Create a vec of tasks to match from written tasks
+                        let chosen_tasks = imp.filter_entry.text();
+                        let mut split_tasks: Vec<&str> = chosen_tasks.trim().split(",").collect();
+                        // Trim whitespace around each task
+                        split_tasks = split_tasks.iter().map(|x| x.trim()).collect();
+                        // Don't allow empty tasks
+                        split_tasks.retain(|&x| !x.trim().is_empty());
+                        // Handle duplicate tasks
+                        split_tasks = split_tasks.into_iter().unique().collect();
+                        // Lowercase tags
+                        let lower_tasks: Vec<String> = split_tasks.iter().map(|x| x.to_lowercase()).collect();
+
+                        if lower_tasks.contains(&task.task_name.to_lowercase()) {
+                            let duration = stop - start;
+                            let duration = duration.num_seconds();
+                            tasks_in_range.push((task, duration));
+                            total_time += duration;
+                        }
+
+                    } else if imp.filter_combo.active_id().unwrap() == "tags_item" {
+                        // Split user chosen tags
+                        let chosen_tasgs = imp.filter_entry.text();
+                        let mut split_tags: Vec<&str> = chosen_tasgs.trim().split(",").collect();
+                        // Trim whitespace around each tag
+                        split_tags = split_tags.iter().map(|x| x.trim()).collect();
+                        // Don't allow empty tags
+                        split_tags.retain(|&x| !x.trim().is_empty());
+                        // Handle duplicate tags
+                        split_tags = split_tags.into_iter().unique().collect();
+                        // Lowercase tags
+                        user_chosen_tags = split_tags.iter().map(|x| x.to_lowercase()).collect();
+
+                        // Split task's tags
+                        let mut split_tags: Vec<&str> = task.tags.trim().split("#").collect();
+                        // Trim whitespace around each tag
+                        split_tags = split_tags.iter().map(|x| x.trim()).collect();
+
+                        // Only keep tasks that contain the user's chosen tags
+                        split_tags.retain(|&x| user_chosen_tags.contains(&x.to_string()));
+                        if !split_tags.is_empty() {
+                            let duration = stop - start;
+                            let duration = duration.num_seconds();
+                            tasks_in_range.push((task, duration));
+                            total_time += duration;
+                        }
+
+                        only_this_tag = true;
+                    }
+                } else {
+                    let duration = stop - start;
+                    let duration = duration.num_seconds();
+                    tasks_in_range.push((task, duration));
+                    total_time += duration;
+                }
+            }
+        }
+
+        let all_tasks_iter:gtk::TreeIter;
+        if tasks_in_range.is_empty() {
+            all_tasks_iter = results_model.insert_with_values(None,
+                                                                None,
+                                                                &[
+                                                                    (0, &gettext("No Results")),
+                                                                    (1, &"")
+                                                                ]);
+        } else {
+            let total_time_str = FurReport::format_duration(total_time);
+            all_tasks_iter = results_model.insert_with_values(None,
+                                                                    None,
+                                                                    &[
+                                                                        (0, &gettext("All Results")),
+                                                                        (1, &total_time_str)
+                                                                    ]);
+        }
+
+        if imp.sort_by_task.is_active() {
+            let mut tasks_by_name: Vec<Vec<(database::Task, i64)>> = Vec::new();
+            for (task, task_duration) in tasks_in_range {
+                let mut unique = true;
+                for i in 0..tasks_by_name.len() {
+                    let (tbn, _) = &tasks_by_name[i][0];
+                    if tbn.task_name == task.task_name {
+                        tasks_by_name[i].push((task.clone(), task_duration));
+                        unique = false;
+                    }
+                }
+                if unique {
+                    // Add unique task to list for group name
+                    let mut new_name_list: Vec<(database::Task, i64)> = Vec::new();
+                    new_name_list.push((task.clone(), task_duration));
+                    tasks_by_name.push(new_name_list);
+                }
+            }
+
+            let mut sorted_tasks_by_duration: Vec<(String, i64, Vec<(String, i64)>)> = Vec::new();
+            for tbn in tasks_by_name {
+                let mut total_duration: i64 = 0;
+                let mut tags_dur: Vec<(String, i64)> = Vec::new();
+                let task_name = tbn[0].0.task_name.to_string();
+                for tbn_tuple in tbn {
+                    let (task, task_duration) = tbn_tuple;
+                    total_duration += task_duration;
+
+                    let mut split_tags: Vec<&str> = task.tags.split("#").collect();
+                    split_tags = split_tags.iter().map(|x| x.trim()).collect();
+                    split_tags.retain(|&x| !x.trim().is_empty());
+                    if !split_tags.is_empty() {
+                        let mut formatted_tags = split_tags.join(" #");
+                        formatted_tags = format!("#{}", formatted_tags);
+                        let mut unique = true;
+                        for i in 0..tags_dur.len() {
+                            let (tags, dur) = &tags_dur[i];
+                            if tags == &formatted_tags {
+                                let new_dur = dur + task_duration;
+                                tags_dur[i] = (formatted_tags.clone(), new_dur);
+                                unique = false;
+                            }
+                        }
+                        if unique {
+                            tags_dur.push((formatted_tags, task_duration))
+                        }
+                    }
+                }
+
+                // Sort tasks and tags in descending order by duration
+                tags_dur.sort_by_key(|k| k.1);
+                tags_dur.reverse();
+                sorted_tasks_by_duration.push((task_name, total_duration, tags_dur));
+                sorted_tasks_by_duration.sort_by_key(|k| k.1);
+                sorted_tasks_by_duration.reverse();
+            }
+
+            for stbd in sorted_tasks_by_duration {
+                let header_iter = results_model.append(Some(&all_tasks_iter));
+                for (task, task_duration) in stbd.2 {
+                    let _child_iter = results_model.insert_with_values(
+                        Some(&header_iter),
+                        None,
+                        &[(0, &task), (1, &FurReport::format_duration(task_duration))]
+                    );
+                }
+                results_model.set(
+                    &header_iter,
+                    &[(0, &stbd.0), (1, &FurReport::format_duration(stbd.1))]
+                );
+            }
+        } else if imp.sort_by_tag.is_active() {
+            let mut tasks_by_tag: Vec<Vec<(String, database::Task, i64)>> = Vec::new();
+            for (task, task_duration) in tasks_in_range {
+                let mut split_tags: Vec<&str> = task.tags.split("#").collect();
+                // Trim whitespace around each tag
+                split_tags = split_tags.iter().map(|x| x.trim()).collect();
+                for tag in split_tags {
+                    let mut unique = true;
+                    for i in 0..tasks_by_tag.len() {
+                        let (tbt_tag, _, _) = &tasks_by_tag[i][0];
+                        if tbt_tag == tag {
+                            tasks_by_tag[i].push((tag.to_string(), task.clone(), task_duration));
+                            unique = false;
+                        }
+                    }
+                    if unique {
+                        // Add unique task to list for group name
+                        let mut new_name_list: Vec<(String, database::Task, i64)> = Vec::new();
+                        new_name_list.push((tag.to_string(), task.clone(), task_duration));
+                        tasks_by_tag.push(new_name_list);
+                    }
+                }
+            }
+
+            let mut sorted_tasks_by_duration: Vec<(String, i64, Vec<(String, i64)>)> = Vec::new();
+            for tbt in tasks_by_tag {
+                let mut total_duration: i64 = 0;
+                let mut tasks_dur: Vec<(String, i64)> = Vec::new();
+                let tag_name = format!("#{}", tbt[0].0);
+                for tbt_tuple in tbt {
+                    let (_, task, tag_duration) = tbt_tuple;
+                    total_duration += tag_duration;
+
+                    let mut unique = true;
+                    for i in 0..tasks_dur.len() {
+                        let (td_task, dur) = &tasks_dur[i];
+                        if td_task == &task.task_name {
+                            let new_dur = dur + tag_duration;
+                            tasks_dur[i] = (task.task_name.clone(), new_dur);
+                            unique = false;
+                        }
+                    }
+                    if unique {
+                        tasks_dur.push((task.task_name.clone(), tag_duration))
+                    }
+                }
+
+                // Sort tags and tasks in descending order by duration
+                tasks_dur.sort_by_key(|k| k.1);
+                tasks_dur.reverse();
+                sorted_tasks_by_duration.push((tag_name, total_duration, tasks_dur));
+                sorted_tasks_by_duration.sort_by_key(|k| k.1);
+                sorted_tasks_by_duration.reverse();
+            }
+
+            for mut stbd in sorted_tasks_by_duration {
+                if !only_this_tag || (only_this_tag && user_chosen_tags.contains(&stbd.0[1..].to_string())) {
+                    let header_iter = results_model.append(Some(&all_tasks_iter));
+                    for (task, task_duration) in stbd.2 {
+                        let _child_iter = results_model.insert_with_values(
+                            Some(&header_iter),
+                            None,
+                            &[(0, &task), (1, &FurReport::format_duration(task_duration))]
+                        );
+                    }
+                    if stbd.0 == "#" {
+                        stbd.0 = gettext("no tags").to_string();
+                    }
+                    results_model.set(
+                        &header_iter,
+                        &[(0, &stbd.0), (1, &FurReport::format_duration(stbd.1))]
+                    );
+                }
+            }
+        }
+
+        imp.results_tree.set_model(Some(&results_model));
+        // Automatically expand All Tasks row
+        let all_tasks_path = gtk::TreePath::new_first();
+        imp.results_tree.expand_row(&all_tasks_path, false);
+    }
+
+    fn format_duration(total_time: i64) -> String {
+         // Format total time to readable string
+        let h = total_time / 3600;
+        let m = total_time % 3600 / 60;
+        let s = total_time % 60;
+        format!("{:02}:{:02}:{:02}", h, m, s)
+    }
+}
+
diff --git a/src/ui/task_details.rs b/src/ui/task_details.rs
index 28d1cd1..0ca40ce 100755
--- a/src/ui/task_details.rs
+++ b/src/ui/task_details.rs
@@ -163,9 +163,8 @@ impl FurTaskDetails {
             imp.all_boxes.borrow_mut().push(task_box);
 
             start.connect_clicked(clone!(@weak self as this => move |_|{
-                let window = FurtheranceWindow::default();
                 let dialog = gtk::MessageDialog::new(
-                    Some(&window),
+                    Some(&this),
                     gtk::DialogFlags::MODAL,
                     gtk::MessageType::Question,
                     gtk::ButtonsType::OkCancel,
@@ -250,7 +249,7 @@ impl FurTaskDetails {
                     @strong task, @strong dialog, @weak this => move |_| {
 
                     let delete_confirmation = gtk::MessageDialog::with_markup(
-                        Some(&window),
+                        Some(&dialog),
                         gtk::DialogFlags::MODAL,
                         gtk::MessageType::Question,
                         gtk::ButtonsType::OkCancel,
@@ -490,10 +489,8 @@ impl FurTaskDetails {
 
         // Change all task names at once
         imp.edit_task_names_btn.connect_clicked(clone!(@weak self as this => move|_| {
-            let window = FurtheranceWindow::default();
-
             let dialog = gtk::MessageDialog::new(
-                Some(&window),
+                Some(&this),
                 gtk::DialogFlags::MODAL,
                 gtk::MessageType::Question,
                 gtk::ButtonsType::OkCancel,
@@ -503,14 +500,15 @@ impl FurTaskDetails {
 
             let message_area = dialog.message_area().downcast::<gtk::Box>().unwrap();
             let new_name_entry = gtk::Entry::new();
-            new_name_entry.set_placeholder_text(Some("New Name #tags"));
-            let cant_be_empty = gtk::Label::new(Some("Task name cannot be empty."));
+            new_name_entry.set_placeholder_text(Some(&gettext("New Name #tags")));
+            let cant_be_empty = gtk::Label::new(Some(&gettext("Task name cannot be empty.")));
             cant_be_empty.add_css_class("error_message");
             cant_be_empty.hide();
             message_area.append(&new_name_entry);
             message_area.append(&cant_be_empty);
 
             dialog.connect_response(move |dialog, resp| {
+                let window = FurtheranceWindow::default();
                 cant_be_empty.hide();
                 if resp == gtk::ResponseType::Ok {
                     let new_name_text = new_name_entry.text();
@@ -557,11 +555,9 @@ impl FurTaskDetails {
 
     fn setup_delete_all(&self) {
         let imp = imp::FurTaskDetails::from_instance(self);
-        let window = FurtheranceWindow::default();
-
         imp.delete_all_btn.connect_clicked(clone!(@weak self as this => move |_|{
             let dialog = gtk::MessageDialog::with_markup(
-                Some(&window),
+                Some(&this),
                 gtk::DialogFlags::MODAL,
                 gtk::MessageType::Warning,
                 gtk::ButtonsType::None,
diff --git a/src/ui/tasks_group.rs b/src/ui/tasks_group.rs
index 2453991..bd55c36 100755
--- a/src/ui/tasks_group.rs
+++ b/src/ui/tasks_group.rs
@@ -81,10 +81,9 @@ impl FurTasksGroup {
         // Check if tasks have the same name. If they do, make one listbox row for all of them.
         // If they don't, move on.
         let mut tasks_by_name: Vec<Vec<database::Task>> = Vec::new();
-        let mut unique: bool;
 
         for task in &tasks {
-            unique = true;
+            let mut unique = true;
             for i in 0..tasks_by_name.len() {
                 if tasks_by_name[i][0].task_name == task.task_name
                     && ( ( settings_manager::get_bool("show-tags")