commit - 3f6f7423d2531a24753bcbc6c39ba4ba3a7947f5
commit + 669b5ffa4afd0be5012728963991aee7bf5ba4c1
blob - 4530c73cfa378cf3b17b2842b830a57fbc7fd25e
blob + 207301ac4b20e2a7e056c0d7c4088400c12ec792
--- tog/tog.c
+++ tog/tog.c
struct got_repository *repo;
};
+/*
+ * We implement two types of views: parent views and child views.
+ *
+ * The 'Tab' key switches between a parent view and its child view.
+ * Child views are shown side-by-side to their parent view, provided
+ * there is enough screen estate.
+ *
+ * When a new view is opened from within a parent view, this new view
+ * becomes a child view of the parent view, replacing any existing child.
+ *
+ * When a new view is opened from within a child view, this new view
+ * becomes a parent view which will obscure the views below until the
+ * user quits the new parent view by typing 'q'.
+ *
+ * This list of views contains parent views only.
+ * Child views are only pointed to by their parent view.
+ */
TAILQ_HEAD(tog_view_list_head, tog_view);
+
struct tog_view {
TAILQ_ENTRY(tog_view) entry;
WINDOW *window;
int nlines, ncols, begin_y, begin_x;
int lines, cols; /* copies of LINES and COLS */
int focussed;
+ struct tog_view *parent;
+ struct tog_view *child;
+ int child_focussed;
/* type-specific state */
enum tog_view_type type;
{
const struct got_error *err = NULL;
+ if (view->child) {
+ view_close(view->child);
+ view->child = NULL;
+ }
if (view->close)
err = view->close(view);
if (view->panel)
view->cols = COLS;
return NULL;
+}
+
+static int
+view_is_parent_view(struct tog_view *view)
+{
+ return view->parent == NULL;
+}
+
+static const struct got_error *
+view_close_child(struct tog_view *view)
+{
+ const struct got_error *err;
+
+ if (view->child == NULL)
+ return NULL;
+
+ err = view_close(view->child);
+ view->child = NULL;
+ return err;
+}
+
+static const struct got_error *
+view_set_child(struct tog_view *view, struct tog_view *child)
+{
+ const struct got_error *err = NULL;
+
+ view->child = child;
+ child->parent = view;
+ return err;
}
static int
view_is_splitscreen(struct tog_view *view)
{
- return view->begin_x > 0;
+ return !view_is_parent_view(view) && view->begin_x > 0;
}
static const struct got_error *
struct tog_view_list_head *views)
{
const struct got_error *err = NULL;
- struct tog_view *next, *prev, *v;
+ struct tog_view *v;
int ch;
*new = NULL;
case ERR:
break;
case '\t':
- next = TAILQ_NEXT(view, entry);
- if (next)
- *focus = next;
- else
- *focus = TAILQ_FIRST(views);
+ if (view->child) {
+ *focus = view->child;
+ view->child_focussed = 1;
+ } else if (view->parent) {
+ *focus = view->parent;
+ view->parent->child_focussed = 0;
+ }
break;
- case '~':
- prev = TAILQ_PREV(view, tog_view_list_head, entry);
- if (prev)
- *focus = prev;
- else
- *focus = TAILQ_LAST(views, tog_view_list_head);
- break;
case 'q':
err = view->input(new, dead, focus, view, ch);
*dead = view;
*done = 1;
break;
case 'f':
- if (view->begin_x == 0)
- err = view_splitscreen(view);
- else
- err = view_fullscreen(view);
- if (err)
- break;
- err = view->input(new, dead, focus, view, KEY_RESIZE);
- if (err)
- break;
- *focus = view;
+ if (view_is_parent_view(view)) {
+ if (view->child == NULL)
+ break;
+ if (view_is_splitscreen(view->child)) {
+ *focus = view->child;
+ view->child_focussed = 1;
+ err = view_fullscreen(view->child);
+ } else
+ err = view_splitscreen(view->child);
+ if (err)
+ break;
+ err = view->child->input(new, dead, focus,
+ view->child, KEY_RESIZE);
+ } else {
+ if (view_is_splitscreen(view)) {
+ *focus = view;
+ view->parent->child_focussed = 1;
+ err = view_fullscreen(view);
+ } else {
+ err = view_splitscreen(view);
+ }
+ if (err)
+ break;
+ err = view->input(new, dead, focus, view,
+ KEY_RESIZE);
+ }
break;
case KEY_RESIZE:
TAILQ_FOREACH(v, views, entry) {
PANEL *panel;
struct tog_view *view_above;
+ if (view->parent)
+ return view_vborder(view->parent);
+
panel = panel_above(view->panel);
if (panel == NULL)
return;
int
view_needs_focus_indication(struct tog_view *view)
{
- PANEL *panel;
-
- if (!view->focussed)
+ if (view_is_parent_view(view)) {
+ if (view->child == NULL || view->child_focussed)
+ return 0;
+ if (!view_is_splitscreen(view->child))
+ return 0;
+ } else if (!view_is_splitscreen(view))
return 0;
- panel = panel_above(view->panel);
- if (panel) {
- struct tog_view *view_above = panel_userptr(panel);
- if (view_above->begin_x > view->begin_x)
- return 1;
- }
-
- panel = panel_below(view->panel);
- if (panel) {
- struct tog_view *view_below = panel_userptr(panel);
- if (view->begin_x > view_below->begin_x)
- return 1;
- }
-
- return 0;
+ return view->focussed;
}
static const struct got_error *
{
const struct got_error *err = NULL;
struct tog_view_list_head views;
- struct tog_view *new_view, *dead_view, *focus_view, *main_view, *v;
+ struct tog_view *new_view, *dead_view, *focus_view, *main_view;
int done = 0;
TAILQ_INIT(&views);
if (err)
break;
if (dead_view) {
- TAILQ_REMOVE(&views, dead_view, entry);
+ struct tog_view *prev = NULL;
+
+ if (view_is_parent_view(dead_view))
+ prev = TAILQ_PREV(dead_view,
+ tog_view_list_head, entry);
+ else
+ prev = view->parent;
+
+ if (dead_view->parent)
+ dead_view->parent->child = NULL;
+ else
+ TAILQ_REMOVE(&views, dead_view, entry);
+
err = view_close(dead_view);
if (err || dead_view == main_view)
goto done;
+
if (view == dead_view) {
if (focus_view)
view = focus_view;
- else if (!TAILQ_EMPTY(&views)) {
+ else if (prev)
+ view = prev;
+ else if (!TAILQ_EMPTY(&views))
view = TAILQ_LAST(&views,
tog_view_list_head);
- focus_view = view;
- } else
+ else
view = NULL;
+ if (view) {
+ if (view->child && view->child_focussed)
+ focus_view = view->child;
+ else
+ focus_view = view;
+ }
}
}
if (new_view) {
- struct tog_view *t;
- /* Only allow one view per type. */
- TAILQ_FOREACH_SAFE(v, &views, entry, t) {
- if (v->type != new_view->type)
- continue;
- TAILQ_REMOVE(&views, v, entry);
- err = view_close(v);
- if (err)
- goto done;
- if (v == view)
- view = new_view;
- break;
- }
TAILQ_INSERT_TAIL(&views, new_view, entry);
if (focus_view == NULL)
focus_view = new_view;
}
if (focus_view) {
show_panel(focus_view->panel);
- if (view) {
- if (focus_view->begin_x == 0 &&
- view->begin_x > 0 &&
- focus_view != new_view)
- show_panel(view->panel);
+ if (view)
view->focussed = 0;
- }
focus_view->focussed = 1;
view = focus_view;
if (new_view)
show_panel(new_view->panel);
+ if (view->child && view_is_splitscreen(view->child))
+ show_panel(view->child->panel);
}
- TAILQ_FOREACH(v, &views, entry) {
- err = v->show(v);
+ if (view) {
+ if (view->parent) {
+ err = view->parent->show(view->parent);
+ if (err)
+ return err;
+ }
+ err = view->show(view);
if (err)
return err;
+ if (view->child) {
+ err = view->child->show(view->child);
+ if (err)
+ return err;
+ }
}
update_panels();
doupdate();
goto done;
}
- diff_view = view_open(0, 0, 0, view_split_begin_x(begin_x),
- TOG_VIEW_DIFF);
+ diff_view = view_open(0, 0, 0, begin_x, TOG_VIEW_DIFF);
if (diff_view == NULL) {
err = got_error_from_errno();
goto done;
if (err)
return err;
- tree_view = view_open(0, 0, 0, view_split_begin_x(begin_x),
- TOG_VIEW_TREE);
+ tree_view = view_open(0, 0, 0, begin_x, TOG_VIEW_TREE);
if (tree_view == NULL)
return got_error_from_errno();
const struct got_error *err = NULL;
struct tog_log_view_state *s = &view->state.log;
char *parent_path;
+ struct tog_view *diff_view = NULL, *tree_view = NULL;
+ int begin_x = 0;
switch (ch) {
case 'k':
break;
case KEY_ENTER:
case '\r':
- err = open_diff_view_for_commit(new_view, view->begin_x,
+ if (view_is_parent_view(view))
+ begin_x = view_split_begin_x(view->begin_x);
+ err = open_diff_view_for_commit(&diff_view, begin_x,
s->selected_entry->id, s->selected_entry->commit,
s->repo);
if (err)
break;
- /* Keep log view focussed in split-screen mode. */
- if (view_is_splitscreen(*new_view))
- *focus_view = view;
+ if (view_is_parent_view(view)) {
+ err = view_close_child(view);
+ if (err)
+ return err;
+ err = view_set_child(view, diff_view);
+ if (err) {
+ view_close(diff_view);
+ break;
+ }
+ if (!view_is_splitscreen(diff_view)) {
+ *focus_view = diff_view;
+ view->child_focussed = 1;
+ }
+ } else
+ *new_view = diff_view;
break;
case 't':
- err = browse_commit(new_view, view->begin_x,
+ if (view_is_parent_view(view))
+ begin_x = view_split_begin_x(view->begin_x);
+ err = browse_commit(&tree_view, begin_x,
s->selected_entry, s->repo);
+ if (view_is_parent_view(view)) {
+ err = view_close_child(view);
+ if (err)
+ return err;
+ err = view_set_child(view, tree_view);
+ if (err) {
+ view_close(tree_view);
+ break;
+ }
+ *focus_view = tree_view;
+ view->child_focussed = 1;
+ } else
+ *new_view = tree_view;
break;
case KEY_BACKSPACE:
if (strcmp(s->in_repo_path, "/") == 0)
parent_path);
if (err)
break;
- *new_view = lv;
+ if (view_is_parent_view(view))
+ *new_view = lv;
+ else {
+ view_set_child(view->parent, lv);
+ *dead_view = view;
+ }
}
break;
default:
if (pthread_mutex_unlock(&s->mutex) != 0 && err == NULL)
err = got_error_from_errno();
+ view_vborder(view);
return err;
}
struct got_object *obj = NULL, *pobj = NULL;
struct tog_view *diff_view;
struct tog_blame_view_state *s = &view->state.blame;
+ int begin_x = 0;
if (pthread_mutex_lock(&s->mutex) != 0) {
err = got_error_from_errno();
break;
if (pobj == NULL && obj == NULL)
break;
- diff_view = view_open(0, 0, 0,
- view_split_begin_x(view->begin_x), TOG_VIEW_DIFF);
+
+ if (view_is_parent_view(view))
+ begin_x = view_split_begin_x(view->begin_x);
+ diff_view = view_open(0, 0, 0, begin_x, TOG_VIEW_DIFF);
if (diff_view == NULL) {
err = got_error_from_errno();
break;
}
- err = open_diff_view(diff_view, pobj, obj,
- s->repo);
+ err = open_diff_view(diff_view, pobj, obj, s->repo);
if (err) {
view_close(diff_view);
break;
}
- *new_view = diff_view;
+ if (view_is_parent_view(view)) {
+ err = view_close_child(view);
+ if (err)
+ return err;
+ err = view_set_child(view, diff_view);
+ if (err) {
+ view_close(diff_view);
+ break;
+ }
+ if (!view_is_splitscreen(diff_view)) {
+ *focus_view = diff_view;
+ view->child_focussed = 1;
+ }
+ } else
+ *new_view = diff_view;
if (pobj) {
got_object_close(pobj);
pobj = NULL;
te = SIMPLEQ_NEXT(te, entry);
}
- view_vborder(view);
return err;
}
if (err)
return err;
- blame_view = view_open(0, 0, 0, 0, TOG_VIEW_BLAME);
+ blame_view = view_open(0, 0, 0, begin_x, TOG_VIEW_BLAME);
if (blame_view == NULL)
return got_error_from_errno();
}
static const struct got_error *
-log_tree_entry(struct tog_view **new_view,
+log_tree_entry(struct tog_view **new_view, int begin_x,
struct got_tree_entry *te, struct tog_parent_trees *parents,
struct got_object_id *commit_id, struct got_repository *repo)
{
const struct got_error *err = NULL;
char *path;
- log_view = view_open(0, 0, 0, 0, TOG_VIEW_LOG);
+ log_view = view_open(0, 0, 0, begin_x, TOG_VIEW_LOG);
if (log_view == NULL)
return got_error_from_errno();
&s->ndisplayed, s->tree_label, s->show_ids, parent_path,
s->entries, s->selected, view->nlines, s->tree == s->root);
free(parent_path);
+
+ view_vborder(view);
return err;
}
{
const struct got_error *err = NULL;
struct tog_tree_view_state *s = &view->state.tree;
+ struct tog_view *log_view;
+ int begin_x = 0;
switch (ch) {
case 'i':
s->show_ids = !s->show_ids;
break;
case 'l':
- if (s->selected_entry) {
- err = log_tree_entry(new_view,
- s->selected_entry, &s->parents,
- s->commit_id, s->repo);
- }
+ if (!s->selected_entry)
+ break;
+ if (view_is_parent_view(view))
+ begin_x = view_split_begin_x(view->begin_x);
+ err = log_tree_entry(&log_view, begin_x,
+ s->selected_entry, &s->parents,
+ s->commit_id, s->repo);
+ if (view_is_parent_view(view)) {
+ err = view_close_child(view);
+ if (err)
+ return err;
+ err = view_set_child(view, log_view);
+ if (err) {
+ view_close(log_view);
+ break;
+ }
+ *focus_view = log_view;
+ view->child_focussed = 1;
+ } else
+ *new_view = log_view;
break;
case 'k':
case KEY_UP:
s->selected = 0;
s->first_displayed_entry = NULL;
} else if (S_ISREG(s->selected_entry->mode)) {
- err = blame_tree_entry(new_view, view->begin_x,
- s->selected_entry, &s->parents,
- s->commit_id, s->repo);
+ struct tog_view *blame_view;
+ int begin_x = view_is_parent_view(view) ?
+ view_split_begin_x(view->begin_x) : 0;
+
+ err = blame_tree_entry(&blame_view, begin_x,
+ s->selected_entry, &s->parents, s->commit_id,
+ s->repo);
if (err)
break;
+ if (view_is_parent_view(view)) {
+ err = view_close_child(view);
+ if (err)
+ return err;
+ err = view_set_child(view, blame_view);
+ if (err) {
+ view_close(blame_view);
+ break;
+ }
+ *focus_view = blame_view;
+ view->child_focussed = 1;
+ } else
+ *new_view = blame_view;
}
break;
case KEY_RESIZE: