00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 #include <config.h>
00013 
00014 #include <kaction.h>
00015 #include <kapplication.h>
00016 #include <kdebug.h>
00017 #include <kglobalsettings.h>
00018 #include <kiconloader.h>
00019 #include <klocale.h>
00020 #include <qclipboard.h>
00021 #include <qcursor.h>
00022 #include <qimage.h>
00023 #include <qpainter.h>
00024 #include <qpixmap.h>
00025 
00026 #include "documentWidget.h"
00027 #include "pageView.h"
00028 #include "documentPageCache.h"
00029 #include "hyperlink.h"
00030 #include "renderedDocumentPagePixmap.h"
00031 #include "textBox.h"
00032 
00033 #include "kvsprefs.h"
00034 
00035 
00036 
00037 const int DocumentWidget::bottom_right_corner[16] =
00038   { 61, 71, 85, 95,
00039     71, 78, 89, 96,
00040     85, 89, 95, 98,
00041     95, 97, 98, 99 };
00042 
00043 const int DocumentWidget::bottom_left_corner[16] =
00044   { 95, 85, 71, 61,
00045     97, 89, 78, 71,
00046     98, 95, 89, 85,
00047     99, 98, 96, 95 };
00048 
00049 const int DocumentWidget::shadow_strip[4] =
00050   { 56, 67, 83, 94 };
00051 
00052 QColor DocumentWidget::backgroundColorForCorners;
00053 
00054 namespace {
00055 
00057 QPixmap* busyIcon = 0;
00058 
00061 QPixmap* URShadow = 0;
00062 QPixmap* BRShadow = 0;
00063 QPixmap* BLShadow = 0;
00064 
00065 } 
00066 
00067 
00068 DocumentWidget::DocumentWidget(QWidget *parent, PageView *sv, DocumentPageCache *cache, const char *name )
00069   : QWidget( parent, name ), indexOfUnderlinedLink(-1)
00070 {
00071   moveTool = true;
00072 
00073   selectionNeedsUpdating = false;
00074 
00075   
00076   animationCounter = 0;
00077   timerIdent       = 0;
00078   documentCache    = cache;
00079   scrollView       = sv;
00080 
00081   pixmapRequested = false;
00082 
00083   scrollGuide = -1;
00084 
00085   setMouseTracking(true);
00086   setFocusPolicy(QWidget::ClickFocus);
00087 
00088   connect(&clearStatusBarTimer, SIGNAL(timeout()), this, SLOT(clearStatusBar()));
00089   setBackgroundMode(Qt::NoBackground);
00090 
00091   if (!busyIcon)
00092   {
00093     busyIcon = new QPixmap(KGlobal::iconLoader()->loadIcon("gear", KIcon::NoGroup, KIcon::SizeMedium));
00094 
00095     URShadow = new QPixmap();
00096     BRShadow = new QPixmap();
00097     BLShadow = new QPixmap();
00098 
00099     URShadow->resize(4,4);
00100     BRShadow->resize(4,4);
00101     BLShadow->resize(4,4);
00102   }
00103 }
00104 
00105 
00106 void DocumentWidget::setPageNumber(Q_UINT16 nr)
00107 {
00108   pageNr = nr;
00109 
00110   selectionNeedsUpdating = true;
00111 
00112   
00113   
00114   indexOfUnderlinedLink = -1;
00115 
00116   
00117   QSize _pageSize = documentCache->sizeOfPageInPixel(pageNr);
00118   if (_pageSize != pageSize())
00119   {
00120     setPageSize(_pageSize);
00121   }
00122   update();
00123 }
00124 
00125 QRect DocumentWidget::linkFlashRect()
00126 {
00127   int width = pageSize().width()/(11 - animationCounter);
00128   int height = pageSize().height()/(60 - 4 * animationCounter);
00129   return QRect((pageSize().width()-width)/2, flashOffset - height/2, width, height);
00130 }
00131 
00132 void DocumentWidget::timerEvent( QTimerEvent *e )
00133 {
00134   if (animationCounter == 0) {
00135     killTimer(e->timerId());
00136     timerIdent = startTimer(50); 
00137   }
00138 
00139   animationCounter++;
00140 
00141   QRect flashRect = linkFlashRect();
00142   flashRect.addCoords(-1, -1, 1, 1);
00143 
00144   if (animationCounter >= 10) {
00145     killTimer(e->timerId());
00146     timerIdent       = 0;
00147     animationCounter = 0;
00148   }
00149 
00150   repaint(flashRect, false);
00151 }
00152 
00153 
00154 void DocumentWidget::flash(int fo)
00155 {
00156   if (timerIdent != 0)
00157   {
00158     killTimer(timerIdent);
00159     
00160     animationCounter = 10;
00161     QRect flashRect = linkFlashRect();
00162     flashRect.addCoords(-1, -1, 1, 1);
00163     repaint(flashRect, false);
00164   }
00165   animationCounter = 0;
00166   flashOffset      = fo;
00167   timerIdent       = startTimer(50); 
00168 }
00169 
00170 
00171 void DocumentWidget::paintEvent(QPaintEvent *e)
00172 {
00173 #ifdef DEBUG_DOCUMENTWIDGET
00174   kdDebug(1223) << "DocumentWidget::paintEvent() called" << endl;
00175 #endif
00176 
00177   
00178   
00179   
00180   
00181   
00182   
00183   
00184   
00185   
00186   
00187   if (!isVisible())
00188   {
00189     
00190     kapp->processEvents();
00191     return;
00192   }
00193 
00194   QPainter p(this);
00195   p.setClipRegion(e->region());
00196 
00197   
00198   p.setRasterOp(Qt::CopyROP);
00199   p.setBrush(NoBrush);
00200   p.setPen(Qt::black);
00201   QRect outlineRect = pageRect();
00202   outlineRect.addCoords(-1, -1, 1, 1);
00203   p.drawRect(outlineRect);
00204 
00205   
00206   QColor backgroundColor = colorGroup().mid();
00207 
00208   
00209   if (backgroundColor != backgroundColorForCorners)
00210   {
00211     backgroundColorForCorners = backgroundColor;
00212     QImage tmp(4, 4, 32);
00213     for(int x=0; x<4; x++)
00214       for(int y=0; y<4; y++)
00215         tmp.setPixel(x, y, backgroundColor.light(bottom_right_corner[x+4*y]).rgb() );
00216     BRShadow->convertFromImage(tmp);
00217 
00218     for(int x=0; x<4; x++)
00219       for(int y=0; y<4; y++)
00220         tmp.setPixel(x, y, backgroundColor.light(bottom_left_corner[x+4*y]).rgb() );
00221     BLShadow->convertFromImage(tmp);
00222 
00223     URShadow->convertFromImage(tmp.mirror(true, true));
00224   }
00225 
00226   
00227   for(int i=0; i<4; i++)
00228   {
00229     p.setPen(backgroundColor.light(shadow_strip[i]));
00230     
00231     p.drawLine(pageSize().width()+i+2, 8, pageSize().width()+i+2, pageSize().height()+2);
00232     
00233     p.drawLine(8, pageSize().height()+i+2, pageSize().width()+2, pageSize().height()+i+2);
00234   }
00235   
00236   p.drawPixmap(pageSize().width()+2, pageSize().height()+2, *BRShadow);
00237   p.drawPixmap(4, pageSize().height()+2, *BLShadow);
00238   p.drawPixmap(pageSize().width()+2, 4, *URShadow);
00239 
00240   
00241   p.fillRect(0, pageSize().height()+2, 4, 4, backgroundColor);
00242   p.fillRect(pageSize().width()+2, 0, 4, 4, backgroundColor);
00243 
00244   if (!documentCache->isPageCached(pageNr, pageSize()))
00245   {
00246     QRect destRect = e->rect().intersect(pageRect());
00247     if (KVSPrefs::changeColors() && KVSPrefs::renderMode() == KVSPrefs::EnumRenderMode::Paper)
00248       p.fillRect(destRect, KVSPrefs::paperColor());
00249     else
00250       p.fillRect(destRect, Qt::white);
00251 
00252     
00253     
00254     
00255     
00256     
00257     p.drawPixmap(10, 10, *busyIcon);
00258 
00259     if (!pixmapRequested)
00260     {
00261       
00262       pixmapRequested = true;
00263       QTimer::singleShot(50, this, SLOT(delayedRequestPage()));
00264     }
00265     return;
00266   }
00267 
00268   RenderedDocumentPagePixmap *pageData = documentCache->getPage(pageNr);
00269   if (pageData == 0) {
00270 #ifdef DEBUG_DOCUMENTWIDGET
00271     kdDebug(1223) << "DocumentWidget::paintEvent: no documentPage generated" << endl;
00272 #endif
00273     return;
00274   }
00275 
00276   QMemArray<QRect> damagedRects = e->region().rects();
00277   for (unsigned int i = 0; i < damagedRects.count(); i++)
00278   {
00279     
00280     QRect destRect = damagedRects[i].intersect(pageRect());
00281 
00282     
00283     
00284     QRect pixmapRect = destRect;
00285     pixmapRect.moveBy(-1,-1);
00286 
00287     if (KVSPrefs::changeColors() && KVSPrefs::renderMode() != KVSPrefs::EnumRenderMode::Paper)
00288     {
00289       
00290       bitBlt ( this, destRect.topLeft(), &pageData->accessiblePixmap(), pixmapRect, CopyROP);
00291     }
00292     else
00293     {
00294       
00295       bitBlt ( this, destRect.topLeft(), pageData, pixmapRect, CopyROP);
00296     }
00297   }
00298   
00299   if (KVSPrefs::underlineLinks() == KVSPrefs::EnumUnderlineLinks::Enabled ||
00300       KVSPrefs::underlineLinks() == KVSPrefs::EnumUnderlineLinks::OnlyOnHover)
00301   {
00302     int h = 2; 
00303     for(int i = 0; i < (int)pageData->hyperLinkList.size(); i++) 
00304     {
00305       if (KVSPrefs::underlineLinks() == KVSPrefs::EnumUnderlineLinks::OnlyOnHover && 
00306           i != indexOfUnderlinedLink)
00307         continue;
00308       int x = pageData->hyperLinkList[i].box.left();
00309       int w = pageData->hyperLinkList[i].box.width();
00310       int y = pageData->hyperLinkList[i].baseline;
00311 
00312       QRect hyperLinkRect(x, y, w, h);
00313       if (hyperLinkRect.intersects(e->rect()))
00314       {
00315 #ifdef DEBUG_DOCUMENTWIDGET
00316         kdDebug(1223) << "Underline hyperlink \"" << pageData->hyperLinkList[i].linkText << "\"" << endl;
00317 #endif
00318         p.fillRect(x, y, w, h, KGlobalSettings::linkColor());
00319       }
00320     }
00321   }
00322 
00323   
00324   if (animationCounter > 0 && animationCounter < 10)
00325   {
00326     int gbChannel = 255 - (9-animationCounter)*28;
00327     p.setPen(QPen(QColor(255, gbChannel, gbChannel), 3));
00328     p.drawRect(linkFlashRect());
00329   }
00330 
00331   
00332   TextSelection selection = documentCache->selectedText();
00333   if ((selection.getPageNumber() != 0) && (selection.getPageNumber() == pageNr))
00334   {
00335     if (selectionNeedsUpdating)
00336     {
00337       
00338       
00339       selectedRegion = pageData->selectedRegion(selection);
00340       selectionNeedsUpdating = false;
00341     }
00342 
00343     p.setPen(NoPen);
00344     p.setBrush(white);
00345     p.setRasterOp(Qt::XorROP);
00346 
00347     QMemArray<QRect> selectionRects = selectedRegion.rects();
00348 
00349     for (unsigned int i = 0; i < selectionRects.count(); i++)
00350       p.drawRect(selectionRects[i]);
00351   }
00352 
00353   
00354   if (scrollGuide >= 0)
00355   {
00356     
00357     p.setClipRegion(e->region().intersect(pageRect()));
00358     p.setRasterOp(Qt::CopyROP);
00359     p.setPen(Qt::red);
00360     p.drawLine(1, scrollGuide, pageSize().width(), scrollGuide);
00361   }
00362 }
00363 
00364 void DocumentWidget::drawScrollGuide(int ycoord)
00365 {
00366   
00367   scrollGuide = ycoord;
00368   update(QRect(1, scrollGuide, pageSize().width(), 1));
00369   QTimer::singleShot(1000, this, SLOT(clearScrollGuide()));
00370 }
00371 
00372 void DocumentWidget::clearScrollGuide()
00373 {
00374   
00375   int temp = scrollGuide;
00376   scrollGuide = -1;
00377   update(QRect(1, temp, pageSize().width(), 1));
00378 }
00379 
00380 void DocumentWidget::select(const TextSelection& newSelection)
00381 {
00382   
00383   RenderedDocumentPage *pageData = documentCache->getPage(pageNr);
00384   if (pageData == 0) {
00385     kdDebug(1223) << "DocumentWidget::select() pageData for page #" << pageNr << " is empty" << endl;
00386     return;
00387   }
00388 
00389   documentCache->selectText(newSelection);
00390 
00391   selectedRegion = pageData->selectedRegion(documentCache->selectedText());
00392   selectionNeedsUpdating = false;
00393 
00394   update();
00395 }
00396 
00397 void DocumentWidget::selectAll()
00398 {
00399   
00400   
00401   if (pageNr == 0)
00402     return;
00403 
00404   
00405   RenderedDocumentPage *pageData = documentCache->getPage(pageNr);
00406   if (pageData == 0) {
00407     kdDebug(1223) << "DocumentWidget::selectAll() pageData for page #" << pageNr << " is empty" << endl;
00408     return;
00409   }
00410 
00411   TextSelection selection;
00412   
00413   QString selectedText("");
00414   for(unsigned int i = 0; i < pageData->textBoxList.size(); i++) {
00415     selectedText += pageData->textBoxList[i].text;
00416     selectedText += "\n";
00417   }
00418   selection.set(pageNr, 0, pageData->textBoxList.size()-1, selectedText);
00419 
00420   selectedRegion = pageData->selectedRegion(selection);
00421 
00422   documentCache->selectText(selection);
00423 
00424   
00425   update();
00426 }
00427 
00428 
00429 void DocumentWidget::mousePressEvent ( QMouseEvent * e )
00430 {
00431 #ifdef DEBUG_DOCUMENTWIDGET
00432   kdDebug(1223) << "DocumentWidget::mousePressEvent(...) called" << endl;
00433 #endif
00434   
00435   
00436   
00437   e->ignore();
00438 
00439   
00440   
00441   if (pageNr == 0)
00442     return;
00443 
00444   
00445   RenderedDocumentPage *pageData = documentCache->getPage(pageNr);
00446   if (pageData == 0) {
00447     kdDebug(1223) << "DocumentWidget::selectAll() pageData for page #" << pageNr << " is empty" << endl;
00448     return;
00449   }
00450 
00451   
00452   if (e->button() == LeftButton) {
00453     if (pageData->hyperLinkList.size() > 0)
00454       for(unsigned int i = 0; i < pageData->hyperLinkList.size(); i++) {
00455         if (pageData->hyperLinkList[i].box.contains(e->pos())) {
00456           emit(localLink(pageData->hyperLinkList[i].linkText));
00457           return;
00458         }
00459       }
00460     if (moveTool)
00461       setCursor(Qt::SizeAllCursor);
00462     else
00463       setCursor(Qt::IbeamCursor);
00464   }
00465 
00466   if (e->button() == RightButton || (!moveTool && e->button() == LeftButton))
00467   {
00468     setCursor(Qt::IbeamCursor);
00469     
00470     
00471     if (!(e->state() & ShiftButton))
00472     {
00473       firstSelectedPoint = e->pos();
00474       selectedRectangle = QRect();
00475       selectedRegion = QRegion();
00476       emit clearSelection();
00477     }
00478   }
00479 }
00480 
00481 
00482 void DocumentWidget::mouseReleaseEvent ( QMouseEvent *e )
00483 {
00484   
00485   
00486   e->ignore();
00487 
00488   if (e->button() == RightButton || (!moveTool && e->button() == LeftButton))
00489   {
00490     
00491     if (firstSelectedPoint == e->pos())
00492     {
00493       if (pageNr == 0)
00494         return;
00495 
00496       
00497       RenderedDocumentPage* pageData = documentCache->getPage(pageNr);
00498       if (pageData == 0) 
00499       {
00500         kdDebug(1223) << "DocumentWidget::mouseReleaseEvent() pageData for page #" << pageNr << " is empty" << endl;
00501         return;
00502       }
00503 
00504       TextSelection newTextSelection = pageData->select(firstSelectedPoint);
00505       updateSelection(newTextSelection);
00506     }
00507   }
00508 
00509   
00510   
00511   setStandardCursor();
00512 }
00513 
00514 
00515 void DocumentWidget::mouseMoveEvent ( QMouseEvent * e )
00516 {
00517 #ifdef DEBUG_DOCUMENTWIDGET
00518   kdDebug(1223) << "DocumentWidget::mouseMoveEvent(...) called" << endl;
00519 #endif
00520 
00521 
00522   
00523   
00524   if (pageNr == 0)
00525     return;
00526 
00527   
00528   RenderedDocumentPage *pageData = documentCache->getPage(pageNr);
00529   if (pageData == 0) {
00530     kdDebug(1223) << "DocumentWidget::selectAll() pageData for page #" << pageNr << " is empty" << endl;
00531     return;
00532   }
00533 
00534   
00535   if (e->state() == 0) {
00536     
00537     int lastUnderlinedLink = indexOfUnderlinedLink;
00538     
00539     for(unsigned int i = 0; i < pageData->hyperLinkList.size(); i++) {
00540       if (pageData->hyperLinkList[i].box.contains(e->pos())) {
00541         clearStatusBarTimer.stop();
00542         setCursor(pointingHandCursor);
00543         QString link = pageData->hyperLinkList[i].linkText;
00544         if ( link.startsWith("#") )
00545           link = link.remove(0,1);
00546 
00547         emit setStatusBarText( i18n("Link to %1").arg(link) );
00548 
00549         indexOfUnderlinedLink = i;
00550         if (KVSPrefs::underlineLinks() == KVSPrefs::EnumUnderlineLinks::OnlyOnHover &&
00551             indexOfUnderlinedLink != lastUnderlinedLink)
00552         {
00553           QRect newUnderline = pageData->hyperLinkList[i].box;
00554           
00555           newUnderline.addCoords(0, 0, 0, 2);
00556           
00557           update(newUnderline);
00558 
00559           if (lastUnderlinedLink != -1 && lastUnderlinedLink < pageData->hyperLinkList.size())
00560           {
00561             
00562             QRect oldUnderline = pageData->hyperLinkList[lastUnderlinedLink].box;
00563             oldUnderline.addCoords(0, 0, 0, 2);
00564             update(oldUnderline);
00565           }
00566         }
00567         return;
00568       }
00569     }
00570     
00571     indexOfUnderlinedLink = -1;
00572     if (KVSPrefs::underlineLinks() == KVSPrefs::EnumUnderlineLinks::OnlyOnHover &&
00573         lastUnderlinedLink != -1 && lastUnderlinedLink < pageData->hyperLinkList.size())
00574     {
00575       
00576       QRect oldUnderline = pageData->hyperLinkList[lastUnderlinedLink].box;
00577       
00578       oldUnderline.addCoords(0, 0, 0, 2);
00579       
00580       update(oldUnderline);
00581     }
00582     
00583     
00584     setStandardCursor();
00585   }
00586 
00587   if (!clearStatusBarTimer.isActive())
00588     clearStatusBarTimer.start(200, true); 
00589 
00590   
00591   if ((e->state() & LeftButton) != 0 && moveTool)
00592   {
00593     
00594     
00595     
00596     e->ignore();
00597   }
00598 
00599   
00600   if ((e->state() & RightButton) != 0 || (!moveTool && (e->state() & LeftButton != 0)))
00601   {
00602     if (selectedRectangle.isEmpty()) {
00603       firstSelectedPoint = e->pos();
00604       selectedRectangle.setRect(e->pos().x(),e->pos().y(),1,1);
00605     } else {
00606       int lx = e->pos().x() < firstSelectedPoint.x() ? e->pos().x() : firstSelectedPoint.x();
00607       int rx = e->pos().x() > firstSelectedPoint.x() ? e->pos().x() : firstSelectedPoint.x();
00608       int ty = e->pos().y() < firstSelectedPoint.y() ? e->pos().y() : firstSelectedPoint.y();
00609       int by = e->pos().y() > firstSelectedPoint.y() ? e->pos().y() : firstSelectedPoint.y();
00610       selectedRectangle.setCoords(lx,ty,rx,by);
00611     }
00612 
00613     
00614     
00615     TextSelection newTextSelection = pageData->select(selectedRectangle);
00616 
00617     updateSelection(newTextSelection);
00618   }
00619 }
00620 
00621 void DocumentWidget::updateSelection(const TextSelection& newTextSelection)
00622 {
00623   if (newTextSelection != documentCache->selectedText())
00624   {
00625     if (newTextSelection.isEmpty())
00626     {
00627       
00628       documentCache->deselectText();
00629       selectedRectangle = QRect();
00630       selectedRegion = QRegion();
00631       update();
00632     }
00633     else
00634     {
00635       if (pageNr == 0)
00636         return;
00637 
00638       
00639       RenderedDocumentPage* pageData = documentCache->getPage(pageNr);
00640       if (pageData == 0) 
00641       {
00642         kdDebug(1223) << "DocumentWidget::mouseReleaseEvent() pageData for page #" << pageNr << " is empty" << endl;
00643         return;
00644       }
00645 
00646       documentCache->selectText(newTextSelection);
00647 
00648       QRegion newlySelectedRegion = pageData->selectedRegion(documentCache->selectedText());
00649 
00650       
00651       QRegion updateRegion;
00652       if(!selectedRegion.isEmpty())
00653       {
00654         updateRegion = newlySelectedRegion.eor(selectedRegion);
00655       }
00656       else
00657       {
00658         updateRegion = newlySelectedRegion;
00659       }
00660 
00661       selectedRegion = newlySelectedRegion;
00662 
00663       QMemArray<QRect> rectangles = updateRegion.rects();
00664       for (unsigned int i = 0; i < rectangles.count(); i++)
00665       {
00666         repaint(rectangles[i]);
00667       }
00668     }
00669   }
00670 }
00671 
00672 
00673 void DocumentWidget::clearStatusBar()
00674 {
00675   emit setStatusBarText( QString::null );
00676 }
00677 
00678 
00679 void DocumentWidget::delayedRequestPage()
00680 {
00681   if (!isVisible())
00682   {
00683     
00684     
00685     
00686 
00687     
00688     pixmapRequested = false;
00689     kapp->processEvents();
00690     return;
00691   }
00692 
00693   documentCache->getPage(pageNr);
00694   pixmapRequested = false;
00695   update();
00696 
00697   
00698   
00699   
00700   
00701   kapp->processEvents();
00702 }
00703 
00704 QSize DocumentWidget::pageSize() const
00705 {
00706   
00707   return size() - QSize(6, 6);
00708 }
00709 
00710 QRect DocumentWidget::pageRect() const
00711 {
00712   QRect boundingRect = rect();
00713   
00714   boundingRect.addCoords(1,1,-5,-5);
00715   return boundingRect;
00716 }
00717 
00718 void DocumentWidget::setPageSize(const QSize& pageSize)
00719 {
00720   
00721   
00722   
00723   
00724   selectionNeedsUpdating = true;
00725 
00726   
00727   resize(pageSize + QSize(6,6));
00728 }
00729 
00730 void DocumentWidget::setPageSize(int width, int height)
00731 {
00732   setPageSize(QSize(width, height));
00733 }
00734 
00735 
00736 void DocumentWidget::slotEnableMoveTool(bool enable)
00737 {
00738   moveTool = enable;
00739 
00740   setStandardCursor();
00741 }
00742 
00743 
00744 void DocumentWidget::setStandardCursor()
00745 {
00746   if (moveTool)
00747   {
00748     setCursor(Qt::arrowCursor);
00749   }
00750   else
00751   {
00752     setCursor(Qt::IbeamCursor);
00753   }
00754 }
00755 
00756 
00757 bool DocumentWidget::isVisible()
00758 {
00759   QRect visibleRect(scrollView->contentsX(), scrollView->contentsY(), scrollView->visibleWidth(), scrollView->visibleHeight());
00760   QRect widgetRect(scrollView->childX(this), scrollView->childY(this), width(), height());
00761   return widgetRect.intersects(visibleRect);
00762 }
00763 
00764 #include "documentWidget.moc"