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