Eskil

Diff
Login

Differences From Artifact [2d34efacf9]:

To Artifact [938f586899]:


1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17









-
+







#!/bin/sh
#
#   diff.tcl
#
#   Purpose
#             Graphical frontend to diff
#
#   Usage
#             Do 'diff.tcl' for interactive mode
#             Do 'diff.tcl -h' for command line usage
#             Do 'diff.tcl --help' for command line usage
#
#   Author    Peter Spjuth  980612
#
#   Revised   Date     Remark
#
#     1.0     980612   New Version.
#     1.1     980807   Parsing of change blocks added
49
50
51
52
53
54
55


56
57


58
59
60
61
62
63
64
49
50
51
52
53
54
55
56
57


58
59
60
61
62
63
64
65
66







+
+
-
-
+
+







#
#-----------------------------------------------
# $Revision$
#-----------------------------------------------
# the next line restarts using wish \
exec wish "$0" "$@"

package require Tk

set debug 0
set diffver "Version 1.9.5  2002-08-09"
set debug 1
set diffver "Version 1.9.5+  2003-01-10"
set tmpcnt 0
set tmpfiles {}
set thisscript [file join [pwd] [info script]]
set thisdir [file dirname $thisscript]
set ::diff(cvsExists) [expr {![string equal [auto_execok cvs] ""]}]
set ::diff(diffexe) diff
# Experimenting with DiffUtil package
483
484
485
486
487
488
489





490
491
492





493
494
495
496
497
498
499
500
501
502
503
504












505
506
507


508
509

510
511
512
513
514

515

516
517
518
519
520
521
522
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504












505
506
507
508
509
510
511
512
513
514
515
516
517


518
519

520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536







+
+
+
+
+



+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
-

+





+

+







#        puts "Best for $j is $bestline : $bestscore"
        set origresult($j) $bestline
        set scores(best,$j) $bestscore
        incr bestsum $bestscore
        incr j
    }
