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