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