Commit | Line | Data |
---|---|---|
805e021f CE |
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 */ |