[gnome-todo] todo-txt: add support for list background color



commit a6787314bf7fcd3a6e9d6a287557fcb85959c1b5
Author: Rohit Kaushik <kaushikrohit325 gmail com>
Date:   Thu May 24 17:10:53 2018 +0530

    todo-txt: add support for list background color

 plugins/todo-txt/gtd-provider-todo-txt.c | 176 +++++++++++++++++++++++++++----
 plugins/todo-txt/gtd-todo-txt-parser.c   | 166 +++++++++++++++++++++--------
 plugins/todo-txt/gtd-todo-txt-parser.h   |  12 ++-
 3 files changed, 288 insertions(+), 66 deletions(-)
---
diff --git a/plugins/todo-txt/gtd-provider-todo-txt.c b/plugins/todo-txt/gtd-provider-todo-txt.c
index 0dcb5ac..a1ac591 100644
--- a/plugins/todo-txt/gtd-provider-todo-txt.c
+++ b/plugins/todo-txt/gtd-provider-todo-txt.c
@@ -74,6 +74,9 @@ enum
   LAST_PROP
 };
 
+typedef gboolean (*GtdLineParserFunc) (GtdProviderTodoTxt *self,
+                                       const gchar        *line,
+                                       GError            **error);
 
 /*
  * Auxiliary methods
@@ -128,11 +131,27 @@ print_task (GString *output,
   g_string_append (output, "\n");
 }
 
+static gchar*
+rgba_to_hex (GdkRGBA *color)
+{
+  g_autofree gchar *color_str = NULL;
+  gint r;
+  gint g;
+  gint b;
+
+  color_str = gdk_rgba_to_string (color);
+  sscanf (color_str, "rgb(%d,%d,%d)", &r, &g, &b);
+
+  return g_strdup_printf ("#%02x%02x%02x", r, g, b);
+}
+
 static void
 update_source (GtdProviderTodoTxt *self)
 {
   g_autofree gchar *output_path = NULL;
   g_autoptr (GString) contents = NULL;
+  g_autoptr (GString) color_line = NULL;
+  g_autoptr (GString) list_line = NULL;
   g_autoptr (GError) error = NULL;
   GtdTaskList *list;
   guint i;
@@ -140,6 +159,8 @@ update_source (GtdProviderTodoTxt *self)
   GTD_ENTRY;
 
   contents = g_string_new ("");
+  color_line = g_string_new ("");
+  list_line = g_string_new ("");
 
   /* Save the tasks first */
   for (i = 0; i < self->cache->len; i++)
@@ -155,24 +176,40 @@ update_source (GtdProviderTodoTxt *self)
         print_task (contents, l->data);
     }
 
-  /* Then the task lists */
+  /* Initialize lists & colors custom lines */
+  g_string_append_printf (list_line, "h:1 Lists ");
+  g_string_append_printf (color_line, "h:1 Colors ");
+
+  /* Then the task lists and color */
   for (i = 0; i < self->cache->len; i++)
     {
       g_autofree gchar *color_str = NULL;
       g_autoptr (GdkRGBA) color = NULL;
+      const gchar *list_name;
 
       list = g_ptr_array_index (self->cache, i);
-
-      /* Print the list as the first line */
+      list_name = gtd_task_list_get_name (list);
       color = gtd_task_list_get_color (list);
-      color_str = gdk_rgba_to_string (color);
+      color_str = rgba_to_hex (color);
+
+      g_string_append_printf (list_line, "@%s", list_name);
+      g_string_append_printf (color_line, "%s:%s", list_name, color_str);
 
-      g_string_append_printf (contents,
-                              "h:1 @%s color:%s\n",
-                              gtd_task_list_get_name (list),
-                              color_str);
+      if (i < self->cache->len - 1)
+        {
+          g_string_append (list_line, " ");
+          g_string_append (color_line, " ");
+        }
     }
 
+  /* Append newline to end of custom lines */
+  g_string_append (list_line, "\n");
+  g_string_append (color_line, "\n");
+
+  /* Finally add custom lines to todo.txt content */
+  g_string_append (contents, list_line->str);
+  g_string_append (contents, color_line->str);
+
   output_path = g_file_get_path (self->source_file);
   g_file_set_contents (output_path, contents->str, contents->len, &error);
 
