1 
2  /***************************************************************************/
3 
4 /*
5  * Portions Copyright (c) 1999 GMRS Software GmbH
6  * Carl-von-Linde-Str. 38, D-85716 Unterschleissheim, http://www.gmrs.de
7  *      All rights reserved.
8  *
9  * Author: Arno Unkrig <arno@unkrig.de>
10  */
11 
12 /* This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License in the file COPYING for more details.
21  */
22 
23  /***************************************************************************/
24 
25 /*
26  * Changes to version 1.2.2 were made by Martin Bayer <mbayer@zedat.fu-berlin.de>
27  * Dates and reasons of modifications:
28  * Wed Jul  2 21:56:45 CEST 2003: ported to g++ 3.3
29  */
30 
31  /***************************************************************************/
32 
33 
34 #include <stdlib.h>
35 #include <string.h>
36 #include <iostream>
37 
38 #include "Area.h"
39 #include "string.h"
40 
41 #define LATIN1_nbsp 160
42 
43 /* ------------------------------------------------------------------------- */
44 
45 #define malloc_array(type, size)\
46 ((type *) malloc(sizeof(type) * (size)))
47 #define realloc_array(array, type, size) \
48 ((array) = (type *) realloc((array), sizeof(type) * (size)))
49 #define copy_array(from, to, type, count) \
50 ((void) memcpy((to), (from), (count) * sizeof(type)))
51 
52 /* ------------------------------------------------------------------------- */
53 
Line(size_type l)54 Line::Line(size_type l) : length_(l), cells_(malloc_array(Cell, l))
55 {
56   Cell *p, *end = cells_ + l;
57   for (p = cells_; p != end; p++) p->clear();
58 }
59 
Line(const char * p)60 Line::Line(const char *p) :
61   length_(strlen(p)),
62   cells_(malloc_array(Cell, length_))
63 {
64   Cell *q = cells_, *end = q + length_;
65   while (q != end) { q->character = *p++; q->attribute = Cell::NONE; q++; }
66 }
67 
Line(const string & s)68 Line::Line(const string &s) :
69   length_(s.length()),
70   cells_(malloc_array(Cell, length_))
71 {
72   const char *p = s.c_str();
73   Cell *q = cells_, *end = q + length_;
74   while (q != end) { q->character = *p++; q->attribute = Cell::NONE; q++; }
75 }
76 
~Line()77 Line::~Line()
78 {
79   free(cells_);
80 }
81 
82 /* ------------------------------------------------------------------------- */
83 
84 void
resize(size_type l)85 Line::resize(size_type l)
86 {
87   if (l == length()) return;
88   realloc_array(cells_, Cell, l);
89   for (size_type x = length(); x < l; x++) cells_[x].clear();
90   length_ = l;
91 }
92 
93 void
insert(const Line & l,size_type x)94 Line::insert(const Line &l, size_type x)
95 {
96   enlarge(x + l.length());
97   const Cell *p = l.cells_, *end = p + l.length();
98   Cell       *q = cells_ + x;
99   while (p != end) *q++ = *p++;
100 }
101 
102 void
insert(const char * p,size_type x)103 Line::insert(const char *p, size_type x)
104 {
105   enlarge(x + strlen(p));
106   Cell *q = cells_ + x;
107   while (*p) q++->character = *p++;
108 }
109 
110 void
insert(const string & s,size_type x)111 Line::insert(const string &s, size_type x)
112 {
113   insert(s.c_str(), x);
114 }
115 
116 void
append(char c)117 Line::append(char c)
118 {
119   size_type x = length_;
120   resize(x + 1);
121   cells_[x].character = c;
122   cells_[x].attribute = Cell::NONE;
123 }
124 
125 void
append(const Line & l)126 Line::append(const Line &l)
127 {
128   size_type x = length_;
129   enlarge(x + l.length_);
130   const Cell *p = l.cells_, *end = p + l.length();
131   Cell       *q = cells_ + x;
132   while (p != end) *q++ = *p++;
133 }
134 
135 void
append(const char * p)136 Line::append(const char *p)
137 {
138   size_type x = length_;
139   enlarge(x + strlen(p));
140   Cell *q = cells_ + x;
141   for (; *p; ++p, ++q) { q->character = *p; q->attribute = Cell::NONE; }
142 }
143 
144 void
add_attribute(char addition)145 Line::add_attribute(char addition)
146 {
147   Cell *p = cells_, *end = cells_ + length_;
148 
149   while(p != end) p++->attribute |= addition;
150 }
151 
152 /* ------------------------------------------------------------------------- */
153 
154 bool Area::use_backspaces = true;
155 
156 /* ------------------------------------------------------------------------- */
157 
Area()158 Area::Area() :
159   width_(0),
160   height_(0),
161   cells_(malloc_array(Cell *, 0))
162 {
163 }
164 
Area(size_type w,size_type h,char c,char a)165 Area::Area(
166   size_type w /*= 0*/ ,
167   size_type h /*= 0*/ ,
168   char      c /*= ' '*/ ,
169   char      a /*= Cell::NONE*/
170 ) :
171   width_(w),
172   height_(h),
173   cells_(malloc_array(Cell *, h))
174 {
175   for (size_type y = 0; y < h; y++) {
176     Cell *p = cells_[y] = malloc_array(Cell, w), *end = p + w;
177     while (p != end) { p->character = c; p->attribute = a; p++; }
178   }
179 }
180 
Area(const char * p)181 Area::Area(const char *p) :
182   width_(strlen(p)),
183   height_(1),
184   cells_(malloc_array(Cell *, 1))
185 {
186   cells_[0] = malloc_array(Cell, width_);
187   Cell *q = cells_[0], *end = q + width_;
188   while (q != end) { q->character = *p++; q->attribute = Cell::NONE; q++; }
189 }
190 
Area(const string & s)191 Area::Area(const string &s) :
192   width_(s.length()),
193   height_(1),
194   cells_(malloc_array(Cell *, 1))
195 {
196   cells_[0] = malloc_array(Cell, width_);
197   Cell *q = cells_[0];
198   for (string::size_type i = 0; i < s.length(); ++i) {
199     q->character = s[i];
200     q->attribute = Cell::NONE;
201     q++;
202   }
203 }
204 
Area(const Line & l)205 Area::Area(const Line &l) :
206   width_(l.length_),
207   height_(1),
208   cells_(malloc_array(Cell *, 1))
209 {
210   cells_[0] = malloc_array(Cell, width_);
211   copy_array(l.cells_, cells_[0], Cell, width_);
212 }
213 
~Area()214 Area::~Area()
215 {
216   for (size_type y = 0; y < height(); y++) free(cells_[y]);
217   free(cells_);
218 }
219 
220 /* ------------------------------------------------------------------------- */
221 
222 const Area &
223 Area::operator>>=(size_type rs)
224 {
225   if (rs > 0) {
226     resize(width_ + rs, height_);
227     for (size_type y = 0; y < height_; y++) {
228       Cell *c = cells_[y];
229       memmove(c + rs, c, (width_ - rs) * sizeof(Cell));
230       for (size_type x = 0; x < rs; x++) {
231         c[x].character = ' ';
232         c[x].attribute = Cell::NONE;
233       }
234     }
235   }
236   return *this;
237 }
238 
239 void
resize(size_type w,size_type h)240 Area::resize(size_type w, size_type h)
241 {
242   size_type y_max = h < height() ? h : height();
243 
244   if (w > width()) {
245     for (size_type y = 0; y < y_max; y++) {
246       realloc_array(cells_[y], Cell, w);
247       Cell *p = cells_[y] + width(), *end = cells_[y] + w;
248       while (p != end) p++->clear();
249     }
250   } else
251   if (w < width()) {
252     for (size_type y = 0; y < y_max; y++) {
253       realloc_array(cells_[y], Cell, w);
254     }
255   }
256 
257   if (h > height()) {
258     realloc_array(cells_, Cell *, h);
259     for (size_type y = height(); y < h; y++) {
260       Cell *p = cells_[y] = malloc_array(Cell, w), *end = p + w;
261       while (p != end) p++->clear();
262     }
263   } else
264   if (h < height()) {
265     for (size_type y = h; y < height(); y++) free(cells_[y]);
266     realloc_array(cells_, Cell *, h);
267   }
268 
269   width_ = w;
270   height_ = h;
271 }
272 
273 void
enlarge(size_type w,size_type h)274 Area::enlarge(size_type w, size_type h)
275 {
276   if (w > width() || h > height()) {
277     resize(w > width() ? w : width(), h > height() ? h : height());
278   }
279 }
280 
281 void
insert(const Area & a,size_type x,size_type y)282 Area::insert(const Area &a, size_type x, size_type y)
283 {
284   enlarge(x + a.width(), y + a.height());
285 
286   for (size_type i = 0; i < a.height(); i++) {
287     const Cell *p = a.cells_[i], *end = p + a.width();
288     Cell       *q = cells_[y + i] + x;
289     while (p != end) *q++ = *p++;
290   }
291 }
292 
293 void
insert(const Area & a,size_type x,size_type y,size_type w,size_type h,int halign,int valign)294 Area::insert(
295   const Area &a,
296   size_type  x,
297   size_type  y,
298   size_type  w,
299   size_type  h,
300   int        halign,
301   int        valign
302 )
303 {
304   if (halign != LEFT && a.width() < w) x += (
305     halign == CENTER ? (w - a.width()) / 2 :
306     halign == RIGHT  ? w - a.width()       :
307     0
308   );
309   if (valign != TOP && a.height() < h) y += (
310     valign == MIDDLE ? (h - a.height()) / 2 :
311     valign == BOTTOM ? h - a.height()       :
312     0
313   );
314   insert(a, x, y);
315 }
316 
317 void
insert(const Cell & c,size_type x,size_type y)318 Area::insert(const Cell &c, size_type x, size_type y)
319 {
320   enlarge(x + 1, y + 1);
321   cells_[y][x] = c;
322 }
323 
324 void
fill(const Cell & c,size_type x,size_type y,size_type w,size_type h)325 Area::fill(const Cell &c, size_type x, size_type y, size_type w, size_type h)
326 {
327   enlarge(x + w, y + h);
328   for (size_type yy = y; yy < y + h; yy++) {
329     Cell *p = &cells_[yy][x];
330     for (size_type i = 0; i < w; i++) *p++ = c;
331   }
332 }
333 
334 void
insert(const Cell * p,size_type count,size_type x,size_type y)335 Area::insert(const Cell *p, size_type count, size_type x, size_type y)
336 {
337   enlarge(x + count, y + 1);
338   Cell *q = &cells_[y][x];
339   while (count--) *q++ = *p++;
340 }
341 
342 void
insert(char c,size_type x,size_type y)343 Area::insert(char c, size_type x, size_type y)
344 {
345   enlarge(x + 1, y + 1);
346   cells_[y][x].character = c;
347 }
348 
349 void
insert(const string & s,size_type x,size_type y)350 Area::insert(const string &s, size_type x, size_type y)
351 {
352   enlarge(x + s.length(), y + 1);
353   Cell *cell = &cells_[y][x];
354   for (string::size_type i = 0; i < s.length(); i++) {
355     cell->character = s[i];
356     cell->attribute = Cell::NONE;
357     cell++;
358   }
359 }
360 
361 void
prepend(int n)362 Area::prepend(int n)
363 {
364   if (n <= 0) return;
365 
366   realloc_array(cells_, Cell *, height() + n);
367   memmove(cells_ + n, cells_, height() * sizeof(*cells_));
368   for (int y = 0; y < n; ++y) {
369     Cell *p = cells_[y] = malloc_array(Cell, width()), *end = p + width();
370     while (p != end) p++->clear();
371   }
372   height_ += n;
373 }
374 
375 const Area &
376 Area::operator+=(const Area &x)
377 {
378   insert(x, 0, height());
379   return *this;
380 }
381 
382 void
fill(char c,size_type x,size_type y,size_type w,size_type h)383 Area::fill(char c, size_type x, size_type y, size_type w, size_type h)
384 {
385   enlarge(x + w, y + h);
386   for (size_type yy = y; yy < y + h; yy++) {
387     Cell *p = &cells_[yy][x];
388     for (size_type i = 0; i < w; i++) p++->character = c;
389   }
390 }
391 
392 void
add_attribute(char addition)393 Area::add_attribute(char addition)
394 {
395   for (size_type y = 0; y < height(); y++) {
396     Cell *p = cells_[y], *end = p + width();
397     while (p != end && p->character == ' ') ++p;
398     Cell *q = p;
399     while (p != end) {
400       if (p++->character != ' ') {
401 	while (q < p) q++->attribute |= addition;
402       }
403     }
404   }
405 }
406 
407 void
add_attribute(char addition,size_type x,size_type y,size_type w,size_type h)408 Area::add_attribute(
409   char      addition,
410   size_type x,
411   size_type y,
412   size_type w,
413   size_type h
414 )
415 {
416   enlarge(x + w, y + h);
417   for (size_type yy = y; yy < y + h; yy++) {
418     Cell *p = &cells_[yy][x], *end = p + w;
419     while (p != end) p++->attribute |= addition;
420   }
421 }
422 
423 /* ------------------------------------------------------------------------- */
424 
backspace(ostream & os)425 ostream &backspace(ostream &os) { return os << '\b'; }
426 
427 ostream &
428 operator<<(ostream &os, const Area &a)
429 {
430   for (Area::size_type y = 0; y < a.height(); y++) {
431     const Cell *cell = a.cells_[y], *end = cell + a.width();
432     while (
433       end != cell &&
434       end[-1].character == ' ' &&
435       (end[-1].attribute & (Cell::UNDERLINE | Cell::STRIKETHROUGH)) == 0
436     ) end--;
437 
438     for (const Cell *p = cell; p != end; p++) {
439       char c = p->character;
440       char a = p->attribute;
441 
442       if (c == (char) LATIN1_nbsp) c = ' ';
443 
444       if (a == Cell::NONE) {
445         os << c;
446       } else {
447 	if (Area::use_backspaces) {
448 
449           /*
450            * No LESS / terminal combination that I know of supports
451            * dash-backspace-character as "strikethrough". Pity.
452            */
453           if (a & Cell::STRIKETHROUGH) os << '-' << backspace;
454 
455           /*
456            * No LESS that I know of can combine underlining and boldface. In
457            * practice, boldface always takes precedence.
458 	   *
459            * It's not a good idea to optimize an underlined space as a single
460            * underscore (as opposed to underscore-backspace-space) -- this
461            * would not look nice next to an underlined character.
462            */
463           if ((a & Cell::UNDERLINE)            ) os << '_' << backspace;
464           if ((a & Cell::BOLD     ) && c != ' ') os << c   << backspace;
465           os << c;
466         } else {
467 	  os << (c == ' ' && (a & Cell::UNDERLINE) ? '_' : c);
468 	}
469       }
470     }
471     os << std::endl;
472   }
473 
474   return os;
475 }
476 
477 /* ------------------------------------------------------------------------- */
478 
479