/* AbiWord * Copyright (c) 2003 Martin Sevior <msevior@physics.unimelb.edu.au> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "fv_VisualDragText.h" #include "fl_DocLayout.h" #include "pd_Document.h" #include "gr_Graphics.h" #include "ut_units.h" #include "fl_BlockLayout.h" #include "fp_Line.h" #include "fp_Run.h" #include "fl_TableLayout.h" #include "fp_TableContainer.h" #include "fv_View.h" #include "gr_Painter.h" FV_VisualDragText::FV_VisualDragText (FV_View * pView) : m_pView (pView), m_iVisualDragMode(FV_VisualDrag_NOT_ACTIVE), m_pDragImage(NULL), m_iLastX(0), m_iLastY(0), m_recCurFrame(0,0,0,0), m_iInitialOffX(0), m_iInitialOffY(0), m_recOrigLeft(0,0,0,0), m_recOrigRight(0,0,0,0), m_bTextCut(false), m_pDocUnderCursor(NULL), m_bCursorDrawn(false), m_recCursor(0,0,0,0), m_pAutoScrollTimer(NULL), m_xLastMouse(1), m_yLastMouse(1) { UT_ASSERT (pView); } FV_VisualDragText::~FV_VisualDragText() { DELETEP(m_pDragImage); if(m_pAutoScrollTimer != NULL) { m_pAutoScrollTimer->stop(); DELETEP(m_pAutoScrollTimer); } } bool FV_VisualDragText::isActive(void) const { return (FV_VisualDrag_NOT_ACTIVE != m_iVisualDragMode); } GR_Graphics * FV_VisualDragText::getGraphics(void) const { return m_pView->getGraphics(); } void FV_VisualDragText::setMode(FV_VisualDragMode iEditMode) { m_iVisualDragMode = iEditMode; } void FV_VisualDragText::_autoScroll(UT_Worker * pWorker) { UT_ASSERT(pWorker); // this is a static callback method and does not have a 'this' pointer. FV_VisualDragText * pVis = static_cast<FV_VisualDragText *>(pWorker->getInstanceData()); UT_ASSERT(pVis); FV_View * pView = pVis->m_pView; pVis->getGraphics()->setClipRect(&pVis->m_recCurFrame); pView->updateScreen(false); pView->getGraphics()->setClipRect(NULL); bool bScrollDown = false; bool bScrollUp = false; bool bScrollLeft = false; bool bScrollRight = false; UT_sint32 y = pVis->m_yLastMouse; UT_sint32 x = pVis->m_xLastMouse; if(y<=0) { bScrollUp = true; } else if(y >= pView->getWindowHeight()) { bScrollDown = true; } if(x <= 0) { bScrollLeft = true; } else if(x >= pView->getWindowWidth()) { bScrollRight = true; } if(bScrollDown || bScrollUp || bScrollLeft || bScrollRight) { if(bScrollUp) { pView->cmdScroll(AV_SCROLLCMD_LINEUP, static_cast<UT_uint32>( -y)); } else if(bScrollDown) { pView->cmdScroll(AV_SCROLLCMD_LINEDOWN, static_cast<UT_uint32>(y - pView->getWindowHeight())); } if(bScrollLeft) { pView->cmdScroll(AV_SCROLLCMD_LINELEFT, static_cast<UT_uint32>(-x)); } else if(bScrollRight) { pView->cmdScroll(AV_SCROLLCMD_LINERIGHT, static_cast<UT_uint32>(x -pView->getWindowWidth())); } pVis->drawImage(); #if 0 PT_DocPosition posAtXY = pVis->getPosFromXY(x,y); pView->_setPoint(posAtXY); pVis->drawCursor(posAtXY); #endif return; } else { pVis->m_pAutoScrollTimer->stop(); DELETEP(pVis->m_pAutoScrollTimer); } } void FV_VisualDragText::mouseDrag(UT_sint32 x, UT_sint32 y) { if((m_iVisualDragMode != FV_VisualDrag_DRAGGING) && (m_iVisualDragMode != FV_VisualDrag_WAIT_FOR_MOUSE_DRAG) ) { // // Haven't started the drag yet so create our image and cut the text. // m_pView->getDocument()->beginUserAtomicGlob(); mouseCut(x,y); m_bTextCut = true; } clearCursor(); m_iVisualDragMode = FV_VisualDrag_DRAGGING; xxx_UT_DEBUGMSG(("x = %d y = %d width \n",x,y)); bool bScrollDown = false; bool bScrollUp = false; bool bScrollLeft = false; bool bScrollRight = false; m_xLastMouse = x; m_yLastMouse = y; if(y<=0) { bScrollUp = true; } else if( y >= m_pView->getWindowHeight()) { bScrollDown = true; } if(x <= 0) { bScrollLeft = true; } else if(x >= m_pView->getWindowWidth()) { bScrollRight = true; } if(bScrollDown || bScrollUp || bScrollLeft || bScrollRight) { if(m_pAutoScrollTimer != NULL) { return; } m_pAutoScrollTimer = UT_Timer::static_constructor(_autoScroll, this, getGraphics()); m_pAutoScrollTimer->set(AUTO_SCROLL_MSECS); m_pAutoScrollTimer->start(); return; } UT_sint32 dx = 0; UT_sint32 dy = 0; UT_Rect expX(0,m_recCurFrame.top,0,m_recCurFrame.height); UT_Rect expY(m_recCurFrame.left,0,m_recCurFrame.width,0); UT_sint32 iext = getGraphics()->tlu(3); dx = x - m_iLastX; dy = y - m_iLastY; m_recCurFrame.left += dx; m_recCurFrame.top += dy; m_recOrigLeft.left += dx; m_recOrigLeft.top += dy; m_recOrigRight.left += dx; m_recOrigRight.top += dy; if(dx < 0) { expX.left = m_recCurFrame.left+m_recCurFrame.width -iext; expX.width = -dx + 2*iext; if(dy > 0) { expX.top -= iext; expX.height += dy + 2*iext; } else { expX.top -= iext; expX.height += (-dy + 2*iext); } } else { expX.left = m_recCurFrame.left - dx - iext; expX.width = dx + 2*iext; if(dy > 0) { expX.top -= iext; expX.height += dy + 2*iext; } else { expX.top -= iext; expX.height += (-dy + 2*iext); } } expY.left -= iext; expY.width += 2*iext; if(dy < 0) { expY.top = m_recCurFrame.top + m_recCurFrame.height -iext; expY.height = -dy + 2*iext; } else { expY.top = m_recCurFrame.top - dy - iext; expY.height = dy + 2*iext; } if(expX.width > 0) { getGraphics()->setClipRect(&expX); m_pView->updateScreen(false); } if(expY.height > 0) { getGraphics()->setClipRect(&expY); m_pView->updateScreen(false); } getGraphics()->setClipRect(NULL); drawImage(); if(m_recOrigLeft.width > 0) { getGraphics()->setClipRect(&m_recOrigLeft); m_pView->updateScreen(false); } if(m_recOrigRight.width > 0) { getGraphics()->setClipRect(&m_recOrigRight); m_pView->updateScreen(false); } m_iLastX = x; m_iLastY = y; getGraphics()->setClipRect(NULL); PT_DocPosition posAtXY = getPosFromXY(x,y); m_pView->_setPoint(posAtXY); // m_pView->_fixInsertionPointCoords(); drawCursor(posAtXY); } void FV_VisualDragText::clearCursor(void) { if(m_bCursorDrawn) { if(m_pDocUnderCursor) { GR_Painter painter(getGraphics()); painter.drawImage(m_pDocUnderCursor,m_recCursor.left,m_recCursor.top); m_bCursorDrawn = false; DELETEP(m_pDocUnderCursor); } else { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } } } void FV_VisualDragText::drawCursor(PT_DocPosition newPos) { fp_Run * pRunLow = NULL; fl_BlockLayout * pBlock = NULL; UT_sint32 xLow, yLow; UT_uint32 heightCaret; UT_sint32 xCaret2, yCaret2; bool bDirection=false; bool bEOL=false; m_pView->_findPositionCoords(newPos, bEOL, xLow, yLow, xCaret2, yCaret2, heightCaret, bDirection, &pBlock, &pRunLow); m_recCursor.left = xLow; m_recCursor.top = yLow; m_recCursor.width = 2; m_recCursor.height = heightCaret; UT_ASSERT(m_pDocUnderCursor == NULL); GR_Painter painter(getGraphics()); m_pDocUnderCursor = painter.genImageFromRectangle(m_recCursor); UT_RGBColor black(0,0,0); painter.fillRect( black, m_recCursor); m_bCursorDrawn = true; } /*! * This method creates an image from the current selection. It sets * the drag rectangle, the initial offsets and the initial positions * of the cursor. */ void FV_VisualDragText::getImageFromSelection(UT_sint32 x, UT_sint32 y) { // // OK first work out the locations in the document of the anchor and point // PT_DocPosition posLow = 0; PT_DocPosition posHigh = 0; fp_Run * pRunLow = NULL; UT_sint32 xLow, yLow,xHigh,yHigh; UT_uint32 heightCaret; UT_sint32 xCaret2, yCaret2; bool bDirection = false; bool bEOL = false; if(m_pView->getSelectionMode() < FV_SelectionMode_Multiple) { if(posLow < m_pView->getPoint()) { posLow = m_pView->getSelectionAnchor(); posHigh = 0; posHigh = m_pView->getPoint(); } else { posLow = m_pView->getPoint(); posHigh = m_pView->getSelectionAnchor(); } } else { UT_sint32 num = m_pView->getNumSelections(); PD_DocumentRange * pR = m_pView->getNthSelection(0); posLow = pR->m_pos1+1; fl_BlockLayout * pBlock = NULL; bDirection = false; bEOL = false; m_pView->_findPositionCoords(posLow, bEOL, xLow, yLow, xCaret2, yCaret2, heightCaret, bDirection, &pBlock, &pRunLow); while(pBlock->isEmbeddedType()) { posLow++; m_pView->_findPositionCoords(posLow, bEOL, xLow, yLow, xCaret2, yCaret2, heightCaret, bDirection, &pBlock, &pRunLow); } fl_ContainerLayout * pCL = pBlock->myContainingLayout(); UT_return_if_fail(pCL->getContainerType() == FL_CONTAINER_CELL); fl_CellLayout * pCell = static_cast<fl_CellLayout *>(pCL); fp_CellContainer * pCCon = static_cast<fp_CellContainer *>(pCL->getFirstContainer()); UT_return_if_fail(pCCon); UT_Rect * pRect = pCCon->getScreenRect(); xLow = pRect->left; yLow = pRect->top; m_recCurFrame.left = xLow; m_recCurFrame.top = yLow; delete pRect; // // Now the other end of the column // pR = m_pView->getNthSelection(num-1); posHigh = pR->m_pos1+1; m_pView->_findPositionCoords(posHigh, bEOL, xHigh, yHigh, xCaret2, yCaret2, heightCaret, bDirection, &pBlock, &pRunLow); while(pBlock->isEmbeddedType()) { posHigh++; m_pView->_findPositionCoords(posHigh, bEOL, xHigh, yHigh, xCaret2, yCaret2, heightCaret, bDirection, &pBlock, &pRunLow); } pCL = pBlock->myContainingLayout(); UT_return_if_fail(pCL->getContainerType() == FL_CONTAINER_CELL); pCell = static_cast<fl_CellLayout *>(pCL); pCCon = static_cast<fp_CellContainer *>(pCL->getFirstContainer()); UT_return_if_fail(pCCon); pRect = pCCon->getScreenRect(); xHigh = pRect->left+ pRect->width; yHigh = pRect->top + pRect->height; delete pRect; m_recCurFrame.width = xHigh - xLow; m_recCurFrame.height = yHigh - yLow; m_recOrigLeft.width = 0; m_recOrigLeft.height = 0; m_recOrigLeft.left = 0; m_recOrigLeft.top = 0; m_recOrigRight.width = 0; m_recOrigRight.height = 0; m_recOrigRight.left = 0; m_recOrigRight.top = 0; m_iLastX = x; m_iLastY = y; m_iInitialOffX = x - m_recCurFrame.left; m_iInitialOffY = y - m_recCurFrame.top; GR_Painter painter(getGraphics()); m_pDragImage = painter.genImageFromRectangle(m_recCurFrame); return; } m_pView->_findPositionCoords(posLow, bEOL, xLow, yLow, xCaret2, yCaret2, heightCaret, bDirection, NULL, &pRunLow); fl_BlockLayout * pBLow1 = pRunLow->getBlock(); fp_Run * pRunLow2 = NULL; m_pView->_findPositionCoords(posLow+1, bEOL, xLow, yLow, xCaret2, yCaret2, heightCaret, bDirection, NULL, &pRunLow2); fl_BlockLayout * pBLow2 = pRunLow2->getBlock(); if(pBLow2 != pBLow1) { pRunLow = pRunLow2; } fp_Line * pLineLow = pRunLow->getLine(); fp_Run * pRunHigh = NULL; m_pView->_findPositionCoords(posHigh, bEOL, xHigh, yHigh, xCaret2, yCaret2, heightCaret, bDirection, NULL, &pRunHigh); fp_Line * pLineHigh = pRunHigh->getLine(); // // OK deal with the nice case of the selection just on the single line // if(pLineLow == pLineHigh) { // // Grab that first charcter! // m_pView->_findPositionCoords(posLow, bEOL, xLow, yLow, xCaret2, yCaret2, heightCaret, bDirection, NULL, &pRunLow2); UT_sint32 xx,yy; pLineLow->getScreenOffsets(pRunLow,xx,yy); m_recCurFrame.left = xLow; m_recCurFrame.width = xHigh - xLow; m_recCurFrame.top = yy; m_recCurFrame.height = pLineLow->getHeight(); m_recOrigLeft.width = 0; m_recOrigLeft.height = 0; m_recOrigLeft.left = 0; m_recOrigLeft.top = 0; m_recOrigRight.width = 0; m_recOrigRight.height = 0; m_recOrigRight.left = 0; m_recOrigRight.top = 0; } else { // // low and high are on different rows. First get top, left // // Fix me! Instead of redrawing over this, do a fillrect(image,src,dest) // three times. // Then left and right are inverted. // UT_sint32 xx,yy; fp_Run * pRun = pLineLow->getFirstRun(); pLineLow->getScreenOffsets(pRun,xx,yy); xx -= pRun->getX(); xx -= pLineLow->getX(); m_recOrigLeft.left = xx; m_recOrigLeft.width = xLow - xx; m_recOrigLeft.top = yy; m_recOrigLeft.height = pLineLow->getHeight(); m_recCurFrame.left = xx; m_recCurFrame.top = yy; fp_Line * pNext = pLineLow; UT_sint32 width = 0; while(pNext && (pNext != pLineHigh)) { pRun = pNext->getFirstRun(); pNext->getScreenOffsets(pRun,xx,yy); xx += pNext->getMaxWidth(); if(xx > width) { width = xx; } fp_ContainerObject * pCon = pNext->getNext(); if(pCon) { pNext = static_cast<fp_Line *>(pCon); } else { fl_BlockLayout * pBL = pNext->getBlock(); pBL = pBL->getNextBlockInDocument(); if(pBL) { pNext = static_cast<fp_Line *>(pBL->getFirstContainer()); } else { pNext = NULL; } } } if(pNext == NULL) { UT_DEBUGMSG(("Last line of selection not found! \n")); UT_ASSERT(UT_SHOULD_NOT_HAPPEN); return; } pRun = pLineHigh->getFirstRun(); pLineHigh->getScreenOffsets(pRun,xx,yy); yy += pLineHigh->getHeight(); m_recCurFrame.width = width - m_recCurFrame.left; m_recCurFrame.height = yy - m_recCurFrame.top; if(m_recCurFrame.top + m_recCurFrame.height > m_pView->getWindowHeight()) { m_recCurFrame.height = m_pView->getWindowHeight() - m_recCurFrame.top; } m_recOrigRight.left = xHigh; m_recOrigRight.width = m_recCurFrame.left + m_recCurFrame.width - xHigh; m_recOrigRight.top = yy - pLineHigh->getHeight(); m_recOrigRight.height = pLineHigh->getHeight(); } m_iLastX = x; m_iLastY = y; m_iInitialOffX = x - m_recCurFrame.left; m_iInitialOffY = y - m_recCurFrame.top; GR_Painter painter(getGraphics()); UT_RGBColor black(0,0,0); UT_RGBColor trans(0,0,0,true); m_pDragImage = painter.genImageFromRectangle(m_recCurFrame); } void FV_VisualDragText::mouseCut(UT_sint32 x, UT_sint32 y) { getImageFromSelection(x,y); m_pView->cmdCut(); m_pView->updateScreen(false); drawImage(); } void FV_VisualDragText::mouseCopy(UT_sint32 x, UT_sint32 y) { getImageFromSelection(x,y); m_pView->cmdCopy(); m_pView->updateScreen(false); drawImage(); m_iVisualDragMode= FV_VisualDrag_WAIT_FOR_MOUSE_DRAG; m_bTextCut = false; m_pView->_resetSelection(); } PT_DocPosition FV_VisualDragText::getPosFromXY(UT_sint32 x, UT_sint32 y) { // // Convert this to a document position and paste! // x -= m_iInitialOffX; y -= m_iInitialOffY; y += getGraphics()->tlu(6); //Otherwise it's too easy to hit the line above x += m_recOrigLeft.width; // Add in offset PT_DocPosition posAtXY = m_pView->getDocPositionFromXY(x,y,false); return posAtXY; } /*! * x and y is the location in the document windows of the mouse in logical * units. */ void FV_VisualDragText::mouseRelease(UT_sint32 x, UT_sint32 y) { if(m_pAutoScrollTimer != NULL) { m_pAutoScrollTimer->stop(); DELETEP(m_pAutoScrollTimer); } clearCursor(); if(m_iVisualDragMode != FV_VisualDrag_DRAGGING) { // // we didn't actually drag anything. Just release the selection. // m_pView->warpInsPtToXY(x, y,true); return; } PT_DocPosition posAtXY = getPosFromXY(x,y); m_pView->setPoint(posAtXY); getGraphics()->setClipRect(&m_recCurFrame); m_pView->updateScreen(false); getGraphics()->setClipRect(NULL); m_iVisualDragMode = FV_VisualDrag_NOT_ACTIVE; m_pView->getMouseContext(x,y); m_iInitialOffX = 0; m_iInitialOffY = 0; PT_DocPosition oldPoint = m_pView->getPoint(); bool bPasteTableCol = (m_pView->getPrevSelectionMode() == FV_SelectionMode_TableColumn); m_pView->cmdPaste(); PT_DocPosition newPoint = m_pView->getPoint(); DELETEP(m_pDragImage); if(m_bTextCut) { m_pView->getDocument()->endUserAtomicGlob(); // End the big undo block } if(!bPasteTableCol) { m_pView->cmdSelect(oldPoint,newPoint); } else { m_pView->cmdSelectColumn(newPoint); } m_bTextCut = false; } void FV_VisualDragText::drawImage(void) { if(m_pDragImage == NULL) { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); return; } GR_Painter painter(getGraphics()); if( m_recOrigLeft.width > 0 || m_recOrigRight.width>0 ) { UT_Rect dest; dest.left = m_recCurFrame.left + m_recOrigLeft.width; dest.top = m_recCurFrame.top; dest.width = m_recCurFrame.width - m_recOrigLeft.width; dest.height = m_recOrigLeft.height; UT_Rect src; src.left = m_recOrigLeft.width; src.top = 0; src.height = m_recOrigLeft.height; src.width = dest.width; if(src.height > 3) { painter.fillRect(m_pDragImage,&src,&dest); } dest.left = m_recCurFrame.left; dest.top = m_recCurFrame.top + m_recOrigLeft.height ; dest.width = m_recCurFrame.width; dest.height = m_recCurFrame.height - m_recOrigLeft.height - m_recOrigRight.height; src.left = 0; src.top = m_recOrigLeft.height ; src.width = m_recCurFrame.width; src.height = dest.height; if(src.height > 3) { painter.fillRect(m_pDragImage,&src,&dest); } dest.left = m_recCurFrame.left; dest.top = m_recCurFrame.top + m_recCurFrame.height - m_recOrigRight.height; dest.width = m_recCurFrame.width - m_recOrigRight.width; dest.height = m_recOrigRight.height; src.top = m_recCurFrame.height - m_recOrigRight.height; src.left = 0; src.width = m_recCurFrame.width - m_recOrigRight.width; src.height = m_recOrigRight.height; if(src.height > 3) { painter.fillRect(m_pDragImage,&src,&dest); } return; } painter.drawImage(m_pDragImage,m_recCurFrame.left,m_recCurFrame.top); }