#    puts "Bestsum: $bestsum"

    # origresult holds a mapping between blocks where each row
    # is paired with its best match. This may not be a possible
    # result since it has to be in order. 

    array set bestresult [array get origresult]
    set bestscoresum -100000

    # If the size is 1, it is automatically in order so we
    # don't need further processing.
    if {$size1 > 1} {

	# If both blocks are the same size, try first with the
    # First try the simplest match, as a base
    if {$size1 > 1 && $size1 == $size2} {
        set sum 0
        array unset result
        for {set i 0} {$i < $size1} {incr i} {
            set result($i) $i
            incr sum $scores($i,$i)
        }
#        puts "Simple map sum: $sum"
        array set bestresult [array get result]
        set bestscoresum $sum
    }
	# simple row to row match, as a base score
	if {$size1 == $size2} {
	    set sum 0
	    array unset result
	    for {set i 0} {$i < $size1} {incr i} {
		set result($i) $i
		incr sum $scores($i,$i)
	    }
#	    puts "Simple map sum: $sum"
	    array set bestresult [array get result]
	    set bestscoresum $sum
	}

    # If result is in order, no problem.
    # Otherwise, try to adjust result to make it ordered
	# If result is in order, no problem.
	# Otherwise, try to adjust result to make it ordered
    if {$size1 > 1} {
        while {1} {
	    # The outer loop restarts from the "best mapping"
            array unset result
            array set result [array get origresult]
            for {set i 0} {$i < $size1} {incr i} {
                set mark($i) 0
            }

            while {1} {
		# The inner loop tries to get the result in order
                set besti 0
                set bestscore -100000
                set order 1
                for {set i 0} {$i < $size1} {incr i} {
                    if {$mark($i) == 0} {
                        for {set j [expr {$i + 1}]} {$j < $size1} {incr j} {
                            if {$mark($j) == 0} break
542
543
544
545
546
547
548

549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570







+







                }
                for {set i [expr {$besti + 1}]} {$i < $size1} {incr i} {
                    if {$mark($i) == 0 && $result($i) <= $bestr} {
                        set mark($i) 2
                    }
                }
            }

            set prev $size2
            for {set i [expr {$size1 - 1}]} {$i >= 0} {incr i -1} {
                if {$mark($i) != 2} {
                    set prev $result($i)
                } else {
                    set high($i) [expr {$prev - 1}]
                }
572
573
574
575
576
577
578


579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598

























599
600
601
602
603




604
605

606
607
608
609
610
611
612
587
588
589
590
591
592
593
594
595




















596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620





621
622
623
624
625

626
627
628
629
630
631
632
633







+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+

-
+







                set j $result($i)
                if {[info exists scores($i,$j)]} {
#                    puts "Score: $i $j $scores($i,$j)"
                    incr scoresum $scores($i,$j)
                }
            }
#            puts "Scoresum: $scoresum ($bestscoresum)"

	    # If it was not an improvement over previous iteration, quit
            if {$scoresum > $bestscoresum} {
                array set bestresult [array get result]
                set bestscoresum $scoresum
                if {$bestscoresum >= (3 * $bestsum / 4)} {
                    break
                }
                # If the result seems too bad, try again but
                # ignore the most awkwardly placed line.
                set mostp -1
                set mosti 0
                for {set i 0} {$i < $size1} {incr i} {
                    if {$mark($i) == 1} {
                        if {abs($result($i) - $i) > $mostp} {
                            set mostp [expr {abs($result($i) - $i)}]
                            set mosti $i
                        }
                    }
                }
#                puts "Most $mosti $mostp"
                set scores(best,$mosti) 0
            if {$scoresum <= $bestscoresum} {
                break
	    }

	    array set bestresult [array get result]
	    set bestscoresum $scoresum
	    # If it is close enough to the theoretical max, take it
	    if {$bestscoresum >= (3 * $bestsum / 4)} {
		break
	    }
	    
	    # We are redoing from start, but try to improve by
	    # ignoring the most awkwardly placed line.
	    set mostp -1
	    set mosti 0
	    for {set i 0} {$i < $size1} {incr i} {
		if {$mark($i) == 1} {
		    if {abs($result($i) - $i) > $mostp} {
			set mostp [expr {abs($result($i) - $i)}]
			set mosti $i
		    }
		}
	    }
#	    puts "Most $mosti $mostp"
	    set scores(best,$mosti) 0
            } else {
                break
            }
        }
    }
        }
    }

    array set result [array get bestresult]

    array set result [array get bestresult]
    # Collect the result into diff-like codes to use as display info.

    set apa {}
    set t1 0
    set t2 0
    while {$t1 < $size1 || $t2 < $size2} {
        if {$t1 < $size1} {
            set r $result($t1)
1070
1071
1072
1073
1074
1075
1076
1077

1078
1079
1080
1081
1082
1083
1084
1085

1086
1087
1088
1089
1090
1091

1092
1093
1094
1095
1096
1097

1098
1099
1100
1101
1102
1103
1104
1105

1106
1107
1108
1109
1110
1111
1112
1091
1092
1093
1094
1095
1096
1097

1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128

1129
1130
1131
1132
1133
1134
1135
1136







-
+








+






+






+







-
+







            }
            continue
        }
        # No change block anymore. If one just ended, display it.
        if {[llength $lblock] > 0 || [llength $rblock] > 0} {
            set ::doingLine1 $lblockl
            set ::doingLine2 $rblockl
            insertMatchingBlocks $lblock $rblock
            incr ::mapMax [insertMatchingBlocks $lblock $rblock]
            set lblock {}
            set rblock {}
        }
        if {$lmode == "" && $rmode == ""} {
            insert 1 $lline $lstr
            insert 2 $rline $rstr
            incr leftc
            incr rightc
            incr ::mapMax
            continue
        }
        if {$lmode == "-"} {
            insert 1 $lline $lstr new1
            emptyline 2
            incr leftc
            incr ::mapMax
            continue
        }
        if {$rmode == "+"} {
            insert 2 $rline $rstr new2
            emptyline 1
            incr rightc
            incr ::mapMax
            continue
        }
    }
}

# Read a patch file and display it
proc displayPatch {} {
    global diff Pref
    global diff Pref changesList mapMax

    set diff(leftLabel) "Patch $diff(patchFile): old"
    set diff(rightLabel) "Patch $diff(patchFile): new"
    update idletasks

    set ch [open $diff(patchFile) r]

1143
1144
1145
1146
1147
1148
1149


1150
1151
1152
1153
1154
1155
1156
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182







+
+







            set rightRE {^\+\+\+\s+(.*)$}
        }
        if {$state == "newfile" && [regexp $leftRE $line -> sub]} {
            emptyline 1
            insert 1 "" $divider
            insert 1 "" $sub
            insert 1 "" $divider
            lappend ::changesList $mapMax 4 change 0 0 0 0
            incr mapMax 4
            continue
        }
        if {$state == "newfile" && [regexp $rightRE $line -> sub]} {
            emptyline 2
            insert 2 "" $divider
            insert 2 "" $sub
            insert 2 "" $divider
1247
1248
1249
1250
1251
1252
1253
1254





1255
1256
1257
1258
1259
1260
1261
1273
1274
1275
1276
1277
1278
1279

1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291







-
+
+
+
+
+







        cd $dir
        set filename [file tail $filename]
    }

    set cmd $args
    set cmd [linsert $args 0 exec cvs -z3 update -p]
    lappend cmd [file nativename $filename] > $outfile
    catch {eval $cmd}
    if {[catch {eval $cmd} res]} {
        if {![string match "*Checking out*" $res]} {
            tk_messageBox -icon error -title "CVS error" -message $res
        }
    }

    if {$old != ""} {
        cd $old
    }
}

