1 //
2 // "$Id: DiffWindow.cxx 407 2006-11-13 18:54:02Z mike $"
3 //
4 // DiffWindow 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 // DiffWindow::DiffWindow() - Create a diff window.
20 // DiffWindow::~DiffWindow() - Destroy a diff window.
21 // DiffWindow::about_cb() - Show the about dialog.
22 // DiffWindow::close_cb() - Close the current window.
23 // DiffWindow::compare_cb() - Compare files.
24 // DiffWindow::compare_selected_cb() - Compare selected files.
25 // DiffWindow::copy_cb() - Copy the diff view selection.
26 // DiffWindow::draw_overlay() - Draw the zoom overlay...
27 // DiffWindow::find_cb() - Find a string in the diff.
28 // DiffWindow::find_next_cb() - Find the next occurrence of the string.
29 // DiffWindow::find_window() - Find a window that already has a diff open.
30 // DiffWindow::handle() - Handle UI events in the window.
31 // DiffWindow::load() - Load a diff.
32 // DiffWindow::load_prefs() - Load preferences...
33 // DiffWindow::next_change_cb() - Show the next change.
34 // DiffWindow::open() - Open a diff.
35 // DiffWindow::prefs_cb() - Show the preferences.
36 // DiffWindow::prev_change_cb() - Show the previous change.
37 // DiffWindow::quit_cb() - Quit the fldiff application.
38 // DiffWindow::rediff_cb() - Redo the diff view.
39 // DiffWindow::resize() - Resize the window...
40 // DiffWindow::save_prefs() - Save preferences...
41 // DiffWindow::select_cb() - Handle selections in the diff view.
42 // DiffWindow::select_left_cb() - Select everything on the left side.
43 // DiffWindow::select_none_cb() - Select nothing.
44 // DiffWindow::select_right_cb() - Select everything on the right side.
45 // DiffWindow::using_cb() - Show help window.
46 //
47
48 #include "DiffWindow.h"
49 #include "DiffOpenWindow.h"
50 #include <FL/Fl_Box.H>
51 #include <FL/Fl_Check_Button.H>
52 #include <FL/Fl_Help_View.H>
53 #include <FL/Fl_Spinner.H>
54 #include <FL/fl_ask.H>
55 #include <FL/fl_show_colormap.H>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <errno.h>
60
61 #if defined(WIN32)
62 #include <io.h>
63 #define snprintf _snprintf
64 #define access _access
65 #else
66 #include <unistd.h>
67 #endif
68
69
70 //
71 // Width/height of scrollbars...
72 //
73
74 #define SCROLLER 16
75
76
77 //
78 // DiffWindow globals...
79 //
80
81 Fl_Preferences DiffWindow::prefs_(Fl_Preferences::USER, "fltk.org", "fldiff");
82 DiffWindow *DiffWindow::first_ = (DiffWindow *)0;
83 const char *DiffWindow::help_text_ =
84 "The bar in the center of the window shows the changes in the\n"
85 "entire file in yellow - the darkened box is the part of the diff\n"
86 "you are viewing. If the diff is larger than can be represented\n"
87 "in the change bar, a small zoom overlay will appear showing a\n"
88 "full window's worth of changes centered at the mouse\n"
89 "position.</p>\n"
90 "<p>Click in the change bar to immediately move to that position\n"
91 "in the file or the up/down arrow button to move up or down in\n"
92 "the diff. You can also use the <kbd>PageUp</kbd>,\n"
93 "<kbd>PageDown</kbd>, <kbd>Up</kbd>, <kbd>Down</kbd>,\n"
94 "<kbd>BackSpace</kbd>, <kbd>Space</kbd>, <kbd>Home</kbd>,\n"
95 "<kbd>End</kbd>, <kbd>N</kbd>, and <kbd>P</kbd> keys to move up\n"
96 "and down within the diff. The <kbd>N</kbd> and <kbd>P</kbd> keys\n"
97 "show the next and previous changes in the diff,\n"
98 "respectively.</p>\n"
99 "<p>Use the scrollbars to scroll within the diff; the scrollbars\n"
100 "are linked so scrolling one side also scrolls the other\n"
101 "side.</p>\n"
102 "<p>You can search both sides of the diff by pressing\n"
103 "<kbd>CTRL+f</kbd> or choosing <var>Find...</var> from the\n"
104 "<var>Search</var> menu. Repeat the search by pressing\n"
105 "<kbd>CTRL+g</kbd> or choosing <var>Find Next</var> from the\n"
106 "<var>Search</var> menu.</p>\n"
107 "<p>Drag the mouse to select the text on either side. Press\n"
108 "<kbd>CTRL+a</kbd> to select all of the text on the left or\n"
109 "<kbd>SHIFT+CTRL+a</kbd> to select all of the text on the right.\n"
110 "Press <kbd>CTRL+c</kbd> to copy the corresponding lines of text\n"
111 "to the clipboard.</p>\n"
112 "<p>The <var>File</var> menu allows you to open new diffs and\n"
113 "close the current diff. You can also drop files in the diff\n"
114 "window to open new diffs or double-click on filenames when\n"
115 "comparing directories.</p>\n"
116 "<p>Choose <var>Preferences</var> from the <var>Edit</var> menu\n"
117 "to show the preferences dialog which allows you to customize the\n"
118 "display. These preferences and the current window size are\n"
119 "remembered each time you run the program or open a new file.</p>\n";
120
121
122 //
123 // 'DiffWindow::DiffWindow()' - Create a diff window.
124 //
125
DiffWindow(const char * file1,const char * file2)126 DiffWindow::DiffWindow(const char *file1,
127 // I - First file
128 const char *file2)
129 // I - Second file
130 : Fl_Overlay_Window(640, 480),
131 menubar_(0, 0, 640, 25),
132 view_(0, 25, 640, 455)
133 {
134 int W, // Width of window
135 H; // Height of window
136
137
138 // Finalize the UI...
139 end();
140
141 resizable(&view_);
142
143 menubar_.box(FL_THIN_UP_BOX);
144 menubar_.add("&File/Open\\/Compare...", FL_COMMAND | 'o',
145 (Fl_Callback *)compare_cb, this);
146 menubar_.add("&File/Open\\/Compare &Selected", FL_COMMAND | 'y',
147 (Fl_Callback *)compare_selected_cb, this);
148 menubar_.add("&File/&Close", FL_COMMAND | 'w',
149 (Fl_Callback *)close_cb, this, FL_MENU_DIVIDER);
150 menubar_.add("&File/&Redo Diff", FL_COMMAND | 'r',
151 (Fl_Callback *)rediff_cb, this, FL_MENU_DIVIDER);
152 menubar_.add("&File/&Quit", FL_COMMAND | 'q',
153 (Fl_Callback *)quit_cb, this);
154
155 menubar_.add("&Edit/Copy", FL_COMMAND | 'c',
156 (Fl_Callback *)copy_cb, this, FL_MENU_DIVIDER);
157 menubar_.add("&Edit/Select Left", FL_COMMAND | 'a',
158 (Fl_Callback *)select_left_cb, this);
159 menubar_.add("&Edit/Select Right", FL_COMMAND | 'A',
160 (Fl_Callback *)select_right_cb, this);
161 menubar_.add("&Edit/Select None", FL_COMMAND | 'n',
162 (Fl_Callback *)select_none_cb, this, FL_MENU_DIVIDER);
163 menubar_.add("&Edit/Preferences...", 0,
164 (Fl_Callback *)prefs_cb, this);
165
166 menubar_.add("&Search/Find...", FL_COMMAND | 'f',
167 (Fl_Callback *)find_cb, this);
168 menubar_.add("&Search/Find Next", FL_COMMAND | 'g',
169 (Fl_Callback *)find_next_cb, this, FL_MENU_DIVIDER);
170 menubar_.add("&Search/Previous Change", 'p',
171 (Fl_Callback *)prev_change_cb, this);
172 menubar_.add("&Search/Next Change", 'n',
173 (Fl_Callback *)next_change_cb, this);
174
175 menubar_.add("&Help/Using fldiff...", FL_F + 1,
176 (Fl_Callback *)using_cb, this);
177 menubar_.add("&Help/About fldiff...", 0,
178 (Fl_Callback *)about_cb, this);
179
180 view_.callback((Fl_Callback *)select_cb, this);
181
182 load_prefs();
183
184 // Get the previous window size and resize...
185 prefs_.get("window_width", W, 640);
186 prefs_.get("window_height", H, 480);
187
188 resize(x(), y(), W, H);
189
190 // Clear the zoom data...
191 zoom_ = true;
192 zoom_pos_ = -1;
193
194 // Clear the search data...
195 search_[0] = '\0';
196 search_line_ = -1;
197 search_right_ = true;
198
199 // Load the diff...
200 file1_ = NULL;
201 file2_ = NULL;
202
203 load(file1, file2);
204
205 select_cb(&view_, this);
206
207 // Add this window to the list...
208 next_ = first_;
209 first_ = this;
210 }
211
212
213 //
214 // 'DiffWindow::~DiffWindow()' - Destroy a diff window.
215 //
216
~DiffWindow()217 DiffWindow::~DiffWindow()
218 {
219 DiffWindow *current, // Current diff window
220 *prev; // Previous diff window
221
222
223 // Free the filenames...
224 if (file1_)
225 free((void *)file1_);
226 if (file2_)
227 free((void *)file2_);
228
229 // Remove the window from the list...
230 for (current = first_, prev = (DiffWindow *)0;
231 current;
232 prev = current, current = current->next_)
233 if (current == this)
234 break;
235
236 if (current)
237 {
238 if (prev)
239 prev->next_ = current->next_;
240 else
241 first_ = current->next_;
242 }
243 }
244
245
246 //
247 // 'DiffWindow::about_cb()' - Show the about dialog.
248 //
249
250 void
about_cb(Fl_Menu_Bar * m,DiffWindow * dw)251 DiffWindow::about_cb(Fl_Menu_Bar *m, // I - Menubar
252 DiffWindow *dw) // I - Window
253 {
254 fl_message(VERSION "\nCopyright 2005-2006 by Michael Sweet\n"
255 "This program is free software provided under the\n"
256 "terms of the GNU General Public License v2.");
257 }
258
259
260 //
261 // 'DiffWindow::close_cb()' - Close the current window.
262 //
263
264 void
close_cb(Fl_Menu_Bar * m,DiffWindow * dw)265 DiffWindow::close_cb(Fl_Menu_Bar *m, // I - Menubar
266 DiffWindow *dw) // I - Window
267 {
268 if (dw->next_ || first_ != dw)
269 Fl::delete_widget(dw);
270 else
271 dw->load(NULL, NULL);
272 }
273
274
275 //
276 // 'DiffWindow::compare_cb()' - Compare files.
277 //
278
279 void
compare_cb(Fl_Menu_Bar * m,DiffWindow * dw)280 DiffWindow::compare_cb(Fl_Menu_Bar *m, // I - Menubar
281 DiffWindow *dw) // I - Window
282 {
283 DiffOpenWindow *dow; // Open dialog window
284 int i, j; // Looping vars
285 int c1, // First count
286 c2; // Second count
287 const char *f1, // First file
288 *f2; // Second file
289 int W, // Width of window
290 H; // Height of window
291
292
293 // Create and show the open window...
294 dow = new DiffOpenWindow(dw->file1_ ? dw->file1_ : ".",
295 dw->file2_ ? dw->file2_ : ".");
296
297 // Get the previous window size and resize...
298 prefs_.get("open_width", W, 640);
299 prefs_.get("open_height", H, 480);
300
301 dow->resize(dow->x(), dow->y(), W, H);
302
303 dow->show();
304
305 while (dow->shown())
306 Fl::wait();
307
308 prefs_.set("open_width", dow->w());
309 prefs_.set("open_height", dow->h());
310
311 // See what is chosen in each window...
312 c1 = dow->dc1()->count();
313 c2 = dow->dc2()->count();
314
315 if (c1 > 0 && c2 <= 1)
316 {
317 // Compare N files against a single file/directory.
318 f2 = dow->dc2()->value();
319 i = strlen(f2);
320 j = strlen(dow->dc1()->directory());
321
322 if (!strncmp(f2, dow->dc1()->directory(), i) &&
323 (i == j || (i == (j - 1) && dow->dc1()->directory()[i] == '/')))
324 f2 = NULL;
325
326 if (c1 <= 1)
327 {
328 // Compare one file/directory against another file/directory...
329 f1 = dow->dc1()->value();
330
331 dw->open(f1, f2);
332 }
333 else
334 {
335 // Compare two or more files/directories against another file/directory...
336 for (i = 1; i <= dow->dc1()->size(); i ++)
337 if (dow->dc1()->selected(i))
338 {
339 f1 = dow->dc1()->value(i);
340
341 dw->open(f1, f2);
342 }
343 }
344 }
345 else if (c1 && c2)
346 {
347 // Compare against multiple files/directories...
348 if (c1 <= 1)
349 {
350 // Compare a single file/directory against N files/directories...
351 f1 = dow->dc1()->value();
352 i = strlen(f1);
353 j = strlen(dow->dc2()->directory());
354
355 if (!strncmp(f1, dow->dc2()->directory(), i) &&
356 (i == j || (i == (j - 1) && dow->dc2()->directory()[i] == '/')))
357 f1 = NULL;
358
359 for (i = 1; i <= dow->dc2()->size(); i ++)
360 if (dow->dc2()->selected(i))
361 {
362 f2 = dow->dc2()->value(i);
363
364 dw->open(f2, f1);
365 }
366 }
367 else if (c1 == c2)
368 {
369 // Compare N files/directories...
370 for (i = 1, j = 0; i <= dow->dc1()->size(); i ++)
371 if (dow->dc1()->selected(i))
372 {
373 f1 = dow->dc1()->value(i);
374
375 for (j ++; j <= dow->dc1()->size(); j ++)
376 if (dow->dc2()->selected(j))
377 break;
378
379 if (j <= dow->dc1()->size())
380 {
381 f2 = dow->dc2()->value(j);
382 dw->open(f1, f2);
383 }
384 }
385 }
386 else
387 {
388 // We don't support comparing N to M...
389 fl_alert("Can only compare N files/directories against 1 or N files/directories!");
390 }
391 }
392
393 // Delete the file chooser...
394 delete dow;
395 }
396
397
398 //
399 // 'DiffWindow::compare_selected_cb()' - Compare selected files.
400 //
401
402 void
compare_selected_cb(Fl_Menu_Bar * m,DiffWindow * dw)403 DiffWindow::compare_selected_cb(Fl_Menu_Bar *m,
404 // I - Menubar
405 DiffWindow *dw)
406 // I - Window
407 {
408 int i; // Looping var
409 char nfile1[1024], // New first file
410 nfile2[1024]; // New second file
411 const char *nfptr2; // Pointer to second file
412
413
414 for (i = dw->view_.select_start(); i <= dw->view_.select_end(); i ++)
415 {
416 if (dw->view_.left(i) && dw->view_.right(i))
417 {
418 snprintf(nfile1, sizeof(nfile1), "%s/%s", dw->file1_, dw->view_.left(i));
419 if (dw->file2_ && dw->file2_[0] != ':')
420 {
421 snprintf(nfile2, sizeof(nfile2), "%s/%s", dw->file2_,
422 dw->view_.right(i));
423 nfptr2 = nfile2;
424 }
425 else
426 nfptr2 = dw->file2_;
427
428 dw->open(nfile1, nfptr2);
429 }
430 else if (dw->view_.left(i))
431 fl_alert("No diff available for \"%s\"", dw->view_.left(i));
432 else
433 fl_alert("No diff available for \"%s\"", dw->view_.right(i));
434 }
435 }
436
437
438 //
439 // 'DiffWindow::copy_cb()' - Copy the diff view selection.
440 //
441
442 void
copy_cb(Fl_Menu_Bar * m,DiffWindow * dw)443 DiffWindow::copy_cb(Fl_Menu_Bar *m, // I - Menubar
444 DiffWindow *dw) // I - Window
445 {
446 char *buf = dw->view_.selection(); // Selection text
447
448
449 if (buf)
450 {
451 Fl::copy(buf, strlen(buf), 1);
452 free(buf);
453 }
454 }
455
456
457 //
458 // 'DiffWindow::draw_overlay()' - Draw the zoom overlay...
459 //
460
461 void
draw_overlay()462 DiffWindow::draw_overlay()
463 {
464 int xoffset, // X offset
465 ystart, // Start Y pos
466 yend, // End Y pos
467 pstart, // Page start Y pos
468 pend; // Page end Y pos
469 int line, // Current line
470 startline, // Start line
471 endline, // End line
472 lines; // Lines per page
473
474
475 if (zoom_pos_ >= 0 && view_.count())
476 {
477 lines = (h() - 25 - SCROLLER) / textsize();
478 line = (zoom_pos_ - 26 - SCROLLER) * view_.count() /
479 (h() - 3 * SCROLLER - 27);
480 startline = line - lines / 2;
481 endline = line + lines / 2;
482
483 if (startline < 0)
484 {
485 endline -= startline;
486 startline = 0;
487 }
488
489 if (endline >= view_.count())
490 {
491 startline -= endline - view_.count() + 1;
492 endline = view_.count() - 1;
493 }
494
495 if (startline < 0)
496 startline = 0;
497
498 xoffset = (w() + SCROLLER) / 2;
499 ystart = zoom_pos_ - line + startline - 1;
500 yend = zoom_pos_ - line + endline + 1;
501
502 if (ystart < 25)
503 {
504 yend -= ystart - 25;
505 ystart = 25;
506 }
507
508 if (yend >= (h() - SCROLLER))
509 {
510 ystart -= yend - h() + SCROLLER + 1;
511 yend = h() - SCROLLER - 1;
512 }
513
514 pstart = startline * (h() - 27 - 3 * SCROLLER) / view_.count() +
515 25 + SCROLLER;
516 pend = (endline + 1) * (h() - 27 - 3 * SCROLLER) / view_.count() +
517 26 + SCROLLER;
518
519 fl_color(fl_color_average(FL_DARK2, textcolor(), 0.5f));
520 fl_rectf(xoffset + 30, ystart, SCROLLER, yend - ystart);
521
522 fl_color(textcolor());
523 fl_rect(xoffset + 30, ystart, SCROLLER, yend - ystart);
524 fl_rect(xoffset - SCROLLER, pstart, SCROLLER, pend - pstart);
525 fl_line(xoffset, pstart, xoffset + 30, ystart);
526 fl_line(xoffset, pend, xoffset + 30, yend);
527
528 fl_color(selection_color());
529 for (line = startline; line <= endline; line ++)
530 if (view_.line_changed(line))
531 {
532 if (view_.left(line) && view_.right(line))
533 fl_xyline(xoffset + 31, ystart + line - startline + 1,
534 xoffset + 30 + SCROLLER);
535 else if (view_.left(line))
536 fl_xyline(xoffset + 31, ystart + line - startline + 1,
537 xoffset + 30 + SCROLLER / 2);
538 else
539 fl_xyline(xoffset + 31 + SCROLLER / 2, ystart + line - startline + 1,
540 xoffset + 30 + SCROLLER);
541 }
542 }
543 }
544
545
546 //
547 // 'DiffWindow::find_cb()' - Find a string in the diff.
548 //
549
550 void
find_cb(Fl_Menu_Bar * m,DiffWindow * dw)551 DiffWindow::find_cb(Fl_Menu_Bar *m, // I - Menubar
552 DiffWindow *dw) // I - Window
553 {
554 const char *s; // String
555
556
557 if ((s = fl_input("Search For?", dw->search_)) != NULL)
558 {
559 strncpy(dw->search_, s, sizeof(dw->search_) - 1);
560 dw->search_[sizeof(dw->search_) - 1] = '\0';
561 dw->search_line_ = -1;
562 dw->search_right_ = true;
563
564 find_next_cb(m, dw);
565 }
566 }
567
568
569 //
570 // 'DiffWindow::find_next_cb()' - Find the next occurrence of the string.
571 //
572
573 void
find_next_cb(Fl_Menu_Bar * m,DiffWindow * dw)574 DiffWindow::find_next_cb(Fl_Menu_Bar *m,// I - Menubar
575 DiffWindow *dw)
576 // I - Window
577 {
578 const char *line; // Text from line
579
580
581 if (!dw->search_[0])
582 {
583 fl_beep();
584 return;
585 }
586
587 dw->view_.select(-1, -1, false);
588
589 while (dw->search_line_ < dw->view_.count())
590 {
591 // Advance to the next
592 if (dw->search_right_)
593 {
594 dw->search_right_ = false;
595 dw->search_line_ ++;
596
597 if (dw->search_line_ >= dw->view_.count())
598 break;
599 }
600 else
601 dw->search_right_ = true;
602
603 if (dw->search_right_)
604 line = dw->view_.right(dw->search_line_);
605 else
606 line = dw->view_.left(dw->search_line_);
607
608 if (line && strstr(line, dw->search_) != NULL)
609 {
610 dw->view_.select(dw->search_line_, dw->search_line_, dw->search_right_);
611 dw->view_.showline(dw->search_line_);
612 return;
613 }
614
615 }
616
617 fl_alert("Search string not found.");
618 }
619
620
621 //
622 // 'DiffWindow::find_window()' - Find a window that already has a diff open.
623 //
624
625 DiffWindow * // O - Window or NULL
find_window(const char * f1,const char * f2)626 DiffWindow::find_window(const char *f1, // I - First file
627 const char *f2) // I - Second file
628 {
629 DiffWindow *dw; // Current window
630 char filename[1024]; // Real second file
631
632
633 filename[sizeof(filename) - 1] = '\0';
634
635 if (f1 && f2)
636 {
637 if (!fl_filename_isdir(f1) && fl_filename_isdir(f2))
638 {
639 if (f2[strlen(f2) - 1] == '/')
640 snprintf(filename, sizeof(filename), "%s%s", f2,
641 fl_filename_name(f1));
642 else
643 snprintf(filename, sizeof(filename), "%s/%s", f2,
644 fl_filename_name(f1));
645 }
646 else
647 strncpy(filename, f2, sizeof(filename) - 1);
648 }
649 else if (f2)
650 strncpy(filename, f2, sizeof(filename) - 1);
651
652 for (dw = first_; dw; dw = dw->next_)
653 if (((dw->file1_ == f1 || (dw->file1_ && f1 && !strcmp(dw->file1_, f1))) &&
654 (dw->file2_ == f2 || (dw->file2_ && f2 &&
655 !strcmp(dw->file2_, filename))) ||
656 ((dw->file1_ && f2 && !strcmp(dw->file1_, filename)) &&
657 (dw->file2_ && f1 && !strcmp(dw->file2_, f1)))))
658 return (dw);
659
660 return ((DiffWindow *)0);
661 }
662
663
664 //
665 // 'DiffWindow::handle()' - Handle UI events in the window.
666 //
667
668 int // O - 1 if handled, 0 otherwise
handle(int event)669 DiffWindow::handle(int event) // I - Event
670 {
671 char *ptr; // Pointer into DND data
672 int num_files; // Number of files in URL list
673 char *files[100]; // Files
674
675
676 switch (event)
677 {
678 case FL_PUSH :
679 if (zoom_pos_ >= 0)
680 {
681 zoom_pos_ = -1;
682 redraw_overlay();
683 }
684 break;
685
686 case FL_RELEASE :
687 case FL_MOVE :
688 case FL_DRAG :
689 if (Fl::event_y() >= (25 + SCROLLER) &&
690 Fl::event_y() < (h() - 2 * SCROLLER) &&
691 Fl::event_x() >= ((w() - SCROLLER) / 2) &&
692 Fl::event_x() < ((w() + SCROLLER) / 2) &&
693 zoom_ &&
694 view_.count() > (h() - 27 - SCROLLER))
695 {
696 zoom_pos_ = Fl::event_y();
697 redraw_overlay();
698 }
699 else if (zoom_pos_ >= 0)
700 {
701 zoom_pos_ = -1;
702 redraw_overlay();
703 }
704 break;
705
706 case FL_DND_ENTER :
707 Fl::focus(this);
708 case FL_ENTER :
709 case FL_LEAVE :
710 case FL_FOCUS :
711 case FL_UNFOCUS :
712 case FL_DND_LEAVE :
713 case FL_DND_DRAG :
714 return (1);
715
716 case FL_DND_RELEASE :
717 take_focus();
718 return (1);
719
720 case FL_PASTE :
721 printf("PASTE: \"%s\"\n", Fl::event_text());
722
723 for (ptr = (char *)Fl::event_text(), num_files = 0; ptr && *ptr;)
724 {
725 if (!strncmp(ptr, "file:", 5))
726 {
727 files[num_files] = ptr + 5;
728 num_files ++;
729 }
730 else if (ptr[0] == '/')
731 {
732 files[num_files] = ptr;
733 num_files ++;
734 }
735
736 if ((ptr = strstr(ptr, "\r\n")) != NULL)
737 {
738 *ptr = '\0';
739 ptr += 2;
740 }
741
742 if (num_files >= (int)(sizeof(files) / sizeof(files[0])))
743 break;
744 }
745
746 if (num_files < 1 || num_files > 2)
747 fl_alert("Sorry, fldiff only supports comparing 1 file to a\n"
748 "repository or 2 files to each other...");
749 else if (file1_)
750 {
751 DiffWindow *dw; // New diff window
752
753
754 if (num_files == 1)
755 dw = new DiffWindow(files[0], NULL);
756 else
757 dw = new DiffWindow(files[0], files[1]);
758
759 dw->show();
760 }
761 else if (num_files == 1)
762 load(files[0], NULL);
763 else
764 load(files[0], files[1]);
765
766 return (1);
767 }
768
769 return (Fl_Overlay_Window::handle(event));
770 }
771
772
773 //
774 // 'DiffWindow::load()' - Load a diff.
775 //
776
777 bool // O - True on success, false on failure
load(const char * file1,const char * file2)778 DiffWindow::load(const char *file1, // I - First file
779 const char *file2) // I - Second file
780 {
781 Fl_Menu_Item *item; // Compare selected menu item...
782 char filename[1024]; // Real second file
783
784
785 item = (Fl_Menu_Item *)menubar_.menu() + 2;
786 item->deactivate();
787
788 redraw();
789
790 if (file1_)
791 free((void *)file1_);
792 if (file2_)
793 free((void *)file2_);
794
795 if (file1 && !*file1)
796 file1 = NULL;
797 if (file2 && !*file2)
798 file2 = NULL;
799
800 filename[sizeof(filename) - 1] = '\0';
801
802 if (file1 && file2)
803 {
804 if (!fl_filename_isdir(file1) && fl_filename_isdir(file2))
805 {
806 if (file2[strlen(file2) - 1] == '/')
807 snprintf(filename, sizeof(filename), "%s%s", file2,
808 fl_filename_name(file1));
809 else
810 snprintf(filename, sizeof(filename), "%s/%s", file2,
811 fl_filename_name(file1));
812 }
813 else
814 strncpy(filename, file2, sizeof(filename) - 1);
815 }
816 else if (file2)
817 strncpy(filename, file2, sizeof(filename) - 1);
818
819 file1_ = file1 ? strdup(file1) : NULL;
820 file2_ = file2 ? strdup(filename) : NULL;
821
822 if (!view_.load(file1_, file2_))
823 {
824 if (file1_ || file2_)
825 fl_alert("Sorry, no diff available!");
826
827 label(VERSION);
828
829 return (false);
830 }
831
832 // Format a title...
833 if (file2_ && file2_[0] != ':')
834 snprintf(title_, sizeof(title_), "%s <-> %s - " VERSION, file1_, file2_);
835 else if (file2_)
836 snprintf(title_, sizeof(title_), "r%s <-> %s - " VERSION, file2_ + 1, file1_);
837 else
838 snprintf(title_, sizeof(title_), "repos <-> %s - " VERSION, file1_);
839
840 label(title_);
841
842 select_cb(&view_, this);
843
844 return (true);
845 }
846
847
848 //
849 // 'DiffWindow::load_prefs()' - Load preferences...
850 //
851
852 void
load_prefs()853 DiffWindow::load_prefs()
854 {
855 int v; // Value
856
857
858 prefs_.get("color", v, FL_WHITE);
859 color((Fl_Color)v);
860
861 prefs_.get("selection_color", v, FL_YELLOW);
862 selection_color((Fl_Color)v);
863
864 prefs_.get("showlinenum", v, true);
865 showlinenum(v ? true : false);
866
867 prefs_.get("tabwidth", v, 8);
868 tabwidth(v);
869
870 prefs_.get("textcolor", v, FL_BLACK);
871 textcolor((Fl_Color)v);
872
873 prefs_.get("textsize", v, FL_NORMAL_SIZE);
874 textsize(v);
875
876 prefs_.get("ignoreblanks", v, false);
877 ignoreblanks(v ? true : false);
878 }
879
880
881 //
882 // 'DiffWindow::next_change_cb()' - Show the next change.
883 //
884
885 void
next_change_cb(Fl_Menu_Bar * m,DiffWindow * dw)886 DiffWindow::next_change_cb(Fl_Menu_Bar *m,
887 // I - Menubar
888 DiffWindow *dw)
889 // I - Window
890 {
891 int i; // Looping var
892
893
894 // Skip the rest of the current change at the bottom of the screen...
895 i = dw->view_.topline() + (dw->view_.h() - SCROLLER) /
896 dw->view_.textsize() - 1;
897
898 while (i < dw->view_.count() && dw->view_.line_changed(i))
899 i ++;
900
901 // Then find the next change...
902 for (; i < dw->view_.count(); i ++)
903 if (dw->view_.line_changed(i))
904 {
905 dw->view_.showline(i);
906 return;
907 }
908
909 fl_alert("No more changes follow the current position.");
910 }
911
912
913 //
914 // 'DiffWindow::open()' - Open a diff.
915 //
916
917 void
open(const char * f1,const char * f2)918 DiffWindow::open(const char *f1, // I - First file
919 const char *f2) // I - Second file
920 {
921 DiffWindow *dw; // New diff window
922
923
924 if ((dw = find_window(f1, f2)) == NULL)
925 {
926 // Diff not already open...
927 if (file1_)
928 {
929 // Create a new window
930 dw = new DiffWindow(f1, f2);
931 }
932 else
933 {
934 // Load in the current window
935 dw = this;
936 load(f1, f2);
937 }
938 }
939
940 // Show/raise the window...
941 dw->show();
942 }
943
944
945 //
946 // 'DiffWindow::prefs_cb()' - Show the preferences.
947 //
948
949 void
prefs_cb(Fl_Menu_Bar * m,DiffWindow * dw)950 DiffWindow::prefs_cb(Fl_Menu_Bar *m, // I - Menubar
951 DiffWindow *dw) // I - Window
952 {
953 Fl_Double_Window *win; // Preferences window
954 Fl_Group *group; // Control group
955 Fl_Spinner *tabwidth, // Tab size
956 *textsize; // Text size
957 Fl_Button *textfg, // Foreground color
958 *textbg, // Background color
959 *hicolor; // Highlight color
960 Fl_Check_Button *linenum; // Show line numbers?
961 Fl_Check_Button *ignoreb; // ignore blanks during diff compare ?
962 Fl_Button *ok, // OK button
963 *cancel; // Cancel button
964 Fl_Widget *current; // Current widget
965
966
967 win = new Fl_Double_Window(220, 310, "Preferences - " VERSION);
968 win->modal();
969
970 group = new Fl_Group(10, 10, 200, 255);
971 group->box(FL_THIN_DOWN_BOX);
972 group->color(FL_GRAY - 1);
973
974 tabwidth = new Fl_Spinner(160, 20, 40, 25, "Tab Width:");
975 tabwidth->align(FL_ALIGN_LEFT);
976 tabwidth->range(1.0, 20.0);
977 tabwidth->step(1.0);
978 tabwidth->value(dw->view_.tabwidth());
979
980 textsize = new Fl_Spinner(160, 55, 40, 25, "Text Size:");
981 textsize->align(FL_ALIGN_LEFT);
982 textsize->range(6.0, 20.0);
983 textsize->step(1.0);
984 textsize->value(dw->view_.textsize());
985
986 textfg = new Fl_Button(160, 90, 25, 25, "Text Color:");
987 textfg->align(FL_ALIGN_LEFT);
988 textfg->color(dw->view_.textcolor());
989 textfg->box(FL_BORDER_BOX);
990
991 textbg = new Fl_Button(160, 125, 25, 25, "Background Color:");
992 textbg->align(FL_ALIGN_LEFT);
993 textbg->color(dw->view_.color());
994 textbg->box(FL_BORDER_BOX);
995
996 hicolor = new Fl_Button(160, 160, 25, 25, "Highlight Color:");
997 hicolor->align(FL_ALIGN_LEFT);
998 hicolor->color(dw->view_.selection_color());
999 hicolor->box(FL_BORDER_BOX);
1000
1001 linenum = new Fl_Check_Button(160, 195, 25, 25, "Show Line Numbers:");
1002 linenum->align(FL_ALIGN_LEFT);
1003 linenum->value(dw->view_.showlinenum());
1004
1005 ignoreb = new Fl_Check_Button(160, 230, 25, 25, "Ignore Whitespace:");
1006 ignoreb->align(FL_ALIGN_LEFT);
1007 ignoreb->value(dw->view_.ignoreblanks());
1008
1009 group->end();
1010
1011 ok = new Fl_Button(100, 275, 40, 25, "OK");
1012 ok->shortcut(FL_Enter);
1013 cancel = new Fl_Button(150, 275, 60, 25, "Cancel");
1014
1015 win->end();
1016 win->hotspot(win);
1017 win->show();
1018
1019 while (win->shown())
1020 {
1021 if ((current = Fl::readqueue()) == cancel)
1022 break;
1023 else if (current == ok)
1024 {
1025 // Apply prefs
1026 dw->view_.color(textbg->color());
1027 dw->view_.textcolor(textfg->color());
1028 dw->view_.tabwidth((int)tabwidth->value());
1029 dw->view_.textsize((int)textsize->value());
1030 dw->view_.selection_color(hicolor->color());
1031 dw->view_.showlinenum(linenum->value() ? true : false);
1032 dw->view_.ignoreblanks(ignoreb->value() ? true : false);
1033
1034 // Reload the diff...
1035 dw->view_.load(dw->file1_, dw->file2_);
1036
1037 // Save prefs...
1038 dw->save_prefs();
1039 break;
1040 }
1041 else if (current == textfg || current == textbg || current == hicolor)
1042 current->color(fl_show_colormap(current->color()));
1043
1044 Fl::wait();
1045 }
1046
1047 win->hide();
1048 delete win;
1049 }
1050
1051
1052 //
1053 // 'DiffWindow::prev_change_cb()' - Show the previous change.
1054 //
1055
1056 void
prev_change_cb(Fl_Menu_Bar * m,DiffWindow * dw)1057 DiffWindow::prev_change_cb(Fl_Menu_Bar *m,
1058 // I - Menubar
1059 DiffWindow *dw)
1060 // I - Window
1061 {
1062 int i; // Looping var
1063
1064
1065 for (i = dw->view_.topline() - 1; i >= 0; i --)
1066 if (dw->view_.line_changed(i))
1067 {
1068 while (i >= 0 && dw->view_.line_changed(i))
1069 i --;
1070
1071 dw->view_.showline(i + 1);
1072 return;
1073 }
1074
1075 fl_alert("No more changes precede the current position.");
1076 }
1077
1078
1079 //
1080 // 'DiffWindow::quit_cb()' - Quit the fldiff application.
1081 //
1082
1083 void
quit_cb(Fl_Menu_Bar * m,DiffWindow * dw)1084 DiffWindow::quit_cb(Fl_Menu_Bar *m, // I - Menubar
1085 DiffWindow *dw) // I - Window
1086 {
1087 for (dw = first_; dw; dw = dw->next_)
1088 Fl::delete_widget(dw);
1089 }
1090
1091
1092 //
1093 // 'DiffWindow::rediff_cb()' - Redo the diff view.
1094 //
1095
1096 void
rediff_cb(Fl_Menu_Bar * m,DiffWindow * dw)1097 DiffWindow::rediff_cb(Fl_Menu_Bar *m, // I - Menubar
1098 DiffWindow *dw) // I - Window
1099 {
1100 dw->view_.load(dw->file1_, dw->file2_);
1101 }
1102
1103
1104 //
1105 // 'DiffWindow::resize()' - Resize the window...
1106 //
1107
1108 void
resize(int X,int Y,int W,int H)1109 DiffWindow::resize(int X, // I - X position
1110 int Y, // I - Y position
1111 int W, // I - Width
1112 int H) // I - Height
1113 {
1114 prefs_.set("window_width", W);
1115 prefs_.set("window_height", H);
1116
1117 Fl_Overlay_Window::resize(X, Y, W, H);
1118 }
1119
1120
1121 //
1122 // 'DiffWindow::save_prefs()' - Save preferences...
1123 //
1124
1125 void
save_prefs()1126 DiffWindow::save_prefs()
1127 {
1128 // Save the window prefs for the next run...
1129 prefs_.set("color", static_cast<int>(color()));
1130 prefs_.set("selection_color", static_cast<int>(selection_color()));
1131 prefs_.set("showlinenum", showlinenum());
1132 prefs_.set("tabwidth", tabwidth());
1133 prefs_.set("textcolor", static_cast<int>(textcolor()));
1134 prefs_.set("textsize", textsize());
1135 prefs_.set("ignoreblanks", ignoreblanks());
1136 }
1137
1138
1139 //
1140 // 'DiffWindow::select_cb()' - Handle selections in the diff view.
1141 //
1142
1143 void
select_cb(DiffView * dv,DiffWindow * dw)1144 DiffWindow::select_cb(DiffView *dv, // I - View
1145 DiffWindow *dw) // I - Window
1146 {
1147 Fl_Menu_Item *item; // Copy menu item...
1148
1149
1150 item = (Fl_Menu_Item *)dw->menubar_.menu() + 8;
1151
1152 if (dw->view_.selection_length())
1153 item->activate();
1154 else
1155 item->deactivate();
1156
1157 item = (Fl_Menu_Item *)dw->menubar_.menu() + 2;
1158
1159 if (dw->view_.directories() && dw->view_.select_start() >= 0)
1160 {
1161 item->activate();
1162
1163 if (Fl::event_clicks())
1164 compare_selected_cb(&(dw->menubar_), dw);
1165 }
1166 else
1167 item->deactivate();
1168 }
1169
1170
1171 //
1172 // 'DiffWindow::select_left_cb()' - Select everything on the left side.
1173 //
1174
1175 void
select_left_cb(Fl_Menu_Bar * m,DiffWindow * dw)1176 DiffWindow::select_left_cb(Fl_Menu_Bar *m,
1177 // I - Menubar
1178 DiffWindow *dw)
1179 // I - Window
1180 {
1181 dw->view_.select(0, dw->view_.count() - 1, false);
1182 }
1183
1184
1185 //
1186 // 'DiffWindow::select_none_cb()' - Select nothing.
1187 //
1188
1189 void
select_none_cb(Fl_Menu_Bar * m,DiffWindow * dw)1190 DiffWindow::select_none_cb(Fl_Menu_Bar *m,
1191 // I - Menubar
1192 DiffWindow *dw)
1193 // I - Window
1194 {
1195 dw->view_.select(-1, -1, false);
1196 }
1197
1198
1199 //
1200 // 'DiffWindow::select_right_cb()' - Select everything on the right side.
1201 //
1202
1203 void
select_right_cb(Fl_Menu_Bar * m,DiffWindow * dw)1204 DiffWindow::select_right_cb(Fl_Menu_Bar *m,
1205 // I - Menubar
1206 DiffWindow *dw)
1207 // I - Window
1208 {
1209 dw->view_.select(0, dw->view_.count() - 1, true);
1210 }
1211
1212
1213 //
1214 // 'DiffWindow::using_cb()' - Show help window.
1215 //
1216
1217 void
using_cb(Fl_Menu_Bar * m,DiffWindow * dw)1218 DiffWindow::using_cb(Fl_Menu_Bar *m, // I - Menubar
1219 DiffWindow *dw) // I - Window
1220 {
1221 Fl_Double_Window *win; // Help window
1222 Fl_Help_View *help; // Help widget
1223 Fl_Button *smaller, // Smaller text
1224 *bigger, // Bigger text
1225 *close; // Close button
1226 Fl_Box *box; // Box
1227 Fl_Group *group; // Resize group
1228 Fl_Widget *current; // Currently pressed widget
1229 int v; // Text size
1230 int W, H; // Width and height
1231
1232
1233 win = new Fl_Double_Window(400, 300, "Help - " VERSION);
1234
1235 dw->prefs_.get("help_textsize", v, FL_NORMAL_SIZE);
1236
1237 help = new Fl_Help_View(10, 10, 380, 245);
1238 help->textsize(v);
1239 help->value(help_text_);
1240
1241 group = new Fl_Group(10, 265, 380, 25);
1242 box = new Fl_Box(10, 265, 25, 25);
1243
1244 smaller = new Fl_Button(270, 265, 25, 25, "F");
1245 smaller->labelsize(8);
1246 smaller->tooltip("Make text smaller...");
1247 if (help->textsize() == 8)
1248 smaller->deactivate();
1249
1250 bigger = new Fl_Button(305, 265, 25, 25, "F");
1251 bigger->labelsize(20);
1252 bigger->tooltip("Make text larger...");
1253 if (help->textsize() == 32)
1254 bigger->deactivate();
1255
1256 close = new Fl_Button(340, 265, 50, 25, "Close");
1257
1258 group->resizable(box);
1259 group->end();
1260
1261 win->end();
1262 win->resizable(help);
1263
1264 dw->prefs_.get("help_window_width", W, 400);
1265 dw->prefs_.get("help_window_height", H, 300);
1266
1267 win->resize(win->x(), win->y(), W, H);
1268 win->show();
1269
1270 while ((current = Fl::readqueue()) != close && win->shown() && dw->shown())
1271 {
1272 if (current == smaller)
1273 {
1274 help->textsize(help->textsize() - 1);
1275
1276 if (help->textsize() == 8)
1277 smaller->deactivate();
1278
1279 bigger->activate();
1280 }
1281 else if (current == bigger)
1282 {
1283 help->textsize(help->textsize() + 1);
1284
1285 if (help->textsize() == 32)
1286 bigger->deactivate();
1287
1288 smaller->activate();
1289 }
1290
1291 Fl::wait();
1292 }
1293
1294 dw->prefs_.set("help_textsize", help->textsize());
1295 dw->prefs_.set("help_window_width", win->w());
1296 dw->prefs_.set("help_window_height", win->h());
1297
1298 win->hide();
1299 delete win;
1300 }
1301
1302
1303 //
1304 // End of "$Id: DiffWindow.cxx 407 2006-11-13 18:54:02Z mike $".
1305 //
1306