+ default:
+ assert(false);
+ }
+}
+
+/*
+ * Use this function to get a container’s border style. This is important
+ * because when inside a stack, the border style is always BS_NORMAL.
+ * For tabbed mode, the same applies, with one exception: when the container is
+ * borderless and the only element in the tabbed container, the border is not
+ * rendered.
+ *
+ * For children of a CT_DOCKAREA, the border style is always none.
+ *
+ */
+int con_border_style(Con *con) {
+ Con *fs = con_get_fullscreen_con(con->parent);
+ if (fs == con) {
+ DLOG("this one is fullscreen! overriding BS_NONE\n");
+ return BS_NONE;
+ }
+
+ if (con->parent->layout == L_STACKED)
+ return (con_num_children(con->parent) == 1 ? con->border_style : BS_NORMAL);
+
+ if (con->parent->layout == L_TABBED && con->border_style != BS_NORMAL)
+ return (con_num_children(con->parent) == 1 ? con->border_style : BS_NORMAL);
+
+ if (con->parent->type == CT_DOCKAREA)
+ return BS_NONE;
+
+ return con->border_style;
+}
+
+/*
+ * This function changes the layout of a given container. Use it to handle
+ * special cases like changing a whole workspace to stacked/tabbed (creates a
+ * new split container before).
+ *
+ */
+void con_set_layout(Con *con, int layout) {
+ /* When the container type is CT_WORKSPACE, the user wants to change the
+ * whole workspace into stacked/tabbed mode. To do this and still allow
+ * intuitive operations (like level-up and then opening a new window), we
+ * need to create a new split container. */
+ if (con->type == CT_WORKSPACE) {
+ DLOG("Creating new split container\n");
+ /* 1: create a new split container */
+ Con *new = con_new(NULL);
+ new->parent = con;
+
+ /* 2: set the requested layout on the split con */
+ new->layout = layout;
+
+ /* 3: While the layout is irrelevant in stacked/tabbed mode, it needs
+ * to be set. Otherwise, this con will not be interpreted as a split
+ * container. */
+ if (config.default_orientation == NO_ORIENTATION) {
+ new->orientation = (con->rect.height > con->rect.width) ? VERT : HORIZ;
+ } else {
+ new->orientation = config.default_orientation;
+ }
+
+ Con *old_focused = TAILQ_FIRST(&(con->focus_head));
+ if (old_focused == TAILQ_END(&(con->focus_head)))
+ old_focused = NULL;
+
+ /* 4: move the existing cons of this workspace below the new con */
+ DLOG("Moving cons\n");
+ Con *child;
+ while (!TAILQ_EMPTY(&(con->nodes_head))) {
+ child = TAILQ_FIRST(&(con->nodes_head));
+ con_detach(child);
+ con_attach(child, new, true);
+ }
+
+ /* 4: attach the new split container to the workspace */
+ DLOG("Attaching new split to ws\n");
+ con_attach(new, con, false);
+
+ if (old_focused)
+ con_focus(old_focused);
+
+ tree_flatten(croot);
+
+ return;
+ }
+
+ con->layout = layout;
+}
+
+/*
+ * Callback which will be called when removing a child from the given con.
+ * Kills the container if it is empty and replaces it with the child if there
+ * is exactly one child.
+ *
+ */
+static void con_on_remove_child(Con *con) {
+ DLOG("on_remove_child\n");
+
+ /* Every container 'above' (in the hierarchy) the workspace content should
+ * not be closed when the last child was removed */
+ if (con->type == CT_WORKSPACE ||
+ con->type == CT_OUTPUT ||
+ con->type == CT_ROOT ||
+ con->type == CT_DOCKAREA) {
+ DLOG("not handling, type = %d\n", con->type);
+ return;
+ }
+
+ /* TODO: check if this container would swallow any other client and
+ * don’t close it automatically. */
+ int children = con_num_children(con);
+ if (children == 0) {
+ DLOG("Container empty, closing\n");
+ tree_close(con, DONT_KILL_WINDOW, false);
+ return;
+ }
+}
+
+/*
+ * Determines the minimum size of the given con by looking at its children (for
+ * split/stacked/tabbed cons). Will be called when resizing floating cons
+ *
+ */
+Rect con_minimum_size(Con *con) {
+ DLOG("Determining minimum size for con %p\n", con);
+
+ if (con_is_leaf(con)) {
+ DLOG("leaf node, returning 75x50\n");
+ return (Rect){ 0, 0, 75, 50 };
+ }
+
+ if (con->type == CT_FLOATING_CON) {
+ DLOG("floating con\n");
+ Con *child = TAILQ_FIRST(&(con->nodes_head));
+ return con_minimum_size(child);
+ }
+
+ if (con->layout == L_STACKED || con->layout == L_TABBED) {
+ uint32_t max_width = 0, max_height = 0, deco_height = 0;
+ Con *child;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
+ Rect min = con_minimum_size(child);
+ deco_height += child->deco_rect.height;
+ max_width = max(max_width, min.width);
+ max_height = max(max_height, min.height);
+ }
+ DLOG("stacked/tabbed now, returning %d x %d + deco_rect = %d\n",
+ max_width, max_height, deco_height);
+ return (Rect){ 0, 0, max_width, max_height + deco_height };
+ }
+
+ /* For horizontal/vertical split containers we sum up the width (h-split)
+ * or height (v-split) and use the maximum of the height (h-split) or width
+ * (v-split) as minimum size. */
+ if (con->orientation == HORIZ || con->orientation == VERT) {
+ uint32_t width = 0, height = 0;
+ Con *child;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
+ Rect min = con_minimum_size(child);
+ if (con->orientation == HORIZ) {
+ width += min.width;
+ height = max(height, min.height);
+ } else {
+ height += min.height;
+ width = max(width, min.width);
+ }
+ }
+ DLOG("split container, returning width = %d x height = %d\n", width, height);
+ return (Rect){ 0, 0, width, height };
+ }
+
+ ELOG("Unhandled case, type = %d, layout = %d, orientation = %d\n",
+ con->type, con->layout, con->orientation);