# Prepare for RCS/CVS diff. Checkout copies of the versions needed.
1342
1343
1344
1345
1346
1347
1348


















1349
1350
1351
1352
1353
1354
1355
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








    cleartmp
    set diff(rightFile) $diff(RCSFile)
    set diff(leftFile) $diff(RCSFile)
    set Pref(dopt) $Pref(old_dopt)
    unset Pref(old_dopt)
}

# Prepare for a diff by creating needed temporary files
proc prepareFiles {} {
    if {$::diff(mode) == "RCS" || $::diff(mode) == "CVS"} {
        prepareRCS
    } elseif {[string match "conflict*" $::diff(mode)]} {
        prepareConflict
    }
}

# Clean up after a diff
proc cleanupFiles {} {
    if {$::diff(mode) == "RCS" || $::diff(mode) == "CVS"} {
        cleanupRCS
    } elseif {[string match "conflict*" $::diff(mode)]} {
        cleanupConflict
    }
}

# Main diff function.
proc doDiff {} {
    global diff Pref
    global doingLine1 doingLine2
    global mapMax changesList

1384
1385
1386
1387
1388
1389
1390
1391
1392


1393
1394
1395
1396
1397
1398
1399
1400
1401
1432
1433
1434
1435
1436
1437
1438


1439
1440


1441
1442
1443
1444
1445
1446
1447







-
-
+
+
-
-







        foreach w {.ft1.tl .ft2.tl} {
            $w configure -state disabled
        }
        update idletasks
        .ft2.tl see 1.0
        normalCursor
        return
    } elseif {$diff(mode) == "RCS" || $diff(mode) == "CVS"} {
        prepareRCS
    } else {
        prepareFiles
    } elseif {[string match "conflict*" $diff(mode)]} {
        prepareConflict
    }

    # Run diff and parse the result.
    if {$::diff(diffutil)} {
        set differr [catch {eval DiffUtil::diffFiles $Pref(ignore) \
                \$diff(leftFile) \$diff(rightFile)} diffres]
    } else {
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520


1521
1522
1523

1524
1525
1526
1527

1528
1529
1530
1531
1532
1533
1534
1557
1558
1559
1560
1561
1562
1563



1564
1565

1566

1567
1568
1569
1570

1571
1572
1573
1574
1575
1576
1577
1578







-
-
-
+
+
-

-
+



-
+







        $w configure -state disabled
    }
    update idletasks
    .ft2.tl see 1.0
    normalCursor
    showDiff 0

    if {$diff(mode) == "RCS" || $diff(mode) == "CVS"} {
        cleanupRCS
    } elseif {[string match "conflict*" $diff(mode)]} {
    cleanupFiles
    if {[string match "conflict*" $diff(mode)]} {
        cleanupConflict
        if {$::diff(eqLabel) != "="} {
            after idle makeMergeWin
            makeMergeWin
        }
    }
    if {$diff(printFile) != ""} {
        after idle {doPrint 1 ; exit}
        after idle {doPrint 1 ; cleanupAndExit}
    }
}

# This is the entrypoint to do a diff via DDE or Send
proc remoteDiff {file1 file2} {
    global diff

1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
2024
2025
2026
2027
2028
2029
2030

2031
2032
2033
2034
2035
2036
2037







-







    set rightMergeData {}
    array unset mergeSelection
}

# Create a window to display merge result.
proc makeMergeWin {} {
    set w .merge
    set geometry ""
    if {![winfo exists $w]} {
        toplevel $w
    } else {
        eval destroy [winfo children $w]
    }

    wm title $w "Merge result"
2232
2233
2234
2235
2236
2237
2238

2239

2240
2241
2242
2243
2244
2245
2246
2275
2276
2277
2278
2279
2280
2281
2282

2283
2284
2285
2286
2287
2288
2289
2290







+
-
+







        bind ISearch $b ""
    }

    bind ISearch <Control-Key-s> "textSearch::startIncrementalSearch %W"
}

