CDragEdit.cpp
上传用户:jackiejxj
上传日期:2007-01-02
资源大小:35k
文件大小:15k
源码类别:

TreeView控件

开发平台:

Visual C++

  1. // CDragEdit.cpp : implementation file
  2. //
  3. // Author : Sam Lu
  4. // E-mail : ysl@springsoft.com.tw
  5. //
  6. #include "stdafx.h"
  7. #include "DragEdit.h"
  8. #include "CDragEdit.h"
  9. #ifdef _DEBUG
  10. #define new DEBUG_NEW
  11. #undef THIS_FILE
  12. static char THIS_FILE[] = __FILE__;
  13. #endif
  14. #define MAXLINELEN 2048
  15. enum {
  16.     DE_INSEL, DE_BEFORESEL, DE_AFTERSEL
  17. };
  18. /////////////////////////////////////////////////////////////////////////////
  19. // CDragEdit
  20. CDragEdit::CDragEdit()
  21. {
  22.     ms_bDragInit=FALSE;
  23.     ms_nEnableFlags=0;
  24.     ms_bInDragging=FALSE;
  25.     ms_bDropEqualDrag=FALSE;
  26. }
  27. CDragEdit::~CDragEdit()
  28. {
  29. }
  30. BEGIN_MESSAGE_MAP(CDragEdit, CDragEditBase)
  31. //{{AFX_MSG_MAP(CDragEdit)
  32. ON_WM_LBUTTONDOWN()
  33. ON_WM_SETCURSOR()
  34. //}}AFX_MSG_MAP
  35. END_MESSAGE_MAP()
  36. /////////////////////////////////////////////////////////////////////////////
  37. // CDragEdit message handlers
  38. BOOL CDragEdit::ms_bOleInit=FALSE;
  39. BOOL CDragEdit::Init(int nFlags) 
  40. {
  41.     if (!ms_bOleInit) {
  42.         if (!AfxOleInit()) {
  43.             TRACE("Fail in OLE initialatingn");
  44.             return FALSE;
  45.         }
  46.         ms_bOleInit=TRUE;
  47.     }
  48.     if (ms_bDragInit) return FALSE;
  49.     ms_nEnableFlags=nFlags;
  50.     if (!GetSafeHwnd()) {
  51.         TRACE("You should create CDragEdit first, before this function is calledn");
  52. return FALSE;
  53.     }
  54. if (!ms_dropTarget.Register(this)) {
  55. TRACE("Fail in registing drop targetn");
  56. return FALSE;
  57. }
  58.     ms_bDragInit=TRUE;
  59. return TRUE;
  60. }
  61. //we reimpelmented this function for fixing SDK's bug that
  62. //it always returns (-1,-1) when the uChar is the last char
  63. CPoint CDragEdit::PosFromChar(UINT uChar)
  64. {
  65.     if (0==uChar)
  66.         return CPoint(0,0);
  67.     CPoint pt=CDragEditBase::PosFromChar(uChar);
  68.     if (pt.x<0 && pt.y<0) {
  69.         int nLine, nPos;
  70.         _CharToLinePos((int)uChar,&nLine,&nPos);
  71.         //get dc and select current using font
  72.         CClientDC dc(this);
  73.         dc.SelectObject(GetFont());
  74.         //get position of previous char
  75.         pt=CDragEditBase::PosFromChar(uChar-1);        
  76.         if (nPos==0) {
  77.             //if current char is the first char
  78.             //we get the current y from previous y plus font height
  79.             CSize szFont=dc.GetTextExtent("A",1);
  80.             pt.y+=szFont.cy;
  81.             pt.x=0;
  82.         }
  83.         else {
  84.             char szBuf[MAXLINELEN];
  85.             GetLine(nLine,szBuf,sizeof(szBuf));
  86.             CSize szFont=dc.GetTextExtent(&szBuf[nPos-1],1);
  87.             pt.x+=szFont.cx;
  88.         }
  89.         dc.SelectStockObject(SYSTEM_FONT);
  90.     }
  91.     return pt;
  92. }
  93. //get line and pos information from current selection range
  94. BOOL CDragEdit::GetCurRange(int& nLine1, int& nPos1, int& nLine2, int& nPos2)
  95. {
  96.     //get current selection range
  97.     GetSel(nPos1,nPos2);
  98.     //and trans. to line, pos
  99.     _CharToLinePos(nPos1,&nLine1,&nPos1);
  100.     _CharToLinePos(nPos2,&nLine2,&nPos2);
  101.     return TRUE;
  102. }
  103. //get line and pos from current cursor position
  104. //return TRUE, if cursor is exactly at a char
  105. //return FALSE, if cursor is at white space area
  106. BOOL CDragEdit::GetLinePosByCursor(int& nLine, int& nPos)
  107. {
  108.     CPoint ptCursor;
  109.     GetCursorPos(&ptCursor);
  110.     ScreenToClient(&ptCursor);
  111.     nPos=(int)(short)LOWORD((DWORD)CharFromPos(ptCursor));
  112.     if (nPos<0) nPos=0;
  113.     _CharToLinePos(nPos,&nLine,&nPos);
  114.     //the following codes will check if cusor is at white space area
  115.     //get the maximum x of nLine
  116.     CPoint ptChar;
  117.     ptChar=PosFromChar(LineIndex(nLine)+LineLength(LineIndex(nLine)));
  118.     if (ptChar.x<ptCursor.x || ptCursor.x<0)
  119.         return FALSE;
  120.     return TRUE;
  121. }
  122. //set edit's caret position by current cursor position
  123. BOOL CDragEdit::SetCaretByCursor()
  124. {
  125.     //get cursor's position and translate it to client coordinate
  126.     CPoint ptCursor;
  127.     GetCursorPos(&ptCursor);
  128.     ScreenToClient(&ptCursor);
  129.     //set caret position
  130.     int nChar=(int)LOWORD((DWORD)CharFromPos(ptCursor));
  131.     SetSel(nChar,nChar);
  132.     return TRUE;
  133. }
  134. BOOL CDragEdit::SetCaret(int nLine, int nPos)
  135. {
  136.     int nChar=_LinePosToChar(nLine,nPos);
  137.     SetSel(nChar,nChar);
  138.     return TRUE;
  139. }
  140. //draw a caret at current cursor position
  141. //this function will not affect the caret position or selection status
  142. BOOL CDragEdit::DrawCaretByCursor()
  143. {
  144.     int nLine, nPos;
  145.     GetLinePosByCursor(nLine,nPos);
  146.     SetCaretPos(GetPosFromLinePos(nLine,nPos));
  147.     return TRUE;
  148. }
  149. //test if (nLine,Pos) is within (nLine1,nPos1)~(nLine2,nPos2)
  150. static int LinePosInRange(int nLine, int nPos,
  151.                            int nLine1, int nPos1,
  152.                            int nLine2, int nPos2)
  153. {
  154.     if (nLine1==nLine2) {//single line selection mark
  155.         if (nLine<nLine1) return DE_BEFORESEL;
  156.         if (nLine>nLine1) return DE_AFTERSEL;
  157.         //nLine==nLine1
  158.         if (nPos<nPos1) return DE_BEFORESEL;
  159.         if (nPos>nPos2) return DE_AFTERSEL;
  160.     }
  161.     else { //multi-line selection mark
  162.         if (nLine<nLine1) return DE_BEFORESEL;
  163.         if (nLine>nLine2) return DE_AFTERSEL;
  164.         if (nLine==nLine1 && 
  165.             nPos<nPos1) return DE_BEFORESEL;
  166.         if (nLine==nLine2 && 
  167.             nPos>nPos2) return DE_AFTERSEL;
  168.     }
  169.     return DE_INSEL;
  170. }
  171. //return TRUE, if cursor is within the selection mark
  172. BOOL CDragEdit::IsInSelRange()
  173. {
  174.     int nLine1, nPos1, nLine2, nPos2;
  175.     GetCurRange(nLine1,nPos1,nLine2,nPos2);
  176.     if (nLine1==nLine2 && nPos1==nPos2) //no selection mark
  177.         return FALSE;
  178.     int nLine, nPos;
  179.     if (!GetLinePosByCursor(nLine,nPos)) //out of selection mark
  180.         return FALSE;
  181.     return (LinePosInRange(nLine,nPos,nLine1,nPos1,
  182.                            nLine2,nPos2)==DE_INSEL) ? TRUE : FALSE;
  183. }
  184. BOOL CDragEdit::_GetSelText(CString& str)
  185. {
  186.     int nLine1, nPos1, nLine2, nPos2;
  187.     GetCurRange(nLine1,nPos1,nLine2,nPos2);
  188.     ASSERT(nLine1>=0 && nPos1>=0 && nLine2>=0 && nPos2>=0);
  189.     char szBuf[MAXLINELEN];
  190.     int nLen;
  191.     //single-line selection
  192.     if (nLine1==nLine2) {
  193.         nLen=GetLine(nLine1,szBuf,sizeof(szBuf));
  194.         szBuf[nLen]='';
  195.         szBuf[nPos2]='';
  196.         str=szBuf+nPos1;
  197.         return TRUE;
  198.     }
  199.     //multi-line section
  200.     nLen=GetLine(nLine1,szBuf,sizeof(szBuf));
  201.     szBuf[nLen]='';
  202.     str=szBuf+nPos1;
  203.     for (int i=nLine1+1; i<nLine2; i++) {
  204.         str+="rn";
  205.         nLen=GetLine(i,szBuf,sizeof(szBuf));
  206.         szBuf[nLen]='';
  207.         str+=szBuf;
  208.     }
  209.     str+="rn";
  210.     nLen=GetLine(nLine2,szBuf,sizeof(szBuf));
  211.     szBuf[nLen]='';
  212.     szBuf[nPos2]='';
  213.     str+=szBuf;
  214.     return TRUE;
  215. }
  216. void CDragEdit::OnLButtonDown(UINT nFlags, CPoint point) 
  217. {
  218.     if (!EnableDrag()) {
  219.         CDragEditBase::OnLButtonDown(nFlags, point);
  220.         return;
  221.     }
  222.     if (IsInSelRange()) {
  223.         //get selected text
  224.         CString str;
  225.         if (!_GetSelText(str)) {
  226.             CDragEditBase::OnLButtonDown(nFlags, point);
  227.             return;
  228.         }
  229.         int nLine1, nPos1, nLine2, nPos2;
  230.         GetCurRange(nLine1,nPos1,nLine2,nPos2);
  231.         //make a copy of selected text to a global memory
  232.         HGLOBAL hData=GlobalAlloc(GHND|GMEM_SHARE,strlen(str)+1);
  233.         strcpy((LPSTR)GlobalLock(hData),str);
  234.         GlobalUnlock(hData);
  235.         ms_dropSource.CacheGlobalData(CF_TEXT,hData);
  236.         //defined dragging area
  237.         CRect   rc(point.x-5, point.y-5, point.x+5, point.y+5);
  238.         //It seems it is a MFC's bug that MFC will set capture to AfxGetMainWnd()
  239.         //and use its coordinate to test the lpRectStartDrag.
  240.         //So, we need to first translate the rc's coordinate.
  241.         MapWindowPoints(AfxGetMainWnd(),&rc);
  242.         //start dragging
  243.         ms_bDropEqualDrag=FALSE;
  244.         ms_bInDragging=TRUE;
  245.         DROPEFFECT dwEffect=ms_dropSource.DoDragDrop(DROPEFFECT_COPY|DROPEFFECT_MOVE,&rc);
  246.         ms_bInDragging=FALSE;
  247.         if (dwEffect & (DROPEFFECT_MOVE | DROPEFFECT_COPY)) {
  248.             if (dwEffect & DROPEFFECT_MOVE) {
  249.                 if (ms_bDropEqualDrag) {
  250.                     //If drag source equal to drop target and user want to
  251.                     //move string
  252.                     ms_bDropEqualDrag=FALSE;
  253.                     int nSel=LinePosInRange(ms_nDropPtLine,ms_nDropPtPos,
  254.                                              nLine1,nPos1,nLine2,nPos2);
  255.                     //we don't allow the string be moved into selection area
  256.                     if (nSel==DE_INSEL)
  257.                         return;
  258.                     else {
  259.                         if (nSel==DE_AFTERSEL) {
  260.                             //If user want to move the string back,
  261.                             //we need to adjust the ms_nDropPtLine 
  262.                             //and ms_nDropPtPos
  263.                             int nChar=_LinePosToChar(ms_nDropPtLine,ms_nDropPtPos);
  264.                             nChar-=str.GetLength();
  265.                             //delet selected string first
  266.                             ReplaceSel("",TRUE);
  267.                             _CharToLinePos(nChar,&ms_nDropPtLine,&ms_nDropPtPos);
  268.                             //set new insert point
  269.                             SetCaret(ms_nDropPtLine,ms_nDropPtPos);
  270.                         }
  271.                         else {
  272.                             //delet selected string first
  273.                             ReplaceSel("",TRUE);
  274.                             //set new insert point
  275.                             SetCaret(ms_nDropPtLine,ms_nDropPtPos);
  276.                         }
  277.                         //insert dragged string and sel. it
  278.                         int nBeg, nEnd;
  279.                         GetSel(nBeg,nEnd);        
  280.                         nEnd+=str.GetLength();
  281.                         ReplaceSel(str,TRUE);
  282.                         SetSel(nBeg,nEnd);
  283.                     }
  284.                 }
  285.                 else
  286.                     ReplaceSel("",TRUE);
  287.             }
  288.             ms_bDropEqualDrag=FALSE;
  289.             return;
  290.         }
  291.         ms_bDropEqualDrag=FALSE;
  292.         //If user does not want to drag string, we reset the caret pos.
  293.     SetCaretByCursor();
  294.         return;
  295.     }
  296. CDragEditBase::OnLButtonDown(nFlags, point);
  297. }
  298. BOOL CDragEdit::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
  299. {
  300.     if (EnableDrag()) {
  301.         if (IsInSelRange()) {
  302.             //if the cursor is over a selection mark
  303.             //we will change the cursor shape to a arrow type
  304.             //It means user can drag it.
  305.             ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  306.             return FALSE;
  307.         }        
  308.     }
  309.     //Otherwise, we keep the cursor shape as its original type
  310. return CDragEditBase::OnSetCursor(pWnd, nHitTest, message);
  311. }
  312. /*************************************************************
  313. CDEDropTarget
  314. **************************************************************/
  315. BOOL CDEDropTarget::Register(CDragEdit* pEdit)
  316. {
  317.     ms_pEditCtl=pEdit;
  318.     return COleDropTarget::Register(pEdit);
  319. }
  320. DROPEFFECT CDEDropTarget::OnDragScroll(CWnd* pWnd, DWORD dwKeyState, CPoint point)
  321. {
  322.     ASSERT(ms_pEditCtl);
  323.     //if pWnd is kind of CView, we let COleDropTarget to handle it
  324. if (pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  325. return COleDropTarget::OnDragScroll(pWnd,dwKeyState,point);
  326.     if (!ms_bBeginDrop)
  327.         return DROPEFFECT_NONE;
  328.     CRect rectClient;
  329. ms_pEditCtl->GetClientRect(&rectClient);
  330. CRect rect = rectClient;
  331.     //nScrollInset is a COleDropTarget's static member variable
  332.     rect.InflateRect(-nScrollInset, -nScrollInset);
  333. // hit-test against inset region
  334. if (rectClient.PtInRect(point) && !rect.PtInRect(point)) {
  335.         UINT        uMsg;
  336.         int         nCode;
  337.         CScrollBar* pScrollBar=NULL;        
  338. // determine which way to scroll along both X & Y axis
  339.         if (point.x<rect.left) {
  340.             pScrollBar=ms_pEditCtl->GetScrollBarCtrl(SB_HORZ);
  341.             uMsg=WM_HSCROLL;
  342. nCode=SB_LINELEFT;
  343.         }
  344.         else if (point.x>=rect.right) {
  345.             pScrollBar=ms_pEditCtl->GetScrollBarCtrl(SB_HORZ);
  346.             uMsg=WM_HSCROLL;
  347. nCode=SB_LINERIGHT;
  348.         }
  349.         if (point.y<rect.top) {
  350.             pScrollBar=ms_pEditCtl->GetScrollBarCtrl(SB_VERT);
  351.             uMsg=WM_VSCROLL;
  352. nCode=SB_LINEUP;
  353.         }
  354.         else if (point.y>=rect.bottom) {
  355. pScrollBar=ms_pEditCtl->GetScrollBarCtrl(SB_VERT);
  356.             uMsg=WM_VSCROLL;
  357. nCode=SB_LINEDOWN;
  358.         }
  359.         LRESULT l=ms_pEditCtl->SendMessage(uMsg,MAKEWPARAM(nCode,0),
  360.             (LPARAM)(pScrollBar ? pScrollBar->GetSafeHwnd() : NULL));
  361.         ms_pEditCtl->DrawCaretByCursor();
  362.         if (dwKeyState & MK_CONTROL)
  363.             return DROPEFFECT_SCROLL | DROPEFFECT_COPY;
  364.         else
  365.             return DROPEFFECT_SCROLL | DROPEFFECT_MOVE;
  366.     }
  367.     return DROPEFFECT_NONE;
  368. }
  369. DROPEFFECT CDEDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, 
  370.                                       DWORD dwKeyState, CPoint point)
  371. {
  372.     ASSERT(ms_pEditCtl);
  373.     if (!ms_pEditCtl->EnableDrop() ||
  374.         !pDataObject->IsDataAvailable(CF_TEXT)) 
  375.         return DROPEFFECT_NONE;
  376.     DROPEFFECT dwEffect;
  377.     
  378.     if (dwKeyState & MK_CONTROL)
  379.         dwEffect=DROPEFFECT_COPY;
  380.     else
  381.         dwEffect=DROPEFFECT_MOVE;
  382.     ms_bBeginDrop=TRUE;
  383.     //we set focus to current window such that the caret will be shown
  384.     ms_pEditCtl->SetFocus();
  385.     return dwEffect;
  386. }
  387. void CDEDropTarget::OnDragLeave(CWnd* pWnd)
  388. {
  389.     ASSERT(ms_pEditCtl);
  390.     ms_bBeginDrop=FALSE;
  391. }
  392. DROPEFFECT CDEDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, 
  393.                                      DWORD dwKeyState, CPoint point)
  394. {
  395.     ASSERT(ms_pEditCtl);
  396.     if (!ms_bBeginDrop)
  397.         return DROPEFFECT_NONE;
  398.     DROPEFFECT dwEffect;
  399.     
  400.     if (dwKeyState & MK_CONTROL)
  401.         dwEffect=DROPEFFECT_COPY;
  402.     else
  403.         dwEffect=DROPEFFECT_MOVE;
  404.     ms_pEditCtl->DrawCaretByCursor();
  405.     return dwEffect;
  406. }
  407. BOOL CDEDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject,
  408.                            DROPEFFECT dropEffect, CPoint point)
  409. {
  410.     ASSERT(ms_pEditCtl);
  411.     if (!ms_bBeginDrop)
  412.         return FALSE;
  413.     if (ms_pEditCtl->IsInDragging() &&
  414.         ms_pEditCtl->IsInSelRange())
  415.         return DROPEFFECT_NONE;
  416.     HGLOBAL hData=pDataObject->GetGlobalData(CF_TEXT);
  417.     if (!hData) {
  418.         TRACE("Fail in getting datan");
  419.         return FALSE;
  420.     }
  421.     LPCSTR lpcszData=(LPCSTR)GlobalLock(hData);
  422.     if ((dropEffect & DROPEFFECT_MOVE) &&
  423.         ms_pEditCtl->IsInDragging()) {
  424.         //If the drag window equal to drop window and
  425.         //user want to move string, we let drag source
  426.         //to move string by itself
  427.         ms_pEditCtl->SetDropEqualDrag(TRUE);
  428.         int nLine, nPos;
  429.         ms_pEditCtl->GetLinePosByCursor(nLine,nPos);
  430.         ms_pEditCtl->SetDropPos(nLine,nPos);
  431.     }
  432.     else {
  433.         //set dropped point
  434.         ms_pEditCtl->SetCaretByCursor();
  435.         //insert string and select the inserted string
  436.         int nBeg, nEnd;
  437.         ms_pEditCtl->GetSel(nBeg,nEnd);        
  438.         nEnd+=strlen(lpcszData);
  439.         ms_pEditCtl->ReplaceSel(lpcszData,TRUE);
  440.         ms_pEditCtl->SetSel(nBeg,nEnd);
  441.     }
  442.     GlobalUnlock(hData);
  443.     return TRUE;
  444. }