//
// aegis - project change supervisor
// Copyright (C) 1999, 2001-2006, 2008, 2012 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
col_pretty::~col_pretty()
{
trace(("col_pretty::destructor(this = %p)\n{\n", this));
trace(("ncolumns = %d\n", (int)ncolumns));
for (size_t j = 0; j < ncolumns; ++j)
{
column[j].clear();
}
trace(("column = %p\n", column));
delete [] column;
trace(("}\n"));
}
static wide_output::pointer
mangle(wide_output::pointer arg)
{
wide_output::pointer arg2 = wide_output_unexpand::open(arg, -1);
if (option_page_headers_get())
arg2 = wide_output_header::open(arg2);
return arg2;
}
col_pretty::col_pretty(const wide_output::pointer &a_deeper) :
deeper(mangle(a_deeper)),
need_to_emit_headers(false),
ncolumns(0),
ncolumns_max(0),
column(0)
{
trace(("col_pretty::col_pretty(this = %p, deeper = %p)\n{\n",
this, deeper.get()));
trace(("}\n"));
}
col::pointer
col_pretty::create(const wide_output::pointer &a_deeper)
{
return pointer(new col_pretty(a_deeper));
}
void
col_pretty::emit(size_t argc, emit_ty *argv, int minlines,
bool this_is_the_header)
{
//
// Figure out how many lines of output we will produce, and ask
// for a page eject if that many lines aren't left on the page.
//
trace(("col_pretty::emit(this = %p)\n{\n", this));
int lines_needed = 0;
for (int line = 0; ; ++line)
{
bool there_was_something_on_this_line = (line < minlines);
int ocol = 0;
for (int j = 0; j < (int)argc; ++j)
{
emit_ty *ep = &argv[j];
if (!ep->content)
continue;
trace(("ep = %p;\n", ep));
column_row_ty *crp = ep->content->get(line);
if (!crp)
continue;
trace(("crp = %p;\n", crp));
there_was_something_on_this_line = true;
if (!crp->length)
continue;
if (ocol > ep->left)
{
++lines_needed;
ocol = 0;
}
while (ocol < ep->left)
++ocol;
ocol += crp->printing_width;
}
if (!there_was_something_on_this_line)
break;
++lines_needed;
}
trace(("lines_needed = %d\n", lines_needed));
if (lines_needed <= 0)
{
trace(("}\n"));
return;
}
if (lines_needed <= 5)
{
wide_output_header *hp =
dynamic_cast(deeper.get());
if (hp)
hp->need(lines_needed);
}
//
// Now send the output.
//
for (int line = 0; ; ++line)
{
//
// Emit the column contents.
//
bool there_was_something_on_this_line = (line < minlines);
int ocol = 0;
for (size_t j = 0; j < argc; ++j)
{
emit_ty *ep = &argv[j];
if (!ep->content)
continue;
trace(("ep = %p;\n", ep));
column_row_ty *crp = ep->content->get(line);
if (!crp)
continue;
trace(("crp = %p;\n", crp));
there_was_something_on_this_line = true;
if (!crp->length)
continue;
if (ocol > ep->left)
{
trace(("zzzt, ping!\n"));
deeper->put_wc(L'\n');
ocol = 0;
}
//
// We have found something that we are about to
// output, emit column headers if necessary.
// (Yes, this could happen in the middle of
// multi-line output.)
//
// We don't do this often (hence the (ocol ==
// 0) test) because it causes a flush of the
// next layer down, and that generally slows
// throughput.
//
if
(
ocol == 0
&&
!this_is_the_header
&&
(
need_to_emit_headers
||
wide_output_header::is_at_top_of_page(deeper)
)
)
emit_header();
//
// Scooch across the to correct column.
//
while (ocol < ep->left)
{
deeper->put_wc(L' ');
++ocol;
}
//
// Write the data, and adjust the column tracking
// to match.
//
deeper->write(crp->text, crp->length);
ocol += crp->printing_width;
trace(("ocol = %d\n", ocol));
}
if (!there_was_something_on_this_line)
break;
deeper->put_wc(L'\n');
}
trace(("}\n"));
}
void
col_pretty::emit_header()
{
trace(("col_pretty::emit_header(this = %p)\n{\n", this));
need_to_emit_headers = 0;
emit_ty *argv = new emit_ty [ncolumns];
for (size_t j = 0; j < ncolumns; ++j)
{
argv[j].content = column[j].header;
argv[j].left = column[j].left;
}
emit(ncolumns, argv, 0, 1);
delete [] argv;
need_to_emit_headers = false;
trace(("}\n"));
}
void
col_pretty::emit_content()
{
//
// Flush all of the user input so that it is guaranteed to be
// in the content buffers.
//
trace(("col_pretty::emit_content(this = %p)\n{\n", this));
for (size_t j = 0; j < ncolumns; ++j)
{
output::pointer w = column[j].content_filter;
w->end_of_line();
w->flush();
}
//
// emit the relevant columns
//
int argc = 0;
emit_ty *argv = new emit_ty [ncolumns];
for (size_t j = 0; j < ncolumns; ++j)
{
if (column[j].content_filter)
{
argv[argc].content = column[j].content;
argv[argc].left = column[j].left;
++argc;
}
}
emit(argc, argv, 1, 0);
delete [] argv;
//
// clear the content buffers
//
for (size_t j = 0; j < ncolumns; ++j)
column[j].content->clear_buffers();
trace(("}\n"));
}
output::pointer
col_pretty::create(int left, int right, const char *ctitle)
{
//
// sanity checks on arguments
//
trace(("col_pretty::create(this = %p, left = %d, right = %d, "
"ctitle = \"%s\")\n{\n", this, left, right,
(ctitle ? ctitle : "")));
if (left < 0)
{
if (ncolumns > 0)
left = column[ncolumns - 1].right + 1;
else
left = 0;
}
if (right <= 0)
right = deeper->page_width();
if (right <= left)
right = left + 8;
//
// make sure we grok enough columns
//
if (ncolumns >= ncolumns_max)
{
size_t new_max = ncolumns_max * 2 + 4;
column_ty *new_column = new column_ty [new_max];
for (size_t j = 0; j < ncolumns; ++j)
new_column[j] = column[j];
delete [] column;
column = new_column;
ncolumns_max = new_max;
}
//
// allocate storage for the column content
//
column_ty *cp = &column[ncolumns++];
trace(("left = %d;\n", left));
trace(("right = %d;\n", right));
trace(("width = %d;\n", right - left));
int paglen = deeper->page_length();
cp->content = wide_output_column::open(right - left, paglen);
cp->left = left;
cp->right = right;
//
// We need to stash the column title specified.
//
cp->header = wide_output_column::open(right - left, paglen);
if (ctitle && *ctitle)
{
int nlines = paglen / 2;
if (nlines < 1)
nlines = 1;
wide_output::pointer fp3 =
wide_output_head::open(cp->header, nlines);
wide_output::pointer fp4 = wide_output_wrap::open(fp3, -1);
wide_output::pointer fp5 = wide_output_expand::open(fp4);
fp5->put_cstr(ctitle);
fp5->end_of_line();
//
// A new column with a header implies we need to emit
// the headers again.
//
need_to_emit_headers = 1;
}
//
// What the client of the interface sees is a de-tabbed and wrapped
// filter into the column content.
//
cp->content_filter =
output_to_wide::open
(
wide_output_expand::open
(
wide_output_wrap::open(cp->content, right - left)
)
);
trace(("return %p;\n", cp->content_filter.get()));
trace(("}\n"));
return cp->content_filter;
}
void
col_pretty::forget(const output::pointer &op)
{
for (size_t j = 0; j < ncolumns; ++j)
{
column_ty *cp = &column[j];
if (cp->content_filter == op)
cp->clear();
}
while (ncolumns > 0 && !column[ncolumns - 1].content_filter)
--ncolumns;
}
void
col_pretty::eoln()
{
trace(("col_pretty::eoln(this = %p)\n{\n", this));
//
// Headings are emitted at the top of the page, and also when
// new columns are added if they have headers.
//
// But there is a catch 22: the automatic page headers are
// emitted when the first character of the new page is written,
// if we were to unconditionally emit them here, they would be
// emitted twice in some cases.
//
if
(
need_to_emit_headers
&&
!wide_output_header::is_at_top_of_page(deeper)
&&
option_page_headers_get()
)
emit_header();
//
// Now emit the content.
//
emit_content();
trace(("}\n"));
}
void
col_pretty::eject()
{
trace(("col_pretty::eject(this = %p)\n{\n", this));
wide_output_header::eject(deeper);
trace(("}\n"));
}
void
col_pretty::need(int n)
{
trace(("col_pretty::need(this = %p, n = %d)\n{\n", this, n));
wide_output_header::need1(deeper, n);
trace(("}\n"));
}
void
col_pretty::title(const nstring &s1, const nstring &s2)
{
trace(("col_pretty::title(this = %p, s1 = %s, s2 = %s)\n{\n",
this, s1.quote_c().c_str(), s2.quote_c().c_str()));
wide_output_header::title(deeper, s1, s2);
trace(("}\n"));
}
col_pretty::column_ty::~column_ty()
{
clear();
}
col_pretty::column_ty::column_ty() :
left(0),
right(0)
{
}
col_pretty::column_ty::column_ty(const column_ty &arg) :
header(arg.header),
content(arg.content),
content_filter(arg.content_filter),
left(arg.left),
right(arg.right)
{
}
col_pretty::column_ty &
col_pretty::column_ty::operator=(const column_ty &arg)
{
if (&arg != this)
{
header = arg.header;
content = arg.content;
content_filter = arg.content_filter;
left = arg.left;
right = arg.right;
}
return *this;
}
void
col_pretty::column_ty::clear()
{
header.reset();
content.reset();
content_filter.reset();
left = 0;
right = 0;
}
void
col_pretty::flush()
{
deeper->flush();
}
// vim: set ts=8 sw=4 et :