okular/core/textpage.cpp
Piotr Szymanski 34b8e28322 - Page/Link: tooltips for links backported
- Page: rotation does not switch height and width
- Document/Part/Generator:
  1. Add API for attaching stuff to the interface: ActionCollection and the Navigation Panel
     also add possibility to merge an XML .rc file with menu layout. Relevant functions are:

     QString Generator::getXMLFile(), returns a QString with your .rc file name.
     void  Generator::setupGUI (KActionCollection* , QToolbox* ), add your components to the user interface

  2. Supporting backend settings:
     If during startup, backends which provide a configuration ([X-KDE-oKularHasInternalSettings]
     set to true) are found, a menu item: configure backends is created, clicking on it results in
     loading all the generators that have settings, but not those that dont. the Generator::addPages(KConfigDialog *dlg)
     function should be overloaded by a generator and dlg->addPage should be used to add pages.

     If a user opens a file that needs an already loaded generator, the already loaded one is used instead of loading another.

  3. Error/Warning/Notice sending support, to send a notice/error/warning, add a relevant notice/error/warning(QString& txt ,int duration)
     to the generator class, and sending a message to the user is as simple as emitting a signal!

  4. Intercepting of events generated by the PageView is done by Generator::handleEvent(QEvent*), subclass it, do a switch on QEvent::type(), handle your
     event and return true if pageview is to proceed with its handling or false if not.

  5. Support configuring the KPrinter on the generator side, use Generator::canConfigurePrinter(), return true there, and you get a nonconfigured KPrinter in your
     Generator::print()

  6. PixmapRequest handling update:
     a.) Generator::canGeneratePixmap is now Generator::canGeneratePixmap(bool async)
     b.) Document::sendGeneratorRequests is a slot now
     c.) Old way of sending pixmaps (Document::requestPixmaps(QValueList<PixmapRequest*> checking if we can generate pixmap if not, waiting for receiving)
         is replaced with: requestPixmaps only queues the pixmap all checking if w can generate is done in sendGeneratorReqest, the sendGeneratorRequest is
         run in three places:
         1.  in requestPixmaps when we receive a request
         2.  in requestDone if pixmapStack is not empty
         3.  sendGeneratorRequest, apart from removing invalid requests, takes the current request and if generator canGeratePixmap(request->async)
        it removes the pixmap from stack and sends to generator if not, QTimer::singleshots to itself after 20ms, it ends when stack has no valid pixmap request

  7. Added a commented out zoom field to PixmapGenerator, mightcome in handy sometime

- TextPage: add instructions that handle simplyfing the RegularAreaRect, no more double painted borders in selection rectangles, this rocks.


svn path=/trunk/playground/graphics/oKular/kpdf/; revision=445196
2005-08-10 16:14:39 +00:00

247 lines
8.5 KiB
C++

/***************************************************************************
* Copyright (C) 2005 by Piotr Szymanski <niedakh@gmail.com> *
* *
* 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. *
***************************************************************************/
#include "textpage.h"
#include "area.h"
#include <kdebug.h>
KPDFTextPage::~KPDFTextPage()
{
QValueList<KPDFTextEntity*>::Iterator it;
for (it=m_words.begin();it!=m_words.end();++it)
{
delete (*it);
}
}
RegularAreaRect* KPDFTextPage::findText(const QString &query, SearchDir & direct,
const bool &strictCase, const RegularAreaRect *area)
{
SearchDir dir=direct;
// invalid search request
if (query.isEmpty() || (area->isNull() && dir!=FromTop))
return 0;
QValueList<KPDFTextEntity*>::Iterator start;
if (dir == FromTop)
{
start=m_words.begin();
dir=NextRes;
}
else
{
// locate the current search position
QString * str=0;
int j=0, len=0, queryLeft=query.length()-1;
bool haveMatch=false;
QValueList<KPDFTextEntity*>::Iterator it;
for( it=m_words.begin() ; it != m_words.end(); ++it )
{
str= &((*it)->txt);
continue;
len=str->length()-1;
// we have equal (or less then) area of the query left as the lengt of the current
// entity
if (queryLeft<=len)
{
if (((strictCase)
? (*str != query)
: (str->lower() != query.lower() ))
|| (!(area->intersects((*it)->area))))
{
// we not have matched
// this means we dont have a complete match
// we need to get back to query start
// and continue the search from this place
haveMatch=false;
j=0;
queryLeft=query.length()-1;
}
else
{
// we have a match
// move the current position in the query
// to the position after the length of this string
// we matched
// substract the length of the current entity from
// the left length of the query
haveMatch=true;
j+=QMIN(queryLeft,len);
queryLeft-=QMIN(queryLeft,len);
}
}
if (haveMatch && queryLeft==0 && j==query.length()-1)
break;
}
start=it;
}
switch (dir)
{
case PrevRes:
return findTextInternal(query,false, strictCase, start,m_words.begin());
break;
case FromTop:
case NextRes:
return findTextInternal(query,true, strictCase, start,m_words.end());
break;
}
return 0;
}
RegularAreaRect* KPDFTextPage::findTextInternal(const QString &query, bool forward,
bool strictCase, const QValueList<KPDFTextEntity*>::Iterator &start,
const QValueList<KPDFTextEntity*>::Iterator &end)
{
RegularAreaRect* ret=new RegularAreaRect;
// j is the current position in our query
// len is the length of the string in kpdftextentity
// queryLeft is the length of the query we have left
QString str;
int j=0, len=0, queryLeft=query.length();
bool haveMatch=false,wasEol=false;
bool dontIncrement=false;
QValueList<KPDFTextEntity*>::Iterator it;
// we dont support backward search yet
for( it=start ; it != end; (!dontIncrement) ? (++it) : it )
{
// a static cast would be faster?
str=(*it)->txt;
if (query.mid(j,1)==" " && wasEol )
{
// lets match newline as a space
//kdDebug(1223) << "Matched, space after eol " << endl;
j++;
queryLeft--;
// since we dont really need to increment this after this
// run of the loop finishes because we are not comparing it
// to any entity, rather we are deducing a situation in a document
dontIncrement=true;
}
else
{
dontIncrement=false;
int min=QMIN(queryLeft,len);
len=str.length();
//kdDebug(1223) << str.left(min) << " : " << query.mid(j,min) << endl;
// we have equal (or less then) area of the query left as the lengt of the current
// entity
if ((strictCase)
? (str.left(min) != query.mid(j,min))
: (str.left(min).lower() != query.mid(j,min).lower())
)
{
// we not have matched
// this means we dont have a complete match
// we need to get back to query start
// and continue the search from this place
haveMatch=false;
delete ret;
ret=new RegularAreaRect;
// kdDebug(1223) << "\tNotmatched" << endl;
j=0;
queryLeft=query.length();
}
else
{
// we have a match
// move the current position in the query
// to the position after the length of this string
// we matched
// substract the length of the current entity from
// the left length of the query
// kdDebug(1223) << "\tMatched" << endl;
haveMatch=true;
ret->append( (*it)->area );
j+=min;
queryLeft-=min;
}
}
wasEol=(*it)->eol;
/*if (wasEol)
kdDebug(1223) << "\t\tENDOFLINE" << endl;*/
if (haveMatch && queryLeft==0 && j==query.length())
{
// RegularAreaRect::ConstIterator i=ret->begin(), end=ret->end;
int end=ret->count(),i=0,x=0;
QValueList <NormalizedRect*> m_remove;
for (;i<end;i++)
{
if ( i < (end-1) )
{
if ( (*ret)[x]->intersects( (*ret)[i+1] ) )
{
(*ret)[x]->left=(QMIN((*ret)[x]->left,(*ret)[i+1]->left));
(*ret)[x]->top=(QMIN((*ret)[x]->top,(*ret)[i+1]->top));
(*ret)[x]->bottom=(QMAX((*ret)[x]->bottom,(*ret)[i+1]->bottom));
(*ret)[x]->right=(QMAX((*ret)[x]->right,(*ret)[i+1]->right));
m_remove.append( (*ret)[i+1] );
}
else
{
x=i;
}
}
}
while (!m_remove.isEmpty())
{
ret->remove( m_remove.last() );
m_remove.pop_back();
}
return ret;
}
}
return 0;
}
QString * KPDFTextPage::getText(const RegularAreaRect *area)
{
if (area->isNull())
return 0;
QString* ret = new QString;
QValueList<KPDFTextEntity*>::Iterator it,end = m_words.end();
KPDFTextEntity * last=0;
for( it=m_words.begin() ; it != end; ++it )
{
// provide the string FIXME?: newline handling
if (area->intersects((*it)->area))
{
*ret += ((*it)->txt);
if (last)
{
if ( (last -> baseline != (*it)-> baseline )
&& ((*it)->rotation % 2 == 0)
&& ((*it)->rotation == last->rotation)
)
{
// on different baseline, on a vertical rotation (0 or 180)
*ret+='\n';
}
// what if horizontal? how do i determine end of line?
/*if (((*it)->rotation == (*last)->rotation)
&& ((*it)->rotation % 2 == 1)
&& ((*
{
// 90 or 270 degrees rotation
// maybe add a tabulation?
}
}*/
}
last=*it;
}
}
return ret;
}