+enum {
+ FOCUS_NONE,
+ FOCUS_PREV,
+ FOCUS_NEXT,
+ FOCUS_LAST_CELL
+};
+
+static gboolean
+gtk_cell_area_box_focus (GtkCellArea *area,
+ GtkDirectionType direction)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ gint cycle = FOCUS_NONE;
+ gboolean cycled_focus = FALSE;
+ GtkCellRenderer *focus_cell;
+
+ focus_cell = gtk_cell_area_get_focus_cell (area);
+
+ /* Special case, when there is no activatable cell, focus
+ * is painted around the entire area... in this case we
+ * let focus leave the area directly.
+ */
+ if (focus_cell && !gtk_cell_area_is_activatable (area))
+ {
+ gtk_cell_area_set_focus_cell (area, NULL);
+ return FALSE;
+ }
+
+ switch (direction)
+ {
+ case GTK_DIR_TAB_FORWARD:
+ cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT;
+ break;
+ case GTK_DIR_TAB_BACKWARD:
+ cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV;
+ break;
+ case GTK_DIR_UP:
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell)
+ cycle = FOCUS_PREV;
+ else if (!focus_cell)
+ cycle = FOCUS_LAST_CELL;
+ break;
+ case GTK_DIR_DOWN:
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell)
+ cycle = FOCUS_NEXT;
+ else if (!focus_cell)
+ cycle = FOCUS_LAST_CELL;
+ break;
+ case GTK_DIR_LEFT:
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell)
+ cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV;
+ else if (!focus_cell)
+ cycle = FOCUS_LAST_CELL;
+ break;
+ case GTK_DIR_RIGHT:
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell)
+ cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT;
+ else if (!focus_cell)
+ cycle = FOCUS_LAST_CELL;
+ break;
+ default:
+ break;
+ }
+
+ if (cycle == FOCUS_LAST_CELL)
+ {
+ gtk_cell_area_set_focus_cell (area, priv->last_focus_cell);
+ cycled_focus = TRUE;
+ }
+ else if (cycle != FOCUS_NONE)
+ {
+ gboolean found_cell = FALSE;
+ GList *list;
+ gint i;
+
+ /* If there is no focused cell, focus on the first (or last) one */
+ if (!focus_cell)
+ found_cell = TRUE;
+
+ for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1;
+ cycled_focus == FALSE && i >= 0 && i < priv->groups->len;
+ i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+
+ for (list = (cycle == FOCUS_NEXT) ? g_list_first (group->cells) : g_list_last (group->cells);
+ cycled_focus == FALSE && list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev)
+ {
+ CellInfo *info = list->data;
+
+ if (info->renderer == focus_cell)
+ found_cell = TRUE;
+ else if (found_cell && /* Dont give focus to cells that are siblings to a focus cell */
+ gtk_cell_area_get_focus_from_sibling (area, info->renderer) == NULL)
+ {
+ gtk_cell_area_set_focus_cell (area, info->renderer);
+ cycled_focus = TRUE;
+ }
+ }
+ }
+ }
+
+ if (!cycled_focus)
+ gtk_cell_area_set_focus_cell (area, NULL);
+
+ return cycled_focus;
+}
+