1 //
2 // "$Id: DiffView.cxx 407 2006-11-13 18:54:02Z mike $"
3 //
4 // DiffView widget code.
5 //
6 // Copyright 2005-2006 by Michael Sweet.
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License v2 as published
10 // by the Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // Contents:
18 //
19 //   DiffView::DiffView()          - Create a diff viewing widget.
20 //   DiffView::~DiffView()         - Destroy a diff viewing widget.
21 //   DiffView::add_line()          - Add a line to the diff...
22 //   DiffView::clear()             - Remove all lines from the viewer.
23 //   DiffView::draw()              - Draw the diff viewer...
24 //   DiffView::expand_tabs()       - Expand tabs on a line.
25 //   DiffView::handle()            - Handle user events.
26 //   DiffView::load()              - Load a diff.
27 //   DiffView::resize()            - Resize a diff viewing widget.
28 //   DiffView::scroll_cb()         - Scroll the diff.
29 //   DiffView::select()            - Select specific lines.
30 //   DiffView::selection()         - Return the current text selection.
31 //   DiffView::selection_length()  - Return the length of the current selection.
32 //   DiffView::showline()          - Show a line in the diff...
33 //   DiffView::timeout_cb()        - Repeat scrollbar buttons...
34 //
35 
36 #include "DiffView.h"
37 #include <FL/filename.H>
38 #include <FL/fl_ask.H>
39 #include <FL/fl_draw.H>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <errno.h>
44 
45 #include "PtProcess.h"
46 
47 //
48 // Width/height of scrollbars...
49 //
50 
51 #define SCROLLER	16
52 
53 
54 //
55 // 'DiffView::DiffView()' - Create a diff viewing widget.
56 //
57 
DiffView(int X,int Y,int W,int H,const char * L)58 DiffView::DiffView(int        X,	// I - X position
59                    int        Y,	// I - Y position
60 		   int        W,	// I - Width
61 		   int        H,	// I - Height
62 		   const char *L)	// I - Label
63   : Fl_Group(X, Y, W, H, L),
64     hscroll1_(X, Y + H - SCROLLER, (W - SCROLLER) / 2, SCROLLER),
65     hscroll2_(X + (W + SCROLLER) / 2, Y + H - SCROLLER, (W - SCROLLER) / 2, SCROLLER)
66 {
67   // Don't add any more widgets to this one...
68   end();
69 
70   // Initialize this instance with defaults...
71   alloc_        = 0;
72   count_        = 0;
73   ignoreblanks_ = false;
74   linefmt_[0]   = '\0';
75   linenum_      = 0;
76   lines_        = (DiffLine **)0;
77   maxwidth_     = 0;
78   select_end_   = -1;
79   select_right_ = false;
80   select_start_ = -1;
81   showlinenum_  = true;
82   state_        = IDLE;
83   tabwidth_     = 8;
84   textcolor_    = FL_BLACK;
85   textfont_     = FL_COURIER;
86   textsize_     = FL_NORMAL_SIZE;
87   topline_      = 0;
88 
89   box(FL_FLAT_BOX);
90   selection_color(FL_YELLOW);
91 
92   // Connect the scrollbars to the DiffView widget and initialize them...
93   hscroll1_.callback((Fl_Callback *)scroll_cb, this);
94   hscroll1_.deactivate();
95   hscroll1_.type(FL_HORIZONTAL);
96   hscroll1_.value(0, w() / 2 - SCROLLER, 0, 1);
97 
98   hscroll2_.callback((Fl_Callback *)scroll_cb, this);
99   hscroll2_.deactivate();
100   hscroll2_.type(FL_HORIZONTAL);
101   hscroll2_.value(0, w() / 2 - SCROLLER, 0, 1);
102 }
103 
104 
105 //
106 // 'DiffView::~DiffView()' - Destroy a diff viewing widget.
107 //
108 
~DiffView()109 DiffView::~DiffView()
110 {
111   // Remove all lines, freeing memory...
112   clear();
113 }
114 
115 
116 //
117 // 'DiffView::add_line()' - Add a line to the diff...
118 //
119 
120 void
add_line(const char * left,const char * right,bool changed,int pos)121 DiffView::add_line(const char *left,	// I - First line or NULL
122                    const char *right,	// I - Second line or NULL
123 		   bool       changed,	// I - Line changed?
124 		   int        pos)	// I - Position to add to
125 {
126   int		start,			// First changed column
127 		end,			// Last changed column
128 		leftlen,		// First line length
129 		rightlen;		// Second line length
130   DiffLine	*line,			// Current line
131 		**temp;			// New array
132 
133 
134   if (pos < 0 || pos >= count_)
135   {
136     // Add a new line...
137     if (count_ >= alloc_)
138     {
139       temp = new DiffLine *[alloc_ + 50];
140 
141       if (count_)
142       {
143 	memcpy(temp, lines_, count_ * sizeof(DiffLine *));
144 	delete[] lines_;
145       }
146 
147       lines_ = temp;
148       alloc_ += 50;
149     }
150 
151     line         = new DiffLine;
152     line->left   = (char *)0;
153     line->dleft  = (char *)0;
154     line->right  = (char *)0;
155     line->dright = (char *)0;
156 
157     lines_[count_] = line;
158     count_ ++;
159   }
160   else
161   {
162     // Update an existing line...
163     line = lines_[pos];
164   }
165 
166   // Copy left/right strings as needed...
167   if (left)
168   {
169     line->left  = strdup(left);
170     line->dleft = expand_tabs(left);
171   }
172 
173   if (right)
174   {
175     line->right  = strdup(right);
176     line->dright = expand_tabs(right);
177   }
178 
179   // Save the "changed" state...
180   line->changed = changed;
181 
182   // Update the maximum width of the text...
183   if (line->dleft && (leftlen = strlen(line->dleft)) > maxwidth_)
184     maxwidth_ = leftlen;
185   else
186     leftlen = 0;
187 
188   if (line->dright && (rightlen = strlen(line->dright)) > maxwidth_)
189     maxwidth_ = rightlen;
190   else
191     rightlen = 0;
192 
193   if (changed)
194   {
195     // Update the start/end offsets so we can highlight the changed portion
196     // of the line.
197     if (!line->dleft)
198     {
199       line->start = 0;
200       line->end   = rightlen;
201     }
202     else if (!line->dright)
203     {
204       line->start = 0;
205       line->end   = leftlen;
206     }
207     else if (directories_)
208     {
209       line->start = 0;
210 
211       if (leftlen > rightlen)
212         line->end = leftlen;
213       else
214         line->end = rightlen;
215     }
216     else
217     {
218       for (start = 0; line->dleft[start] == line->dright[start]; start ++);
219 
220       line->start = start;
221 
222       if (leftlen > rightlen)
223         line->end = leftlen;
224       else if (rightlen > leftlen)
225         line->end = rightlen;
226       else
227       {
228 	for (end = start + 1; line->dleft[end] && line->dright[end]; end ++)
229           if (line->dleft[end] != line->dright[end])
230 	    line->end = end + 1;
231       }
232     }
233   }
234 }
235 
236 
237 //
238 // 'DiffView::clear()' - Remove all lines from the viewer.
239 //
240 
241 void
clear()242 DiffView::clear()
243 {
244   int	i;				// Looping var
245 
246 
247   // Free the text on each line...
248   for (i = 0; i < count_; i ++)
249   {
250     if (lines_[i]->left)
251       free(lines_[i]->left);
252     if (lines_[i]->dleft)
253       free(lines_[i]->dleft);
254 
255     if (lines_[i]->right)
256       free(lines_[i]->right);
257     if (lines_[i]->dright)
258       free(lines_[i]->dright);
259   }
260 
261   // Free the line array...
262   if (alloc_)
263     delete[] lines_;
264 
265   // Reset array and display data...
266   alloc_        = 0;
267   count_        = 0;
268   directories_  = false;
269   lines_        = (DiffLine **)0;
270   linefmt_[0]   = '\0';
271   linenum_      = 0;
272   maxwidth_     = 0;
273   select_start_ = -1;
274   select_end_   = -1;
275   select_right_ = false;
276   topline_      = 0;
277 
278   hscroll1_.deactivate();
279   hscroll1_.value(0, w() / 2 - SCROLLER, 0, 1);
280 
281   hscroll2_.deactivate();
282   hscroll2_.value(0, w() / 2 - SCROLLER, 0, 1);
283 
284   Fl::remove_timeout((void (*)(void *))timeout_cb, (void *)this);
285 }
286 
287 
288 //
289 // 'DiffView::draw()' - Draw the diff viewer...
290 //
291 
292 void
draw()293 DiffView::draw()
294 {
295   int		i,			// Looping var
296 		yy,			// Current Y position
297 		xoffset,		// X offset for line
298 		xwidth,			// Width of column
299 		ystart,			// Start line of change
300 		yend,			// End line of change
301 		pstart,			// Start line of page
302 		pend,			// End line of page
303 		firstline,		// First line number
304 		secondline;		// Second line number
305   char		number[255];		// Line number
306   DiffLine	**line,			// Current line
307 		**last;			// Last line
308 
309 
310   if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_CHILD))
311   {
312     // Do a full redraw...
313     Fl_Group::draw();
314 
315     fl_color(FL_GRAY);
316     fl_rectf(x() + (w() - SCROLLER) / 2, y() + h() - SCROLLER,
317              SCROLLER, SCROLLER);
318   }
319   else
320   {
321     // Do a partial redraw of the text (scrolled)...
322     fl_color(color());
323     fl_rectf(x(), y(), (w() - SCROLLER) / 2, h() - SCROLLER);
324     fl_rectf(x() + (w() + SCROLLER) / 2, y(), (w() - SCROLLER) / 2, h() - SCROLLER);
325   }
326 
327   // Clear the diff bar...
328   xoffset = x() + (w() - SCROLLER) / 2;
329 
330   fl_color(FL_DARK2);
331   fl_rectf(xoffset, y(), SCROLLER, h() - SCROLLER);
332 
333   // Draw the up/down arrows...
334   if ((count_ * textsize_) <= (h() - SCROLLER))
335     set_flag(INACTIVE);
336   draw_box(state_ == UPARROW ? FL_DOWN_BOX : FL_UP_BOX,
337            xoffset, y(), SCROLLER, SCROLLER, FL_GRAY);
338   draw_box(state_ == DOWNARROW ? FL_DOWN_BOX : FL_UP_BOX,
339            xoffset, y() + h() - 2 * SCROLLER, SCROLLER, SCROLLER, FL_GRAY);
340   clear_flag(INACTIVE);
341 
342   // Stolen from Fl_Scrollbar.cxx... :)
343   if ((count_ * textsize_) > (h() - SCROLLER))
344     fl_color(labelcolor());
345   else
346     fl_color(fl_inactive(labelcolor()));
347   int w1 = (SCROLLER-4)/3;
348   int x1 = xoffset+(SCROLLER-2*w1-1)/2;
349   int yy1 = y()+(SCROLLER-w1-1)/2;
350   if (Fl::scheme_ && !strcmp(Fl::scheme_, "gtk+")) {
351     fl_polygon(x1, yy1+w1, x1+w1, yy1+w1-1, x1+2*w1, yy1+w1, x1+w1, yy1);
352     yy1 += h()-2*SCROLLER;
353     fl_polygon(x1, yy1, x1+w1, yy1+1, x1+w1, yy1+w1);
354     fl_polygon(x1+w1, yy1+1, x1+2*w1, yy1, x1+w1, yy1+w1);
355   } else {
356     fl_polygon(x1, yy1+w1, x1+2*w1, yy1+w1, x1+w1, yy1);
357     yy1 += h()-2*SCROLLER;
358     fl_polygon(x1, yy1, x1+w1, yy1+w1, x1+2*w1, yy1);
359   }
360 
361   // Return early if we have no lines...
362   if (!count_)
363     return;
364 
365   // Prepare the text font and display vars...
366   fl_font(textfont_, textsize_);
367 
368   xwidth = (int)fl_width(' ');
369   last   = lines_ + count_;
370 
371   // Draw the left side (first file)...
372   fl_push_clip(x() + linenum_, y(),
373                (w() - SCROLLER) / 2 - linenum_, h() - SCROLLER);
374   firstline = topline_ / textsize_;
375   line      = lines_ + firstline;
376   yy        = y() + textsize_ - topline_ + firstline * textsize_;
377   for (i = (h() + textsize_ - 1) / textsize_,
378            xoffset = x() + linenum_ - hscroll1_.value();
379        i > 0 && line < last;
380        i --, yy += textsize_, line ++, firstline ++)
381   {
382     if (!(*line)->dleft)
383     {
384       fl_color(fl_color_average(textcolor(), color(), 0.1f));
385       fl_rectf(x() + linenum_, yy - textsize_,
386                (w() - SCROLLER) / 2 - linenum_, textsize_);
387       continue;
388     }
389 
390     if (firstline >= select_start_ && firstline <= select_end_ &&
391         (!select_right_ || directories_))
392     {
393       // Show selected line...
394       fl_color(textcolor_);
395       fl_rectf(x() + linenum_, yy - textsize_,
396                (w() - SCROLLER) / 2 - linenum_, textsize_);
397       fl_color(color());
398     }
399     else if ((*line)->changed)
400     {
401       // Highlight change...
402       fl_color(fl_color_average(selection_color(), color(), 0.33f));
403       fl_rectf(x() + linenum_, yy - textsize_,
404                (w() - SCROLLER) / 2 - linenum_, textsize_);
405 
406       fl_color(selection_color());
407       fl_rectf(xoffset + (*line)->start * xwidth, yy - textsize_,
408                ((*line)->end - (*line)->start) * xwidth, textsize_);
409 
410       fl_color(fl_contrast(textcolor_, selection_color()));
411     }
412     else
413       fl_color(textcolor_);
414 
415     // Draw the left text...
416     fl_draw((*line)->dleft, xoffset, yy - fl_descent());
417   }
418   fl_pop_clip();
419 
420   // Draw the right side (second file)...
421   fl_push_clip(x() + (w() + SCROLLER) / 2 + linenum_, y(),
422                (w() - SCROLLER) / 2 - linenum_, h() - SCROLLER);
423   secondline = topline_ / textsize_;
424   line       = lines_ + secondline;
425   yy         = y() + textsize_ - topline_ + secondline * textsize_;
426   for (i = (h() + textsize_ - 1) / textsize_,
427 	   xoffset = x() + (w() + SCROLLER) / 2 + linenum_ - hscroll1_.value();
428        i > 0 && line < last;
429        i --, yy += textsize_, line ++, secondline ++)
430   {
431     if (!(*line)->dright)
432     {
433       fl_color(fl_color_average(textcolor(), color(), 0.1f));
434       fl_rectf(x() + (w() + SCROLLER) / 2 + linenum_, yy - textsize_,
435                (w() - SCROLLER) / 2 - linenum_, textsize_);
436       continue;
437     }
438 
439     if (secondline >= select_start_ && secondline <= select_end_ &&
440         (select_right_ || directories_))
441     {
442       // Show selected line...
443       fl_color(textcolor_);
444       fl_rectf(x() + (w() + SCROLLER) / 2 + linenum_, yy - textsize_,
445                (w() - SCROLLER) / 2 - linenum_, textsize_);
446       fl_color(color());
447     }
448     else if ((*line)->changed)
449     {
450       // Highlight change...
451       fl_color(fl_color_average(selection_color(), color(), 0.33f));
452       fl_rectf(x() + (w() + SCROLLER) / 2 + linenum_, yy - textsize_,
453                (w() - SCROLLER) / 2 - linenum_, textsize_);
454 
455       fl_color(selection_color());
456       fl_rectf(xoffset + (*line)->start * xwidth, yy - textsize_,
457                ((*line)->end - (*line)->start) * xwidth, textsize_);
458 
459       fl_color(fl_contrast(textcolor_, selection_color()));
460     }
461     else
462       fl_color(textcolor_);
463 
464     // Draw the right text...
465     fl_draw((*line)->dright, xoffset, yy - fl_descent());
466   }
467   fl_pop_clip();
468 
469   // Draw a box for the current position in the diff...
470   xoffset = x() + (w() - SCROLLER) / 2;
471   if ((count_ * textsize_) > (h() - SCROLLER))
472   {
473     pstart  = topline_ * (h() - 3 * SCROLLER - 2) / textsize_ / count_;
474     pend    = (topline_ + h() - SCROLLER) * (h() - 3 * SCROLLER - 2) /
475               textsize_ / count_;
476   }
477   else
478   {
479     pstart = 0;
480     pend   = h() - 3 * SCROLLER - 2;
481   }
482 
483   fl_color(fl_color_average(FL_DARK2, textcolor_, 0.5f));
484   fl_rectf(xoffset, y() + pstart + SCROLLER, SCROLLER, pend - pstart + 2);
485 
486   // Draw change markers...
487   fl_color(selection_color());
488 
489   for (line = lines_, i = 0, ystart = -1, yend = -1; line < last; line ++, i ++)
490   {
491     if ((*line)->changed)
492     {
493       yend = i;
494 
495       if (ystart < 0)
496         ystart = i;
497     }
498     else if (ystart >= 0)
499     {
500       ystart = ystart * (h() - 3 * SCROLLER - 2) / count_;
501       yend   = (yend + 1) * (h() - 3 * SCROLLER - 2) / count_ - 1;
502 
503       if (ystart >= yend)
504         fl_xyline(xoffset + 1, ystart + y() + SCROLLER + 1, xoffset + SCROLLER - 2);
505       else
506         fl_rectf(xoffset + 1, ystart + y() + SCROLLER + 1, SCROLLER - 2, yend - ystart);
507 
508       ystart = -1;
509     }
510   }
511 
512   if (ystart >= 0)
513   {
514     ystart = ystart * (h() - 3 * SCROLLER - 2) / count_;
515     yend   = (yend + 1) * (h() - 3 * SCROLLER - 2) / count_ - 1;
516 
517     if (ystart >= yend)
518       fl_xyline(xoffset + 1, ystart + y() + SCROLLER + 1, xoffset + SCROLLER - 2);
519     else
520       fl_rectf(xoffset + 1, ystart + y() + SCROLLER + 1, SCROLLER - 2, yend - ystart);
521 
522     ystart = -1;
523   }
524 
525   // Outline the box...
526   fl_color(textcolor_);
527   fl_rect(xoffset, pstart + y() + SCROLLER, SCROLLER, pend - pstart + 2);
528 
529   // Draw line numbers...
530   if (showlinenum_)
531   {
532     fl_push_clip(x(), y(), w(), h() - SCROLLER);
533 
534     fl_color(fl_color_average(color(), textcolor_, 0.75f));
535     fl_yxline(x() + linenum_ - 2, y(), y() + h() - SCROLLER);
536     fl_yxline(x() + (w() + SCROLLER) / 2 + linenum_ - 2, y(), y() + h() - SCROLLER);
537 
538     fl_color(textcolor_);
539 
540     line = lines_;
541     yy   = y() + textsize_ - fl_descent() - topline_;
542     for (firstline = 1, secondline = 1; line < last; yy += textsize_, line ++)
543     {
544       if (yy < y())
545       {
546         if ((*line)->left)
547 	  firstline ++;
548 
549         if ((*line)->right)
550 	  secondline ++;
551 
552         continue;
553       }
554 
555       if (yy >= (y() + h() - SCROLLER))
556         break;
557 
558       if ((*line)->left)
559       {
560         sprintf(number, linefmt_, firstline);
561 	fl_draw(number, x(), yy);
562 	firstline ++;
563       }
564 
565       if ((*line)->right)
566       {
567         sprintf(number, linefmt_, secondline);
568 	fl_draw(number, x() + (w() + SCROLLER) / 2, yy);
569 	secondline ++;
570       }
571     }
572 
573     fl_pop_clip();
574   }
575 }
576 
577 
578 //
579 // 'DiffView::expand_tabs()' - Expand tabs on a line.
580 //
581 
582 char *					// O - Copy of expanded string
expand_tabs(const char * s)583 DiffView::expand_tabs(const char *s)	// I - String to expand
584 {
585   char		*ptr,			// Pointer into new line
586 		buf[2048];		// New string
587   int		col;			// Current column
588 
589 
590   if (!s)
591     return (NULL);
592 
593   for (ptr = buf, col = 0; *s && *s != '\n' && *s != '\r'; s ++)
594     if (*s == '\t')
595     {
596       do
597       {
598         if (ptr < (buf + sizeof(buf) - 1))
599 	  *ptr++ = ' ';
600 
601 	col ++;
602       }
603       while (col % tabwidth_);
604     }
605     else if (ptr < (buf + sizeof(buf) - 1))
606     {
607       *ptr++ = *s;
608       col ++;
609     }
610     else
611       break;
612 
613   *ptr = '\0';
614 
615   return (strdup(buf));
616 }
617 
618 
619 //
620 // 'DiffView::handle()' - Handle user events.
621 //
622 
623 int					// O - 1 if handled, 0 otherwise
handle(int event)624 DiffView::handle(int event)		// I - Event
625 {
626   int	v;				// Temporary value
627   bool	doscrolling;			// True if we should scroll vertically...
628 
629 
630   doscrolling = (count_ * textsize_) > (h() - SCROLLER);
631 
632   switch (event)
633   {
634     case FL_PUSH :
635         if (Fl::event_y() >= y() && Fl::event_y() < (y() + h() - SCROLLER))
636 	{
637 	  if (Fl::event_x() < (x() + (w() - SCROLLER) / 2))
638 	  {
639 	    state_        = SELECTING;
640 	    select_start_ = (Fl::event_y() - y() + topline_) / textsize_;
641 	    select_end_   = -1;
642 	    select_right_ = false;
643 
644             if (select_start_ >= count_)
645 	      select_start_ = count_ - 1;
646 
647             if (Fl::event_clicks() > 0)
648 	    {
649 	      select_end_ = select_start_;
650 
651 	      if (lines_[select_start_]->left)
652 		Fl::copy(lines_[select_start_]->left, 1);
653 	    }
654 
655 	    damage(FL_DAMAGE_SCROLL);
656 	    return (1);
657 	  }
658 	  else if (Fl::event_x() >= (x() + (w() + SCROLLER) / 2))
659 	  {
660 	    state_        = SELECTING;
661 	    select_start_ = (Fl::event_y() - y() + topline_) / textsize_;
662 	    select_end_   = -1;
663 	    select_right_ = true;
664 
665             if (select_start_ >= count_)
666 	      select_start_ = count_ - 1;
667 
668             if (Fl::event_clicks() > 0)
669 	    {
670 	      select_end_ = select_start_;
671 
672 	      if (lines_[select_start_]->right)
673 		Fl::copy(lines_[select_start_]->right, 1);
674 	    }
675 
676 	    damage(FL_DAMAGE_SCROLL);
677 	    return (1);
678 	  }
679           else if (Fl::event_y() < (y() + SCROLLER))
680 	  {
681 	    state_ = UPARROW;
682 
683             if ((count_ * textsize_) > (h() - SCROLLER))
684 	    {
685               topline_ -= textsize_;
686 	      if (topline_ < 0)
687 		topline_ = 0;
688 	      else
689         	Fl::add_timeout(0.5f, (void (*)(void *))timeout_cb, (void *)this);
690 
691               damage(FL_DAMAGE_SCROLL);
692             }
693 
694 	    return (1);
695 	  }
696           else if (Fl::event_y() >= (y() + h() - 2 * SCROLLER))
697 	  {
698 	    state_ = DOWNARROW;
699 
700             if ((count_ * textsize_) > (h() - SCROLLER))
701 	    {
702               topline_ += textsize_;
703 	      if (topline_ > (count_ * textsize_ - h() + SCROLLER))
704 		topline_ = count_ * textsize_ - h() + SCROLLER;
705 	      else
706         	Fl::add_timeout(0.5f, (void (*)(void *))timeout_cb, (void *)this);
707 
708               damage(FL_DAMAGE_SCROLL);
709 	    }
710 
711 	    return (1);
712 	  }
713 	  else if (doscrolling)
714 	    state_ = SCROLLING;
715 	}
716 
717     case FL_DRAG :
718         switch (state_)
719 	{
720 	  case IDLE :
721 	      break;
722 
723           case SCROLLING :
724 	      v = (Fl::event_y() - y() - SCROLLER - 1) * count_ * textsize_ /
725 	          (h() - 3 * SCROLLER - 2) - (h() - SCROLLER) / 2;
726 
727 	      if (v > (count_ * textsize_ - h() + SCROLLER))
728 		v = count_ * textsize_ - h() + SCROLLER;
729 	      else if (v < 0)
730 		v = 0;
731 
732               topline_ = v;
733 	      damage(FL_DAMAGE_SCROLL);
734 	      return (1);
735 
736           case SELECTING :
737 	      // First determine the current line...
738 	      select_end_ = (Fl::event_y() - y() + topline_) / textsize_;
739 
740               if (select_end_ >= count_)
741 		select_end_ = count_ - 1;
742 
743               // Then scroll as needed...
744 	      if ((Fl::event_y() - y()) < textsize_)
745 	      {
746 	        // Scroll up...
747 	        v = topline_ - textsize_;
748 
749 		if (v < 0)
750 		  v = 0;
751 
752 		topline_ = v;
753 	      }
754 	      else if ((Fl::event_y() - y()) >= (h() - textsize_ - SCROLLER))
755 	      {
756 	        // Scroll down...
757 	        v = topline_ + textsize_;
758 
759 		if (v > (count_ * textsize_ - h() + SCROLLER))
760 		  v = count_ * textsize_ - h() + SCROLLER;
761 
762 		topline_ = v;
763 	      }
764 
765 	      damage(FL_DAMAGE_SCROLL);
766 	      return (1);
767         }
768 	break;
769 
770     case FL_RELEASE :
771         switch (state_)
772 	{
773 	  case SELECTING :
774 	      if (select_end_ >= 0)
775 	      {
776 	        // Copy the selection to the selection buffer...
777 	        char	*buf = selection();
778 
779 
780                 Fl::copy(buf, strlen(buf), 0);
781 		free(buf);
782 	      }
783 
784 	      do_callback();
785 
786 	  case UPARROW :
787 	  case DOWNARROW :
788 	      damage(FL_DAMAGE_SCROLL);
789 
790 	  case SCROLLING :
791               state_ = IDLE;
792 	      return (1);
793 	}
794 	break;
795 
796     case FL_MOUSEWHEEL :
797         if (Fl::event_dy() && doscrolling)
798 	{
799 	  // Scroll vertically...
800           topline_ += (h() - SCROLLER) * Fl::event_dy() / 8;
801 
802 	  if (topline_ < 0)
803 	    topline_ = 0;
804 	  else if (topline_ > (count_ * textsize_ - h() + SCROLLER))
805 	    topline_ = count_ * textsize_ - h() + SCROLLER;
806 
807           damage(FL_DAMAGE_SCROLL);
808 
809 	  return (1);
810 	}
811 	break;
812 
813     case FL_SHORTCUT :
814         if (doscrolling)
815           switch (Fl::event_key())
816 	  {
817 	    case FL_Up :
818 		topline_ -= textsize_;
819 
820 		if (topline_ < 0)
821 	          topline_ = 0;
822 
823 		damage(FL_DAMAGE_SCROLL);
824 		return (1);
825 
826 	    case FL_Down :
827 		topline_ += textsize_;
828 
829 		if (topline_ > (count_ * textsize_ - h() + SCROLLER))
830 	          topline_ = count_ * textsize_ - h() + SCROLLER;
831 
832 		damage(FL_DAMAGE_SCROLL);
833 		return (1);
834 
835 	    case FL_Page_Up :
836 	    case FL_BackSpace :
837 		topline_ -= h() - SCROLLER;
838 
839 		if (topline_ < 0)
840 	          topline_ = 0;
841 
842 		damage(FL_DAMAGE_SCROLL);
843 		return (1);
844 
845 	    case FL_Page_Down :
846 	    case ' ' :
847 		topline_ += h() - SCROLLER;
848 
849 		if (topline_ > (count_ * textsize_ - h() + SCROLLER))
850 	          topline_ = count_ * textsize_ - h() + SCROLLER;
851 
852 		damage(FL_DAMAGE_SCROLL);
853 		return (1);
854 
855 	    case FL_Home :
856 		topline_ = 0;
857 
858 		damage(FL_DAMAGE_SCROLL);
859 		return (1);
860 
861 	    case FL_End :
862 		topline_ = count_ * textsize_ - h() + SCROLLER;
863 
864 		damage(FL_DAMAGE_SCROLL);
865 		return (1);
866 	  }
867 	break;
868   }
869 
870   return (Fl_Group::handle(event));
871 }
872 
873 
874 //
875 // 'DiffView::load()' - Load a diff.
876 //
877 
878 bool					// O - True on success, false otherwise
load(const char * file1,const char * file2)879 DiffView::load(const char *file1,	// I - First file
880                const char *file2)	// I - Second file
881 {
882   char		command[1024];		// Diff command
883   CPtProcess	pdiff,			// Diff process
884 		pfp;			// Original file/directory listing
885   const char	*cptr;			// Const pointer into line
886   char		*ptr,			// Pointer into line
887 		line[10240],		// Line from file/diff
888 		*dirname,		// Directory name from directory diff
889 		*filename,		// Filename from directory diff
890 		*left,			// Left file
891 		*right;			// Right file
892   bool		changed;		// Changed?
893   int		fplinenum,		// Line number in first file
894 		start,			// Current start line
895 		count,			// Count of lines
896 		newstart,		// New file start
897 		newcount,		// New file count
898 		xwidth,			// Column pixel width
899 		pos;			// Insert position
900 
901 
902   // Clear the current view...
903   clear();
904 
905   // Validate input...
906   if (!file1)
907     return (false);
908 
909   // Do the diff...
910   if (file2 && file2[0] != ':')
911   {
912     const char *diffopt = ignoreblanks() ? "-bBwE" : "";
913 
914     if (fl_filename_isdir(file1))
915     {
916       if (fl_filename_isdir(file2))
917       {
918 	snprintf(command, sizeof(command), "diff %s -q \"%s\" \"%s\"",
919 	         diffopt, file1, file2);
920 	directories_ = true;
921       }
922       else
923       {
924 	if ((cptr = strrchr(file2, '/')) != NULL)
925           cptr ++;
926 	else
927           cptr = file2;
928 
929 	snprintf(command, sizeof(command), "diff %s -u \"%s/%s\" \"%s\"",
930 	         diffopt, file1, cptr, file2);
931       }
932     }
933     else if (fl_filename_isdir(file2))
934     {
935       if ((cptr = strrchr(file1, '/')) != NULL)
936         cptr ++;
937       else
938         cptr = file1;
939 
940       snprintf(command, sizeof(command), "diff %s -u \"%s\" \"%s/%s\"",
941                diffopt, file1, file2, cptr);
942     }
943     else
944       snprintf(command, sizeof(command), "diff %s -u \"%s\" \"%s\"", diffopt,
945 	       file1, file2);
946   }
947   else if (fl_filename_isdir(file1))
948   {
949     directories_ = true;
950 
951     snprintf(line, sizeof(line), "%s/CVS", file1);
952 
953     if (!access(line, 0))
954     {
955       if (file2)
956 	snprintf(command, sizeof(command), "cvs diff -r \"%s\" -u \"%s\"",
957 	         file2 + 1, file1);
958       else
959 	snprintf(command, sizeof(command), "cvs diff -u \"%s\"",
960 	         file1);
961     }
962     else
963     {
964       snprintf(line, sizeof(line), "%s/.svn", file1);
965 
966       if (!access(line, 0))
967       {
968         if (file2)
969           snprintf(command, sizeof(command), "svn diff -N -r \"%s\" \"%s\"",
970 		   file2 + 1, file1);
971 	else
972           snprintf(command, sizeof(command), "svn diff -N \"%s\"", file1);
973       }
974       else
975         return (false);
976     }
977   }
978   else
979   {
980     strncpy(line, file1, sizeof(line) - 1);
981     line[sizeof(line) - 1] = '\0';
982 
983     if ((ptr = strrchr(line, '/')) != NULL)
984       ptr ++;
985     else
986       ptr = line;
987 
988     strncpy(ptr, "CVS", sizeof(line) - 1 - (ptr - line));
989 
990     if (!access(line, 0))
991     {
992       if (file2)
993 	snprintf(command, sizeof(command), "cvs -q diff -r \"%s\" -u \"%s\"",
994 	         file2 + 1, file1);
995       else
996 	snprintf(command, sizeof(command), "cvs -q diff -u \"%s\"", file1);
997     }
998     else
999     {
1000       strncpy(ptr, ".svn", sizeof(line) - 1 - (ptr - line));
1001 
1002       if (!access(line, 0))
1003       {
1004         const char *diffopt = ignoreblanks() ?
1005 	                          "--diff-cmd diff --extensions '-bBwEu'" : "";
1006         if (file2)
1007           snprintf(command, sizeof(command), "svn diff %s -r \"%s\" \"%s\"",
1008 	           diffopt, file2 + 1, file1);
1009 	else
1010           snprintf(command, sizeof(command), "svn diff %s \"%s\"",
1011 	           diffopt, file1);
1012       }
1013       else
1014         return (false);
1015     }
1016   }
1017 
1018   if (!pdiff.popen(command))
1019   {
1020     fl_alert("Diff command failed!\n\n%s", command);
1021     return (false);
1022   }
1023 
1024   // Open the original file...
1025   if (file2 && file2[0] != ':')
1026   {
1027     if (directories_)
1028     {
1029       snprintf(command, sizeof(command), "ls -a1 \"%s\"", file2);
1030       pfp.popen(command, "r");
1031     }
1032     else
1033       pfp.fopen(file2, "r");
1034   }
1035   else if (directories_)
1036   {
1037     snprintf(command, sizeof(command), "ls -a1 \"%s\"", file1);
1038     pfp.popen(command, "r");
1039   }
1040   else
1041     pfp.fopen(file1, "r");
1042 
1043   if (!pfp.is_open())
1044   {
1045     fl_alert("Unable to open \"%s\":\n\n%s", file2 ? file2 : file1,
1046              strerror(errno));
1047     pdiff.close();
1048     return (false);
1049   }
1050 
1051   // Loop until we run out of diffs...
1052   fplinenum = 1;
1053 
1054   if (directories_)
1055   {
1056     while (pfp.get_line(line, sizeof(line)))
1057     {
1058       ptr = line + strlen(line) - 1;
1059       if (ptr >= line && *ptr == '\n')
1060 	*ptr = '\0';
1061 
1062       add_line(line, line, false);
1063     }
1064   }
1065 
1066   while (pdiff.get_line(line, sizeof(line)))
1067   {
1068     if (directories_)
1069     {
1070       changed = true;
1071 
1072       if (!strncmp(line, "Only in ", 8) &&
1073           (ptr = strstr(line + 8, ": ")) != NULL)
1074       {
1075         // Extract directory and path...
1076         *ptr     = '\0';
1077 	dirname  = line + 8;
1078 	filename = ptr + 2;
1079 
1080 	if (filename[0])
1081 	  filename[strlen(filename) - 1] = '\0';
1082 
1083         if (!strcmp(file1, dirname))
1084 	{
1085 	  left  = filename;
1086 	  right = NULL;
1087 	}
1088 	else
1089 	{
1090 	  left  = NULL;
1091 	  right = filename;
1092 	}
1093       }
1094       else if (!strncmp(line, "Files ", 6) &&
1095                (ptr = strstr(line + 6, " and ")) != NULL)
1096       {
1097         *ptr = '\0';
1098 
1099 	if ((filename = strrchr(line + 6, '/')) != NULL)
1100 	  filename ++;
1101 	else
1102 	  filename = line + 6;
1103 
1104         left  = filename;
1105 	right = filename;
1106       }
1107       else if (!strncmp(line, "Common subdirectories: ", 23) &&
1108                (ptr = strstr(line + 23, " and ")) != NULL)
1109       {
1110         changed = false;
1111 
1112         *ptr = '\0';
1113 
1114 	if ((filename = strrchr(line + 23, '/')) != NULL)
1115 	  filename ++;
1116 	else
1117 	  filename = line + 23;
1118 
1119         left  = filename;
1120 	right = filename;
1121       }
1122       else if (!strncmp(line, "Index: ", 7) ||
1123                !strncmp(line, "M      ", 7))
1124       {
1125         if ((filename = strrchr(line + 7, '/')) != NULL)
1126 	  filename ++;
1127 	else
1128           filename = line + 7;
1129 
1130 	ptr = filename + strlen(filename) - 1;
1131 	if (ptr >= filename && *ptr == '\n')
1132 	  *ptr = '\0';
1133 
1134         left  = filename;
1135 	right = filename;
1136       }
1137       else if (!strncmp(line, "?      ", 7) ||
1138                !strncmp(line, "A      ", 7))
1139       {
1140         if ((filename = strrchr(line + 7, '/')) != NULL)
1141 	  filename ++;
1142 	else
1143           filename = line + 7;
1144 
1145 	ptr = filename + strlen(filename) - 1;
1146 	if (ptr >= filename && *ptr == '\n')
1147 	  *ptr = '\0';
1148 
1149         left  = NULL;
1150 	right = filename;
1151       }
1152       else if (!strncmp(line, "? ", 2))
1153       {
1154         if ((filename = strrchr(line + 2, '/')) != NULL)
1155 	  filename ++;
1156 	else
1157           filename = line + 2;
1158 
1159 	ptr = filename + strlen(filename) - 1;
1160 	if (ptr >= filename && *ptr == '\n')
1161 	  *ptr = '\0';
1162 
1163         left  = NULL;
1164 	right = filename;
1165       }
1166       else
1167         continue;
1168 
1169       if (!changed)
1170         continue;
1171 
1172       if (left && !right)
1173       {
1174         // Insert line
1175 	for (pos = 0; pos < count_; pos ++)
1176 	  if (lines_[pos]->right && strcmp(lines_[pos]->right, filename) > 0)
1177 	    break;
1178 
1179         if (pos >= count_)
1180 	  add_line(left, right, changed);
1181 	else
1182 	{
1183            DiffLine	*line,		// Current line
1184 			**temp;		// New array
1185 
1186 	  if (count_ >= alloc_)
1187 	  {
1188 	    temp = new DiffLine *[alloc_ + 50];
1189 
1190 	    if (count_)
1191 	    {
1192 	      memcpy(temp, lines_, count_ * sizeof(DiffLine *));
1193 	      delete[] lines_;
1194 	    }
1195 
1196 	    lines_ = temp;
1197 	    alloc_ += 50;
1198 	  }
1199 
1200 	  line          = new DiffLine;
1201 	  line->left    = strdup(left);
1202 	  line->dleft   = expand_tabs(left);
1203 	  line->right   = (char *)0;
1204 	  line->dright  = (char *)0;
1205 	  line->changed = true;
1206 	  line->start   = 0;
1207 	  line->end     = strlen(line->dleft);
1208 
1209           memmove(lines_ + pos + 1, lines_ + pos,
1210 	          (count_ - pos) * sizeof(DiffLine *));
1211 	  lines_[pos] = line;
1212 	  count_ ++;
1213 	}
1214       }
1215       else
1216       {
1217         // Update existing line
1218 	for (pos = 0; pos < count_; pos ++)
1219 	  if (lines_[pos]->right && !strcmp(lines_[pos]->right, filename))
1220 	  {
1221 	    if (!left)
1222 	    {
1223 	      free(lines_[pos]->left);
1224 	      lines_[pos]->left = (char *)0;
1225 	      free(lines_[pos]->dleft);
1226 	      lines_[pos]->dleft = (char *)0;
1227 	    }
1228 	    else if (!right)
1229 	    {
1230 	      free(lines_[pos]->right);
1231 	      lines_[pos]->right = (char *)0;
1232 	      free(lines_[pos]->dright);
1233 	      lines_[pos]->dright = (char *)0;
1234 	    }
1235 
1236 	    lines_[pos]->changed = true;
1237 	    break;
1238 	  }
1239       }
1240     }
1241     else
1242     {
1243       // Skip until we see "@@ -start,count +start,count @@"...
1244       if (sscanf(line, "@@ -%d,%d +%d,%d @@", &start, &count, &newstart,
1245         	 &newcount) != 4)
1246 	continue;
1247 
1248       while (fplinenum < newstart)
1249       {
1250 	if (!pfp.get_line(line, sizeof(line)))
1251           break;
1252 
1253 	add_line(line, line, false);
1254 	fplinenum ++;
1255       }
1256 
1257       pos = -1;
1258 
1259       while (count > 0 || newcount > 0)
1260       {
1261 	if (!pdiff.get_line(line, sizeof(line)))
1262           break;
1263 
1264 	if (line[0] == '+')
1265 	{
1266           add_line(NULL, line + 1, true, pos);
1267 	  newcount --;
1268 
1269 	  if (pos >= 0)
1270 	    pos ++;
1271 	}
1272 	else if (line[0] == '-')
1273 	{
1274           if (pos < 0)
1275 	    pos = count_;
1276 
1277           add_line(line + 1, NULL, true);
1278 	  count --;
1279 	}
1280 	else
1281 	{
1282           add_line(line + 1, line + 1, false);
1283 	  count --;
1284 	  newcount --;
1285 	  pos = -1;
1286 	}
1287 
1288 	if (line[0] != '-')
1289 	{
1290           pfp.get_line(line, sizeof(line));
1291           fplinenum ++;
1292 	}
1293       }
1294     }
1295   }
1296 
1297   pdiff.close();
1298 
1299   if (directories_)
1300     pfp.close();
1301   else
1302   {
1303     while (pfp.get_line(line, sizeof(line)) != NULL)
1304       add_line(line, line, false);
1305 
1306     pfp.close();
1307   }
1308 
1309   fl_font(textfont_, textsize_);
1310 
1311   xwidth    = (int)fl_width(' ');
1312   maxwidth_ *= xwidth;
1313 
1314   if (showlinenum_)
1315   {
1316     sprintf(line, "%d", count_);
1317     linenum_ = (int)strlen(line);
1318     sprintf(linefmt_, "%%%dd ", linenum_);
1319     linenum_ = (linenum_ + 1) * xwidth;
1320   }
1321 
1322   hscroll1_.value(0, w() / 2 - SCROLLER - linenum_, 0, maxwidth_);
1323   hscroll2_.value(0, w() / 2 - SCROLLER - linenum_, 0, maxwidth_);
1324   topline_ = 0;
1325 
1326   if (maxwidth_ > (w() / 2 - SCROLLER - linenum_))
1327   {
1328     hscroll1_.activate();
1329     hscroll2_.activate();
1330   }
1331 
1332   redraw();
1333 
1334   return (true);
1335 }
1336 
1337 
1338 //
1339 // 'DiffView::resize()' - Resize a diff viewing widget.
1340 //
1341 
1342 void
resize(int X,int Y,int W,int H)1343 DiffView::resize(int X,			// I - X position
1344                  int Y,			// I - Y position
1345 		 int W,			// I - Width
1346 		 int H)			// I - Height
1347 {
1348   int	v;				// Scroll value
1349 
1350 
1351   Fl_Widget::resize(X, Y, W, H);
1352 
1353   hscroll1_.resize(X, Y + H - SCROLLER, (W - SCROLLER) / 2, SCROLLER);
1354   hscroll2_.resize(X + (W + SCROLLER) / 2, Y + H - SCROLLER,
1355                    (W - SCROLLER) / 2, SCROLLER);
1356 
1357   // Update scrollbars...
1358   if (maxwidth_ > ((W - SCROLLER) / 2 - linenum_))
1359   {
1360     hscroll1_.activate();
1361     hscroll2_.activate();
1362 
1363     if ((hscroll1_.value() + (W - SCROLLER) / 2 - linenum_) > maxwidth_)
1364       v = maxwidth_ - (W - SCROLLER) / 2 + linenum_;
1365     else
1366       v = hscroll1_.value();
1367   }
1368   else
1369   {
1370     hscroll1_.deactivate();
1371     hscroll2_.deactivate();
1372     v = 0;
1373   }
1374 
1375   hscroll1_.value(v, (W - SCROLLER) / 2 - linenum_, 0, maxwidth_);
1376   hscroll2_.value(v, (W - SCROLLER) / 2 - linenum_, 0, maxwidth_);
1377 
1378   if ((count_ * textsize_) > (h() - SCROLLER))
1379   {
1380     if ((topline_ + h() - SCROLLER) > (count_ * textsize_))
1381       topline_ = count_ * textsize_ - h() + SCROLLER;
1382   }
1383   else
1384     topline_ = 0;
1385 }
1386 
1387 
1388 //
1389 // 'DiffView::scroll_cb()' - Scroll the diff.
1390 //
1391 
1392 void
scroll_cb(Fl_Scrollbar * s,DiffView * d)1393 DiffView::scroll_cb(Fl_Scrollbar *s,	// I - Scrollbar widget
1394                     DiffView     *d)	// I - DiffView widget
1395 {
1396   if (s == &(d->hscroll1_))
1397     d->hscroll2_.value(d->hscroll1_.value(),
1398                        (d->w() - SCROLLER) / 2 - d->linenum_, 0,
1399                        d->maxwidth_);
1400   else if (s == &(d->hscroll2_))
1401     d->hscroll1_.value(d->hscroll2_.value(),
1402                        (d->w() - SCROLLER) / 2 - d->linenum_, 0,
1403                        d->maxwidth_);
1404 
1405   d->damage(FL_DAMAGE_SCROLL);
1406 }
1407 
1408 
1409 //
1410 // 'DiffView::select()' - Select specific lines.
1411 //
1412 
1413 void
select(int s,int e,bool r)1414 DiffView::select(int  s,		// I - Start line
1415                  int  e,		// I - End line
1416 		 bool r)		// I - Righthand side?
1417 {
1418   char	*buf;				// Selection buffer
1419 
1420 
1421   select_start_ = s;
1422   select_end_   = e;
1423   select_right_ = r;
1424 
1425   damage(FL_DAMAGE_SCROLL);
1426 
1427   if ((buf = selection()) != NULL)
1428   {
1429     Fl::copy(buf, strlen(buf), 0);
1430     free(buf);
1431   }
1432 }
1433 
1434 
1435 //
1436 // 'DiffView::selection()' - Return the current text selection.
1437 //
1438 
1439 char *					// O - Selected text or NULL
selection()1440 DiffView::selection()
1441 {
1442   int	i;				// Looping var
1443   char	*buf,				// Selection buffer
1444 	*ptr;				// Pointer into line
1445 
1446 
1447   if (select_end_ < 0)
1448     return (NULL);
1449 
1450   buf = (char *)calloc(1, selection_length() + 1);
1451 
1452   for (ptr = buf, i = select_start_; i <= select_end_; i ++, ptr += strlen(ptr))
1453     if (select_right_ && lines_[i]->right)
1454       strcpy(ptr, lines_[i]->right);
1455     else if (!select_right_ && lines_[i]->left)
1456       strcpy(ptr, lines_[i]->left);
1457 
1458   return (buf);
1459 }
1460 
1461 
1462 //
1463 // 'DiffView::selection_length()' - Return the length of the current selection.
1464 //
1465 
1466 int					// Length of selection text
selection_length()1467 DiffView::selection_length()
1468 {
1469   int	i;				// Looping var
1470   int	len;				// Length of selection text
1471 
1472 
1473   if (select_end_ < 0)
1474     return (0);
1475 
1476   for (len = 0, i = select_start_; i <= select_end_; i ++)
1477     if (select_right_ && lines_[i]->right)
1478       len += strlen(lines_[i]->right);
1479     else if (!select_right_ && lines_[i]->left)
1480       len += strlen(lines_[i]->left);
1481 
1482   return (len);
1483 }
1484 
1485 
1486 //
1487 // 'DiffView::showline()' - Show a line in the diff...
1488 //
1489 
1490 void
showline(int l)1491 DiffView::showline(int l)		// I - Line to show
1492 {
1493   l *= textsize_;
1494 
1495   if (l < topline_ || l >= (topline_ + h() - SCROLLER))
1496   {
1497     l -= (h() - SCROLLER) / 2;
1498 
1499     if (l < 0)
1500       l = 0;
1501     else if (l > (count_ * textsize_ - h() + SCROLLER))
1502       l = count_ * textsize_ - h() + SCROLLER;
1503 
1504     topline_ = l;
1505 
1506     damage(FL_DAMAGE_SCROLL);
1507   }
1508 }
1509 
1510 
1511 //
1512 // 'DiffView::timeout_cb()' - Repeat scrollbar buttons...
1513 //
1514 
1515 void
timeout_cb(DiffView * d)1516 DiffView::timeout_cb(DiffView *d)	// I - DiffView widget
1517 {
1518   if (d->state_ == UPARROW && d->topline_ > 0)
1519   {
1520     d->topline_ -= 2 * d->textsize_;
1521 
1522     if (d->topline_ <= 0)
1523       d->topline_ = 0;
1524     else
1525       Fl::add_timeout(0.1f, (void (*)(void *))timeout_cb, (void *)d);
1526 
1527     d->damage(FL_DAMAGE_SCROLL);
1528   }
1529   else if (d->state_ == DOWNARROW &&
1530            d->topline_ < (d->count_ * d->textsize_ - d->h() + SCROLLER))
1531   {
1532     d->topline_ += 2 * d->textsize_;
1533 
1534     if (d->topline_ >= (d->count_ * d->textsize_ - d->h() + SCROLLER))
1535       d->topline_ = d->count_ * d->textsize_ - d->h() + SCROLLER;
1536     else
1537       Fl::add_timeout(0.1f, (void (*)(void *))timeout_cb, (void *)d);
1538 
1539     d->damage(FL_DAMAGE_SCROLL);
1540   }
1541 }
1542 
1543 
1544 //
1545 // End of "$Id: DiffView.cxx 407 2006-11-13 18:54:02Z mike $".
1546 //
1547