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