From db38b9bbf3cce4083c538209a7ce5ef1a1cf5f3f Mon Sep 17 00:00:00 2001
From: Ryan Dwyer <ryandwyer1@gmail.com>
Date: Fri, 25 May 2018 21:07:59 +1000
Subject: [PATCH] Clean up container title functions

* Add and use lenient_strcat and lenient_strncat functions
* Rename `concatenate_child_titles` function as that's no longer what it
does
* Rename `container_notify_child_title_changed` because we only need to
notify that the tree structure has changed, not titles
* Don't notify parents when a child changes its title
* Update ancestor titles when changing a container's layout
	* Eg. create nested tabs and change the inner container to stacking
* No need to store tree presentation in both container->name and
formatted_title
---
 common/stringop.c             | 14 ++++++
 include/stringop.h            |  5 ++
 include/sway/tree/container.h |  6 ++-
 sway/commands/layout.c        |  1 +
 sway/tree/container.c         | 90 +++++++++++++++--------------------
 sway/tree/layout.c            | 12 ++---
 sway/tree/view.c              | 34 ++++---------
 7 files changed, 79 insertions(+), 83 deletions(-)

diff --git a/common/stringop.c b/common/stringop.c
index 4a37543d..d9ae9925 100644
--- a/common/stringop.c
+++ b/common/stringop.c
@@ -55,6 +55,20 @@ void strip_quotes(char *str) {
 	*end = '\0';
 }
 