@@ -200,18 +237,31 @@ add_task_list (GtdProviderTodoTxt *self,
   self->task_lists = g_list_append (self->task_lists, list);
 }
 
-static void
-parse_task_list (GtdProviderTodoTxt *self,
-                 const gchar        *line)
+static gboolean
+parse_lists_line (GtdProviderTodoTxt *self,
+                  const gchar        *line,
+                  GError            **error)
 {
-  g_autoptr (GtdTaskList) list = NULL;
+  g_autoptr (GPtrArray) lists = NULL;
+  guint i;
 
-  list = gtd_todo_txt_parser_parse_task_list (GTD_PROVIDER (self), line);
+  lists = gtd_todo_txt_parser_parse_task_lists (GTD_PROVIDER (self), line, error);
 
-  if (!list)
-    return;
+  if (!lists)
+    return FALSE;
+
+  for (i = 0; i < lists->len; i++)
+    add_task_list (self, g_ptr_array_index (lists, i));
 
-  add_task_list (self, g_steal_pointer (&list));
+  return TRUE;
+}
+
+static gboolean
+parse_list_colors_line (GtdProviderTodoTxt *self,
+                        const gchar        *line,
+                        GError            **error)
+{
+  return gtd_todo_txt_parser_parse_task_list_color (self->lists, line, error);
 }
 
 static void
@@ -254,13 +304,54 @@ parse_task (GtdProviderTodoTxt *self,
   self->task_counter++;
 }
 
+struct
+{
+  GtdTodoTxtLineType  type;
+  const gchar        *identifier;
+  GtdLineParserFunc   parse;
+} custom_lines_vtable[] =
+{
+  { GTD_TODO_TXT_LINE_TYPE_TASKLIST,    "Lists",  parse_lists_line },
+  { GTD_TODO_TXT_LINE_TYPE_LIST_COLORS, "Colors", parse_list_colors_line }
+};
+
+static GPtrArray*
+remove_irrelevant_lines (GStrv lines)
+{
+  g_autoptr (GPtrArray) l = NULL;
+  guint len;
+  guint i;
+
+  len = g_strv_length (lines);
+  l = g_ptr_array_new ();
+
+  for (i = 0; i < len; i++)
+    {
+      gchar *line;
+
+      line = lines[i];
+
+      g_strstrip (line);
+
+      if (!line || g_str_equal (line, "") || g_str_equal (line, "\n"))
+        continue;
+
+      g_ptr_array_add (l, lines[i]);
+    }
+
+  return  g_steal_pointer (&l);
+}
+
 static void
 reload_tasks (GtdProviderTodoTxt *self)
 {
   g_autofree gchar *input_path = NULL;
   g_autofree gchar *file_contents = NULL;
   g_autoptr (GError) error = NULL;
+  g_autoptr (GPtrArray) valid_lines = NULL;
   g_auto (GStrv) lines = NULL;
+  guint vtable_len;
+  guint n_lines;
   guint i;
 
   GTD_ENTRY;
@@ -279,14 +370,53 @@ reload_tasks (GtdProviderTodoTxt *self)
 
   self->task_counter = 0;
 
+  if (g_str_equal (file_contents, ""))
+    return;
+
   lines = g_strsplit (file_contents, "\n", -1);
+  valid_lines = remove_irrelevant_lines (lines);
+  n_lines = valid_lines->len;
+  vtable_len = G_N_ELEMENTS (custom_lines_vtable);
+
+  /* First parse the custom lines at the end of todo.txt */
 
-  for (i = 0; lines && lines[i]; i++)
+  for (i = 0; i < vtable_len; i++)
+    {
+      GtdTodoTxtLineType line_type;
+      const gchar *line;
+
+      if (n_lines < vtable_len)
+        break;
+
+      line = g_ptr_array_index(valid_lines, n_lines - vtable_len + i);
+
+      line_type = gtd_todo_txt_parser_get_line_type (line, &error);
+
+      if (error)
+        {
+          g_warning ("Error parsing custom line %d: %s", n_lines - vtable_len + i, error->message);
+          g_clear_error (&error);
+          continue;
+        }
+
+      if (custom_lines_vtable[i].type == line_type)
+        custom_lines_vtable[i].parse (self, line, &error);
+
+      if (error)
+        {
+          g_warning ("Error parsing custom line %d: %s", n_lines - vtable_len + i, error->message);
+          g_clear_error (&error);
+          continue;
+        }
+    }
+
+  /* Then regular task lines */
+  for (i = 0; i < n_lines - vtable_len; i++)
     {
       GtdTodoTxtLineType line_type;
       gchar *line;
 
-      line = lines[i];
+      line = g_ptr_array_index (valid_lines, i);
 
       /* Last element of the array is NULL */
       if (!line || line[0] == '\0')
@@ -308,12 +438,20 @@ reload_tasks (GtdProviderTodoTxt *self)
       switch (line_type)
         {
         case GTD_TODO_TXT_LINE_TYPE_TASKLIST:
-          parse_task_list (self, line);
           break;
 
         case GTD_TODO_TXT_LINE_TYPE_TASK:
           parse_task (self, line);
           break;
+
+        case GTD_TODO_TXT_LINE_TYPE_LIST_COLORS:
+          break;
+
+        case GTD_TODO_TXT_LINE_TYPE_UNKNOWN:
+          break;
+
+        default:
+          break;
         }
     }
 
diff --git a/plugins/todo-txt/gtd-todo-txt-parser.c b/plugins/todo-txt/gtd-todo-txt-parser.c
index 5e1db05..6ba1ff0 100644
--- a/plugins/todo-txt/gtd-todo-txt-parser.c
+++ b/plugins/todo-txt/gtd-todo-txt-parser.c
@@ -197,12 +197,13 @@ parse_token_id (const gchar           *token,
 }
 
 static GStrv
-tokenize_line (const gchar *line)
+tokenize_line (const gchar *line,
+               const gchar *delimiter)
 {
   GStrv tokens = NULL;
   gsize i;
 
-  tokens = g_strsplit (line, " ", -1);
+  tokens = g_strsplit (line, delimiter, -1);
 
   for (i = 0; tokens && tokens[i]; i++)
     g_strstrip (tokens[i]);
@@ -235,7 +236,7 @@ gtd_todo_txt_parser_parse_task (GtdProvider  *provider,
   state.in_description = FALSE;
 
   task = GTD_TASK (gtd_provider_todo_txt_generate_task (GTD_PROVIDER_TODO_TXT (provider)));
-  tokens = tokenize_line (line);
+  tokens = tokenize_line (line, " ");
 
   for (i = 0; tokens && tokens[i]; i++)
     {
@@ -304,73 +305,142 @@ gtd_todo_txt_parser_parse_task (GtdProvider  *provider,
 }
 
 /**
- * gtd_todo_txt_parser_parse_task_list:
+ * gtd_todo_txt_parser_parse_task_lists:
  * provider: the @GtdProvider of the new tasklist
  * @line: the tasklist line to be parsed
  *
- * Parses a @GtdTaskList from @line. If there is a 'color:' token,
- * it is taken into account.
+ * Parses a list of @GtdTaskList from @line.
  *
- * Returns: (transfer full)(nullable): A @GtdTaskList
+ * Returns: (transfer full)(nullable): A @GPtrArray
  */
-GtdTaskList*
-gtd_todo_txt_parser_parse_task_list (GtdProvider *provider,
-                                     const gchar *line)
+GPtrArray*
+gtd_todo_txt_parser_parse_task_lists (GtdProvider *provider,
+                                      const gchar *line,
+                                      GError     **error)
 {
-  g_autoptr (GtdTaskList) new_list = NULL;
-  g_autoptr (GString) list_name = NULL;
-  g_autofree gchar *color = NULL;
-  g_auto (GStrv) tokens = NULL;
+  g_auto (GStrv) lists = NULL;
+  g_autoptr (GPtrArray) l = NULL;
   guint i;
 
-  tokens = tokenize_line (line);
-  list_name = g_string_new (NULL);
+  l = g_ptr_array_new ();
+  lists = tokenize_line (line + strlen ("h:1 Lists "), " ");
 
   GTD_TRACE_MSG ("Parsing tasklist from line '%s'", line);
 
-  for (i = 0; tokens && tokens[i]; i++)
+  for (i = 0; lists && lists[i]; i++)
     {
-      const gchar *token = tokens[i];
+      g_autoptr (GtdTaskList) new_list = NULL;
+      g_autoptr (GString) list_name = NULL;
+      g_autofree gchar *color = NULL;
+      g_auto (GStrv) tokens = NULL;
+      const gchar *token = lists[i];
 
       if (!token)
         break;
 
-      /* Color */
-      if (g_str_has_prefix (token, "color:"))
-        {
-          color = g_strdup (token + strlen ("color:"));
-          continue;
-        }
+      list_name = g_string_new (NULL);
+      g_string_append_printf (list_name, "%s ", token[0] == '@' ? token + 1 : token);
 
-      /* Hidden token */
-      if (g_str_has_prefix (token, "h:1"))
+      if (list_name->len == 0)
         continue;
 
-      /* Title */
-      g_string_append_printf (list_name, "%s ", token[0] == '@' ? token + 1 : token);
+      g_strstrip (list_name->str);
+
+      new_list = g_object_new (GTD_TYPE_TASK_LIST,
+                               "provider", provider,
+                               "name", list_name->str,
+                               "is-removable", TRUE,
+                               NULL);
+
+      g_ptr_array_add (l, g_steal_pointer (&new_list));
     }
 
-  if (list_name->len == 0)
-    return NULL;
+  return g_steal_pointer (&l);
+}
 
-  g_strstrip (list_name->str);
+/**
+ * gtd_todo_txt_parser_parse_task_list_color:
+ * map: the @GHashTable of task lists
+ * @line: the tasklist line to be parsed
+ *
+ * Parses colors from line and set the corresponding
+ * list color.
+ *
+ * Returns: %TRUE if color line was parsed without error,
+ * %FALSE otherwise.
+ */
+gboolean
+gtd_todo_txt_parser_parse_task_list_color (GHashTable  *name_to_tasklist,
+                                           const gchar *line,
+                                           GError     **error)
+{
+  g_auto (GStrv) lists = NULL;
+  guint tokens_len_after_split = 2;
+  guint i;
 
-  new_list = g_object_new (GTD_TYPE_TASK_LIST,
-                           "provider", provider,
-                           "name", list_name->str,
-                           "is-removable", TRUE,
-                           NULL);
+  lists = tokenize_line (line + strlen ("h:1 Colors "), " ");
 
-  if (color)
+  GTD_TRACE_MSG ("Parsing colors from line '%s'", line);
+
+  for (i = 0; lists && lists[i]; i++)
     {
-      GdkRGBA rgba;
+      gchar *list_name = NULL;
+      gchar *color = NULL;
+      g_auto (GStrv) tokens = NULL;
+      const gchar *list = lists[i];
+      GtdTaskList *task_list;
 
-      gdk_rgba_parse (&rgba, color);
+      if (!list)
+        break;
 
-      gtd_task_list_set_color (new_list, &rgba);
-    }
+      tokens = tokenize_line (list, ":");
 
-  return g_steal_pointer (&new_list);
+      if (g_strv_length (tokens) != tokens_len_after_split)
+        {
+          g_set_error (error,
+                       GTD_TODO_TXT_PARSER_ERROR,
+                       GTD_TODO_TXT_PARSER_INVALID_LINE,
+                       "Invalid list found in color line");
+
+          GTD_RETURN (FALSE);
+        }
+
+      list_name = tokens[0];
+      color = tokens[1];
+
+      g_strstrip (list_name);
+      g_strstrip (color);
+
+      task_list = g_hash_table_lookup (name_to_tasklist, list_name);
+
+      if (!task_list)
+        {
+          g_set_error (error,
+                       GTD_TODO_TXT_PARSER_ERROR,
+                       GTD_TODO_TXT_PARSER_INVALID_LINE,
+                       "Invalid list found in color line");
+
+          GTD_RETURN (FALSE);
+        }
+
+      if (color && g_strcmp0 (color, "") != 0 && g_strcmp0 (color, "null") != 0)
+        {
+          GdkRGBA rgba;
+
+          if (!gdk_rgba_parse (&rgba, color))
+            {
+              g_set_error (error,
+                           GTD_TODO_TXT_PARSER_ERROR,
+                           GTD_TODO_TXT_PARSER_INVALID_COLOR_HEX,
+                           "Invalid color found");
+
+              GTD_RETURN (FALSE);
+            }
+
+          gtd_task_list_set_color (task_list, &rgba);
+        }
+    }
+  return TRUE;
 }
 
 /**
@@ -387,7 +457,7 @@ gtd_todo_txt_parser_get_line_type (const gchar  *line,
                                    GError      **error)
 {
   GtdTodoTxtLineType line_type;
-  g_auto (GStrv) tokens;
+  g_auto (GStrv) tokens = NULL;
   GtdTodoTxtParserState state;
   gboolean task_list_name_tk;
   Token token_id;
@@ -395,9 +465,15 @@ gtd_todo_txt_parser_get_line_type (const gchar  *line,
 
   GTD_ENTRY;
 
-  tokens = tokenize_line (line);
+  if (g_str_has_prefix (line, "h:1 Lists"))
+    return GTD_TODO_TXT_LINE_TYPE_TASKLIST;
+
+  if (g_str_has_prefix (line, "h:1 Colors"))
+    return GTD_TODO_TXT_LINE_TYPE_LIST_COLORS;
+
+  tokens = tokenize_line (line, " ");
   state.last_token = TOKEN_START;
-  line_type = GTD_TODO_TXT_LINE_TYPE_TASKLIST;
+  line_type = GTD_TODO_TXT_LINE_TYPE_UNKNOWN;
   task_list_name_tk = FALSE;
 
   for (i = 0; tokens && tokens[i]; i++)
diff --git a/plugins/todo-txt/gtd-todo-txt-parser.h b/plugins/todo-txt/gtd-todo-txt-parser.h
index be758aa..b94fd07 100644
--- a/plugins/todo-txt/gtd-todo-txt-parser.h
+++ b/plugins/todo-txt/gtd-todo-txt-parser.h
@@ -29,6 +29,7 @@ G_BEGIN_DECLS
 typedef enum
 {
   GTD_TODO_TXT_PARSER_INVALID_DUE_DATE,
+  GTD_TODO_TXT_PARSER_INVALID_COLOR_HEX,
   GTD_TODO_TXT_PARSER_INVALID_LINE,
   GTD_TODO_TXT_PARSER_UNSUPPORTED_TOKEN,
   GTD_TODO_TXT_PARSER_WRONG_LINE_TYPE,
@@ -38,6 +39,8 @@ typedef enum
 {
   GTD_TODO_TXT_LINE_TYPE_TASKLIST,
   GTD_TODO_TXT_LINE_TYPE_TASK,
+  GTD_TODO_TXT_LINE_TYPE_LIST_COLORS,
+  GTD_TODO_TXT_LINE_TYPE_UNKNOWN
 } GtdTodoTxtLineType;
 
 #define GTD_TODO_TXT_PARSER_ERROR (gtd_todo_txt_parser_error_quark ())
@@ -47,13 +50,18 @@ GQuark               gtd_todo_txt_parser_error_quark             (void);
 GtdTodoTxtLineType   gtd_todo_txt_parser_get_line_type           (const gchar       *line,
                                                                   GError           **error);
 
-GtdTaskList*         gtd_todo_txt_parser_parse_task_list         (GtdProvider       *provider,
-                                                                  const gchar       *line);
+GPtrArray*           gtd_todo_txt_parser_parse_task_lists        (GtdProvider       *provider,
+                                                                  const gchar       *line,
+                                                                  GError           **error);
 
 GtdTask*             gtd_todo_txt_parser_parse_task              (GtdProvider       *provider,
                                                                   const gchar       *line,
                                                                   gchar            **out_list_name);
 
+gboolean             gtd_todo_txt_parser_parse_task_list_color   (GHashTable        *name_to_tasklist,
+                                                                  const gchar       *line,
+                                                                  GError           **error);
+
 G_END_DECLS
 
 #endif /* GTD_TODO_TXT_PARSER_H */


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