/******************************************************************************\ * This is a part of the Microsoft Source Code Samples. * Copyright (C) 1993-1997 Microsoft Corporation. * All rights reserved. * This source code is only intended as a supplement to * Microsoft Development Tools and/or WinHelp documentation. * See these sources for detailed information regarding the * Microsoft samples programs. \******************************************************************************/ /****************************** Module Header ******************************* * Module Name: BAR.C * * This module contains functions for bar window * graphically showing two lists of sections and showing * colored vertical bars for the sections of text, * with linking lines for the sections that are the same. * * Functions: * * BarWndProc() * BarPaint() * DrawSection() * DrawLink() * BarClick() * InitBarClass() * BarDrawPosition() * * Comments: * ****************************************************************************/ #include #include #include "gutils.h" #include "table.h" #include "state.h" #include "wdiffrc.h" #include "windiff.h" #include "list.h" #include "line.h" #include "scandir.h" #include "file.h" #include "section.h" #include "compitem.h" #include "complist.h" #include "view.h" long APIENTRY BarWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam); void BarPaint(HWND hwnd); void DrawSection(HDC hdc, int cx, int cy, int lines, SECTION sec, int sidecode); void DrawLink(HDC hdc, int cx, int cy, int lines, SECTION sec); void BarClick(HWND hwnd, int x, int y); HPEN hpenSame, hpenLeft, hpenRight; HBRUSH hbrSame, hbrLeft, hbrRight; HBRUSH hbrSideBar; /*************************************************************************** * Function: InitBarClass * * Purpose: * * Create bar window class */ BOOL InitBarClass(HINSTANCE hInstance) { WNDCLASS wc; BOOL resp; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = BarWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszClassName = "BarClass"; wc.lpszMenuName = NULL; resp = RegisterClass(&wc); return(resp); } /*************************************************************************** * Function: BarWndProc * * Purpose: * * Window procedure supporting bar window * */ long APIENTRY BarWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam) { switch(message) { case WM_CREATE: hpenSame = CreatePen(PS_SOLID, 1, RGB(0,0,0)); hbrSame = CreateSolidBrush(RGB(255,255,255)); hpenLeft = CreatePen(PS_SOLID, 1, rgb_barleft); hbrLeft = CreateSolidBrush(rgb_barleft); hpenRight = CreatePen(PS_SOLID, 1, rgb_barright); hbrRight = CreateSolidBrush(rgb_barright); hbrSideBar = CreateSolidBrush(rgb_barcurrent); break; case WM_DESTROY: DeleteObject(hpenSame); DeleteObject(hpenLeft); DeleteObject(hpenRight); DeleteObject(hbrSame); DeleteObject(hbrLeft); DeleteObject(hbrRight); DeleteObject(hbrSideBar); break; case WM_PAINT: BarPaint(hWnd); break; case WM_LBUTTONDOWN: BarClick(hWnd, LOWORD(lParam), HIWORD(lParam)); break; default: return(DefWindowProc(hWnd, message, wParam, lParam)); } return 0; } /*************************************************************************** * Function: BarDrawPosition * * Purpose: * * Draw the current position as side-bars down the bar window, * showing which lines from each file are currently in view. HDC can be * NULL (we get one ourselves if so). If bErase is true, we clear * the previous side-bars first. * * This is called from BarPaint when we paint the whole window, and * from TableServer() whenever it receives a TQ_SCROLL notification that * the table window has been scrolled. */ void BarDrawPosition(HWND hwndBar, HDC hdcIn, BOOL bErase) { HDC hdc; int total_lines, cy, cx; RECT rc, rcLeft, rcRight; VIEW view; COMPITEM item; LIST listleft, listright; long toprow, endrow, i; int left_first, left_last, right_first, right_last, linenr; /* get a hdc if we weren't given one */ if (hdcIn == NULL) { hdc = GetDC(hwndBar); } else { hdc = hdcIn; } /* set horz position of bars */ GetClientRect(hwndBar, &rc); cx = (int)(rc.right - rc.left); cy = (int)(rc.bottom - rc.top); /* layout constants are defined as percentages of window width */ rcLeft.left = cx * L_POS_START / 100; rcRight.left = cx * R_POS_START / 100; rcLeft.right = rcLeft.left + (cx * L_POS_WIDTH / 100); rcRight.right = rcRight.left + (cx * R_POS_WIDTH / 100); /* erase the whole marker section if requested */ if (bErase) { rcLeft.top = rc.top; rcLeft.bottom = rc.bottom; rcRight.top = rc.top; rcRight.bottom = rc.bottom; FillRect(hdc, &rcLeft, GetStockObject(WHITE_BRUSH)); FillRect(hdc, &rcRight, GetStockObject(WHITE_BRUSH)); } /* * calculate the vertical scaling - depends on the * total number of lines shown */ /* get the handles to the two lists of sections */ view = (VIEW) SendMessage(hwndClient, TM_CURRENTVIEW, 0, 0); /* make sure we are in expand mode */ if (view_isexpanded(view) == FALSE) { /* get rid of the dc if we made it ourselves */ if (hdcIn == NULL) { ReleaseDC(hwndBar, hdc); } return; } item = view_getitem(view, 0); listleft = compitem_getleftsections(item); listright = compitem_getrightsections(item); /* if there is only one list of sections, draw nothing. The * picture for a single file is not very exciting. */ if ((listleft == NULL) || (listright == NULL)) { /* get rid of the dc if we made it ourselves */ if (hdcIn == NULL) { ReleaseDC(hwndBar, hdc); } return; } /* take the longest of the two files and use this * for vertical scaling. the scale is such that the longest file * *just fits*. */ total_lines = line_getlinenr(section_getlastline(List_Last(listleft))); total_lines = max(total_lines, (int) line_getlinenr(section_getlastline(List_Last(listright)))); /* get the current top row and nr of rows visible */ toprow = SendMessage(hwndRCD, TM_TOPROW, FALSE, 0); endrow = SendMessage(hwndRCD, TM_ENDROW, FALSE, 0); endrow = min(endrow, view_getrowcount(view)-1); /* * find the first and last line nrs from each file currently visible. * */ left_first = left_last = right_first = right_last = 0; for (i = toprow; i <= endrow; i++) { linenr = view_getlinenr_left(view, i); if (linenr > 0) { if (left_first == 0) { left_first = linenr; } left_first = min(left_first, linenr); left_last = max(left_last, linenr); } linenr = view_getlinenr_right(view, i); if (linenr > 0) { if (right_first == 0) { right_first = linenr; } right_first = min(right_first, linenr); right_last = max(right_last, linenr); } } /* draw the two markers as thick bars -> elongated rectangles */ rcLeft.top = MulDiv(left_first-1, cy, total_lines); rcLeft.bottom = MulDiv(left_last, cy, total_lines); FillRect(hdc, &rcLeft, hbrSideBar); rcRight.top = MulDiv(right_first-1, cy, total_lines); rcRight.bottom = MulDiv(right_last, cy, total_lines); FillRect(hdc, &rcRight, hbrSideBar); /* get rid of the dc if we made it ourselves */ if (hdcIn == NULL) { ReleaseDC(hwndBar, hdc); } } /*************************************************************************** * Function: BarPaint * * Purpose: * * Paint the bar window */ void BarPaint(HWND hwnd) { PAINTSTRUCT ps; HDC hdc; VIEW view; COMPITEM item; LIST listleft, listright; SECTION sec; int total_lines, cx, cy; RECT rc; hdc = BeginPaint(hwnd, &ps); /* draw a separator line at the very edge of the window */ GetClientRect(hwnd, &rc); MoveToEx(hdc, (int)(rc.right-1), rc.top, NULL); LineTo(hdc, (int)(rc.right-1), rc.bottom); /* first gather information about what is to be displayed */ /* find the total lines (for horz. scaling) */ /* get the handles to the two lists of sections */ view = (VIEW) SendMessage(hwndClient, TM_CURRENTVIEW, 0, 0); /* make sure we are in expand mode */ if (view_isexpanded(view) == FALSE) { return; } item = view_getitem(view, 0); listleft = compitem_getleftsections(item); listright = compitem_getrightsections(item); /* * don't bother if there is only one list - not very interesting */ if ((listleft == NULL) || (listright == NULL)) { EndPaint(hwnd, &ps); return; } /* take the longest of the two files and use this * for vertical scaling. the scale is such that the longest file * *just fits*. */ total_lines = (int) line_getlinenr(section_getlastline(List_Last(listleft))); total_lines = max(total_lines, (int) line_getlinenr(section_getlastline(List_Last(listright)))); /* horizontal spacing: * * there are two columns, for the left and right files, and a gap * between them criss-crossed by lines marking the links. * * Each of the columns then has three sections, for the * position marker, the different sections * and the linked sections. The width and positions of these items * are defined (in windiff.h) as percentages of the window width. */ cx = (int)(rc.right - rc.left); cy = (int)(rc.bottom - rc.top); /* draw all the left sections and links */ List_TRAVERSE(listleft, sec) { DrawSection(hdc, cx, cy, total_lines, sec, STATE_LEFTONLY); if (section_getlink(sec) != NULL) { DrawLink(hdc, cx, cy, total_lines, sec); } } /* draw all the right sections */ List_TRAVERSE(listright, sec) { DrawSection(hdc, cx, cy, total_lines, sec, STATE_RIGHTONLY); } /* now draw current position markers */ BarDrawPosition(hwnd, hdc, FALSE); EndPaint(hwnd, &ps); } /*************************************************************************** * Function: DrawSection * * Purpose: * * Paint a single section */ void DrawSection(HDC hdc, int cx, int cy, int lines, SECTION sec, int sidecode) { int x1, y1, x2, y2; HPEN hpenOld; HBRUSH hbrOld; /* calculate the vertical position from the scaling. the scaling * is such that the longest file just fits */ y1 = MulDiv(line_getlinenr(section_getfirstline(sec))- 1, cy, lines); y2 = MulDiv(line_getlinenr(section_getlastline(sec)), cy, lines); /* left or right - set bar position and width*/ if (sidecode == STATE_LEFTONLY) { if (section_getlink(sec) != NULL) { x1 = L_MATCH_START; x2 = L_MATCH_WIDTH; } else { x1 = L_UNMATCH_START; x2 = L_UNMATCH_WIDTH; } } else { if (section_getlink(sec) != NULL) { x1 = R_MATCH_START; x2 = R_MATCH_WIDTH; } else { x1 = R_UNMATCH_START; x2 = R_UNMATCH_WIDTH; } } /* bar position defines are in percentages of the win width (cx) */ x1 = cx * x1 / 100; x2 = (cx * x2 / 100) + x1; /* select pens and brushes */ if (section_getlink(sec) != NULL) { hpenOld = SelectObject(hdc, hpenSame); hbrOld = SelectObject(hdc, hbrSame); } else if (sidecode == STATE_LEFTONLY) { hpenOld = SelectObject(hdc, hpenLeft); hbrOld = SelectObject(hdc, hbrLeft); } else { hpenOld = SelectObject(hdc, hpenRight); hbrOld = SelectObject(hdc, hbrRight); } /* draw the section as a coloured elongated rectangle */ Rectangle(hdc, x1, y1, x2, y2); /* de-select the pen and brush in favour of the default */ SelectObject(hdc, hpenOld); SelectObject(hdc, hbrOld); } /*************************************************************************** * Function: DrawLink * * Purpose: * * Draw a line linking two sections. Indicates a section from each * file that match each other. psec points to the section in the * left file. */ void DrawLink(HDC hdc, int cx, int cy, int lines, SECTION sec) { int x1, y1, x2, y2; int ybase, yrange; SECTION other; other = section_getlink(sec); /* position the link line halfway down the section * - allow for the case where * the section is one line (ie halve the co-ords, not the line nr) */ ybase = MulDiv(line_getlinenr(section_getfirstline(sec)) - 1, cy, lines); yrange = MulDiv(line_getlinenr(section_getlastline(sec)), cy, lines); y1 = ((yrange - ybase) / 2) + ybase; ybase = MulDiv(line_getlinenr(section_getfirstline(other)) - 1, cy, lines); yrange = MulDiv(line_getlinenr(section_getlastline(other)), cy, lines); y2 = ((yrange - ybase) / 2) + ybase; /* horizontal layout constants are defined as percentages of the * window width */ x1 = cx * (L_MATCH_START + L_MATCH_WIDTH) / 100; x2 = cx * R_UNMATCH_START / 100; MoveToEx(hdc, x1, y1, NULL); LineTo(hdc, x2, y2); } /*************************************************************************** * Function: BarClick * * Purpose: * * The user has clicked on the bar window. Translate the clicked position into * a line in one of the files if possible, and scroll the table window to * show that line. */ void BarClick(HWND hwnd, int x, int y) { RECT rc; int xleft, xright; int linenr, i, this; BOOL bIsLeft; int tot_left, tot_right, total_lines; LIST listleft, listright; VIEW view; COMPITEM item; TableSelection select; /* find size of the window to get horz scaling, and see * where click was */ GetClientRect(hwnd, &rc); /* was it near either of the bars ? */ /* horz positioning is in percentages of window width */ xleft = max(L_UNMATCH_START + L_UNMATCH_WIDTH, L_MATCH_START + L_MATCH_WIDTH); xright = min(R_UNMATCH_START, R_MATCH_START); xleft = xleft * (rc.right - rc.left) / 100; xright = xright * (rc.right - rc.left) / 100; if (x < xleft) { bIsLeft = TRUE; } else if (x > xright) { bIsLeft = FALSE; } else { /* click was between the two bars - ignore it */ return; } /* calculate the vertical scaling (based on total lines displayed) * so that we can convert the y position into a line nr */ /* get the handles to the two lists of sections */ view = (VIEW) SendMessage(hwndClient, TM_CURRENTVIEW, 0, 0); /* make sure we are in expand mode */ if (view_isexpanded(view) == FALSE) { return; } item = view_getitem(view, 0); listleft = compitem_getleftsections(item); listright = compitem_getrightsections(item); /* ignore the click if only one list of sections, since in * this case there is nothing drawn for him to click on. */ if ((listleft == NULL) || (listright == NULL)) { return; } /* take the longest of the two files and use this * for vertical scaling. the scale is such that the longest file * *just fits*. */ tot_left = line_getlinenr(section_getlastline(List_Last(listleft))); tot_right = line_getlinenr(section_getlastline(List_Last(listright))); total_lines = max(tot_left, tot_right); /* convert vertical position into a line nr. The vertical scaling * can be calculated from knowing that the longest list of * lines just fits in the window. */ linenr = (int) (((long) total_lines * y) / (rc.bottom - rc.top)) + 1; /* check that the line is valid */ if (bIsLeft) { if (linenr > tot_left) { return; } } else { if (linenr > tot_right) { return; } } /* search the current view, looking for a row with this * line nr on the correct side */ for (i = 0; i < view_getrowcount(view); i++) { if (bIsLeft) { this = view_getlinenr_left(view,i); } else { this = view_getlinenr_right(view,i); } if (linenr == this) { /* found the matching line- select it in the * table window */ select.startrow = i; select.startcell = 0; select.nrows = 1; select.ncells = 1; SendMessage(hwndRCD, TM_SELECT, 0, (long) (LPSTR)&select); return; } } windiff_UI(TRUE); MessageBox(hwndClient, LoadRcString(IDS_LINE_NOT_VISIBLE), "WinDiff", MB_ICONSTOP|MB_OK); windiff_UI(FALSE); }