|
|
|
|
| 107 |
nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext) |
107 |
nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext) |
| 108 |
:nsBoxFrame(aShell, aContext), |
108 |
:nsBoxFrame(aShell, aContext), |
| 109 |
mCurrentMenu(nsnull), |
109 |
mCurrentMenu(nsnull), |
| 110 |
mPrefSize(-1, -1), |
110 |
mPrefSize(-1, -1), |
| 111 |
mPopupType(ePopupTypePanel), |
111 |
mPopupType(ePopupTypePanel), |
| 112 |
mPopupState(ePopupClosed), |
112 |
mPopupState(ePopupClosed), |
| 113 |
mPopupAlignment(POPUPALIGNMENT_NONE), |
113 |
mPopupAlignment(POPUPALIGNMENT_NONE), |
| 114 |
mPopupAnchor(POPUPALIGNMENT_NONE), |
114 |
mPopupAnchor(POPUPALIGNMENT_NONE), |
|
|
115 |
mInsideAnchored(PR_FALSE), |
| 115 |
mIsOpenChanged(PR_FALSE), |
116 |
mIsOpenChanged(PR_FALSE), |
| 116 |
mIsContextMenu(PR_FALSE), |
117 |
mIsContextMenu(PR_FALSE), |
| 117 |
mAdjustOffsetForContextMenu(PR_FALSE), |
118 |
mAdjustOffsetForContextMenu(PR_FALSE), |
| 118 |
mGeneratedChildren(PR_FALSE), |
119 |
mGeneratedChildren(PR_FALSE), |
| 119 |
mMenuCanOverlapOSBar(PR_FALSE), |
120 |
mMenuCanOverlapOSBar(PR_FALSE), |
| 120 |
mShouldAutoPosition(PR_TRUE), |
121 |
mShouldAutoPosition(PR_TRUE), |
| 121 |
mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT), |
122 |
mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT), |
| 122 |
mInContentShell(PR_TRUE), |
123 |
mInContentShell(PR_TRUE), |
|
|
| 566 |
position.Assign(aPosition); |
567 |
position.Assign(aPosition); |
| 567 |
else |
568 |
else |
| 568 |
mXPos = mYPos = 0; |
569 |
mXPos = mYPos = 0; |
| 569 |
} |
570 |
} |
| 570 |
else if (!aPosition.IsEmpty()) { |
571 |
else if (!aPosition.IsEmpty()) { |
| 571 |
position.Assign(aPosition); |
572 |
position.Assign(aPosition); |
| 572 |
} |
573 |
} |
| 573 |
|
574 |
|
|
|
575 |
mInsideAnchored = StringEndsWith(position, NS_LITERAL_STRING(" inside")); |
| 576 |
if (mInsideAnchored) { |
| 577 |
position = Substring(position, 0, position.Length() - 7); |
| 578 |
} |
| 579 |
|
| 574 |
if (position.EqualsLiteral("before_start")) { |
580 |
if (position.EqualsLiteral("before_start")) { |
| 575 |
mPopupAnchor = POPUPALIGNMENT_TOPLEFT; |
581 |
mPopupAnchor = POPUPALIGNMENT_TOPLEFT; |
| 576 |
mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT; |
582 |
mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT; |
| 577 |
} |
583 |
} |
| 578 |
else if (position.EqualsLiteral("before_end")) { |
584 |
else if (position.EqualsLiteral("before_end")) { |
| 579 |
mPopupAnchor = POPUPALIGNMENT_TOPRIGHT; |
585 |
mPopupAnchor = POPUPALIGNMENT_TOPRIGHT; |
| 580 |
mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT; |
586 |
mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT; |
| 581 |
} |
587 |
} |
|
|
| 648 |
PRBool aIsContextMenu) |
654 |
PRBool aIsContextMenu) |
| 649 |
{ |
655 |
{ |
| 650 |
EnsureWidget(); |
656 |
EnsureWidget(); |
| 651 |
|
657 |
|
| 652 |
mPopupState = ePopupShowing; |
658 |
mPopupState = ePopupShowing; |
| 653 |
mAnchorContent = nsnull; |
659 |
mAnchorContent = nsnull; |
| 654 |
mScreenXPos = aXPos; |
660 |
mScreenXPos = aXPos; |
| 655 |
mScreenYPos = aYPos; |
661 |
mScreenYPos = aYPos; |
|
|
662 |
mInsideAnchored = PR_FALSE; |
| 656 |
mPopupAnchor = POPUPALIGNMENT_NONE; |
663 |
mPopupAnchor = POPUPALIGNMENT_NONE; |
| 657 |
mPopupAlignment = POPUPALIGNMENT_NONE; |
664 |
mPopupAlignment = POPUPALIGNMENT_NONE; |
| 658 |
mIsContextMenu = aIsContextMenu; |
665 |
mIsContextMenu = aIsContextMenu; |
| 659 |
mAdjustOffsetForContextMenu = aIsContextMenu; |
666 |
mAdjustOffsetForContextMenu = aIsContextMenu; |
| 660 |
} |
667 |
} |
| 661 |
|
668 |
|
| 662 |
void |
669 |
void |
| 663 |
nsMenuPopupFrame::InitializePopupWithAnchorAlign(nsIContent* aAnchorContent, |
670 |
nsMenuPopupFrame::InitializePopupWithAnchorAlign(nsIContent* aAnchorContent, |
| 664 |
nsAString& aAnchor, |
671 |
nsAString& aAnchor, |
| 665 |
nsAString& aAlign, |
672 |
nsAString& aAlign, |
| 666 |
PRInt32 aXPos, PRInt32 aYPos) |
673 |
PRInt32 aXPos, PRInt32 aYPos) |
| 667 |
{ |
674 |
{ |
| 668 |
EnsureWidget(); |
675 |
EnsureWidget(); |
| 669 |
|
676 |
|
| 670 |
mPopupState = ePopupShowing; |
677 |
mPopupState = ePopupShowing; |
| 671 |
mAdjustOffsetForContextMenu = PR_FALSE; |
678 |
mAdjustOffsetForContextMenu = PR_FALSE; |
|
|
679 |
mInsideAnchored = PR_FALSE; |
| 672 |
|
680 |
|
| 673 |
// this popup opening function is provided for backwards compatibility |
681 |
// this popup opening function is provided for backwards compatibility |
| 674 |
// only. It accepts either coordinates or an anchor and alignment value |
682 |
// only. It accepts either coordinates or an anchor and alignment value |
| 675 |
// but doesn't use both together. |
683 |
// but doesn't use both together. |
| 676 |
if (aXPos == -1 && aYPos == -1) { |
684 |
if (aXPos == -1 && aYPos == -1) { |
| 677 |
mAnchorContent = aAnchorContent; |
685 |
mAnchorContent = aAnchorContent; |
| 678 |
mScreenXPos = -1; |
686 |
mScreenXPos = -1; |
| 679 |
mScreenYPos = -1; |
687 |
mScreenYPos = -1; |
|
|
| 824 |
view = temp; |
832 |
view = temp; |
| 825 |
} |
833 |
} |
| 826 |
|
834 |
|
| 827 |
return nsnull; |
835 |
return nsnull; |
| 828 |
} |
836 |
} |
| 829 |
|
837 |
|
| 830 |
nsPoint |
838 |
nsPoint |
| 831 |
nsMenuPopupFrame::AdjustPositionForAnchorAlign(const nsRect& anchorRect, |
839 |
nsMenuPopupFrame::AdjustPositionForAnchorAlign(const nsRect& anchorRect, |
| 832 |
PRBool& aHFlip, PRBool& aVFlip) |
840 |
FlipStyle& aHFlip, FlipStyle& aVFlip) |
| 833 |
{ |
841 |
{ |
| 834 |
// flip the anchor and alignment for right-to-left |
842 |
// flip the anchor and alignment for right-to-left |
| 835 |
PRInt8 popupAnchor(mPopupAnchor); |
843 |
PRInt8 popupAnchor(mPopupAnchor); |
| 836 |
PRInt8 popupAlign(mPopupAlignment); |
844 |
PRInt8 popupAlign(mPopupAlignment); |
| 837 |
if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { |
845 |
if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { |
| 838 |
popupAnchor = -popupAnchor; |
846 |
popupAnchor = -popupAnchor; |
| 839 |
popupAlign = -popupAlign; |
847 |
popupAlign = -popupAlign; |
| 840 |
} |
848 |
} |
|
|
| 881 |
|
889 |
|
| 882 |
// Flipping horizontally is allowed as long as the popup is above or below |
890 |
// Flipping horizontally is allowed as long as the popup is above or below |
| 883 |
// the anchor. This will happen if both the anchor and alignment are top or |
891 |
// the anchor. This will happen if both the anchor and alignment are top or |
| 884 |
// both are bottom, but different values. Similarly, flipping vertically is |
892 |
// both are bottom, but different values. Similarly, flipping vertically is |
| 885 |
// allowed if the popup is to the left or right of the anchor. In this case, |
893 |
// allowed if the popup is to the left or right of the anchor. In this case, |
| 886 |
// the values of the constants are such that both must be positive or both |
894 |
// the values of the constants are such that both must be positive or both |
| 887 |
// must be negative. A special case, used for overlap, allows flipping |
895 |
// must be negative. A special case, used for overlap, allows flipping |
| 888 |
// vertically as well. |
896 |
// vertically as well. |
| 889 |
aHFlip = (popupAnchor == -popupAlign); |
897 |
// However, if we are inside anchored, we allow flipping on the inside edge. |
| 890 |
aVFlip = ((popupAnchor > 0) == (popupAlign > 0)) || |
898 |
FlipStyle anchorEdge = mInsideAnchored ? FlipStyle_Inside: FlipStyle_None; |
| 891 |
(popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT); |
899 |
aHFlip = (popupAnchor == -popupAlign) ? FlipStyle_Outside : anchorEdge; |
|
|
900 |
if (((popupAnchor > 0) == (popupAlign > 0)) || |
| 901 |
(popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT)) |
| 902 |
aVFlip = FlipStyle_Outside; |
| 903 |
else |
| 904 |
aVFlip = anchorEdge; |
| 892 |
|
905 |
|
| 893 |
return pnt; |
906 |
return pnt; |
| 894 |
} |
907 |
} |
| 895 |
|
908 |
|
| 896 |
nscoord |
909 |
nscoord |
| 897 |
nsMenuPopupFrame::FlipOrResize(nscoord& aScreenPoint, nscoord aSize, |
910 |
nsMenuPopupFrame::FlipOrResize(nscoord& aScreenPoint, nscoord aSize, |
| 898 |
nscoord aScreenBegin, nscoord aScreenEnd, |
911 |
nscoord aScreenBegin, nscoord aScreenEnd, |
| 899 |
nscoord aAnchorBegin, nscoord aAnchorEnd, |
912 |
nscoord aAnchorBegin, nscoord aAnchorEnd, |
| 900 |
nscoord aMarginBegin, nscoord aMarginEnd, |
913 |
nscoord aMarginBegin, nscoord aMarginEnd, |
| 901 |
nscoord aOffsetForContextMenu, PRBool aFlip, |
914 |
nscoord aOffsetForContextMenu, FlipStyle aFlip, |
| 902 |
PRPackedBool* aFlipSide) |
915 |
PRPackedBool* aFlipSide) |
| 903 |
{ |
916 |
{ |
| 904 |
// all of the coordinates used here are in app units relative to the screen |
917 |
// all of the coordinates used here are in app units relative to the screen |
| 905 |
nscoord popupSize = aSize; |
918 |
nscoord popupSize = aSize; |
| 906 |
if (aScreenPoint < aScreenBegin) { |
919 |
if (aScreenPoint < aScreenBegin) { |
| 907 |
// at its current position, the popup would extend past the left or top |
920 |
// at its current position, the popup would extend past the left or top |
| 908 |
// edge of the screen, so it will have to be moved or resized. |
921 |
// edge of the screen, so it will have to be moved or resized. |
| 909 |
if (aFlip) { |
922 |
if (aFlip) { |
|
|
923 |
// for inside flips, we flip on the opposite side of the anchor |
| 924 |
nscoord startpos = aFlip == FlipStyle_Outside ? aAnchorBegin : aAnchorEnd; |
| 925 |
nscoord endpos = aFlip == FlipStyle_Outside ? aAnchorEnd : aAnchorBegin; |
| 926 |
|
| 910 |
// check whether there is more room to the left and right (or top and |
927 |
// check whether there is more room to the left and right (or top and |
| 911 |
// bottom) of the anchor and put the popup on the side with more room. |
928 |
// bottom) of the anchor and put the popup on the side with more room. |
| 912 |
if (aAnchorBegin - aScreenBegin >= aScreenEnd - aAnchorEnd) { |
929 |
if (startpos - aScreenBegin >= aScreenEnd - endpos) { |
| 913 |
aScreenPoint = aScreenBegin; |
930 |
aScreenPoint = aScreenBegin; |
| 914 |
popupSize = aAnchorBegin - aScreenPoint - aMarginEnd; |
931 |
popupSize = startpos - aScreenPoint - aMarginEnd; |
| 915 |
} |
932 |
} |
| 916 |
else { |
933 |
else { |
| 917 |
// flip such that the popup is to the right or bottom of the anchor |
934 |
// flip such that the popup is to the right or bottom of the anchor |
| 918 |
// point instead. However, when flipping use the same margin size. |
935 |
// point instead. However, when flipping use the same margin size. |
| 919 |
*aFlipSide = PR_TRUE; |
936 |
*aFlipSide = PR_TRUE; |
| 920 |
aScreenPoint = aAnchorEnd + aMarginEnd; |
937 |
aScreenPoint = endpos + aMarginEnd; |
| 921 |
// check if the new position is still off the right or bottom edge of |
938 |
// check if the new position is still off the right or bottom edge of |
| 922 |
// the screen. If so, resize the popup. |
939 |
// the screen. If so, resize the popup. |
| 923 |
if (aScreenPoint + aSize > aScreenEnd) { |
940 |
if (aScreenPoint + aSize > aScreenEnd) { |
| 924 |
popupSize = aScreenEnd - aScreenPoint; |
941 |
popupSize = aScreenEnd - aScreenPoint; |
| 925 |
} |
942 |
} |
| 926 |
} |
943 |
} |
| 927 |
} |
944 |
} |
| 928 |
else { |
945 |
else { |
| 929 |
aScreenPoint = aScreenBegin; |
946 |
aScreenPoint = aScreenBegin; |
| 930 |
} |
947 |
} |
| 931 |
} |
948 |
} |
| 932 |
else if (aScreenPoint + aSize > aScreenEnd) { |
949 |
else if (aScreenPoint + aSize > aScreenEnd) { |
| 933 |
// at its current position, the popup would extend past the right or |
950 |
// at its current position, the popup would extend past the right or |
| 934 |
// bottom edge of the screen, so it will have to be moved or resized. |
951 |
// bottom edge of the screen, so it will have to be moved or resized. |
| 935 |
if (aFlip) { |
952 |
if (aFlip) { |
|
|
953 |
// for inside flips, we flip on the opposite side of the anchor |
| 954 |
nscoord startpos = aFlip == FlipStyle_Outside ? aAnchorBegin : aAnchorEnd; |
| 955 |
nscoord endpos = aFlip == FlipStyle_Outside ? aAnchorEnd : aAnchorBegin; |
| 956 |
|
| 936 |
// check whether there is more room to the left and right (or top and |
957 |
// check whether there is more room to the left and right (or top and |
| 937 |
// bottom) of the anchor and put the popup on the side with more room. |
958 |
// bottom) of the anchor and put the popup on the side with more room. |
| 938 |
if (aScreenEnd - aAnchorEnd >= aAnchorBegin - aScreenBegin) { |
959 |
if (aScreenEnd - endpos >= startpos - aScreenBegin) { |
| 939 |
if (mIsContextMenu) { |
960 |
if (mIsContextMenu) { |
| 940 |
aScreenPoint = aScreenEnd - aSize; |
961 |
aScreenPoint = aScreenEnd - aSize; |
| 941 |
} |
962 |
} |
| 942 |
else { |
963 |
else { |
| 943 |
popupSize = aScreenEnd - aScreenPoint; |
964 |
popupSize = aScreenEnd - aScreenPoint; |
| 944 |
} |
965 |
} |
| 945 |
} |
966 |
} |
| 946 |
else { |
967 |
else { |
| 947 |
// flip such that the popup is to the left or top of the anchor point |
968 |
// flip such that the popup is to the left or top of the anchor point |
| 948 |
// instead. |
969 |
// instead. |
| 949 |
*aFlipSide = PR_TRUE; |
970 |
*aFlipSide = PR_TRUE; |
| 950 |
aScreenPoint = aAnchorBegin - aSize - aMarginBegin - aOffsetForContextMenu; |
971 |
aScreenPoint = startpos - aSize - aMarginBegin - aOffsetForContextMenu; |
|
|
972 |
|
| 951 |
// check if the new position is still off the left or top edge of the |
973 |
// check if the new position is still off the left or top edge of the |
| 952 |
// screen. If so, resize the popup. |
974 |
// screen. If so, resize the popup. |
| 953 |
if (aScreenPoint < aScreenBegin) { |
975 |
if (aScreenPoint < aScreenBegin) { |
| 954 |
aScreenPoint = aScreenBegin; |
976 |
aScreenPoint = aScreenBegin; |
| 955 |
if (!mIsContextMenu) { |
977 |
if (!mIsContextMenu) { |
| 956 |
popupSize = aAnchorBegin - aScreenPoint - aMarginBegin; |
978 |
popupSize = startpos - aScreenPoint - aMarginBegin; |
| 957 |
} |
979 |
} |
| 958 |
} |
980 |
} |
| 959 |
} |
981 |
} |
| 960 |
} |
982 |
} |
| 961 |
else { |
983 |
else { |
| 962 |
aScreenPoint = aScreenEnd - aSize; |
984 |
aScreenPoint = aScreenEnd - aSize; |
| 963 |
} |
985 |
} |
| 964 |
} |
986 |
} |
|
|
| 1021 |
// the screen position in app units where the popup should appear |
1043 |
// the screen position in app units where the popup should appear |
| 1022 |
nsPoint screenPoint; |
1044 |
nsPoint screenPoint; |
| 1023 |
|
1045 |
|
| 1024 |
// For anchored popups, the anchor rectangle. For non-anchored popups, the |
1046 |
// For anchored popups, the anchor rectangle. For non-anchored popups, the |
| 1025 |
// size will be 0. |
1047 |
// size will be 0. |
| 1026 |
nsRect anchorRect = parentRect; |
1048 |
nsRect anchorRect = parentRect; |
| 1027 |
|
1049 |
|
| 1028 |
// indicators of whether the popup should be flipped or resized. |
1050 |
// indicators of whether the popup should be flipped or resized. |
| 1029 |
PRBool hFlip = PR_FALSE, vFlip = PR_FALSE; |
1051 |
FlipStyle hFlip = FlipStyle_None, vFlip = FlipStyle_None; |
| 1030 |
|
1052 |
|
| 1031 |
nsMargin margin(0, 0, 0, 0); |
1053 |
nsMargin margin(0, 0, 0, 0); |
| 1032 |
GetStyleMargin()->GetMargin(margin); |
1054 |
GetStyleMargin()->GetMargin(margin); |
| 1033 |
|
1055 |
|
| 1034 |
// the screen rectangle of the root frame, in dev pixels. |
1056 |
// the screen rectangle of the root frame, in dev pixels. |
| 1035 |
nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits(); |
1057 |
nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits(); |
| 1036 |
|
1058 |
|
| 1037 |
nsIDeviceContext* devContext = presContext->DeviceContext(); |
1059 |
nsIDeviceContext* devContext = presContext->DeviceContext(); |
| 1038 |
nscoord offsetForContextMenu = 0; |
1060 |
nscoord offsetForContextMenu = 0; |
|
|
| 1102 |
nsPresContext::CSSPixelsToAppUnits(mScreenYPos) / factor); |
1124 |
nsPresContext::CSSPixelsToAppUnits(mScreenYPos) / factor); |
| 1103 |
anchorRect = nsRect(screenPoint, nsSize(0, 0)); |
1125 |
anchorRect = nsRect(screenPoint, nsSize(0, 0)); |
| 1104 |
|
1126 |
|
| 1105 |
// add the margins on the popup |
1127 |
// add the margins on the popup |
| 1106 |
screenPoint.MoveBy(margin.left + offsetForContextMenu, |
1128 |
screenPoint.MoveBy(margin.left + offsetForContextMenu, |
| 1107 |
margin.top + offsetForContextMenu); |
1129 |
margin.top + offsetForContextMenu); |
| 1108 |
|
1130 |
|
| 1109 |
// screen positioned popups can be flipped vertically but never horizontally |
1131 |
// screen positioned popups can be flipped vertically but never horizontally |
| 1110 |
vFlip = PR_TRUE; |
1132 |
vFlip = FlipStyle_Outside; |
| 1111 |
} |
1133 |
} |
| 1112 |
|
1134 |
|
| 1113 |
// if a panel is being moved, don't flip it. But always do this for content |
1135 |
// if a panel is being moved, don't flip it. But always do this for content |
| 1114 |
// shells, so that the popup doesn't extend outside the containing frame. |
1136 |
// shells, so that the popup doesn't extend outside the containing frame. |
| 1115 |
if (aIsMove && mPopupType == ePopupTypePanel && !mInContentShell) { |
1137 |
if (aIsMove && mPopupType == ePopupTypePanel && !mInContentShell) { |
| 1116 |
hFlip = vFlip = PR_FALSE; |
1138 |
hFlip = vFlip = FlipStyle_None; |
| 1117 |
} |
1139 |
} |
| 1118 |
|
1140 |
|
| 1119 |
nsRect screenRect = GetConstraintRect(anchorRect.TopLeft(), rootScreenRect); |
1141 |
nsRect screenRect = GetConstraintRect(anchorRect.TopLeft(), rootScreenRect); |
| 1120 |
|
1142 |
|
| 1121 |
// ensure that anchorRect is on screen |
1143 |
// ensure that anchorRect is on screen |
| 1122 |
if (!anchorRect.IntersectRect(anchorRect, screenRect)) { |
1144 |
if (!anchorRect.IntersectRect(anchorRect, screenRect)) { |
| 1123 |
anchorRect.width = anchorRect.height = 0; |
1145 |
anchorRect.width = anchorRect.height = 0; |
| 1124 |
// if the anchor isn't within the screen, move it to the edge of the screen. |
1146 |
// if the anchor isn't within the screen, move it to the edge of the screen. |