author | Lars Hjemli <hjemli@gmail.com> | 2008-04-12 18:11:49 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2008-04-12 18:11:49 (UTC) |
commit | 68cb84839f8fbc20688b22202489f4c2a54d3f55 (patch) (side-by-side diff) | |
tree | 315289eaa849faf549c4882b1769b81597587b10 | |
parent | 7c0d2d9fbd3a29d295c8067f7798507853759eae (diff) | |
download | cgit-68cb84839f8fbc20688b22202489f4c2a54d3f55.zip cgit-68cb84839f8fbc20688b22202489f4c2a54d3f55.tar.gz cgit-68cb84839f8fbc20688b22202489f4c2a54d3f55.tar.bz2 |
Reintroduce the branch switcher
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
-rw-r--r-- | cgit.css | 10 | ||||
-rw-r--r-- | ui-shared.c | 7 |
2 files changed, 17 insertions, 0 deletions
@@ -1,406 +1,416 @@ body, table { padding: 0em; margin: 0em; } body { font-family: sans; font-size: 10pt; color: #333; background: white; padding: 4px; } a { color: blue; text-decoration: none; } a:hover { text-decoration: underline; } table { border-collapse: collapse; } table#header { width: 100%; margin-bottom: 1em; } table#header td.logo { width: 96px; } table#header td.main { font-size: 200%; } table#header td.sub { color: #777; border-top: solid 1px #ccc; } table.tabs { border-bottom: solid 2px #ccc; border-collapse: collapse; margin-top: 2em; margin-bottom: 1em; width: 100%; } table.tabs td { padding: 0px 0.5em; + vertical-align: bottom; } table.tabs td a { padding: 2px 1em; color: #007; } table.tabs td a.active { color: #000; background-color: #ccc; } +table.tabs td.branch { + text-align: right; +} + +table.tabs td.branch form { + padding-bottom: 2px; + font-size: 90%; +} + div.content { margin: 0px; padding: 1em; } table.list { width: 100%; border: none; border-collapse: collapse; } table.list tr { background: white; } table.list tr:hover { background: #eee; } table.list tr.nohover:hover { background: white; } table.list th { font-weight: normal; border-bottom: solid 1px #ccc; padding: 0.1em 0.5em 0.1em 0.5em; vertical-align: baseline; } table.list td { border: none; padding: 0.1em 0.5em 0.1em 0.5em; } table.list td a { color: black; } img { border: none; } input#switch-btn { margin: 2px 0px 0px 0px; } td#sidebar input.txt { width: 100%; margin: 2px 0px 0px 0px; } table#grid { margin: 0px; } td#content { vertical-align: top; padding: 1em 2em 1em 1em; border: none; } div#summary { vertical-align: top; margin-bottom: 1em; } table#downloads { float: right; border-collapse: collapse; border: solid 1px #777; margin-left: 0.5em; margin-bottom: 0.5em; } table#downloads th { background-color: #ccc; } div#blob { border: solid 1px black; } div.error { color: red; font-weight: bold; margin: 1em 2em; } a.ls-blob, a.ls-dir, a.ls-mod { font-family: monospace; } td.ls-size { text-align: right; } td.ls-size { font-family: monospace; } td.ls-mode { font-family: monospace; } table.blob { margin-top: 0.5em; border-top: solid 1px black; } table.blob td.no { border-right: solid 1px black; color: black; background-color: #eee; text-align: right; } table.blob td.no a { color: black; } table.blob td.no a:hover { color: black; text-decoration: none; } table.blob td.txt { white-space: pre; font-family: monospace; padding-left: 0.5em; } table.nowrap td { white-space: nowrap; } table.commit-info { border-collapse: collapse; margin-top: 1.5em; } table.commit-info th { text-align: left; font-weight: normal; padding: 0.1em 1em 0.1em 0.1em; vertical-align: top; } table.commit-info td { font-weight: normal; padding: 0.1em 1em 0.1em 0.1em; } div.commit-subject { font-weight: bold; font-size: 125%; margin: 1.5em 0em 0.5em 0em; padding: 0em; } div.commit-msg { white-space: pre; font-family: monospace; } div.diffstat-header { font-weight: bold; padding-top: 1.5em; } table.diffstat { border-collapse: collapse; border: solid 1px #aaa; background-color: #eee; } table.diffstat th { font-weight: normal; text-align: left; text-decoration: underline; padding: 0.1em 1em 0.1em 0.1em; font-size: 100%; } table.diffstat td { padding: 0.2em 0.2em 0.1em 0.1em; font-size: 100%; border: none; } table.diffstat td.mode { white-space: nowrap; } table.diffstat td span.modechange { padding-left: 1em; color: red; } table.diffstat td.add a { color: green; } table.diffstat td.del a { color: red; } table.diffstat td.upd a { color: blue; } table.diffstat td.graph { width: 500px; vertical-align: middle; } table.diffstat td.graph table { border: none; } table.diffstat td.graph td { padding: 0px; border: 0px; height: 7pt; } table.diffstat td.graph td.add { background-color: #5c5; } table.diffstat td.graph td.rem { background-color: #c55; } div.diffstat-summary { color: #888; padding-top: 0.5em; } table.diff { width: 100%; } table.diff td { font-family: monospace; white-space: pre; } table.diff td div.head { font-weight: bold; margin-top: 1em; color: black; } table.diff td div.hunk { color: #009; } table.diff td div.add { color: green; } table.diff td div.del { color: red; } .sha1 { font-family: monospace; font-size: 90%; } .left { text-align: left; } .right { text-align: right; } table.list td.repogroup { font-style: italic; color: #888; } a.button { font-size: 80%; color: #aaa; background-color: #eee; border: solid 1px #aaa; padding: 0em 0.5em; margin: 0.1em 0.25em; } a.button:hover { text-decoration: none; color: #333; background-color: #ccc; } a.primary { font-size: 100%; } a.secondary { font-size: 90%; } td.toplevel-repo { } table.list td.sublevel-repo { padding-left: 1.5em; } span.age-mins { font-weight: bold; color: #080; } span.age-hours { color: #080; } span.age-days { color: #040; } span.age-weeks { color: #444; } span.age-months { color: #888; } span.age-years { color: #bbb; } diff --git a/ui-shared.c b/ui-shared.c index 03d147f..73e3d4c 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -1,560 +1,567 @@ /* ui-shared.c: common web output functions * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" #include "cmd.h" #include "html.h" const char cgit_doctype[] = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; static char *http_date(time_t t) { static char day[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static char month[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; struct tm *tm = gmtime(&t); return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); } void cgit_print_error(char *msg) { html("<div class='error'>"); html_txt(msg); html("</div>\n"); } char *cgit_rooturl() { if (ctx.cfg.virtual_root) return fmt("%s/", ctx.cfg.virtual_root); else return ctx.cfg.script_name; } char *cgit_repourl(const char *reponame) { if (ctx.cfg.virtual_root) { return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); } else { return fmt("?r=%s", reponame); } } char *cgit_fileurl(const char *reponame, const char *pagename, const char *filename, const char *query) { char *tmp; char *delim; if (ctx.cfg.virtual_root) { tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, pagename, (filename ? filename:"")); delim = "?"; } else { tmp = fmt("?url=%s/%s/%s", reponame, pagename, (filename ? filename : "")); delim = "&"; } if (query) tmp = fmt("%s%s%s", tmp, delim, query); return tmp; } char *cgit_pageurl(const char *reponame, const char *pagename, const char *query) { return cgit_fileurl(reponame,pagename,0,query); } const char *cgit_repobasename(const char *reponame) { /* I assume we don't need to store more than one repo basename */ static char rvbuf[1024]; int p; const char *rv; strncpy(rvbuf,reponame,sizeof(rvbuf)); if(rvbuf[sizeof(rvbuf)-1]) die("cgit_repobasename: truncated repository name '%s'", reponame); p = strlen(rvbuf)-1; /* strip trailing slashes */ while(p && rvbuf[p]=='/') rvbuf[p--]=0; /* strip trailing .git */ if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { p -= 3; rvbuf[p--] = 0; } /* strip more trailing slashes if any */ while( p && rvbuf[p]=='/') rvbuf[p--]=0; /* find last slash in the remaining string */ rv = strrchr(rvbuf,'/'); if(rv) return ++rv; return rvbuf; } char *cgit_currurl() { if (!ctx.cfg.virtual_root) return ctx.cfg.script_name; else if (ctx.qry.page) return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page); else if (ctx.qry.repo) return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo); else return fmt("%s/", ctx.cfg.virtual_root); } static char *repolink(char *title, char *class, char *page, char *head, char *path) { char *delim = "?"; html("<a"); if (title) { html(" title='"); html_attr(title); html("'"); } if (class) { html(" class='"); html_attr(class); html("'"); } html(" href='"); if (ctx.cfg.virtual_root) { html_attr(ctx.cfg.virtual_root); if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') html("/"); html_attr(ctx.repo->url); if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') html("/"); if (page) { html(page); html("/"); if (path) html_attr(path); } } else { html(ctx.cfg.script_name); html("?url="); html_attr(ctx.repo->url); if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') html("/"); if (page) { html(page); html("/"); if (path) html_attr(path); } delim = "&"; } if (head && strcmp(head, ctx.repo->defbranch)) { html(delim); html("h="); html_attr(head); delim = "&"; } return fmt("%s", delim); } static void reporevlink(char *page, char *name, char *title, char *class, char *head, char *rev, char *path) { char *delim; delim = repolink(title, class, page, head, path); if (rev && strcmp(rev, ctx.qry.head)) { html(delim); html("id="); html_attr(rev); } html("'>"); html_txt(name); html("</a>"); } void cgit_tree_link(char *name, char *title, char *class, char *head, char *rev, char *path) { reporevlink("tree", name, title, class, head, rev, path); } void cgit_log_link(char *name, char *title, char *class, char *head, char *rev, char *path, int ofs, char *grep, char *pattern) { char *delim; delim = repolink(title, class, "log", head, path); if (rev && strcmp(rev, ctx.qry.head)) { html(delim); html("id="); html_attr(rev); delim = "&"; } if (grep && pattern) { html(delim); html("qt="); html_attr(grep); delim = "&"; html(delim); html("q="); html_attr(pattern); } if (ofs > 0) { html(delim); html("ofs="); htmlf("%d", ofs); } html("'>"); html_txt(name); html("</a>"); } void cgit_commit_link(char *name, char *title, char *class, char *head, char *rev) { if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { name[ctx.cfg.max_msg_len] = '\0'; name[ctx.cfg.max_msg_len - 1] = '.'; name[ctx.cfg.max_msg_len - 2] = '.'; name[ctx.cfg.max_msg_len - 3] = '.'; } reporevlink("commit", name, title, class, head, rev, NULL); } void cgit_refs_link(char *name, char *title, char *class, char *head, char *rev, char *path) { reporevlink("refs", name, title, class, head, rev, path); } void cgit_snapshot_link(char *name, char *title, char *class, char *head, char *rev, char *archivename) { reporevlink("snapshot", name, title, class, head, rev, archivename); } void cgit_diff_link(char *name, char *title, char *class, char *head, char *new_rev, char *old_rev, char *path) { char *delim; delim = repolink(title, class, "diff", head, path); if (new_rev && strcmp(new_rev, ctx.qry.head)) { html(delim); html("id="); html_attr(new_rev); delim = "&"; } if (old_rev) { html(delim); html("id2="); html_attr(old_rev); } html("'>"); html_txt(name); html("</a>"); } void cgit_patch_link(char *name, char *title, char *class, char *head, char *rev) { reporevlink("patch", name, title, class, head, rev, NULL); } void cgit_object_link(struct object *obj) { char *page, *arg, *url; if (obj->type == OBJ_COMMIT) { cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, ctx.qry.head, sha1_to_hex(obj->sha1)); return; } else if (obj->type == OBJ_TREE) { page = "tree"; arg = "id"; } else if (obj->type == OBJ_TAG) { page = "tag"; arg = "id"; } else { page = "blob"; arg = "id"; } url = cgit_pageurl(ctx.qry.repo, page, fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); html_link_open(url, NULL, NULL); htmlf("%s %s", typename(obj->type), sha1_to_hex(obj->sha1)); html_link_close(); } void cgit_print_date(time_t secs, char *format) { char buf[64]; struct tm *time; if (!secs) return; time = gmtime(&secs); strftime(buf, sizeof(buf)-1, format, time); html_txt(buf); } void cgit_print_age(time_t t, time_t max_relative, char *format) { time_t now, secs; if (!t) return; time(&now); secs = now - t; if (secs > max_relative && max_relative >= 0) { cgit_print_date(t, format); return; } if (secs < TM_HOUR * 2) { htmlf("<span class='age-mins'>%.0f min.</span>", secs * 1.0 / TM_MIN); return; } if (secs < TM_DAY * 2) { htmlf("<span class='age-hours'>%.0f hours</span>", secs * 1.0 / TM_HOUR); return; } if (secs < TM_WEEK * 2) { htmlf("<span class='age-days'>%.0f days</span>", secs * 1.0 / TM_DAY); return; } if (secs < TM_MONTH * 2) { htmlf("<span class='age-weeks'>%.0f weeks</span>", secs * 1.0 / TM_WEEK); return; } if (secs < TM_YEAR * 2) { htmlf("<span class='age-months'>%.0f months</span>", secs * 1.0 / TM_MONTH); return; } htmlf("<span class='age-years'>%.0f years</span>", secs * 1.0 / TM_YEAR); } void cgit_print_http_headers(struct cgit_context *ctx) { if (ctx->page.mimetype && ctx->page.charset) htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, ctx->page.charset); else if (ctx->page.mimetype) htmlf("Content-Type: %s\n", ctx->page.mimetype); if (ctx->page.filename) htmlf("Content-Disposition: inline; filename=\"%s\"\n", ctx->page.filename); htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); htmlf("Expires: %s\n", http_date(ctx->page.expires)); html("\n"); } void cgit_print_docstart(struct cgit_context *ctx) { html(cgit_doctype); html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); html("<head>\n"); html("<title>"); html_txt(ctx->page.title); html("</title>\n"); htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); if (ctx->cfg.robots && *ctx->cfg.robots) htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); html("<link rel='stylesheet' type='text/css' href='"); html_attr(ctx->cfg.css); html("'/>\n"); html("</head>\n"); html("<body>\n"); } void cgit_print_docend() { html("</td>\n</tr>\n</table>\n</body>\n</html>\n"); } int print_branch_option(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { char *name = (char *)refname; html_option(name, name, ctx.qry.head); return 0; } int print_archive_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct tag *tag; struct taginfo *info; struct object *obj; char buf[256], *url; unsigned char fileid[20]; int *header = (int *)cb_data; if (prefixcmp(refname, "refs/archives")) return 0; strncpy(buf, refname+14, sizeof(buf)); obj = parse_object(sha1); if (!obj) return 1; if (obj->type == OBJ_TAG) { tag = lookup_tag(sha1); if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) return 0; hashcpy(fileid, tag->tagged->sha1); } else if (obj->type != OBJ_BLOB) { return 0; } else { hashcpy(fileid, sha1); } if (!*header) { html("<h1>download</h1>\n"); *header = 1; } url = cgit_pageurl(ctx.qry.repo, "blob", fmt("id=%s&path=%s", sha1_to_hex(fileid), buf)); html_link_open(url, NULL, "menu"); html_txt(strlpart(buf, 20)); html_link_close(); return 0; } void add_hidden_formfields(int incl_head, int incl_search, char *page) { char *url; if (!ctx.cfg.virtual_root) { url = fmt("%s/%s", ctx.qry.repo, page); if (ctx.qry.path) url = fmt("%s/%s", url, ctx.qry.path); html_hidden("url", url); } if (incl_head && strcmp(ctx.qry.head, ctx.repo->defbranch)) html_hidden("h", ctx.qry.head); if (ctx.qry.sha1) html_hidden("id", ctx.qry.sha1); if (ctx.qry.sha2) html_hidden("id2", ctx.qry.sha2); if (incl_search) { if (ctx.qry.grep) html_hidden("qt", ctx.qry.grep); if (ctx.qry.search) html_hidden("q", ctx.qry.search); } } char *hc(struct cgit_cmd *cmd, const char *page) { return (strcmp(cmd->name, page) ? NULL : "active"); } void cgit_print_pageheader(struct cgit_context *ctx) { struct cgit_cmd *cmd = cgit_get_cmd(ctx); html("<table id='header'>\n"); html("<tr>\n"); html("<td class='logo' rowspan='2'><a href='"); if (ctx->cfg.logo_link) html_attr(ctx->cfg.logo_link); else html_attr(cgit_rooturl()); html("'><img src='"); html_attr(ctx->cfg.logo); html("'/></a></td>\n"); html("<td class='main'>"); if (ctx->repo) { html("<a href='"); html_attr(cgit_rooturl()); html("'>index</a> / "); html_txt(ctx->repo->name); } else html_txt(ctx->cfg.root_title); html("</td></tr>\n"); html("<tr><td class='sub'>"); if (ctx->repo) html_txt(ctx->repo->desc); else html_txt(ctx->cfg.index_info); html("</td></tr>\n"); html("</table>\n"); html("<table class='tabs'><tr><td>\n"); if (ctx->repo) { reporevlink(NULL, "summary", NULL, hc(cmd, "summary"), ctx->qry.head, NULL, NULL); cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head, ctx->qry.sha1, NULL); cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head, NULL, NULL, 0, NULL, NULL); cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, ctx->qry.sha1, NULL); cgit_commit_link("commit", NULL, hc(cmd, "commit"), ctx->qry.head, ctx->qry.sha1); cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, ctx->qry.sha1, ctx->qry.sha2, NULL); cgit_patch_link("patch", NULL, hc(cmd, "patch"), ctx->qry.head, ctx->qry.sha1); + html("</td><td class='branch'>"); + html("<form method='get' action=''>\n"); + add_hidden_formfields(0, 1, ctx->qry.page); + html("<select name='h' onchange='this.form.submit();'>\n"); + for_each_branch_ref(print_branch_option, ctx->qry.head); + html("</select> "); + html("<input type='submit' name='' value='switch'/>"); } else { html("<a class='active' href='"); html_attr(cgit_rooturl()); html("'>index</a>\n"); } html("</td></tr></table>\n"); html("<div class='content'>"); } void cgit_print_filemode(unsigned short mode) { if (S_ISDIR(mode)) html("d"); else if (S_ISLNK(mode)) html("l"); else if (S_ISGITLINK(mode)) html("m"); else html("-"); html_fileperm(mode >> 6); html_fileperm(mode >> 3); html_fileperm(mode); } void cgit_print_snapshot_links(const char *repo, const char *head, const char *hex, int snapshots) { const struct cgit_snapshot_format* f; char *filename; for (f = cgit_snapshot_formats; f->suffix; f++) { if (!(snapshots & f->bit)) continue; filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, f->suffix); cgit_snapshot_link(filename, NULL, NULL, (char *)head, (char *)hex, filename); html("<br/>"); } } |