+char *lenient_strcat(char *dest, const char *src) {
+	if (dest && src) {
+		return strcat(dest, src);
+	}
+	return dest;
+}
+
+char *lenient_strncat(char *dest, const char *src, size_t len) {
+	if (dest && src) {
+		return strncat(dest, src, len);
+	}
+	return dest;
+}
+
 // strcmp that also handles null pointers.
 int lenient_strcmp(char *a, char *b) {
 	if (a == b) {
diff --git a/include/stringop.h b/include/stringop.h
index 7c29a745..e7f58011 100644
--- a/include/stringop.h
+++ b/include/stringop.h
@@ -1,5 +1,6 @@
 #ifndef _SWAY_STRINGOP_H
 #define _SWAY_STRINGOP_H
+#include <stdlib.h>
 #include "list.h"
 
 #if !HAVE_DECL_SETENV
@@ -14,6 +15,10 @@ char *strip_whitespace(char *str);
 char *strip_comments(char *str);
 void strip_quotes(char *str);
 
+// strcat that does nothing if dest or src is NULL
+char *lenient_strcat(char *dest, const char *src);
+char *lenient_strncat(char *dest, const char *src, size_t len);
+
 // strcmp that also handles null pointers.
 int lenient_strcmp(char *a, char *b);
 
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 493c70e2..a5f591ce 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -216,7 +216,11 @@ void container_update_title_textures(struct sway_container *container);
  */
 void container_calculate_title_height(struct sway_container *container);
 
-void container_notify_child_title_changed(struct sway_container *container);
+/**
+ * Notify a container that a tree modification has changed in its children,
+ * so the container can update its tree representation.
+ */
+void container_notify_subtree_changed(struct sway_container *container);
 
 /**
  * Return the height of a regular title bar.
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 58728f16..6b44b001 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -52,6 +52,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
 		}
 	}
 
+	container_notify_subtree_changed(parent);
 	arrange_children_of(parent);
 
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 9cf18f61..f29a9adc 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -21,6 +21,7 @@
 #include "sway/tree/view.h"
 #include "sway/tree/workspace.h"
 #include "log.h"
+#include "stringop.h"
 
 static list_t *bfs_queue;
 
@@ -774,42 +775,36 @@ void container_calculate_title_height(struct sway_container *container) {
 }
 
 /**
- * Calculate and return the length of the concatenated child titles.
- * An example concatenated title is: V[Terminal, Firefox]
- * If buffer is not NULL, also populate the buffer with the concatenated title.
+ * Calculate and return the length of the tree representation.
+ * An example tree representation is: V[Terminal, Firefox]
+ * If buffer is not NULL, also populate the buffer with the representation.
  */
-static size_t concatenate_child_titles(struct sway_container *parent,
-		char *buffer) {
-	size_t len = 2; // V[
-	if (buffer) {
-		switch (parent->layout) {
-		case L_VERT:
-			strcpy(buffer, "V[");
-			break;
-		case L_HORIZ:
-			strcpy(buffer, "H[");
-			break;
-		case L_TABBED:
-			strcpy(buffer, "T[");
-			break;
-		case L_STACKED:
-			strcpy(buffer, "S[");
-			break;
-		case L_FLOATING:
-			strcpy(buffer, "F[");
-			break;
-		case L_NONE:
-			strcpy(buffer, "D[");
-			break;
-		}
+static size_t get_tree_representation(struct sway_container *parent, char *buffer) {
+	size_t len = 2;
+	switch (parent->layout) {
+	case L_VERT:
+		lenient_strcat(buffer, "V[");
+		break;
+	case L_HORIZ:
+		lenient_strcat(buffer, "H[");
+		break;
+	case L_TABBED:
+		lenient_strcat(buffer, "T[");
+		break;
+	case L_STACKED:
+		lenient_strcat(buffer, "S[");
+		break;
+	case L_FLOATING:
+		lenient_strcat(buffer, "F[");
+		break;
+	case L_NONE:
+		lenient_strcat(buffer, "D[");
+		break;
 	}
-
 	for (int i = 0; i < parent->children->length; ++i) {
 		if (i != 0) {
-			len += 1;
-			if (buffer) {
-				strcat(buffer, " ");
-			}
+			++len;
+			lenient_strcat(buffer, " ");
 		}
 		struct sway_container *child = parent->children->items[i];
 		const char *identifier = NULL;
@@ -819,48 +814,39 @@ static size_t concatenate_child_titles(struct sway_container *parent,
 				identifier = view_get_app_id(child->sway_view);
 			}
 		} else {
-			identifier = child->name;
+			identifier = child->formatted_title;
 		}
 		if (identifier) {
 			len += strlen(identifier);
-			if (buffer) {
-				strcat(buffer, identifier);
-			}
+			lenient_strcat(buffer, identifier);
 		} else {
 			len += 6;
-			if (buffer) {
-				strcat(buffer, "(null)");
-			}
+			lenient_strcat(buffer, "(null)");
 		}
 	}
-
-	len += 1;
-	if (buffer) {
-		strcat(buffer, "]");
-	}
+	++len;
+	lenient_strcat(buffer, "]");
 	return len;
 }
 
-void container_notify_child_title_changed(struct sway_container *container) {
+void container_notify_subtree_changed(struct sway_container *container) {
 	if (!container || container->type != C_CONTAINER) {
 		return;
 	}
-	if (container->formatted_title) {
-		free(container->formatted_title);
-	}
+	free(container->formatted_title);
+	container->formatted_title = NULL;
 
-	size_t len = concatenate_child_titles(container, NULL);
+	size_t len = get_tree_representation(container, NULL);
 	char *buffer = calloc(len + 1, sizeof(char));
 	if (!sway_assert(buffer, "Unable to allocate title string")) {
 		return;
 	}
-	concatenate_child_titles(container, buffer);
+	get_tree_representation(container, buffer);
 
-	container->name = buffer;
 	container->formatted_title = buffer;
 	container_calculate_title_height(container);
 	container_update_title_textures(container);
-	container_notify_child_title_changed(container->parent);
+	container_notify_subtree_changed(container->parent);
 }
 
 size_t container_titlebar_height() {
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 91759f7b..edc24deb 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -149,7 +149,7 @@ struct sway_container *container_remove_child(struct sway_container *child) {
 		}
 	}
 	child->parent = NULL;
-	container_notify_child_title_changed(parent);
+	container_notify_subtree_changed(parent);
 
 	return parent;
 }
@@ -184,8 +184,8 @@ void container_move_to(struct sway_container *container,
 		container_sort_workspaces(new_parent);
 		seat_set_focus(seat, new_parent);
 	}
-	container_notify_child_title_changed(old_parent);
-	container_notify_child_title_changed(new_parent);
+	container_notify_subtree_changed(old_parent);
+	container_notify_subtree_changed(new_parent);
 	if (old_parent) {
 		if (old_parent->type == C_OUTPUT) {
 			arrange_output(old_parent);
@@ -497,8 +497,8 @@ void container_move(struct sway_container *container,
 		}
 	}
 
-	container_notify_child_title_changed(old_parent);
-	container_notify_child_title_changed(container->parent);
+	container_notify_subtree_changed(old_parent);
+	container_notify_subtree_changed(container->parent);
 
 	if (old_parent) {
 		seat_set_focus(config->handler_context.seat, old_parent);
@@ -847,7 +847,7 @@ struct sway_container *container_split(struct sway_container *child,
 		container_add_child(cont, child);
 	}
 
-	container_notify_child_title_changed(cont);
+	container_notify_subtree_changed(cont);
 
 	return cont;
 }
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 07157818..2f718a73 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -17,6 +17,7 @@
 #include "sway/tree/workspace.h"
 #include "sway/config.h"
 #include "pango.h"
+#include "stringop.h"
 
 void view_init(struct sway_view *view, enum sway_view_type type,
 		const struct sway_view_impl *impl) {
@@ -470,7 +471,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
 	input_manager_set_focus(input_manager, cont);
 
 	view_update_title(view, false);
-	container_notify_child_title_changed(view->swayc->parent);
+	container_notify_subtree_changed(view->swayc->parent);
 	view_execute_criteria(view);
 
 	container_damage_whole(cont);
@@ -661,49 +662,35 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
 	char *format = view->title_format;
 	char *next = strchr(format, '%');
 	while (next) {
-		if (buffer) {
-			// Copy everything up to the %
-			strncat(buffer, format, next - format);
-		}
+		// Copy everything up to the %
+		lenient_strncat(buffer, format, next - format);
 		len += next - format;
 		format = next;
 
 		if (strncmp(next, "%title", 6) == 0) {
-			if (buffer && title) {
-				strcat(buffer, title);
-			}
+			lenient_strcat(buffer, title);
 			len += title_len;
 			format += 6;
 		} else if (strncmp(next, "%class", 6) == 0) {
-			if (buffer && class) {
-				strcat(buffer, class);
-			}
+			lenient_strcat(buffer, class);
 			len += class_len;
 			format += 6;
 		} else if (strncmp(next, "%instance", 9) == 0) {
-			if (buffer && instance) {
-				strcat(buffer, instance);
-			}
+			lenient_strcat(buffer, instance);
 			len += instance_len;
 			format += 9;
 		} else if (strncmp(next, "%shell", 6) == 0) {
-			if (buffer) {
-				strcat(buffer, shell);
-			}
+			lenient_strcat(buffer, shell);
 			len += shell_len;
 			format += 6;
 		} else {
-			if (buffer) {
-				strcat(buffer, "%");
-			}
+			lenient_strcat(buffer, "%");
 			++format;
 			++len;
 		}
 		next = strchr(format, '%');
 	}
-	if (buffer) {
-		strcat(buffer, format);
-	}
+	lenient_strcat(buffer, format);
 	len += strlen(format);
 
 	return len;
@@ -759,7 +746,6 @@ void view_update_title(struct sway_view *view, bool force) {
 	}
 	container_calculate_title_height(view->swayc);
 	container_update_title_textures(view->swayc);
-	container_notify_child_title_changed(view->swayc->parent);
 	config_update_font_height(false);
 }