|
|
1.1 ! root 1: ! 2: /******************************************************************************\ ! 3: * This is a part of the Microsoft Source Code Samples. ! 4: * Copyright (C) 1993 Microsoft Corporation. ! 5: * All rights reserved. ! 6: * This source code is only intended as a supplement to ! 7: * Microsoft Development Tools and/or WinHelp documentation. ! 8: * See these sources for detailed information regarding the ! 9: * Microsoft samples programs. ! 10: \******************************************************************************/ ! 11: ! 12: /****************************** Module Header ******************************* ! 13: * Module Name: VIEW.C ! 14: * ! 15: * Maps rows in window to items in COMPLIST ! 16: * ! 17: * Functions: ! 18: * ! 19: * view_new() ! 20: * view_setcomplist() ! 21: * view_getcomplist() ! 22: * view_close() ! 23: * view_delete() ! 24: * view_outline() ! 25: * view_expand() ! 26: * view_gettext() ! 27: * view_getlinenr_left() ! 28: * view_getlinenr_right() ! 29: * view_getwidth() ! 30: * view_getrowcount() ! 31: * view_getstate() ! 32: * view_getitem() ! 33: * view_isexpanded() ! 34: * view_getcurrenttag() ! 35: * view_newitem() ! 36: * view_changeviewoptions() ! 37: * view_changediffoptions() ! 38: * view_findchange() ! 39: * view_outline_opt() ! 40: * view_freemappings() ! 41: * view_findrow() ! 42: * view_expand_item() ! 43: * ! 44: * Comments: ! 45: * ! 46: * A view owns a COMPLIST, and talks to a table window. The table window ! 47: * shows 3 columns: line nr, tag and text. We also need to supply a state ! 48: * for each row (used to select colour scheme). ! 49: * ! 50: * The COMPLIST can give us a list of its COMPITEMs. Each of these can give ! 51: * us a tag (eg the filenames compared) and the text (usually the compare ! 52: * result), and the state. We make the line number from the ! 53: * COMPITEM's place in the list. ! 54: * ! 55: * If we are asked to switch to 'expand' mode, we ask the selected COMPITEM ! 56: * for its composite section list. We can then get the state (and thus ! 57: * the tag) from each SECTION, and the line nr and text from the LINEs within ! 58: * each section. ! 59: * ! 60: * When moving between expand and outline, and when refreshing the view ! 61: * for some option change, we have to be careful to keep the current row ! 62: * and the selected row in the table what the user would expect. ! 63: * ! 64: * Functions in this module can be called from the UI thread (to refresh ! 65: * the display) and simultaneously from a worker thread to update the ! 66: * view mapping (view_setcomplist, view_newitem). We use a critical section ! 67: * to manage the synchronisation. We need to protect all access/modification ! 68: * to the view structure elements (particularly bExpand, rows, pLines and ! 69: * pItems), BUT we must not hold the critical section over any calls ! 70: * to SendMessage. ! 71: * ! 72: * We use the global options in windiff.h, and we allocate memory from the ! 73: * heap hHeap which has been initialised elsewhere. Points in time-intensive ! 74: * loops call Poll() defined elsewhere. ! 75: * ! 76: ****************************************************************************/ ! 77: ! 78: #include <windows.h> ! 79: #include <stdlib.h> ! 80: #include <commdlg.h> ! 81: ! 82: #include "gutils.h" ! 83: #include "table.h" ! 84: #include "state.h" ! 85: #include "windiff.h" ! 86: #include "wdiffrc.h" ! 87: #include "list.h" ! 88: #include "line.h" ! 89: #include "scandir.h" ! 90: #include "file.h" ! 91: #include "section.h" ! 92: #include "compitem.h" ! 93: #include "complist.h" ! 94: #include "view.h" ! 95: ! 96: /* ! 97: * data structures ! 98: */ ! 99: ! 100: /* in expand mode, we keep an array of one of these per screen line. */ ! 101: typedef struct viewline { ! 102: LINE line; /* handle to LINE for this row */ ! 103: SECTION section; /* handle to section containing this line */ ! 104: int nr_left; /* line nr in left file */ ! 105: int nr_right; /* line nr in right file */ ! 106: } VIEWLINE, FAR * PVIEWLINE; ! 107: ! 108: ! 109: /* ! 110: * The users VIEW handle is in fact a pointer to this structure ! 111: */ ! 112: struct view { ! 113: ! 114: HWND hwnd; /* the table window to send notifies to */ ! 115: ! 116: COMPLIST cl; /* the complist that we own */ ! 117: ! 118: BOOL bExpand; /* true if we are in expand mode */ ! 119: ! 120: COMPITEM ciSelect; /* selected compitem (in expand mode) */ ! 121: ! 122: int rows; /* number of rows in this view */ ! 123: ! 124: char nrtext[12]; /* we use this in view_gettext for the line ! 125: * number column. overwritten on each call ! 126: */ ! 127: int maxtag, maxrest;/* column widths in characters for cols 1, 2 */ ! 128: ! 129: /* if we are in outline mode, we map the row number to one entry ! 130: * in this array of COMPITEM handles. this pointer will ! 131: * be NULL in expand mode ! 132: */ ! 133: COMPITEM FAR * pItems; ! 134: ! 135: /* in expand mode we use this array of line and section handles */ ! 136: PVIEWLINE pLines; ! 137: }; ! 138: ! 139: ! 140: CRITICAL_SECTION CSView; ! 141: static BOOL bDoneInit = FALSE; ! 142: ! 143: #define ViewEnter() EnterCriticalSection(&CSView); ! 144: #define ViewLeave() LeaveCriticalSection(&CSView); ! 145: ! 146: void view_outline_opt(VIEW view, BOOL bRedraw); ! 147: void view_freemappings(VIEW view); ! 148: int view_findrow(VIEW view, int number, BOOL bRight); ! 149: BOOL view_expand_item(VIEW view, COMPITEM ci); ! 150: ! 151: ! 152: /*************************************************************************** ! 153: * Function: view_new ! 154: * ! 155: * Purpose: ! 156: * ! 157: * Create a new view. At this point, we are told the table window handle, ! 158: * and nothing else. ! 159: * ! 160: */ ! 161: VIEW ! 162: view_new(HWND hwndTable) ! 163: { ! 164: VIEW view; ! 165: ! 166: if (!bDoneInit) { ! 167: InitializeCriticalSection(&CSView); ! 168: bDoneInit = TRUE; ! 169: } ! 170: ! 171: /* alloc the view from the heap */ ! 172: view = (VIEW) gmem_get(hHeap, sizeof(struct view)); ! 173: ! 174: /* set the default fields */ ! 175: view->hwnd = hwndTable; ! 176: view->cl = NULL; ! 177: view->bExpand = FALSE; ! 178: view->ciSelect = NULL; ! 179: view->rows = 0; ! 180: view->pItems = NULL; ! 181: view->pLines = NULL; ! 182: ! 183: return(view); ! 184: } ! 185: ! 186: ! 187: /*************************************************************************** ! 188: * Function: view_setcomplist ! 189: * ! 190: * Purpose: ! 191: * ! 192: * We have to separate view_new and view_setcomplist because we need ! 193: * to give the view handle to the complist and the complist handle to the ! 194: * view. So do a view_new to create a null view; then complist_new() to ! 195: * which you pass a view handle. The complist will then register itself ! 196: * with the view by calling this function. During the build of the complist, ! 197: * it will also update us by calling view_additem, so that we can refresh ! 198: * the display. ! 199: * ! 200: * Here we should initialise an outline view of the complist. ! 201: * ! 202: * We also talk to the status bar using SetNames to set the names of ! 203: * the two items. ! 204: */ ! 205: BOOL ! 206: view_setcomplist(VIEW view, COMPLIST cl) ! 207: { ! 208: LPSTR left, right, both; ! 209: ! 210: if (view == NULL) { ! 211: return(FALSE); ! 212: } ! 213: ! 214: /* there can be only one call to this per VIEW */ ! 215: if (view->cl != NULL) { ! 216: return (FALSE); ! 217: } ! 218: ! 219: ViewEnter(); ! 220: ! 221: view->cl = cl; ! 222: ! 223: /* set names on status bar to root names of left and right trees */ ! 224: left = complist_getroot_left(cl); ! 225: right = complist_getroot_right(cl); ! 226: both = gmem_get(hHeap, lstrlen(left) + lstrlen(right) +4); ! 227: wsprintf((LPTSTR)both, "%s : %s", left, right); ! 228: ViewLeave(); ! 229: SetNames(both); ! 230: ViewEnter(); ! 231: gmem_free(hHeap, both, lstrlen(both)+1); ! 232: complist_freeroot_left(cl, left); ! 233: complist_freeroot_right(cl, right); ! 234: ! 235: ViewLeave(); ! 236: ! 237: view_outline(view); ! 238: } ! 239: ! 240: ! 241: /*************************************************************************** ! 242: * Function: view_getcomplist ! 243: * ! 244: * Purpose: ! 245: * ! 246: * Return a handle to the complist owned by this view ! 247: */ ! 248: COMPLIST ! 249: view_getcomplist(VIEW view) ! 250: { ! 251: if (view == NULL) { ! 252: return(NULL); ! 253: } ! 254: ! 255: return(view->cl); ! 256: } ! 257: ! 258: ! 259: /*************************************************************************** ! 260: * Function: view_close ! 261: * ! 262: * Purpose: ! 263: * ! 264: * Close a view. Notify the table window that this view should be ! 265: * closed. When the table window has finished with it, it will send ! 266: * a TQ_CLOSE notify that should result in view_delete being called ! 267: * and the memory being freed. ! 268: */ ! 269: void ! 270: view_close(VIEW view) ! 271: { ! 272: if (view == NULL) { ! 273: return; ! 274: } ! 275: ! 276: SendMessage(view->hwnd, TM_NEWID, 0, 0); ! 277: } ! 278: ! 279: ! 280: /*************************************************************************** ! 281: * Function: view_delete ! 282: * ! 283: * Purpose: ! 284: * ! 285: * Delete a view and all associated data. ! 286: * ! 287: * This function should only be called in response to the table window ! 288: * sending a TQ_CLOSE message. To close the view, call view_close and ! 289: * wait for the TQ_CLOSE before calling this. ! 290: * ! 291: * We delete the associated COMPLIST and all its associated structures. ! 292: */ ! 293: void ! 294: view_delete(VIEW view) ! 295: { ! 296: if (view == NULL) { ! 297: return; ! 298: } ! 299: ! 300: /* we have two arrays that are used for the mapping - an array ! 301: * of compitem handles in outline mode, and an array of ! 302: * VIEWLINE structures in expand mode ! 303: */ ! 304: ! 305: view_freemappings(view); ! 306: ! 307: complist_delete(view->cl); ! 308: ! 309: gmem_free(hHeap, (LPSTR) view, sizeof(struct view)); ! 310: } ! 311: ! 312: ! 313: /*************************************************************************** ! 314: * Function: view_outline ! 315: * ! 316: * Purpose: ! 317: * ! 318: * Build an outline mode mapping where one row represents one COMPITEM in ! 319: * the list. Check the global option flag outline_include to see which items ! 320: * we should include. ! 321: * ! 322: * If we were in expand mode, then set as the selection the row in outline mode ! 323: * that we were expanding. Also remember to free up the expand mode mapping ! 324: * array ! 325: * ! 326: * Once we have built the new mapping, notify the table window to ! 327: * redraw itself. ! 328: */ ! 329: void ! 330: view_outline(VIEW view) ! 331: { ! 332: if (view == NULL) { ! 333: return; ! 334: } ! 335: ! 336: /* all work done by view_outline_opt - this function ! 337: * gives us the option of not updating the display ! 338: */ ! 339: view_outline_opt(view, TRUE); ! 340: } ! 341: ! 342: ! 343: ! 344: /*************************************************************************** ! 345: * Function: view_expand ! 346: * ! 347: * Purpose: ! 348: * ! 349: * Switch to expand mode, expanding the given row into a view ! 350: * of the differences in that file. ! 351: * ! 352: * Map the given row nr into a compitem handle, and then ! 353: * call the internal function with that. ! 354: */ ! 355: BOOL ! 356: view_expand(VIEW view, long row) ! 357: { ! 358: COMPITEM ci; ! 359: BOOL bRet; ! 360: ! 361: ViewEnter(); ! 362: ! 363: if ((view == NULL) || (view->bExpand)) { ! 364: /* no view, or already expanded */ ! 365: ViewLeave(); ! 366: return(FALSE); ! 367: } ! 368: ! 369: if (row >= view->rows) { ! 370: /* no such row */ ! 371: ViewLeave(); ! 372: return FALSE; ! 373: } ! 374: ! 375: /* remember the compitem we are expanding */ ! 376: ci = view->pItems[row]; ! 377: ! 378: bRet = view_expand_item(view, ci); ! 379: // view_expand_item does the... ! 380: // ViewLeave(); ! 381: return(bRet); ! 382: } ! 383: ! 384: ! 385: /*************************************************************************** ! 386: * Function: view_gettext ! 387: * ! 388: * Purpose: ! 389: * ! 390: * Return the text associated with a given column of a given row. ! 391: * Return a pointer that does not need to be freed after use - ie ! 392: * a pointer into our data somewhere, not a copy ! 393: */ ! 394: LPSTR ! 395: view_gettext(VIEW view, long row, int col) ! 396: { ! 397: int line; ! 398: int state; ! 399: LPSTR pstr; ! 400: ! 401: ! 402: if (view == NULL) { ! 403: return (NULL); ! 404: } ! 405: ! 406: ViewEnter(); ! 407: ! 408: if (row >= view->rows) { ! 409: ViewLeave(); ! 410: return(NULL); ! 411: } ! 412: ! 413: if (view->bExpand) { ! 414: /* we are in expand mode */ ! 415: ! 416: state = section_getstate(view->pLines[row].section); ! 417: ! 418: switch(col) { ! 419: case 0: ! 420: /* row nr */ ! 421: ! 422: /* line numbers can be from either original file ! 423: * this is a menu-selectable option ! 424: */ ! 425: switch(line_numbers) { ! 426: case IDM_NONRS: ! 427: pstr = NULL; ! 428: break; ! 429: ! 430: case IDM_LNRS: ! 431: line = view->pLines[row].nr_left; ! 432: if (state == STATE_MOVEDRIGHT) { ! 433: line = -line; ! 434: } ! 435: break; ! 436: ! 437: case IDM_RNRS: ! 438: line = view->pLines[row].nr_right; ! 439: if (state == STATE_MOVEDLEFT) { ! 440: line = -line; ! 441: } ! 442: break; ! 443: } ! 444: if (line == 0) { ! 445: ViewLeave(); ! 446: return(NULL); ! 447: } ! 448: ! 449: if (line < 0) { ! 450: /* lines that are moved appear twice. ! 451: * show the correct-sequence line nr ! 452: * for the out-of-seq. copy in brackets. ! 453: */ ! 454: wsprintf((LPTSTR)view->nrtext, "(%d)", abs(line)); ! 455: } else { ! 456: wsprintf((LPTSTR)view->nrtext, "%d", line); ! 457: } ! 458: pstr = view->nrtext; ! 459: break; ! 460: ! 461: case 1: ! 462: /* tag text - represents the state of the line */ ! 463: ! 464: ! 465: switch(state) { ! 466: case STATE_SAME: ! 467: pstr = " "; ! 468: break; ! 469: ! 470: case STATE_LEFTONLY: ! 471: pstr = " <! "; ! 472: break; ! 473: ! 474: case STATE_RIGHTONLY: ! 475: pstr = " !> "; ! 476: break; ! 477: ! 478: case STATE_MOVEDLEFT: ! 479: pstr = " <- "; ! 480: break; ! 481: ! 482: case STATE_MOVEDRIGHT: ! 483: pstr = " -> "; ! 484: break; ! 485: } ! 486: break; ! 487: ! 488: case 2: ! 489: /* main text - line */ ! 490: pstr = line_gettext(view->pLines[row].line); ! 491: break; ! 492: } ! 493: } else { ! 494: /* outline mode */ ! 495: switch(col) { ! 496: case 0: ! 497: /* row number - just the line number */ ! 498: wsprintf((LPTSTR)view->nrtext, "%d", row+1); ! 499: pstr = view->nrtext; ! 500: break; ! 501: ! 502: case 1: ! 503: /* tag */ ! 504: pstr = compitem_gettext_tag(view->pItems[row]); ! 505: break; ! 506: ! 507: case 2: ! 508: /* result text */ ! 509: pstr = compitem_gettext_result(view->pItems[row]); ! 510: break; ! 511: } ! 512: } ! 513: ViewLeave(); ! 514: return(pstr); ! 515: } ! 516: ! 517: /*************************************************************************** ! 518: * Function: view_getlinenr_left ! 519: * ! 520: * Purpose: ! 521: * ! 522: * Return the line number that this row had in the original left ! 523: * file. 0 if not in expand mode. 0 if this row was not in the left file. ! 524: * -(linenr) if this row is a MOVED line, and this is the right file ! 525: * copy ! 526: */ ! 527: int ! 528: view_getlinenr_left(VIEW view, long row) ! 529: { ! 530: int state, line; ! 531: ! 532: if ((view == NULL) || (row >= view->rows) || !view->bExpand) { ! 533: return 0; ! 534: } ! 535: ! 536: ViewEnter(); ! 537: state = section_getstate(view->pLines[row].section); ! 538: line = view->pLines[row].nr_left; ! 539: if (state == STATE_MOVEDRIGHT) { ! 540: line = -line; ! 541: } ! 542: ViewLeave(); ! 543: ! 544: return(line); ! 545: } ! 546: ! 547: /*************************************************************************** ! 548: * Function: view_getlinenr_right ! 549: * ! 550: * Purpose: ! 551: * ! 552: * Return the line number that this row had in the original right ! 553: * file. 0 if not in expand mode. 0 if this row was not in the right file. ! 554: * -(linenr) if this row is a MOVED line, and this is the left file ! 555: * copy ! 556: */ ! 557: int ! 558: view_getlinenr_right(VIEW view, long row) ! 559: { ! 560: int state, line; ! 561: ! 562: if ((view == NULL) || (row > view->rows) || !view->bExpand) { ! 563: return 0; ! 564: } ! 565: ! 566: ViewEnter(); ! 567: ! 568: state = section_getstate(view->pLines[row].section); ! 569: line = view->pLines[row].nr_right; ! 570: if (state == STATE_MOVEDLEFT) { ! 571: line = -line; ! 572: } ! 573: ViewLeave(); ! 574: ! 575: return(line); ! 576: } ! 577: ! 578: ! 579: /*************************************************************************** ! 580: * Function: view_getwidth ! 581: * ! 582: * Purpose: ! 583: * ! 584: * Find the maximum width in characters for the given column ! 585: */ ! 586: int ! 587: view_getwidth(VIEW view, int col) ! 588: { ! 589: if (view == NULL) { ! 590: return(0); ! 591: } ! 592: ! 593: switch(col) { ! 594: case 0: ! 595: /* line nr column - always 5 characters wide */ ! 596: return(5); ! 597: ! 598: case 1: ! 599: /* this is a proportional font field, so add on a margin ! 600: * for error ! 601: */ ! 602: return(view->maxtag + (view->maxtag / 20)); ! 603: case 2: ! 604: /* this now includes the tab expansion allowance */ ! 605: return(view->maxrest); ! 606: default: ! 607: return(0); ! 608: } ! 609: } ! 610: ! 611: /*************************************************************************** ! 612: * Function: view_getrowcount ! 613: * ! 614: * Purpose: ! 615: * ! 616: * How many rows are there in this view ? ! 617: */ ! 618: long ! 619: view_getrowcount(VIEW view) ! 620: { ! 621: if (view == NULL) { ! 622: return(0); ! 623: } ! 624: ! 625: return(view->rows); ! 626: } ! 627: ! 628: /*************************************************************************** ! 629: * Function: view_getstate ! 630: * ! 631: * Purpose: ! 632: * ! 633: * Return the state for the current row. This is used ! 634: * to select the text colour for the row ! 635: * ! 636: * States for sections are obtained from section_getstate (and apply, and ! 637: * to all lines in that section. States for compitems are obtained ! 638: * from compitem_getstate. ! 639: */ ! 640: int ! 641: view_getstate(VIEW view, long row) ! 642: { ! 643: int state; ! 644: ! 645: if (view == NULL) { ! 646: return(0); ! 647: } ! 648: ! 649: ViewEnter(); ! 650: if (row >= view->rows) { ! 651: state = 0; ! 652: } else if (view->bExpand) { ! 653: /* its a line state that's needed */ ! 654: state = section_getstate(view->pLines[row].section); ! 655: } else { ! 656: ! 657: /* its a compitem state */ ! 658: state = compitem_getstate(view->pItems[row]); ! 659: } ! 660: ViewLeave(); ! 661: return(state); ! 662: } ! 663: ! 664: /*************************************************************************** ! 665: * Function: view_gethandle ! 666: * ! 667: * Purpose: ! 668: * ! 669: * Return a handle to the current compitem. In expand mode, ! 670: * returns the handle to the compitem we are expanding. In outline ! 671: * mode, returns the handle to the compitem for the given row, if valid, ! 672: * or NULL otherwise. row is only used if not in expand mode. ! 673: */ ! 674: COMPITEM ! 675: view_getitem(VIEW view, long row) ! 676: { ! 677: COMPITEM ci; ! 678: ! 679: if (view == NULL) { ! 680: return(NULL); ! 681: } ! 682: ! 683: ViewEnter(); ! 684: ! 685: if (!view->bExpand) { ! 686: if ((row >= 0) && (row < view->rows)) { ! 687: ci = view->pItems[row]; ! 688: } else { ! 689: ci = NULL; ! 690: } ! 691: } else { ! 692: ci = view->ciSelect; ! 693: } ! 694: ! 695: ViewLeave(); ! 696: return(ci); ! 697: } ! 698: ! 699: /*************************************************************************** ! 700: * Function: view_isexpanded ! 701: * ! 702: * Purpose: ! 703: * ! 704: * Return TRUE if the current mapping is expanded mode ! 705: */ ! 706: BOOL ! 707: view_isexpanded(VIEW view) ! 708: { ! 709: if (view == NULL) { ! 710: return(FALSE); ! 711: } ! 712: return(view->bExpand); ! 713: } ! 714: ! 715: ! 716: /*************************************************************************** ! 717: * Function: view_getcurrenttag ! 718: * ! 719: * Purpose: ! 720: * ! 721: * Return a text string describing the view. This is NULL in outline mode, ! 722: * or the tag text for the current compitem in expanded mode ! 723: */ ! 724: LPSTR ! 725: view_getcurrenttag(VIEW view) ! 726: { ! 727: LPSTR str; ! 728: ! 729: if ((view == NULL) || (!view->bExpand)) { ! 730: return(NULL); ! 731: } else { ! 732: ViewEnter(); ! 733: ! 734: str = compitem_gettext_tag(view->ciSelect); ! 735: ! 736: ViewLeave(); ! 737: return(str); ! 738: ! 739: } ! 740: } ! 741: ! 742: ! 743: /*************************************************************************** ! 744: * Function: view_newitem ! 745: * ! 746: * Purpose: ! 747: * ! 748: * Notify that CompItems have been added to the complist. ! 749: * ! 750: * Rebuild the view (if in outline mode), and refresh the table. Use ! 751: * the table message TM_APPEND if possible (if column widths have not ! 752: * change). If we have to do TM_NEWLAYOUT, then ensure we scroll ! 753: * back to the right row afterwards. ! 754: * ! 755: * This causes a Poll() to take place. We return TRUE if an abort is ! 756: * pending - in this case, the caller should abandon the scan loop. ! 757: * ! 758: * Enter the critical section for this function since this can be ! 759: * called from the worker thread while the UI thread is using the ! 760: * view that we are about to change. ! 761: * ! 762: * EXCEPT THAT WE DON'T DARE. We cannot ever call SendMessage from the ! 763: * worker thread within CSView. If there is conflict, it will hang. ! 764: */ ! 765: BOOL ! 766: view_newitem(VIEW view) ! 767: { ! 768: int maxtag, maxrest; ! 769: long rownr; ! 770: ! 771: if ((view == NULL) || (view->bExpand)) { ! 772: /* not in outline mode - nothing to do */ ! 773: return(Poll()); ! 774: } ! 775: ! 776: /* save some state about the present mapping */ ! 777: maxtag = view->maxtag; ! 778: maxrest = view->maxrest; ! 779: ! 780: /* re-do the outline mapping, but don't tell the table ! 781: * class. ! 782: */ ! 783: view_outline_opt(view, FALSE); ! 784: ! 785: /* have the column widths changed ? */ ! 786: if ((maxtag < view->maxtag) || (maxrest < view->maxrest)) { ! 787: /* yes - need complete redraw */ ! 788: ! 789: /* find the row at the top of the window */ ! 790: rownr = SendMessage(view->hwnd, TM_TOPROW, FALSE, 0); ! 791: ! 792: /* switch to new mapping */ ! 793: SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (DWORD) view); ! 794: ! 795: /* return to old row if possible - we know ! 796: * that row is still there since we have only added ! 797: * rows, and not changed any of the existing mapping ! 798: * ! 799: * Alas this is no longer true. However the table class ! 800: * will defend itself against calls for a bogus top row. ! 801: */ ! 802: if (rownr >= 0) { ! 803: SendMessage(view->hwnd, TM_TOPROW, TRUE, rownr); ! 804: } ! 805: } else { ! 806: /* no - we can just append */ ! 807: ! 808: /* ! 809: * The mapping may have ! 810: * changed since we released the critsec. however we are still ! 811: * safe. The table will not allow us to reduce the number of ! 812: * rows, so the worst that can happen is that the table will ! 813: * think there are too many rows, and the table message handler ! 814: * will handle this correctly (return null for the text). ! 815: * The only visible effect is therefore that the scrollbar ! 816: * position is wrong. ! 817: */ ! 818: ! 819: SendMessage(view->hwnd, TM_APPEND, view->rows, (DWORD) view); ! 820: } ! 821: ! 822: ! 823: /* Poll to keep the UI updated on NT. Returns true if abort pending. ! 824: */ ! 825: return(Poll()); ! 826: } ! 827: ! 828: /*************************************************************************** ! 829: * Function: view_changeviewoptions ! 830: * ! 831: * Purpose: ! 832: * ! 833: * The view mapping options (eg outline_include, expand_mode) have changed - ! 834: * re-do the mapping and then scroll back to the same position in the window ! 835: * if possible. ! 836: */ ! 837: void ! 838: view_changeviewoptions(VIEW view) ! 839: { ! 840: long row; ! 841: int state, number; ! 842: BOOL bRight; ! 843: ! 844: if (view == NULL) { ! 845: return; ! 846: } ! 847: ! 848: /* find what row we are currently on. Do this BEFORE we enter CSView */ ! 849: row = SendMessage(view->hwnd, TM_TOPROW, FALSE, 0); ! 850: ! 851: ViewEnter(); ! 852: ! 853: if (!view->bExpand) { ! 854: ! 855: /* outline mode. maintaining current position is ! 856: * unimportant ! 857: */ ! 858: view_outline(view); ! 859: ViewLeave(); ! 860: return; ! 861: } ! 862: ! 863: /* expanded mode */ ! 864: ! 865: ! 866: /* save the line number on one side (and remember which side) */ ! 867: if (row >= view->rows) { ! 868: number = -1; ! 869: } else { ! 870: state = section_getstate(view->pLines[row].section); ! 871: if ((state == STATE_MOVEDRIGHT) || ! 872: (state == STATE_RIGHTONLY)) { ! 873: bRight = TRUE; ! 874: number = view->pLines[row].nr_right; ! 875: } else { ! 876: bRight = FALSE; ! 877: number = view->pLines[row].nr_left; ! 878: } ! 879: } ! 880: ! 881: /* make the new mapping */ ! 882: view_expand_item(view, view->ciSelect); ! 883: ! 884: /* find the nearest row in the new view */ ! 885: if (number >= 0) { ! 886: ! 887: ViewEnter(); ! 888: row = view_findrow(view, number, bRight); ! 889: ViewLeave(); ! 890: ! 891: /* scroll this row to top of window */ ! 892: if (row >= 0) { ! 893: ! 894: SendMessage(view->hwnd, TM_TOPROW, TRUE, row); ! 895: return; ! 896: } ! 897: } ! 898: } ! 899: ! 900: /*************************************************************************** ! 901: * Function: view_changediffoptions ! 902: * ! 903: * Purpose: ! 904: * ! 905: * The compare options have changed - re-do the compare completely ! 906: * and make the new mapping. Retain current position in the file. ! 907: */ ! 908: void ! 909: view_changediffoptions(VIEW view) ! 910: { ! 911: int state, number; ! 912: long row; ! 913: BOOL bRight; ! 914: LIST li; ! 915: COMPITEM ci; ! 916: ! 917: if (view == NULL) { ! 918: return; ! 919: } ! 920: ! 921: /* ! 922: * get current row before entering critsec. ! 923: */ ! 924: row = SendMessage(view->hwnd, TM_TOPROW, FALSE, 0); ! 925: ! 926: ViewEnter(); ! 927: ! 928: /* find the current line number so we can go back to it ! 929: * (only if we are in expanded mode ! 930: */ ! 931: if (view->bExpand) { ! 932: ! 933: state = section_getstate(view->pLines[row].section); ! 934: if ((state == STATE_MOVEDRIGHT) || ! 935: (state == STATE_RIGHTONLY)) { ! 936: bRight = TRUE; ! 937: number = view->pLines[row].nr_right; ! 938: } else { ! 939: bRight = FALSE; ! 940: number = view->pLines[row].nr_left; ! 941: } ! 942: } ! 943: ! 944: /* To force a recompare using the new options, we must ! 945: * tell each compitem to discard its current compare result. ! 946: * We need to traverse the list of compitems calling this ! 947: * for each compare. ! 948: */ ! 949: li = complist_getitems(view->cl); ! 950: ! 951: for (ci = (COMPITEM) List_First(li); ci != NULL; ci = (COMPITEM) List_Next(ci)) { ! 952: compitem_discardsections(ci); ! 953: } ! 954: ! 955: /* if we are in outline mode, we have nothing more to do */ ! 956: if (!view->bExpand) { ! 957: ViewLeave(); ! 958: return; ! 959: } ! 960: ! 961: view_expand_item(view, view->ciSelect); ! 962: ! 963: /* find the nearest row in the new view */ ! 964: ViewEnter(); ! 965: row = view_findrow(view, number, bRight); ! 966: ViewLeave(); ! 967: ! 968: /* scroll this row to top of window */ ! 969: if (row >= 0) { ! 970: SendMessage(view->hwnd, TM_TOPROW, TRUE, row); ! 971: } ! 972: } ! 973: ! 974: ! 975: /*************************************************************************** ! 976: * Function: view_findchange ! 977: * ! 978: * Purpose: ! 979: * ! 980: * Find the next changed - ie non-same - row in a given direction. ! 981: * For outline mode we find the next STATE_DIFFER. For expand mode, we ! 982: * find the next section ! 983: */ ! 984: long ! 985: view_findchange(VIEW view, long startrow, BOOL bForward) ! 986: { ! 987: long i; ! 988: ! 989: if (view == NULL) { ! 990: return(0); ! 991: } ! 992: ! 993: ViewEnter(); ! 994: ! 995: if (bForward) { ! 996: ! 997: if (startrow >= view->rows) { ! 998: ViewLeave(); ! 999: return(-1); ! 1000: } ! 1001: ! 1002: if (!view->bExpand) { ! 1003: ! 1004: /* look for next compitem with an expandable state*/ ! 1005: for (i = startrow; i < view->rows; i++) { ! 1006: if (compitem_getstate(view->pItems[i]) == STATE_DIFFER) { ! 1007: ViewLeave(); ! 1008: return(i); ! 1009: } ! 1010: } ! 1011: /* none found */ ! 1012: ViewLeave(); ! 1013: return(-1); ! 1014: } else { ! 1015: /* ! 1016: * find the next line that matches, then go on to the ! 1017: * next line that does not match ! 1018: * ! 1019: */ ! 1020: for (i= startrow; i < view->rows; i++) { ! 1021: if (section_getstate(view->pLines[i].section) ! 1022: == STATE_SAME) { ! 1023: break; ! 1024: } ! 1025: } ! 1026: for ( ; i < view->rows; i++) { ! 1027: if (section_getstate(view->pLines[i].section) ! 1028: != STATE_SAME) { ! 1029: ViewLeave(); ! 1030: return(i); ! 1031: } ! 1032: } ! 1033: ! 1034: ViewLeave(); ! 1035: ! 1036: return(-1); ! 1037: } ! 1038: } else { ! 1039: /* same search backwards */ ! 1040: if (startrow <= 0) { ! 1041: ViewLeave(); ! 1042: return(-1); ! 1043: } ! 1044: if (view->bExpand) { ! 1045: /* search backwards for first row that is not ! 1046: * changed (has state SAME). then carry on for ! 1047: * the next changed row. ! 1048: */ ! 1049: for (i = startrow; i >= 0; i--) { ! 1050: if (section_getstate(view->pLines[i].section) ! 1051: == STATE_SAME) { ! 1052: break; ! 1053: } ! 1054: } ! 1055: for ( ; i >= 0; i--) { ! 1056: if (section_getstate(view->pLines[i].section) ! 1057: != STATE_SAME) { ! 1058: ViewLeave(); ! 1059: return(i); ! 1060: } ! 1061: } ! 1062: ViewLeave(); ! 1063: return(-1); ! 1064: } else { ! 1065: for (i = startrow; i >= 0; i--) { ! 1066: if(compitem_getstate(view->pItems[i]) == STATE_DIFFER) { ! 1067: ViewLeave(); ! 1068: return(i); ! 1069: } ! 1070: } ! 1071: ViewLeave(); ! 1072: return(-1); ! 1073: } ! 1074: } ! 1075: } ! 1076: ! 1077: ! 1078: ! 1079: /*************************************************************************** ! 1080: * Function: view_findrow ! 1081: * ! 1082: * Purpose: ! 1083: * ! 1084: * Find the new row number for the line numbered 'number' ! 1085: * or the nearest line if possible. If bRight is true, number is ! 1086: * a right file number; otherwise it is a left file number. ! 1087: * ! 1088: * We must be in expand mode ! 1089: */ ! 1090: int ! 1091: view_findrow(VIEW view, int number, BOOL bRight) ! 1092: { ! 1093: int i; ! 1094: ! 1095: if (!view->bExpand) { ! 1096: return(0); ! 1097: } ! 1098: ! 1099: for (i = 0; i < view->rows; i++) { ! 1100: ! 1101: if (bRight) { ! 1102: if (view->pLines[i].nr_right == number) { ! 1103: ! 1104: /* found the exact number */ ! 1105: return(i); ! 1106: ! 1107: } else if (view->pLines[i].nr_right > number) { ! 1108: ! 1109: /* passed our line -stop here */ ! 1110: return(i); ! 1111: } ! 1112: } else { ! 1113: if (view->pLines[i].nr_left == number) { ! 1114: ! 1115: /* found the exact number */ ! 1116: return(i); ! 1117: ! 1118: } else if (view->pLines[i].nr_left > number) { ! 1119: ! 1120: /* passed our line -stop here */ ! 1121: return(i); ! 1122: } ! 1123: } ! 1124: } ! 1125: return(-1); ! 1126: } ! 1127: ! 1128: /*************************************************************************** ! 1129: * Function: view_freemappings ! 1130: * ! 1131: * Purpose: ! 1132: * ! 1133: * Free memory associated with the expand mode or outline mode mappings ! 1134: * called whenever we rebuild the mapping, and on deletion ! 1135: */ ! 1136: void ! 1137: view_freemappings(VIEW view) ! 1138: { ! 1139: ! 1140: if (view->pLines) { ! 1141: gmem_free(hHeap, (LPSTR) view->pLines, ! 1142: view->rows * sizeof(VIEWLINE)); ! 1143: view->pLines = NULL; ! 1144: } else if (view->pItems) { ! 1145: ! 1146: /* previous outline mapping array is still there - free it ! 1147: * before we build a new one ! 1148: */ ! 1149: ! 1150: gmem_free(hHeap, (LPSTR) view->pItems, ! 1151: view->rows * sizeof(COMPLIST)); ! 1152: view->pItems = NULL; ! 1153: } ! 1154: } ! 1155: ! 1156: /*************************************************************************** ! 1157: * Function: view_outline_opt ! 1158: * ! 1159: * Purpose: ! 1160: * ! 1161: * Build a view outline to map one row to a COMPITEM handle by traversing ! 1162: * the list of COMPITEMs obtained from our complist. ! 1163: * Optionally tell the table class to redraw (if bRedraw), and if so, ! 1164: * scroll the new table to select the row that represents the ! 1165: * file we were expanding, if possible ! 1166: */ ! 1167: void ! 1168: view_outline_opt(VIEW view, BOOL bRedraw) ! 1169: { ! 1170: int prev_row = -1; /* the row nr of the previously-expanded row*/ ! 1171: int i; /* nr of includable items */ ! 1172: LIST li; ! 1173: COMPITEM ci; ! 1174: int state; ! 1175: TableSelection select; ! 1176: ! 1177: /* ! 1178: * check that view_setcomplist has already been called. if not, ! 1179: * nothing to do ! 1180: */ ! 1181: if (view->cl == NULL) { ! 1182: return; ! 1183: } ! 1184: ! 1185: ViewEnter(); ! 1186: ! 1187: /* clear the mode flag and free up memory associated with expand mode */ ! 1188: view->bExpand = FALSE; ! 1189: view_freemappings(view); ! 1190: ! 1191: /* traverse the list of compitems counting up the number of ! 1192: * includable items ! 1193: */ ! 1194: li = complist_getitems(view->cl); ! 1195: ! 1196: ci = (COMPITEM) List_First(li); ! 1197: for (i = 0; ci != NULL; ci = (COMPITEM) List_Next(ci)) { ! 1198: ! 1199: state = compitem_getstate(ci); ! 1200: ! 1201: if (((outline_include & INCLUDE_SAME) && (state == STATE_SAME)) || ! 1202: ((outline_include & INCLUDE_DIFFER) && (state == STATE_DIFFER)) || ! 1203: ((outline_include & INCLUDE_LEFTONLY) && (state == STATE_FILELEFTONLY)) || ! 1204: ((outline_include & INCLUDE_RIGHTONLY) && (state == STATE_FILERIGHTONLY))) { ! 1205: i++; ! 1206: } ! 1207: } ! 1208: ! 1209: ! 1210: /* allocate an array big enough for all of these */ ! 1211: view->pItems = (COMPITEM FAR *) gmem_get(hHeap, i * sizeof(COMPITEM)); ! 1212: view->rows = i; ! 1213: ! 1214: /* keep track of the column widths */ ! 1215: view->maxtag = 0; ! 1216: view->maxrest = 0; ! 1217: ! 1218: /* loop through again filling the array, and at the same time looking ! 1219: * out for the handle of the previously expanded item ! 1220: */ ! 1221: ci = (COMPITEM) List_First(li); ! 1222: for (i = 0; ci != NULL; ci = (COMPITEM) List_Next(ci)) { ! 1223: ! 1224: state = compitem_getstate(ci); ! 1225: ! 1226: if (((outline_include & INCLUDE_SAME) && (state == STATE_SAME)) || ! 1227: ((outline_include & INCLUDE_DIFFER) && (state == STATE_DIFFER)) || ! 1228: ((outline_include & INCLUDE_LEFTONLY) && (state == STATE_FILELEFTONLY)) || ! 1229: ((outline_include & INCLUDE_RIGHTONLY) && (state == STATE_FILERIGHTONLY))) { ! 1230: ! 1231: view->pItems[i] = ci; ! 1232: ! 1233: if (ci == view->ciSelect) { ! 1234: prev_row = i; ! 1235: } ! 1236: ! 1237: /* check the column widths in characters */ ! 1238: view->maxtag = max(view->maxtag, ! 1239: lstrlen(compitem_gettext_tag(ci))); ! 1240: view->maxrest = max(view->maxrest, ! 1241: lstrlen(compitem_gettext_result(ci))); ! 1242: ! 1243: ! 1244: i++; ! 1245: ! 1246: } ! 1247: } ! 1248: ViewLeave(); ! 1249: ! 1250: /* inform table of new layout of table - force refresh */ ! 1251: if (bRedraw) { ! 1252: SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (DWORD) view); ! 1253: ! 1254: /* scroll to and highlight the row that represents the file ! 1255: * we were previously expanding ! 1256: */ ! 1257: if (prev_row != -1) { ! 1258: select.startrow = prev_row; ! 1259: select.startcell = 0; ! 1260: select.nrows = 1; ! 1261: select.ncells = 1; ! 1262: SendMessage(view->hwnd, TM_SELECT, 0, ! 1263: (DWORD) (LPSTR) &select); ! 1264: } ! 1265: } ! 1266: } ! 1267: ! 1268: ! 1269: /*************************************************************************** ! 1270: * Function: view_expand_item ! 1271: * ! 1272: * Purpose: ! 1273: * ! 1274: * Expand a view - given the handle to the compitem to expand. ! 1275: * ! 1276: * Called from view_expand, and also to re-do an expanded view ! 1277: * after options change in view_changediffoptions and _changeviewoptions ! 1278: * ! 1279: * We get the composite section list from the compitem, ! 1280: * and pick out all the sections that are includable (according ! 1281: * to the global option expand_mode: we include all sections, or ! 1282: * just those in one side left or right). Once we know the count of rows, ! 1283: * allocate the mapping array: in each element of the array we keep ! 1284: * a handle to the section for that row (to get the state and hence the ! 1285: * tag text), and a handle to the line within that section (for the line text). ! 1286: * ! 1287: * We no longer insist on only expanding text files that differ - if the ! 1288: * compitem can give us a composite section list, we will map it. ! 1289: * ! 1290: * We need to be able to give a line number for a line, in either of ! 1291: * the original files according to which option is in force. Each section ! 1292: * can give us its base line number (number of first line in section) in ! 1293: * each of the two files or 0 if not present, and we track these here. ! 1294: * ! 1295: * MUST BE INSIDE CSView BEFORE CALLING HERE. ! 1296: */ ! 1297: BOOL ! 1298: view_expand_item(VIEW view, COMPITEM ci) ! 1299: { ! 1300: LIST li; ! 1301: SECTION sh; ! 1302: LINE line1, line2; ! 1303: int i, base_left, base_right, state; ! 1304: ! 1305: /* remember the compitem we are expanding */ ! 1306: view->ciSelect = ci; ! 1307: ! 1308: /* get the composite section list */ ! 1309: li = compitem_getcomposite(view->ciSelect); ! 1310: if (li == NULL) { ! 1311: ViewLeave(); ! 1312: return FALSE; ! 1313: } ! 1314: ! 1315: /* switch modes and free the current mapping ! 1316: * ! 1317: * NOTE: must do this AFTER the compitem_getcomposite, ! 1318: * since that can fail: if it fails it could put up a ! 1319: * message box, and that could cause a queued paint message ! 1320: * to be processed, which would cause us to use these mappings ! 1321: * and gpfault if they had been cleared first. ! 1322: */ ! 1323: view->bExpand = TRUE; ! 1324: view_freemappings(view); ! 1325: ! 1326: ! 1327: /* loop through totalling the lines in sections ! 1328: * that we should include ! 1329: */ ! 1330: view->rows = 0; ! 1331: for (sh = (SECTION) List_First(li); sh != NULL; ! 1332: sh = (SECTION) List_Next(sh)) { ! 1333: ! 1334: state = section_getstate(sh); ! 1335: ! 1336: if (expand_mode == IDM_RONLY) { ! 1337: if ((state == STATE_LEFTONLY) || ! 1338: (state == STATE_MOVEDLEFT)) { ! 1339: continue; ! 1340: } ! 1341: } else if (expand_mode == IDM_LONLY) { ! 1342: if ((state == STATE_RIGHTONLY) || ! 1343: (state == STATE_MOVEDRIGHT)) { ! 1344: continue; ! 1345: } ! 1346: } ! 1347: ! 1348: /* include all lines in this section */ ! 1349: view->rows += section_getlinecount(sh); ! 1350: } ! 1351: ! 1352: /* allocate the memory for the mapping array */ ! 1353: view->pLines = (PVIEWLINE) gmem_get(hHeap, view->rows * sizeof(VIEWLINE)); ! 1354: ! 1355: /* loop through the sections again filling in the mapping array */ ! 1356: i = 0; ! 1357: view->maxtag = 5; ! 1358: view->maxrest = 0; ! 1359: for (sh = (SECTION) List_First(li); sh != NULL; ! 1360: sh = (SECTION) List_Next(sh)) { ! 1361: ! 1362: state = section_getstate(sh); ! 1363: ! 1364: if (expand_mode == IDM_RONLY) { ! 1365: if ((state == STATE_LEFTONLY) || ! 1366: (state == STATE_MOVEDLEFT)) { ! 1367: continue; ! 1368: } ! 1369: } else if (expand_mode == IDM_LONLY) { ! 1370: if ((state == STATE_RIGHTONLY) || ! 1371: (state == STATE_MOVEDRIGHT)) { ! 1372: continue; ! 1373: } ! 1374: } ! 1375: ! 1376: /* find the base line number in each file */ ! 1377: base_left = section_getleftbasenr(sh); ! 1378: base_right = section_getrightbasenr(sh); ! 1379: ! 1380: /* add each line in section to the view. section_getfirst() ! 1381: * returns us to a handle that is in a list. We can ! 1382: * call List_Next and will eventually get to the ! 1383: * line returned by section_getlast(). Sections always have ! 1384: * at least one line ! 1385: */ ! 1386: line1 = section_getfirstline(sh); ! 1387: line2 = section_getlastline(sh); ! 1388: ! 1389: for (; line1 != NULL; line1 = (LINE) List_Next(line1)) { ! 1390: ! 1391: view->pLines[i].line = line1; ! 1392: view->pLines[i].section = sh; ! 1393: ! 1394: /* calculate the line number for this line by ! 1395: * incrementing the base nr for this section ! 1396: */ ! 1397: ! 1398: view->pLines[i].nr_left = base_left; ! 1399: if (base_left != 0) { ! 1400: base_left++; ! 1401: } ! 1402: ! 1403: view->pLines[i].nr_right = base_right; ! 1404: if (base_right != 0) { ! 1405: base_right++; ! 1406: } ! 1407: ! 1408: /* increment index into view */ ! 1409: i++; ! 1410: ! 1411: /* check the column widths */ ! 1412: view->maxrest = max(view->maxrest, ! 1413: (line_gettabbedlength(line1, 8))); ! 1414: ! 1415: /* end of section ? */ ! 1416: if (line1 == line2) { ! 1417: break; ! 1418: } ! 1419: } ! 1420: } ! 1421: ! 1422: /* We must NOT hold a critical section here as SendMessage may hang */ ! 1423: ViewLeave(); ! 1424: ! 1425: /*inform table window of revised mapping */ ! 1426: SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (DWORD) view); ! 1427: ! 1428: return(TRUE); ! 1429: } ! 1430:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.