internal VirtualTreeExtendedHitInfo(ref VirtualTreeHitInfo hitInfo, ref ExtraHitInfo extraHitInfo) { myHitInfo = hitInfo; myExtraHitInfo = extraHitInfo; }
private VirtualTreeHitInfo HitInfo(int x, int y, out ExtraHitInfo extraInfo, bool populateExtras) { extraInfo = new ExtraHitInfo(); var target = VirtualTreeHitTargets.Uninitialized; var absIndex = VirtualTreeConstant.NullIndex; var column = 0; var nativeColumn = 0; var rawRow = absIndex; var rawColumn = absIndex; if (x < 0) { target |= VirtualTreeHitTargets.ToLeft; } else if (x > ClientSize.Width) { target |= VirtualTreeHitTargets.ToRight; } if (y < 0) { target |= VirtualTreeHitTargets.Above; } else if (y > ClientSize.Height) { target |= VirtualTreeHitTargets.Below; } if (target != VirtualTreeHitTargets.Uninitialized) { return new VirtualTreeHitInfo(absIndex, column, target); } absIndex = rawRow = IndexFromPoint(x, y); if ((absIndex < 0) || (myTree == null) || (absIndex >= myTree.VisibleItemCount)) { return new VirtualTreeHitInfo(VirtualTreeConstant.NullIndex, column, VirtualTreeHitTargets.NoWhere); } // Shift by the horizontal scroll position var scrollShift = myXPos; x += scrollShift; // Determine the current column int level; int relIndex; IBranch branch; var multiColumn = myMctree != null; var labelShift = 0; int itemWidth; int itemLeft; if (multiColumn) { column = rawColumn = ColumnHitTest(x, out itemLeft, out itemWidth); int columns; if (myColumnPermutation == null) { columns = myMctree.ColumnCount; nativeColumn = column; } else { columns = myColumnPermutation.VisibleColumnCount; nativeColumn = myColumnPermutation.GetNativeColumn(column); } if (column >= columns) { return new VirtualTreeHitInfo(VirtualTreeConstant.NullIndex, column, VirtualTreeHitTargets.NoWhere); } } else { itemLeft = 0; itemWidth = ClientSize.Width; } var leadingBlanksLeft = itemLeft; var checkRootLines = GetAnyStyleFlag(VTCStyleFlags.HasRootLines | VTCStyleFlags.HasRootButtons); var info = myTree.GetItemInfo(absIndex, nativeColumn, checkRootLines && nativeColumn > 0); VirtualTreeHitTargets blankTargetBit = 0; var testingBlank = info.Blank; if (multiColumn) { var expansion = myTree.GetBlankExpansion(absIndex, column, myColumnPermutation); //Debug.WriteLine(string.Format("Left/Top ({0}, {1}) Anchor ({2}, {3}) Width {4} Height {5}", expansion.LeftColumn, expansion.TopRow, expansion.AnchorColumn, expansion.TopRow, expansion.Width, expansion.Height)); if (expansion.AnchorColumn != VirtualTreeConstant.NullIndex) { if (testingBlank) { blankTargetBit = VirtualTreeHitTargets.OnBlankItem; absIndex = expansion.TopRow; column = expansion.AnchorColumn; nativeColumn = (myColumnPermutation == null) ? column : myColumnPermutation.GetNativeColumn(column); // If the expansion width is 1, then we're in a blank expansion below // the last item in a column. Treat/ this the same as hovering on the // main item, except that we don't acknowledge glyph or button hovers. info = myTree.GetItemInfo(absIndex, nativeColumn, checkRootLines && nativeColumn > 0); } if (expansion.Width > 1) { // The item is wider than a single column. This type of item // always extends the full width of the tree. Retrieve new information. GetColumnBounds(expansion.LeftColumn, expansion.RightColumn, out itemLeft, out itemWidth); leadingBlanksLeft = itemLeft; if (expansion.LeftColumn != expansion.AnchorColumn) { GetColumnBounds(expansion.AnchorColumn, expansion.RightColumn, out itemLeft, out itemWidth); } } } } if (info.Blank) { return new VirtualTreeHitInfo(rawRow, rawColumn, VirtualTreeHitTargets.OnBlankItem); } else if (itemLeft != leadingBlanksLeft && x < itemLeft) { return new VirtualTreeHitInfo( absIndex, column, nativeColumn, rawRow, rawColumn, VirtualTreeHitTargets.OnItemLeft | blankTargetBit); } level = info.Level; branch = info.Branch; relIndex = info.Row; // Shift by the indent width var xItemStart = level * myIndentWidth + itemLeft; if (checkRootLines && (nativeColumn == 0 || !info.SimpleCell)) { xItemStart += myIndentWidth; } if (x < xItemStart) { // We're in the indent region target = VirtualTreeHitTargets.OnItemIndent; // See if we're actually on a button, not just an indent if (HasButtons && ((x + myIndentWidth) > xItemStart)) { if (myTree.IsExpandable(absIndex, nativeColumn)) { target = VirtualTreeHitTargets.OnItemButton; } } //UNDONE Find the item whose expansion we're clicking on, and check whether //or not we're within the bounds of the button width of the item. return new VirtualTreeHitInfo(absIndex, column, nativeColumn, rawRow, rawColumn, target | blankTargetBit); } else { x -= xItemStart; var tddMasks = new VirtualTreeDisplayDataMasks( VirtualTreeDisplayMasks.State, VirtualTreeDisplayStates.Bold | VirtualTreeDisplayStates.TextAlignFar); if (myImageWidth > 0) { tddMasks.Mask |= VirtualTreeDisplayMasks.Image; } if (myStateImageWidth > 0) { tddMasks.Mask |= VirtualTreeDisplayMasks.StateImage; } var tdd = branch.GetDisplayData(relIndex, info.Column, tddMasks); // Check if we're on the state icon. if (myStateImageWidth > 0) { if (tdd.StateImageIndex >= 0) { if (x < myStateImageWidth) { target = VirtualTreeHitTargets.OnItemStateIcon; if (StandardCheckBoxes && tdd.StateImageList == null && (tdd.StateImageIndex == (int)StandardCheckBoxImage.Unchecked || tdd.StateImageIndex == (int)StandardCheckBoxImage.Checked || tdd.StateImageIndex == (int)StandardCheckBoxImage.Indeterminate)) { target |= VirtualTreeHitTargets.StateIconHotTracked; } } else { x -= myStateImageWidth; } labelShift += myStateImageWidth; } } // Check if we're on the image icon if (myImageWidth > 0) { if (tdd.Image != -1) { if (x < myImageWidth) { if (target == VirtualTreeHitTargets.Uninitialized) { target = VirtualTreeHitTargets.OnItemIcon; } } else { x -= myImageWidth; } labelShift += myImageWidth; } } if (populateExtras || target == VirtualTreeHitTargets.Uninitialized) { // See where we are on the item's label var labelFont = (0 == (tdd.State & VirtualTreeDisplayStates.Bold)) ? Font : BoldFont; var iWidth = ListItemStringWidth(labelFont, ref info); var textAlign = GetLabelTextAlignment(ref tdd); if (RightToLeft == RightToLeft.Yes) { // For purposes of hit test calculations, RTL switches near/far alignment. textAlign = textAlign == StringAlignment.Far ? StringAlignment.Near : StringAlignment.Far; } if (target == VirtualTreeHitTargets.Uninitialized) { if (textAlign == StringAlignment.Near) { target = (x < iWidth) ? VirtualTreeHitTargets.OnItemLabel : VirtualTreeHitTargets.OnItemRight; } else { // add labelShift and xItemStart because they've been subtracted from original x value above. target = (x + labelShift + xItemStart > itemLeft + itemWidth - iWidth) ? VirtualTreeHitTargets.OnItemLabel : VirtualTreeHitTargets.OnItemLeft; } } if (populateExtras) { var currentTopIndex = TopIndex; extraInfo.IsTruncated = (rawRow != absIndex && absIndex < currentTopIndex) || ((rawRow == absIndex) && (0 == (target & (VirtualTreeHitTargets.OnItemRight | VirtualTreeHitTargets.OnItemLeft))) && ((xItemStart + labelShift + SystemInformation.Border3DSize.Width + iWidth > itemLeft + itemWidth) || (labelShift + SystemInformation.Border3DSize.Width < 0))); extraInfo.LabelOffset = labelShift; var top = (rawRow - currentTopIndex) * myItemHeight; extraInfo.ClippedItemRectangle = new Rectangle( xItemStart - scrollShift, top, (extraInfo.IsTruncated || textAlign == StringAlignment.Far) ? itemLeft + itemWidth - xItemStart : labelShift + iWidth, // in right align case, clipped item rectangle will always extend the full width, since text draws from the right. myItemHeight); extraInfo.FullLabelRectangle = new Rectangle( xItemStart + labelShift - scrollShift, top, iWidth, myItemHeight); if (textAlign == StringAlignment.Far) { // account for alignment away from glyph. extraInfo.FullLabelRectangle.X = itemLeft + itemWidth - scrollShift - iWidth; } else if (multiColumn && HasVerticalGridLines && (column != 0)) { // account for vertical gridlines extraInfo.FullLabelRectangle.X += 1; } extraInfo.LabelFont = labelFont; extraInfo.LabelFormat = StringFormat; extraInfo.LabelFormat.Alignment = GetLabelTextAlignment(ref tdd); // get alignment again, so it's not adjusted for RTL. } } } Debug.Assert(target != VirtualTreeHitTargets.Uninitialized); return new VirtualTreeHitInfo(absIndex, column, nativeColumn, rawRow, rawColumn, target | blankTargetBit); /*INDEX index; int cxState, cxImage; int xShift; ULONG Level; IVsLiteTreeList *ptl; ULONG ListIndex; WORD iWidth; VSTREEDISPLAYDATA tdd; xShift = Level * pTree->cxIndent; xShift -= pTree->xPos; if ((pTree->ci.style & (TVS_HASLINES | TVS_HASBUTTONS)) && (pTree->ci.style &TVS_LINESATROOT)) { // Subtract some more to make up for the pluses at the root xShift += pTree->cxIndent; } x -= xShift; //Get image offsets. Not all items have an image, so //we have to check that as well here. cxState = 0; cxImage = pTree->cxImage; tdd.Mask = 0; tdd.StateMask = 0; if (cxImage) { tdd.Mask |= TDM_IMAGE; tdd.hImageList = NULL; } if (pTree->himlState) { tdd.Mask |= TDM_STATE; tdd.StateMask |= TDS_STATEIMAGEMASK; tdd.State = 0; //in case of failure } if (tdd.Mask) { ptl->GetDisplayData(ListIndex, &tdd); cxState = TV_StateIndex(tdd.State) ? pTree->cxState : 0; if (cxImage && tdd.hImageList == NULL && tdd.Image == (USHORT)(-1)) { cxImage = 0; } } //iWidth adjusted from regular //treeview which caches iWidth for an item. iWidth = TV_ListItemStringWidth(pTree, ptl, ListIndex); if (x <= (int) (cxImage + cxState)) { if (x >= 0) { if (pTree->himlState && (x < cxState)) { *wHitCode = TVHT_ONITEMSTATEICON; } else if (pTree->hImageList && (x < (int) cxImage + cxState)) { *wHitCode = TVHT_ONITEMICON; } else { *wHitCode = TVHT_ONITEMLABEL; } } else { if ((x >= -pTree->cxIndent) && (pTree->ci.style & TVS_HASBUTTONS)) { BOOL expandable = FALSE; pTree->pVsTree->GetExpandableAbsolute(index, &expandable); *wHitCode = expandable ? TVHT_ONITEMBUTTON : TVHT_ONITEMINDENT; } else { *wHitCode = TVHT_ONITEMINDENT; } } if (pfIsStringTruncated) { *pfIsStringTruncated = ((xShift + cxImage + cxState + g_cxEdge + iWidth > pTree->cxWnd) || (xShift + cxImage + cxState + g_cxEdge < 0)); } } else { if (x <= (int) (iWidth + cxImage + cxState)) { *wHitCode = TVHT_ONITEMLABEL; if (pfIsStringTruncated) { *pfIsStringTruncated = ((xShift + cxImage + cxState + g_cxEdge + iWidth > pTree->cxWnd) || (xShift + cxImage + cxState + g_cxEdge < 0)); } } else { *wHitCode = TVHT_ONITEMRIGHT; if (pfIsStringTruncated) { *pfIsStringTruncated = false; } } } if (lprcItem && (*wHitCode & TVHT_ONITEM)) { lprcItem->left = xShift; lprcItem->right = (pfIsStringTruncated && *pfIsStringTruncated) ? pTree->cxWnd : xShift + cxImage + cxState + iWidth; lprcItem->top = (index - pTree->iTop) * pTree->cyItem; lprcItem->bottom = lprcItem->top + pTree->cyItem; if (pcxShiftToLabel) { *pcxShiftToLabel = (LONG)(cxState + cxImage); } } if (ptl) { ptl->Release(); } if (pLevel) { *pLevel = Level; } return (INDEX) index;*/ }
public void Activate(ref VirtualTreeHitInfo hit, ref ExtraHitInfo extraInfo, ref Point mousePos, bool immediateActivation) { var hWnd = Handle.ToInt32(); if (hWnd == 0) { return; } var ctrlhWnd = Handle; NativeMethods.SendMessage(ctrlhWnd, NativeMethods.TTM_ACTIVATE, 0, hWnd); if (hit.Row != VirtualTreeConstant.NullIndex) { var ti = new NativeMethods.TOOLINFO(); ti.SetSize(); ti.uId = hWnd; ti.uFlags = NativeMethods.TTF_IDISHWND; ti.rect = NativeMethods.RECT.FromXYWH( extraInfo.ClippedItemRectangle.X, extraInfo.ClippedItemRectangle.Y, extraInfo.ClippedItemRectangle.Width, extraInfo.ClippedItemRectangle.Height); NativeMethods.SendMessage( ctrlhWnd, NativeMethods.TTM_SETDELAYTIME, NativeMethods.TTDT_INITIAL, immediateActivation ? 0 : 250); NativeMethods.SendMessage(ctrlhWnd, NativeMethods.TTM_NEWTOOLRECT, 0, ref ti); NativeMethods.SendMessage(ctrlhWnd, NativeMethods.TTM_ACTIVATE, 1, hWnd); Font = extraInfo.LabelFont; myFormat = extraInfo.LabelFormat; myFullLabelRectangle = extraInfo.FullLabelRectangle; myActivatingMousePos = mousePos; } }