# Dialog functions from "Practical Programming in Tcl And Tk" by Welch.
namespace eval XXtextSearch {}
proc textSearch::DialogCreate {top title args} {
proc XXtextSearch::DialogCreate {top title args} {
    variable dialog
    if {[winfo exists $top]} {
        switch -- [wm state $top] {
            normal {
                # Raise a buried window
                raise $top
            }
2255
2256
2257
2258
2259
2260
2261
2262

2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283

2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294

2295
2296
2297
2298
2299
2300
2301
2299
2300
2301
2302
2303
2304
2305

2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326

2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337

2338
2339
2340
2341
2342
2343
2344
2345







-
+




















-
+










-
+







    } else {
        eval {toplevel $top} $args
        wm title $top $title
        return 1
    }
}

proc textSearch::DialogWait {top varName {focus {}}} {
proc XXtextSearch::DialogWait {top varName {focus {}}} {
    upvar $varName var

    # Poke the variable if the user nukes the window
    bind $top <Destroy> [list set $varName $var]

    # Grab focus for the dialog
    if {[string length $focus] == 0} {
        set focus $top
    }
    set old [focus -displayof $top]
    focus $focus
    catch {tkwait visibility $top}
    catch {grab $top}

    # Wait for the dialog to complete
    tkwait variable $varName
    catch {grab release $top}
    focus $old
}

proc textSearch::DialogDismiss {top} {
proc XXtextSearch::DialogDismiss {top} {
    variable dialog
    # Save current size and position
    catch {
        # window may have been deleted
        set dialog(geo,$top) [wm geometry $top]
        wm withdraw $top
    }
}

# Ask for a search string
proc textSearch::FindDialog {string} {
proc XXtextSearch::FindDialog {string} {
    variable prompt
    set f .prompt
    if {[DialogCreate $f "Find" -borderwidth 10]} {
        message $f.msg -text $string -aspect 1000
        entry $f.entry -textvariable ::textSearch::prompt(result)

        checkbutton $f.case -text "Match Case" -anchor w\
2317
2318
2319
2320
2321
2322
2323





































































































2324
2325
2326
2327
2328
2329
2330
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        bind $f.entry <Key-Return> {set ::textSearch::prompt(ok) 1 ; break}
        bind $f.entry <Key-Escape> {set ::textSearch::prompt(ok) 0 ; break}
    }

    set prompt(ok) 0
    DialogWait $f ::textSearch::prompt(ok) $f.entry
    DialogDismiss $f
    if {$prompt(ok)} {
        return $prompt(result)
    } else {
        return {}
    }
}

proc textSearch::Dialog {args} {
    set arg(-parent) .
    set arg(-title) ""
    set arg(-body) {pack [button $top.b -text Ok -command "destroy $top"]}

    foreach {opt val} $args {
	set arg($opt) $val
    }

    if {$arg(-parent) == "."} {
	set arg(-parent) ""
    }
    set t 0
    set top $arg(-parent).dialog_$t
    while {[winfo exists $top]} {
	incr t
	set top $arg(-parent).dialog_$t
    }

    toplevel $top
    wm title $top $arg(-title)

    set oldfocus [focus -displayof $top]

    if {[uplevel 1 {info exists top}]} {
	set oldtop [uplevel 1 {set top}]
    }

    uplevel 1 [list set top $top]
    uplevel 1 $arg(-body)

    if {[info exists oldtop]} {
	uplevel 1 [list set top $oldtop]
    } else {
	uplevel 1 {unset top}
    }

    # Grab focus for the dialog unless the user did it in the body
    if {[winfo toplevel [focus -displayof $top]] != $top} {
	focus $top
    }
    catch {tkwait visibility $top}
    catch {grab $top}

    # Wait for the dialog to complete
    tkwait window $top
    catch {grab release $top}
    focus $oldfocus
}

proc textSearch::DismissDialog {top result} {
    variable prompt
    
    set prompt(ok) $result
    set prompt(geo) [wm geometry $top]
    destroy $top
}

# Ask for a search string
proc textSearch::FindDialog {string} {
    variable prompt

    set prompt(ok) 0

    Dialog -title Find -body {
	message $top.msg -text $string -aspect 1000
        entry $top.entry -textvariable ::textSearch::prompt(result)

        checkbutton $top.case -text "Match Case" -anchor w \
	        -variable ::textSearch::searchCase

        button $top.ok     -text OK     -width 7 -default active \
	        -command "::textSearch::DismissDialog $top 1"
        button $top.cancel -text Cancel -width 7 \
	        -command "::textSearch::DismissDialog $top 0"

        grid $top.msg   - - -sticky w  -padx 2 -pady 2
        grid $top.entry - - -sticky we -padx 2 -pady 2
        grid $top.case  - - -sticky nw  -padx 2 -pady 2
        grid $top.ok x $top.cancel -sticky we -padx 2 -pady 2
        grid columnconfigure $top {0 2} -weight 1
        grid columnconfigure $top 1 -minsize 10 -weight 2
        grid rowconfigure $top 2 -weight 1

        bind $top.entry <Key-Return> \
	        "::textSearch::DismissDialog $top 1 ; break"
        bind $top.entry <Key-Escape> \
	        "::textSearch::DismissDialog $top 0 ; break"
	focus $top.entry
	if {[info exists prompt(geo)]} {
	    wm geometry $top $prompt(geo)
	}
    }

    if {$prompt(ok)} {
        return $prompt(result)
    } else {
        return {}
    }
}

2724
2725
2726
2727
2728
2729
2730
2731










































































































2732
2733
2734
2735
2736
2737







2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750

2751
2752
2753

2754
2755
2756
2757
2758
2759
2760
2761
2762
2763


2764
2765


2766
2767



2768
2769
2770
2771
2772
2773
2774
2775
2776

2777

2778

2779
2780
















2781
2782
2783
2784
2785
2786
2787

2788
2789
2790
2791
2792
2793
2794
2795
2796
2797







2798
2799
2800
2801
2802
2803
2804
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997







2998
2999
3000

3001
3002
3003

3004
3005
3006
3007
3008
3009
3010
3011
3012
3013

3014
3015
3016
3017
3018
3019


3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037

3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059

3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+
+
+
+
+
+
+


-
-
-
-
-
-
-



-
+


-
+









-
+
+


+
+
-
-
+
+
+









+

+

+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
+










+
+
+
+
+
+
+







    pack .pr.r1 .pr.r2 .pr.r3 .pr.r4 -in .pr.f -side left -fill x -expand 1

}

#####################################
# GUI stuff
#####################################

proc formatAlignPattern {p} {
    set raw [binary format I $p]
    binary scan $raw B* bin
    set bin [string trimleft [string range $bin 0 end-8] 0][string range $bin end-7 end]
    set pat [string map {0 . 1 ,} $bin]
    return $pat
}

proc runAlign {} {
    if {![info exists ::diff(aligns)] || [llength $::diff(aligns)] == 0} {
        return
    }

    set pattern 0
    foreach align $::diff(aligns) {
        foreach {lline rline level} $align break
        
        set pre {}
        set post {}
        for {set t 1} {$t <= $level} {incr t} {
            lappend pre [formatAlignPattern $pattern]
            incr pattern
            lappend post [formatAlignPattern $pattern]
            incr pattern
        }

        set fix1($lline) [list [join $pre \n] [join $post \n]]
        set fix2($rline) [list [join $pre \n] [join $post \n]]
    }

    prepareFiles
    foreach n {1 2} src {leftFile rightFile} {
        set tmp [tmpfile]
        set f$n $tmp
        set cho [open $tmp w]
        set chi [open $::diff($src) r]
        set lineNo 1
        while {[gets $chi line] >= 0} {
            if {[info exists fix${n}($lineNo)]} {
                foreach {pre post} [set fix${n}($lineNo)] break
                puts $cho $pre
                puts $cho $line
                puts $cho $post
            } else {
                puts $cho $line
            }
            incr lineNo
        }
        close $cho
        close $chi
    }
    cleanupFiles

    catch {exec [info nameofexecutable] diff.tcl $f1 $f2 &}

    set ::diff(aligns) ""
}

# Mark a line as aligned.
proc markAlign {n line text} {
    set ::diff(align$n) $line
    set ::diff(aligntext$n) $text

    if {[info exists ::diff(align1)] && [info exists ::diff(align2)]} {
        set level 1
        if {![string equal $::diff(aligntext1) $::diff(aligntext2)]} {
            set apa [tk_messageBox -icon question -title "Align" -type yesno \
                    -message "Those lines are not equal.\nReally align them?"]
            if {$apa != "yes"} {
                return
            }
            set level 3
        }

        lappend ::diff(aligns) [list $::diff(align1) $::diff(align2) $level]

        unset ::diff(align1)
        unset ::diff(align2)
    }
}

# Called by popup menus over row numbers to add command for alignment.
# Returns 1 of nothing added.
proc alignMenu {m n x y} {
    # Get the row that was clicked
    set index [.ft$n.tl index @$x,$y]
    set row [lindex [split $index "."] 0]

    set data [.ft$n.tl get $row.0 $row.end]
    if {![regexp {\d+} $data line]} {
        return 1
    }
    set text [.ft$n.tt get $row.0 $row.end]

    set other [expr {$n == 1 ? 2 : 1}]
    if {![info exists ::diff(align$other)]} {
        set label "Mark line for alignment"
    } else {
        set label "Align with line $::diff(align$other) on other side"
    }

    .lpm add command -label $label -command [list markAlign $n $line $text]
    return 0
}


proc hlSelect {hl} {
    highLightChange $hl
}

proc hlSeparate {n hl} {
    set ::diff(separate$n) $hl
    if {$hl == ""} {
        set range [.ft$n.tt tag ranges sel]
    } else {
        set range [.ft$n.tl tag ranges hl$::diff(separate$n)]
    }
    set text [eval .ft$n.tt get $range]
    set ::diff(separatetext$n) $text

    if {[info exists ::diff(separate1)] && [info exists ::diff(separate2)]} {
        set tag1 hl$::diff(separate1)
        set tag2 hl$::diff(separate2)
        set range1 [.ft1.tl tag ranges $tag1]
        set range2 [.ft2.tl tag ranges $tag2]
        set text1 [eval .ft1.tt get $range1]
        set text2 [eval .ft2.tt get $range2]

        set f1 [tmpfile]
        set f2 [tmpfile]
        set ch [open $f1 w]
        puts $ch $text1
        puts $ch $::diff(separatetext1)
        close $ch
        set ch [open $f2 w]
        puts $ch $text2
        puts $ch $::diff(separatetext2)
        close $ch

        catch {exec [info nameofexecutable] diff.tcl $f1 $f2 &}

        unset ::diff(separate1)
        unset ::diff(separate2)
    }
}

proc hlPopup {n hl X Y} {
proc hlPopup {n hl X Y x y} {
    if {[info exists ::diff(nopopup)] && $::diff(nopopup)} return
    destroy .lpm
    menu .lpm -tearoff 0

    if {$hl != ""} {
    .lpm add command -label "Select" \
            -command [list hlSelect $hl]
        .lpm add command -label "Select" \
                -command [list hlSelect $hl]
    }

    set other [expr {$n == 1 ? 2 : 1}]
    if {![info exists ::diff(separate$other)]} {
        set label "Mark for Separate Diff"
    } else {
        set label "Separate Diff"
    }

    .lpm add command -label $label -command [list hlSeparate $n $hl]
    alignMenu .lpm $n $x $y

    set ::diff(nopopup) 1
    tk_popup .lpm $X $Y
    after idle {after 1 {set ::diff(nopopup) 0}}

    return -code break
    return
}

proc rowPopup {w X Y x y} {
    if {[info exists ::diff(nopopup)] && $::diff(nopopup)} return
    destroy .lpm
    menu .lpm -tearoff 0

    regexp {\d+} $w n
    if {[alignMenu .lpm $n $x $y]} {
        return
    }

    set ::diff(nopopup) 1
    tk_popup .lpm $X $Y
    after idle {after 1 {set ::diff(nopopup) 0}}
}

proc bindHighlight {} {
    set tag hl$::HighLightCount
    foreach n {1 2} {
        .ft$n.tl tag bind $tag <ButtonPress-3> \
                "hlPopup $n $::HighLightCount %X %Y"
                "hlPopup $n $::HighLightCount %X %Y %x %y ; break"
        .ft$n.tl tag bind $tag <ButtonPress-1> \
                "hlSelect $::HighLightCount"
    }
}

proc zoomRow {w X Y x y} {
    global Pref
    # Get the row that was clicked
    set index [$w index @$x,$y]
    set row [lindex [split $index "."] 0]

    # Check if it is selected
    if {[lsearch [$w tag names $index] sel] >= 0} {
        regexp {\d+} $w n
        hlPopup $n "" $X $Y $x $y
        return
    }

    # Extract the data
    set data1 [.ft1.tt dump -tag -text $row.0 $row.end]
    set data2 [.ft2.tt dump -tag -text $row.0 $row.end]
    if {[llength $data1] == 0 && [llength $data2] == 0} return

    set font [.ft1.tt cget -font]
2918
2919
2920
2921
2922
2923
2924



























2925
2926
2927
2928
2929
2930
2931
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








# Scroll text windows
proc scroll {n what} {
    if {![string match ".ft?.tt" [focus]]} {
        .ft1.tt yview scroll $n $what
    }
}

# Emulate a label that:
# 1 : Displays the right part of the text if there isn't enough room
# 2 : Justfify text to the left if there is enough room.
# 3 : Does not try to allocate space according to its contents
proc fileLabel {w args} {
    eval label $w $args
    set fg [$w cget -foreground]
    set bg [$w cget -background]
    set font [$w cget -font]
    destroy $w

    entry $w -relief flat -bd 0 -foreground $fg -background $bg -font $font
    eval $w configure $args

    $w configure -takefocus 0 -state disabled
    if {[info tclversion] >= 8.4} {
        $w configure -state readonly
    }

    set i [lsearch $args -textvariable]
    if {$i >= 0} {
	set var [lindex $args [expr {$i + 1}]]
	uplevel #0 "trace variable $var w \
		{after idle {$w xview end} ;#}"
    }
}

# Build the main window
proc makeDiffWin {} {
    global Pref tcl_platform debug
    eval destroy [winfo children .]

    wm protocol . WM_DELETE_WINDOW cleanupAndExit
2953
2954
2955
2956
2957
2958
2959
2960

2961
2962
2963
2964
2965
2966
2967
3260
3261
3262
3263
3264
3265
3266

3267
3268
3269
3270
3271
3272
3273
3274







-
+







    }
    if {$::diff(cvsExists)} {
        .mf.m add command -label "CVSDiff" -underline 0 -command openCVS
    }
    .mf.m add separator
    .mf.m add command -label "Print" -underline 0 -command doPrint
    .mf.m add separator
    .mf.m add command -label "Quit" -command cleanupAndExit
    .mf.m add command -label "Quit" -underline 0 -command cleanupAndExit

    menubutton .mo -text Options -underline 0 -menu .mo.m
    menu .mo.m
    .mo.m add cascade -label Font -underline 0 -menu .mo.mf
    .mo.m add cascade -label Ignore -underline 0 -menu .mo.mi
    .mo.m add cascade -label Parse -underline 0 -menu .mo.mp
    .mo.m add command -label Colours -underline 0 -command makePrefWin
3022
3023
3024
3025
3026
3027
3028
3029
3030




3031
3032
3033
3034
3035
3036

3037

3038

3039
3040
3041
3042
3043
3044
3045
3046
3047

3048

3049

3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064

3065
3066
3067



3068
3069
3070
3071
3072
3073
3074
3329
3330
3331
3332
3333
3334
3335


3336
3337
3338
3339
3340
3341
3342
3343
3344

3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357

3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391







-
-
+
+
+
+





-
+

+

+








-
+

+

+















+



+
+
+







    button .bfp -text "Prev Diff" -relief raised -command {findDiff -1}
    entry .eo -width 10 -textvariable Pref(dopt)
    label .lo -text "Diff Options"

    catch {font delete myfont}
    font create myfont -family $Pref(fontfamily) -size $Pref(fontsize)

    label .l1 -textvariable diff(leftLabel) -anchor e -width 10
    label .l2 -textvariable diff(rightLabel) -anchor e -width 10
    #label .l1 -textvariable diff(leftLabel) -anchor e -width 10
    #label .l2 -textvariable diff(rightLabel) -anchor e -width 10
    fileLabel .l1 -textvariable diff(leftLabel)
    fileLabel .l2 -textvariable diff(rightLabel)

    frame .ft1 -borderwidth 2 -relief sunken
    text .ft1.tl -height 40 -width 5 -wrap none -yscrollcommand my_yscroll \
            -font myfont -borderwidth 0 -padx 0 -highlightthickness 0
    text .ft1.tt -height 40 -width 80 -wrap none -yscrollcommand my_yscroll \
            -xscrollcommand ".sbx1 set" -font myfont -borderwidth 0 -padx 0 \
            -xscrollcommand ".sbx1 set" -font myfont -borderwidth 0 -padx 1 \
            -highlightthickness 0
    frame .ft1.f -width 2 -height 2 -bg lightgray
    pack .ft1.tl -side left -fill y
    pack .ft1.f -side left -fill y
    pack .ft1.tt -side right -fill both -expand 1
    scrollbar .sby -orient vertical -command "my_yview"
    scrollbar .sbx1 -orient horizontal -command ".ft1.tt xview"

    frame .ft2 -borderwidth 2 -relief sunken
    text .ft2.tl -height 60 -width 5 -wrap none -yscrollcommand my_yscroll \
            -font myfont -borderwidth 0 -padx 0 -highlightthickness 0
    text .ft2.tt -height 60 -width 80 -wrap none -yscrollcommand my_yscroll \
            -xscrollcommand ".sbx2 set" -font myfont -borderwidth 0 -padx 0 \
            -xscrollcommand ".sbx2 set" -font myfont -borderwidth 0 -padx 1 \
            -highlightthickness 0
    frame .ft2.f -width 2 -height 2 -bg lightgray
    pack .ft2.tl -side left -fill y
    pack .ft2.f -side left -fill y
    pack .ft2.tt -side right -fill both -expand 1
    scrollbar .sbx2 -orient horizontal -command ".ft2.tt xview"

    # Set up a tag for incremental search bindings
    textSearch::enableSearch .ft1.tt
    textSearch::enableSearch .ft2.tt

    label .le -textvariable ::diff(eqLabel) -width 1
    label .ls -textvariable ::textSearch::statusLabel -width 1 -pady 0 -padx 0
    canvas .c -width 6 -bd 0 -selectborderwidth 0 -highlightthickness 0

    applyColor
    .ft1.tt tag configure last -underline 1
    .ft2.tt tag configure last -underline 1
    foreach w {.ft1.tt .ft2.tt} {
        $w tag raise sel
        bind $w <ButtonPress-3> "zoomRow %W %X %Y %x %y"
        bind $w <ButtonRelease-3> "unzoomRow"
    }
    foreach w {.ft1.tl .ft2.tl} {
        bind $w <ButtonPress-3> "rowPopup %W %X %Y %x %y"
    }

    grid .l1   .le -    .l2   -row 1 -sticky news
    grid .ft1  .c  .sby .ft2  -row 2 -sticky news
    grid .sbx1 .ls -    .sbx2 -row 3 -sticky news
    grid columnconfigure . {0 3} -weight 1
    grid rowconfigure . 2 -weight 1
    grid .c -pady [expr {[.sby cget -width] + 2}]
3099
3100
3101
3102
3103
3104
3105
3106

3107
3108
3109
3110
3111
3112
3113
3416
3417
3418
3419
3420
3421
3422

3423
3424
3425
3426
3427
3428
3429
3430







-
+







        .md.m add radiobutton -label "Context 10" -variable ::Pref(context) -value 10
        .md.m add radiobutton -label "Context 20" -variable ::Pref(context) -value 20
        .md.m add separator
        .md.m add checkbutton -label Wrap -variable wrapstate -onvalue char\
                -offvalue none -command {.ft1.tt configure -wrap $wrapstate ;\
                .ft2.tt configure -wrap $wrapstate}
        .md.m add command -label "Merge" -command {makeMergeWin}
        .md.m add command -label "Stack trace" -command {bgerror Debug}
        .md.m add command -label "Align" -command {runAlign}
        .md.m add separator
        .md.m add command -label "Reread Source" -command {source $thisscript}
        .md.m add separator
        .md.m add command -label "Redraw Window" -command {makeDiffWin}
        .md.m add separator
        .md.m add command -label "Normal Cursor" -command {normalCursor}
        .md.m add separator
3439
3440
3441
3442
3443
3444
3445
3446



3447
3448
3449

3450
3451
3452
3453
3454
3455
3456
3756
3757
3758
3759
3760
3761
3762

3763
3764
3765
3766
3767

3768
3769
3770
3771
3772
3773
3774
3775







-
+
+
+


-
+








} "" {Bindings} ul {

Up, Down, Page Up and Page Down scrolls main windows.

Escape takes focus out of text windows.

Right mouse button "zooms" a line of text.
Right mouse button "zooms" a line of text. If the text under the cursor
is selected, a menu appears where the selected text can be used for a
separate diff.

Ctrl-s starts incremental search. Incremental search is stopped by Escape
  or Ctrl-g.
or Ctrl-g.

Ctrl-f brings up search dialog. F3 is "search again".

Left mouse click on the line number of a diff highlights it.

Right mouse click on the line number of a diff gives a menu where it can
be selected for separate diff. This can be used to check a block that has
3627
3628
3629
3630
3631
3632
3633
3634

3635
3636
3637
3638
3639
3640
3641
3946
3947
3948
3949
3950
3951
3952

3953
3954
3955
3956
3957
3958
3959
3960







-
+







                set diff(limitlines) $arg
            }
            set nextArg ""
            continue
        }
        if {$arg == "-w"} {
            set Pref(ignore) "-w"
        } elseif {$arg == "-h"} {
        } elseif {$arg == "--help"} {
            printUsage
            exit
        } elseif {$arg == "-b"} {
            set Pref(ignore) "-b"
        } elseif {$arg == "-noignore"} {
            set Pref(ignore) " "
        } elseif {$arg == "-noparse"} {
3791
3792
3793
3794
3795
3796
3797
3798





3799
3800
3801
3802
3803
3804
3805
4110
4111
4112
4113
4114
4115
4116

4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128







-
+
+
+
+
+







        }
    }
}

proc saveOptions {} {
    global Pref

    set ch [open "~/.diffrc" w]
    if {[catch {set ch [open "~/.diffrc" w]} err]} {
        tk_messageBox -icon error -title "File error" -message \
                "Error when trying to save preferences:\n$err"
        return
    }

    foreach i [array names Pref] {
        if {$i != "dopt"} {
            puts $ch [list set Pref($i) $Pref($i)]
        }
    }
    close $ch