Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / gtx / textobject.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10 /*
11 * Description:
12 * Implementation of the gator text object.
13 *------------------------------------------------------------------------*/
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17 #include <roken.h>
18
19 #include "gtxtextobj.h" /*Interface for this module */
20 #include "gtxwindows.h" /*Gator window interface */
21 #include "gtxcurseswin.h" /*Gator curses window interface */
22 #include "gtxdumbwin.h" /*Gator dumb terminal window interface */
23 #include "gtxX11win.h" /*Gator X11 window interface */
24
25 /*Externally-advertised array of text onode operations*/
26 struct onodeops gator_text_ops = {
27 gator_text_destroy,
28 gator_text_display,
29 gator_text_release
30 };
31
32 static char mn[] = "gator_textobject"; /*Module name */
33
34 #define GATOR_TEXTCB_DO_BOX 0
35
36 /*------------------------------------------------------------------------
37 * gator_text_create
38 *
39 * Description:
40 * Create a gator text object.
41 *
42 * Arguments:
43 * struct onode *text_onp : Ptr to the text onode to fill out.
44 * struct onode_createparams *params : Generic ptr to creation
45 * parameters.
46 *
47 * Returns:
48 * Zero if successful,
49 * Error value otherwise.
50 *
51 * Environment:
52 * The base onode fields have already been set. Text onodes are
53 * empty upon creation.
54 *
55 * Side Effects:
56 * Upon successful creation, the onode's o_window field is
57 * replaced with a new window created for the text object,
58 * with the parent window is remembered within the new window
59 * structure. On failure, it remains unchanged.
60 *------------------------------------------------------------------------*/
61
62 int
63 gator_text_create(struct onode *text_onp, struct onode_createparams *params)
64 { /*gator_text_create */
65
66 static char rn[] = "gator_text_create"; /*Routine name */
67 struct gator_textobj_params *text_params; /*My specific creation params */
68 struct gator_textobj *text_data; /*Ptr to private data */
69 struct gator_textcb_hdr *newCB; /*Ptr to CB hdr */
70
71 text_params = (struct gator_textobj_params *)params;
72 if (objects_debug) {
73 fprintf(stderr,
74 "[%s:%s] Private data passed to text object at %p:\n", mn,
75 rn, text_onp);
76 fprintf(stderr, "\tmaxEntries: %d, maxCharsPerEntry: %d\n",
77 text_params->maxEntries, text_params->maxCharsPerEntry);
78 }
79
80 /*
81 * Allocate the private data area.
82 */
83 if (objects_debug)
84 fprintf(stderr,
85 "[%s:%s] Allocating %" AFS_SIZET_FMT " bytes for text object private data region\n",
86 mn, rn, sizeof(struct gator_textobj));
87 text_data = malloc(sizeof(struct gator_textobj));
88 if (text_data == (struct gator_textobj *)0) {
89 fprintf(stderr,
90 "[%s:%s] Can't allocate %" AFS_SIZET_FMT " bytes for text object private data region, errno is %d\n",
91 mn, rn, sizeof(struct gator_textobj), errno);
92 return (errno);
93 }
94
95 /*
96 * Create the text circular buffer for this new object.
97 */
98 if (objects_debug)
99 fprintf(stderr, "[%s:%s] Creating circular buffer, %dx%d chars\n", mn,
100 rn, text_params->maxEntries, text_params->maxCharsPerEntry);
101 newCB =
102 gator_textcb_Create(text_params->maxEntries,
103 text_params->maxCharsPerEntry);
104 if (newCB == (struct gator_textcb_hdr *)0) {
105 fprintf(stderr, "[%s:%s] Can't create text object circular buffer\n",
106 mn, rn);
107 free(text_data);
108 return (-1);
109 }
110
111 /*
112 * Now that we have the private structures allocated, set them up.
113 */
114 text_data->llrock = (int *)0;
115 text_data->numLines = text_onp->o_height;
116 text_data->cbHdr = newCB;
117 text_data->firstEntShown = 0;
118 text_data->lastEntShown = 0;
119 if (objects_debug)
120 fprintf(stderr, "[%s:%s] Number of lines in window: %d: ", mn, rn,
121 text_data->numLines);
122
123 /*
124 * Attach the text-specific private
125 * data to the generic onode and return the happy news.
126 */
127 text_onp->o_data = (int *)text_data;
128 return (0);
129
130 } /*gator_text_create */
131
132 /*------------------------------------------------------------------------
133 * gator_text_destroy
134 *
135 * Description:
136 * Destroy a gator text object.
137 *
138 * Arguments:
139 * struct onode *onp : Ptr to the text onode to delete.
140 *
141 * Returns:
142 * 0: Success
143 * Error value otherwise.
144 *
145 * Environment:
146 * Nothing interesting.
147 *
148 * Side Effects:
149 * As advertised.
150 *------------------------------------------------------------------------*/
151
152 int
153 gator_text_destroy(struct onode *onp)
154 { /*gator_text_destroy */
155
156 /*
157 * For now, this is a no-op.
158 */
159 return (0);
160
161 } /*gator_text_destroy */
162
163 /*------------------------------------------------------------------------
164 * gator_text_display
165 *
166 * Description:
167 * Display/redraw a gator text object.
168 *
169 * Arguments:
170 * struct onode *onp: Ptr to the text onode to display.
171 *
172 * Returns:
173 * 0: Success
174 * Error value otherwise.
175 *
176 * Environment:
177 * Nothing interesting.
178 *
179 * Side Effects:
180 * As advertised.
181 *------------------------------------------------------------------------*/
182
183 int
184 gator_text_display(struct onode *onp)
185 { /*gator_text_display */
186
187 static char rn[] = "gator_text_display"; /*Routine name */
188 struct gator_textobj *text_data; /*Ptr to text obj data */
189 struct gator_textcb_hdr *cbHdr; /*Ptr to CB header */
190 struct gwin_strparams strparams; /*String-drawing params */
191 int currLine; /*Current line being updated */
192 int currLinesUsed; /*Num screen lines used */
193 int currIdx; /*Current line index */
194 int currEnt; /*Current entry being drawn */
195 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
196
197 if (objects_debug)
198 fprintf(stderr, "[%s:%s] Displaying text object at %p\n", mn, rn,
199 onp);
200 text_data = (struct gator_textobj *)(onp->o_data);
201 cbHdr = text_data->cbHdr;
202 if (objects_debug)
203 fprintf(stderr,
204 "[%s:%s] Displaying text object at %p, object-specific data at %p\n",
205 mn, rn, onp, text_data);
206
207 /*
208 * Update each line in the screen buffer with its proper contents.
209 */
210 currEnt = text_data->firstEntShown;
211 currLinesUsed = text_data->lastEntShown - currEnt + 1;
212 currIdx =
213 (cbHdr->oldestEntIdx +
214 (currEnt - cbHdr->oldestEnt)) % cbHdr->maxEntriesStored;
215 curr_ent = cbHdr->entry + currIdx;
216
217 if (objects_debug)
218 fprintf(stderr,
219 "[%s:%s] Drawing %d populated lines, starting with entry %d (index %d) at %p",
220 mn, rn, currLinesUsed, currEnt, currIdx, curr_ent);
221
222 strparams.x = onp->o_x;
223 strparams.y = onp->o_y;
224 for (currLine = 0; currLine < text_data->numLines; currLine++) {
225 /*
226 * Draw the current entry.
227 */
228 if (currLinesUsed > 0) {
229 /*
230 * Drawing a populated line. We need to iterate if there are
231 * inversions (I don't feel like doing this now).
232 */
233 strparams.s = curr_ent->textp;
234 strparams.highlight = curr_ent->highlight;
235 WOP_DRAWSTRING(onp->o_window, &strparams);
236
237 currLinesUsed--;
238 currEnt++;
239 currIdx++;
240 if (currIdx >= cbHdr->maxEntriesStored) {
241 currIdx = 0;
242 curr_ent = cbHdr->entry;
243 } else
244 curr_ent++;
245 } else {
246 /*
247 * Draw a blank line.
248 */
249 strparams.s = cbHdr->blankLine;
250 strparams.highlight = 0;
251 WOP_DRAWSTRING(onp->o_window, &strparams);
252 }
253
254 /*
255 * Adjust the X and Y locations.
256 */
257 strparams.x = 0;
258 strparams.y++;
259
260 } /*Update loop */
261
262 /*
263 * Box the window before we leave.
264 */
265 #if GATOR_TEXTCB_DO_BOX
266 if (objects_debug)
267 fprintf(stderr, "[%s:%s] Boxing window structure at 0x%x\n", mn, rn,
268 onp->o_window);
269 WOP_BOX(onp->o_window);
270 #endif /* GATOR_TEXTCB_DO_BOX */
271
272 /*
273 * For now, this is all we do.
274 */
275 return (0);
276
277 } /*gator_text_display */
278
279 /*------------------------------------------------------------------------
280 * gator_text_release
281 *
282 * Description:
283 * Drop the refcount on a gator text object.
284 *
285 * Arguments:
286 * struct onode *onp : Ptr to the onode whose refcount is
287 * to be dropped.
288 *
289 * Returns:
290 * 0: Success
291 * Error value otherwise.
292 *
293 * Environment:
294 * Nothing interesting.
295 *
296 * Side Effects:
297 * As advertised.
298 *------------------------------------------------------------------------*/
299
300 int
301 gator_text_release(struct onode *onp)
302 { /*gator_text_release */
303
304 /*
305 * For now, this is a no-op.
306 */
307 return (0);
308
309 } /*gator_text_release */
310
311 /*------------------------------------------------------------------------
312 * gator_text_Scroll
313 *
314 * Description:
315 * Scroll a text object some number of lines.
316 *
317 * Arguments:
318 * struct onode *onp : Ptr to the text onode to be scrolled.
319 * int nlines : Number of lines to scroll.
320 * int direction : Scroll up or down?
321 *
322 * Returns:
323 * 0: Success,
324 * Error value otherwise.
325 *
326 * Environment:
327 * Invariant: the text object's firstEntShown and lastEntShown
328 * are always between oldestEnt and currEnt (inclusive).
329 *
330 * Side Effects:
331 * As advertised.
332 *------------------------------------------------------------------------*/
333
334 int
335 gator_text_Scroll(struct onode *onp, int nlines, int direction)
336 { /*gator_text_Scroll */
337
338 static char rn[] = "gator_text_Scroll"; /*Routine name */
339 struct gator_textobj *text_data; /*Ptr to text obj data */
340
341 /*
342 * We move the markers for first & last entries displayed, depending
343 * on what's available to us in the circular buffer. We never leave
344 * the window empty.
345 */
346 if (objects_debug)
347 fprintf(stderr, "[%s:%s] Scrolling text object %d lines %s\n",
348 mn, rn, nlines,
349 (direction == GATOR_TEXT_SCROLL_UP) ? "UP" : "DOWN");
350
351 text_data = (struct gator_textobj *)(onp->o_data);
352 if (direction == GATOR_TEXT_SCROLL_DOWN) {
353 /*
354 * Move the object's text ``down'' by sliding the window up.
355 */
356 text_data->firstEntShown -= nlines;
357 if (text_data->firstEntShown < text_data->cbHdr->oldestEnt)
358 text_data->firstEntShown = text_data->cbHdr->oldestEnt;
359
360 text_data->lastEntShown -= nlines;
361 if (text_data->lastEntShown < text_data->cbHdr->oldestEnt)
362 text_data->lastEntShown = text_data->cbHdr->oldestEnt;
363
364 } /*Moving down */
365 else {
366 /*
367 * Move the object's text ``up'' by sliding the window down.
368 */
369 text_data->firstEntShown += nlines;
370 if (text_data->firstEntShown > text_data->cbHdr->currEnt)
371 text_data->firstEntShown = text_data->cbHdr->currEnt;
372
373 text_data->lastEntShown += nlines;
374 if (text_data->lastEntShown > text_data->cbHdr->currEnt)
375 text_data->lastEntShown = text_data->cbHdr->currEnt;
376
377 } /*Moving up */
378
379 /*
380 * Return the happy news.
381 */
382 return (0);
383
384 } /*gator_text_Scroll */
385
386 /*------------------------------------------------------------------------
387 * gator_text_Write
388 *
389 * Description:
390 * Write the given string to the end of the gator text object.
391 *
392 * Arguments:
393 * struct onode *onp : Ptr to the onode whose to which we're
394 * writing.
395 * char *strToWrite : String to write.
396 * int numChars : Number of chars to write.
397 * int highlight : Use highlighting?
398 * int skip : Force a skip to the next line?
399 *
400 * Returns:
401 * 0: Success
402 * Error value otherwise.
403 *
404 * Environment:
405 * Nothing interesting.
406 *
407 * Side Effects:
408 * As advertised.
409 *------------------------------------------------------------------------*/
410
411 int
412 gator_text_Write(struct onode *onp, char *strToWrite, int numChars,
413 int highlight, int skip)
414 { /*gator_text_Write */
415
416 static char rn[] = "gator_text_Write"; /*Routine name */
417 int code; /*Return value on routines */
418 struct gator_textobj *text_data; /*Ptr to text obj data */
419 struct gator_textcb_hdr *cbHdr; /*Ptr to text CB header */
420 int i; /*Loop variable */
421 int oldCurrEnt; /*CB's old currEnt value */
422 int redisplay; /*Redisplay after write? */
423 int shownDiff; /*Diff between 1st & last lines */
424 int writeDiff; /*Num lines really written */
425 int bumpAmount; /*Amount to bump count */
426
427 /*
428 *
429 */
430 if (objects_debug) {
431 fprintf(stderr,
432 "[%s:%s] Writing %d chars to text object at %p (highlight=%d, skip=%d: '",
433 mn, rn, numChars, onp, highlight, skip);
434 for (i = 0; i < numChars; i++)
435 fprintf(stderr, "%c", strToWrite[i]);
436 fprintf(stderr, "\n");
437 }
438
439 if (numChars == 0)
440 numChars = strlen(strToWrite); /* simplify caller */
441 text_data = (struct gator_textobj *)(onp->o_data);
442 cbHdr = text_data->cbHdr;
443 if (cbHdr == (struct gator_textcb_hdr *)0) {
444 fprintf(stderr, "[%s:%s] Text object missing its circular buffer!\n",
445 mn, rn);
446 return (-1);
447 }
448 /*
449 * If the current CB entry is being displayed, we track the write
450 * visually and redisplay.
451 */
452 if ((cbHdr->currEnt <= text_data->lastEntShown)
453 && (cbHdr->currEnt >= text_data->firstEntShown)) {
454 if (objects_debug)
455 fprintf(stderr,
456 "[%s:%s] Current entry is on screen. Tracking this write\n",
457 mn, rn);
458 oldCurrEnt = cbHdr->currEnt;
459 redisplay = 1;
460 } else {
461 if (objects_debug)
462 fprintf(stderr,
463 "[%s:%s] Current entry NOT on screen, not tracking write\n",
464 mn, rn);
465 redisplay = 0;
466 }
467
468
469 if (redisplay) {
470 /*
471 * We're tracking the write. Compute the number of screen lines
472 * actually written and adjust our own numbers, then call the
473 * display function.
474 */
475 shownDiff = text_data->lastEntShown - text_data->firstEntShown;
476 writeDiff = cbHdr->currEnt - oldCurrEnt;
477 if (objects_debug)
478 fprintf(stderr,
479 "[%s:%s] Preparing to redisplay. Difference in shown lines=%d, difference in written lines=%d\n",
480 mn, rn, shownDiff, writeDiff);
481 if (shownDiff < (text_data->numLines - 1)) {
482 /*
483 * We weren't showing a full screen of stuff. Bump the last
484 * line shown by the minimum of the number of free lines still
485 * on the screen and the number of new lines actually written.
486 */
487 bumpAmount = (text_data->numLines - 1) - shownDiff;
488 if (writeDiff < bumpAmount)
489 bumpAmount = writeDiff;
490 text_data->lastEntShown += bumpAmount;
491 writeDiff -= bumpAmount;
492 if (objects_debug)
493 fprintf(stderr,
494 "[%s:%s] Empty lines appeared on screen, bumping bottom line shown by %d; new writeDiff is %d\n",
495 mn, rn, bumpAmount, writeDiff);
496 }
497
498 /*
499 * If we have any more lines that were written not taken care
500 * of by the above, we just bump the counters.
501 */
502 if (writeDiff > 0) {
503 if (objects_debug)
504 fprintf(stderr,
505 "[%s:%s] Still more lines need to be tracked. Moving first & last shown down by %d\n",
506 mn, rn, writeDiff);
507 text_data->firstEntShown += writeDiff;
508 text_data->lastEntShown += writeDiff;
509 }
510 }
511
512 /*Redisplay needed */
513 /*
514 * Simply call the circular buffer write op.
515 */
516 code = gator_textcb_Write(cbHdr, strToWrite, numChars, highlight, skip);
517 if (code) {
518 fprintf(stderr,
519 "[%s:%s] Can't write to text object's circular buffer, errror code is %d\n",
520 mn, rn, code);
521 return (code);
522 }
523
524 /*
525 * Everything went well. Return the happy news.
526 */
527 return (0);
528
529 } /*gator_text_Write */
530
531 /*------------------------------------------------------------------------
532 * gator_text_BlankLine
533 *
534 * Description:
535 * Write a given number of blank lines to the given text object.
536 *
537 * Arguments:
538 * struct onode *onp : Ptr to the onode to which we're writing.
539 * int numBlanks : Number of blank lines to write.
540 *
541 * Returns:
542 * 0: Success,
543 * Error value otherwise.
544 *
545 * Environment:
546 * Nothing interesting.
547 *
548 * Side Effects:
549 * As advertised.
550 *------------------------------------------------------------------------*/
551
552 int
553 gator_text_BlankLine(struct onode *onp, int numBlanks)
554 { /*gator_text_BlankLine */
555
556 static char rn[] = "gator_text_BlankLine"; /*Routine name */
557 int code; /*Return value on routines */
558 struct gator_textobj *text_data; /*Ptr to text obj data */
559
560 /*
561 * We just call the circular buffer routine directly.
562 */
563 if (objects_debug)
564 fprintf(stderr,
565 "[%s:%s] Writing %d blank lines to text object at %p\n", mn,
566 rn, numBlanks, onp);
567
568 text_data = (struct gator_textobj *)(onp->o_data);
569 code = gator_textcb_BlankLine(text_data->cbHdr, numBlanks);
570 if (code) {
571 fprintf(stderr,
572 "[%s:%s] Can't write %d blank lines to text object at %p\n",
573 mn, rn, numBlanks, onp);
574 return (code);
575 }
576
577 /*
578 * Blank lines written successfully. Adjust what lines are currently
579 * shown. Iff we were tracking the end of the buffer, we have to
580 * follow the blank lines.
581 */
582 if (text_data->lastEntShown == text_data->cbHdr->currEnt - numBlanks) {
583 text_data->firstEntShown += numBlanks;
584 text_data->lastEntShown += numBlanks;
585 }
586
587 /*
588 * Return the happy news.
589 */
590 return (0);
591
592 } /*gator_text_BlankLine */