Commit 872c537f authored by Michel Ludwig's avatar Michel Ludwig
Browse files

Merge branch 'master' into viewerinterface

parents a823df5c 412e3e73
......@@ -37,19 +37,16 @@ class OkularBookmarkAction : public KBookmarkAction
public:
OkularBookmarkAction( const Okular::DocumentViewport& vp, const KBookmark& bk, KBookmarkOwner* owner, QObject *parent )
: KBookmarkAction( bk, owner, parent )
, m_pageNumber( vp.pageNumber + 1 )
{
if ( vp.isValid() )
setText( QString::number( vp.pageNumber + 1 ) + " - " + text() );
setProperty("pageNumber", vp.pageNumber + 1);
}
inline int pageNumber() const
{
return m_pageNumber;
return property("pageNumber").toInt();
}
private:
const int m_pageNumber;
};
inline bool okularBookmarkActionLessThan( QAction * a1, QAction * a2 )
......@@ -225,6 +222,25 @@ KBookmark::List BookmarkManager::bookmarks( const KUrl& url ) const
return ret;
}
KBookmark::List BookmarkManager::bookmarks() const
{
return bookmarks( d->url );
}
KBookmark BookmarkManager::bookmark( int page ) const
{
const KBookmark::List bmarks = bookmarks();
foreach( const KBookmark &bm, bmarks )
{
DocumentViewport vp( bm.url().htmlRef() );
if ( vp.isValid() && vp.pageNumber == page )
{
return bm;
}
}
return KBookmark();
}
void BookmarkManager::save() const
{
d->manager->emitChanged();
......@@ -334,6 +350,18 @@ void BookmarkManager::removeBookmark( int n )
}
}
void BookmarkManager::renameBookmark( KBookmark* bm, const QString& newName)
{
KBookmarkGroup thebg;
QHash<KUrl, QString>::iterator it = d->bookmarkFind( d->url, false, &thebg );
Q_ASSERT ( it != d->knownFiles.end() );
if ( it == d->knownFiles.end() )
return;
bm->setFullText( newName );
d->manager->emitChanged( thebg );
}
int BookmarkManager::removeBookmark( const KUrl& referurl, const KBookmark& bm )
{
if ( !referurl.isValid() || bm.isNull() || bm.isGroup() || bm.isSeparator() )
......
......@@ -40,11 +40,24 @@ class OKULAR_EXPORT BookmarkManager : public QObject
* Returns the list of documents with bookmarks.
*/
KUrl::List files() const;
/**
* Returns the list of bookmarks for the specified @p url.
*/
KBookmark::List bookmarks( const KUrl& url ) const;
/**
* Returns the list of bookmarks for document
* @since 0.14 (KDE 4.8)
*/
KBookmark::List bookmarks() const;
/**
* Returns the bookmark for the given page of the document
* @since 0.14 (KDE 4.8)
*/
KBookmark bookmark( int page ) const;
/**
* Forces to save the list of bookmarks.
*/
......@@ -82,6 +95,12 @@ class OKULAR_EXPORT BookmarkManager : public QObject
*/
void removeBookmarks( const KUrl& referurl, const KBookmark::List& list );
/**
* Returns the bookmark given bookmark of the document
* @since 0.14 (KDE 4.8)
*/
void renameBookmark( KBookmark* bm, const QString& newName );
/**
* Returns whether the given @p page is bookmarked.
*/
......
......@@ -300,6 +300,33 @@ QString Page::text( const RegularAreaRect * area, TextPage::TextAreaInclusionBeh
return ret;
}
TextEntity::List Page::words( const RegularAreaRect * area, TextPage::TextAreaInclusionBehaviour b ) const
{
TextEntity::List ret;
if ( !d->m_text )
return ret;
if ( area )
{
RegularAreaRect rotatedArea = *area;
rotatedArea.transform( d->rotationMatrix().inverted() );
ret = d->m_text->words( &rotatedArea, b );
}
else
ret = d->m_text->words( 0, b );
for (int i = 0; i < ret.length(); ++i)
{
const TextEntity * orig = ret[i];
ret[i] = new TextEntity( orig->text(), new Okular::NormalizedRect(orig->transformedArea ( d->rotationMatrix() )) );
delete orig;
}
return ret;
}
void PagePrivate::rotateAt( Rotation orientation )
{
if ( orientation == m_rotation )
......
......@@ -196,6 +196,15 @@ class OKULAR_EXPORT Page
*/
QString text( const RegularAreaRect * rect, TextPage::TextAreaInclusionBehaviour b ) const;
/**
* Returns the page text (or part of it) including the bounding
* rectangles. Note that ownership of the contents of the returned
* list belongs to the caller.
* @see TextPage::words()
* @since 0.14 (KDE 4.8)
*/
TextEntity::List words( const RegularAreaRect * rect, TextPage::TextAreaInclusionBehaviour b ) const;
/**
* Returns the rectangular area of the given @p selection.
*/
......
......@@ -1960,3 +1960,40 @@ void TextPagePrivate::correctTextOrder()
*/
breakWordIntoCharacters(word_chars_map);
}
TextEntity::List TextPage::words(const RegularAreaRect *area, TextAreaInclusionBehaviour b) const
{
if ( area && area->isNull() )
return TextEntity::List();
TextEntity::List ret;
if ( area )
{
foreach (TinyTextEntity *te, d->m_words)
{
if (b == AnyPixelTextAreaInclusionBehaviour)
{
if ( area->intersects( te->area ) )
{
ret.append( new TextEntity( te->text(), new Okular::NormalizedRect( te->area) ) );
}
}
else
{
const NormalizedPoint center = te->area.center();
if ( area->contains( center.x, center.y ) )
{
ret.append( new TextEntity( te->text(), new Okular::NormalizedRect( te->area) ) );
}
}
}
}
else
{
foreach (TinyTextEntity *te, d->m_words)
{
ret.append( new TextEntity( te->text(), new Okular::NormalizedRect( te->area) ) );
}
}
return ret;
}
......@@ -162,6 +162,15 @@ class OKULAR_EXPORT TextPage
*/
QString text( const RegularAreaRect * rect, TextAreaInclusionBehaviour b ) const;
/**
* Text entity extraction function. Similar to text() but returns
* the words including their bounding rectangles. Note that
* ownership of the contents of the returned list belongs to the
* caller.
* @since 0.14 (KDE 4.8)
*/
TextEntity::List words( const RegularAreaRect * rect, TextAreaInclusionBehaviour b ) const;
/**
* Returns the rectangular area of the given @p selection.
*/
......
......@@ -41,6 +41,7 @@
#include <kstandardaction.h>
#include <kpluginfactory.h>
#include <kfiledialog.h>
#include <kinputdialog.h>
#include <kmessagebox.h>
#include <knuminput.h>
#include <kio/netaccess.h>
......@@ -62,6 +63,7 @@
#endif
#include <kdeprintdialog.h>
#include <kprintpreview.h>
#include <kbookmarkmenu.h>
// local includes
#include "aboutdata.h"
......@@ -556,6 +558,12 @@ void Part::setupViewerActions()
m_addBookmarkText = m_addBookmark->text();
m_addBookmarkIcon = m_addBookmark->icon();
m_renameBookmark = ac->addAction("rename_bookmark");
m_renameBookmark->setText(i18n( "Rename Bookmark" ));
m_renameBookmark->setIcon(KIcon( "edit-rename" ));
m_renameBookmark->setWhatsThis( i18n( "Rename the current page bookmark" ) );
connect( m_renameBookmark, SIGNAL(triggered()), this, SLOT(slotRenameCurrentPageBookmark()) );
m_prevBookmark = ac->addAction("previous_bookmark");
m_prevBookmark->setText(i18n( "Previous Bookmark" ));
m_prevBookmark->setIcon(KIcon( "go-up-search" ));
......@@ -1562,11 +1570,13 @@ void Part::updateBookmarksActions()
{
m_addBookmark->setText( i18n( "Remove Bookmark" ) );
m_addBookmark->setIcon( KIcon( "edit-delete-bookmark" ) );
m_renameBookmark->setEnabled( true );
}
else
{
m_addBookmark->setText( m_addBookmarkText );
m_addBookmark->setIcon( m_addBookmarkIcon );
m_renameBookmark->setEnabled( false );
}
}
else
......@@ -1574,6 +1584,7 @@ void Part::updateBookmarksActions()
m_addBookmark->setEnabled( false );
m_addBookmark->setText( m_addBookmarkText );
m_addBookmark->setIcon( m_addBookmarkIcon );
m_renameBookmark->setEnabled( false );
}
}
......@@ -1716,6 +1727,54 @@ void Part::slotAddBookmark()
}
}
void Part::slotRenameBookmark( int page )
{
Q_ASSERT(m_document->bookmarkManager()->isBookmarked( page ));
if ( m_document->bookmarkManager()->isBookmarked( page ) )
{
KBookmark bookmark = m_document->bookmarkManager()->bookmark( page );
const QString newName = KInputDialog::getText( i18n( "Rename Bookmark" ), i18n( "Enter the new name of the bookmark:" ), bookmark.fullText(), 0, widget());
if (!newName.isEmpty())
{
m_document->bookmarkManager()->renameBookmark(&bookmark, newName);
}
}
}
void Part::slotRenameBookmarkFromMenu()
{
QAction *action = dynamic_cast<QAction *>(sender());
Q_ASSERT( action );
if ( action )
{
slotRenameBookmark( action->data().toInt() );
}
}
void Part::slotRenameCurrentPageBookmark()
{
slotRenameBookmark( m_document->currentPage() );
}
void Part::slotAboutToShowContextMenu(KMenu * /*menu*/, QAction *action, QMenu *contextMenu)
{
const QList<QAction*> actions = contextMenu->findChildren<QAction*>("OkularPrivateRenameBookmarkActions");
foreach(QAction *a, actions)
{
contextMenu->removeAction(a);
delete a;
}
KBookmarkAction *ba = dynamic_cast<KBookmarkAction*>(action);
if (ba != NULL)
{
QAction *separatorAction = contextMenu->addSeparator();
separatorAction->setObjectName("OkularPrivateRenameBookmarkActions");
QAction *renameAction = contextMenu->addAction( KIcon( "edit-rename" ), i18n( "Rename this Bookmark" ), this, SLOT(slotRenameBookmarkFromMenu()) );
renameAction->setData(ba->property("pageNumber").toInt() - 1); // These page numbers are indexed starting on 1
renameAction->setObjectName("OkularPrivateRenameBookmarkActions");
}
}
void Part::slotPreviousBookmark()
{
......@@ -2005,7 +2064,7 @@ void Part::slotShowMenu(const Okular::Page *page, const QPoint &point)
if (factory())
{
QList<KXMLGUIClient*> clients(factory()->clients());
const QList<KXMLGUIClient*> clients(factory()->clients());
for(int i = 0 ; (!m_showMenuBarAction || !m_showFullScreenAction) && i < clients.size(); ++i)
{
ac = clients.at(i)->actionCollection();
......@@ -2461,6 +2520,23 @@ void Part::rebuildBookmarkMenu( bool unplugActions )
m_bookmarkActions.append( a );
}
plugActionList( "bookmarks_currentdocument", m_bookmarkActions );
if (factory())
{
const QList<KXMLGUIClient*> clients(factory()->clients());
bool containerFound = false;
for (int i = 0; !containerFound && i < clients.size(); ++i)
{
QWidget *container = factory()->container("bookmarks", clients[i]);
if (container && container->actions().contains(m_bookmarkActions.first()))
{
Q_ASSERT(dynamic_cast<KMenu*>(container));
disconnect(container, 0, this, 0);
connect(container, SIGNAL(aboutToShowContextMenu(KMenu*,QAction*,QMenu*)), this, SLOT(slotAboutToShowContextMenu(KMenu*,QAction*,QMenu*)));
containerFound = true;
}
}
}
m_prevBookmark->setEnabled( havebookmarks );
m_nextBookmark->setEnabled( havebookmarks );
......
......@@ -42,6 +42,7 @@ class KSelectAction;
class KAboutData;
class KTemporaryFile;
class KAction;
class KMenu;
namespace KParts { class GUIActivateEvent; }
class FindBar;
......@@ -146,6 +147,9 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi
void slotHistoryBack();
void slotHistoryNext();
void slotAddBookmark();
void slotRenameBookmarkFromMenu();
void slotRenameCurrentPageBookmark();
void slotAboutToShowContextMenu(KMenu *menu, QAction *action, QMenu *contextMenu);
void slotPreviousBookmark();
void slotNextBookmark();
void slotFindNext();
......@@ -200,6 +204,7 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi
void rebuildBookmarkMenu( bool unplugActions = true );
void updateAboutBackendAction();
void unsetDummyMode();
void slotRenameBookmark( int page );
KTemporaryFile *m_tempfile;
......@@ -246,6 +251,7 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi
KAction *m_historyBack;
KAction *m_historyNext;
KAction *m_addBookmark;
KAction *m_renameBookmark;
KAction *m_prevBookmark;
KAction *m_nextBookmark;
KAction *m_copy;
......
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui name="okular_part" version="31">
<kpartgui name="okular_part" version="32">
<MenuBar>
<Menu name="file"><text>&amp;File</text>
<Action name="get_new_stuff" group="file_open"/>
......@@ -58,6 +58,7 @@
</Menu>
<Menu name="bookmarks"><text>&amp;Bookmarks</text>
<Action name="bookmark_add"/>
<Action name="rename_bookmark"/>
<Action name="previous_bookmark"/>
<Action name="next_bookmark" />
<Separator/>
......
......@@ -68,6 +68,9 @@ LatexRenderer::Error LatexRenderer::renderLatexInHtml( QString& html, const QCol
if (formul.isEmpty() || !securityCheck(formul))
continue;
//unescape formula
formul.replace("&gt;",">").replace("&lt;","<").replace("&amp;","&").replace("&quot;","\"").replace("&apos;","\'").replace("<br>"," ");
QString fileName;
Error returnCode = handleLatex(fileName, formul, textColor, fontSize, resolution, latexOutput);
if (returnCode != NoError)
......@@ -89,7 +92,7 @@ LatexRenderer::Error LatexRenderer::renderLatexInHtml( QString& html, const QCol
imagePxWidth = theImage.width();
imagePxHeight = theImage.height();
QString escapedLATEX=Qt::escape(it.key()).replace('\"',"&quot;"); //we need the escape quotes because that string will be in a title="" argument, but not the \n
html.replace(Qt::escape(it.key()), " <img width=\"" + QString::number(imagePxWidth) + "\" height=\"" + QString::number(imagePxHeight) + "\" align=\"middle\" src=\"" + (*it) + "\" alt=\"" + escapedLATEX +"\" title=\"" + escapedLATEX +"\" /> ");
html.replace(it.key(), " <img width=\"" + QString::number(imagePxWidth) + "\" height=\"" + QString::number(imagePxHeight) + "\" align=\"middle\" src=\"" + (*it) + "\" alt=\"" + escapedLATEX +"\" title=\"" + escapedLATEX +"\" /> ");
}
return NoError;
}
......
......@@ -138,6 +138,7 @@ public:
QList<double> tableSelectionCols;
QList<double> tableSelectionRows;
QList<TableSelectionPart> tableSelectionParts;
bool tableDividersGuessed;
// viewport move
bool viewportMoveActive;
......@@ -272,6 +273,7 @@ PageView::PageView( QWidget *parent, Okular::Document *document )
d->mouseTextSelecting = false;
d->mouseOnRect = false;
d->mouseAnn = 0;
d->tableDividersGuessed = false;
d->viewportMoveActive = false;
d->viewportMoveTimer = 0;
d->scrollIncrement = 0;
......@@ -1425,6 +1427,11 @@ void PageView::drawTableDividers(QPainter * screenPainter)
{
if (!d->tableSelectionParts.isEmpty()) {
screenPainter->setPen( d->mouseSelectionColor.dark() );
if (d->tableDividersGuessed) {
QPen p = screenPainter->pen();
p.setStyle( Qt::DashLine );
screenPainter->setPen( p );
}
foreach (const TableSelectionPart &tsp, d->tableSelectionParts) {
QRect selectionPartRect = tsp.rectInItem.geometry(tsp.item->uncroppedWidth(), tsp.item->uncroppedHeight());
selectionPartRect.translate( tsp.item->uncroppedGeometry().topLeft () );
......@@ -1558,7 +1565,7 @@ void PageView::keyPressEvent( QKeyEvent * e )
horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd );
break;
case Qt::Key_Escape:
selectionClear();
selectionClear( d->tableDividersGuessed ? ClearOnlyDividers : ClearAllSelection );
d->mousePressPos = QPoint();
if ( d->aPrevAction )
{
......@@ -1966,6 +1973,9 @@ void PageView::mousePressEvent( QMouseEvent * e )
if (!selectionPartRect.contains(eventPos))
continue;
// At this point it's clear we're either adding or removing a divider manually, so obviously the user is happy with the guess (if any).
d->tableDividersGuessed = false;
// There's probably a neat trick to finding which edge it's closest to,
// but this way has the advantage of simplicity.
const int fromLeft = abs(selectionPartRect.left() - eventPos.x());
......@@ -2469,6 +2479,7 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
d->mouseSelectionRect.setCoords( 0, 0, 0, 0 );
d->tableSelectionCols.clear();
d->tableSelectionRows.clear();
guessTableDividers();
viewport()->update( updatedRect );
}
......@@ -2606,6 +2617,114 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
d->mousePressPos = QPoint();
}
void PageView::guessTableDividers()
{
QList< QPair<double, int> > colTicks, rowTicks, colSelectionTicks, rowSelectionTicks;
foreach ( const TableSelectionPart& tsp, d->tableSelectionParts )
{
// add ticks for the edges of this area...
colSelectionTicks.append( qMakePair( tsp.rectInSelection.left, +1 ) );
colSelectionTicks.append( qMakePair( tsp.rectInSelection.right, -1 ) );
rowSelectionTicks.append( qMakePair( tsp.rectInSelection.top, +1 ) );
rowSelectionTicks.append( qMakePair( tsp.rectInSelection.bottom, -1 ) );
// get the words in this part
Okular::RegularAreaRect rects;
rects.append( tsp.rectInItem );
const Okular::TextEntity::List words = tsp.item->page()->words( &rects, Okular::TextPage::CentralPixelTextAreaInclusionBehaviour );
foreach (Okular::TextEntity *te, words)
{
if (te->text().isEmpty()) {
delete te;
continue;
}
Okular::NormalizedRect wordArea = *te->area();
// convert it from item coordinates to part coordinates
wordArea.left -= tsp.rectInItem.left;
wordArea.left /= (tsp.rectInItem.right - tsp.rectInItem.left);
wordArea.right -= tsp.rectInItem.left;
wordArea.right /= (tsp.rectInItem.right - tsp.rectInItem.left);
wordArea.top -= tsp.rectInItem.top;
wordArea.top /= (tsp.rectInItem.bottom - tsp.rectInItem.top);
wordArea.bottom -= tsp.rectInItem.top;
wordArea.bottom /= (tsp.rectInItem.bottom - tsp.rectInItem.top);
// convert from part coordinates to table coordinates
wordArea.left *= (tsp.rectInSelection.right - tsp.rectInSelection.left);
wordArea.left += tsp.rectInSelection.left;
wordArea.right *= (tsp.rectInSelection.right - tsp.rectInSelection.left);
wordArea.right += tsp.rectInSelection.left;
wordArea.top *= (tsp.rectInSelection.bottom - tsp.rectInSelection.top);
wordArea.top += tsp.rectInSelection.top;
wordArea.bottom *= (tsp.rectInSelection.bottom - tsp.rectInSelection.top);
wordArea.bottom += tsp.rectInSelection.top;
// add to the ticks arrays...
colTicks.append( qMakePair( wordArea.left, +1) );
colTicks.append( qMakePair( wordArea.right, -1) );
rowTicks.append( qMakePair( wordArea.top, +1) );
rowTicks.append( qMakePair( wordArea.bottom, -1) );
delete te;
}
}
int tally = 0;
qSort( colSelectionTicks );
qSort( rowSelectionTicks );
for (int i = 0; i < colSelectionTicks.length(); ++i)
{
tally += colSelectionTicks[i].second;
if ( tally == 0 && i + 1 < colSelectionTicks.length() && colSelectionTicks[i+1].first != colSelectionTicks[i].first)
{
colTicks.append( qMakePair( colSelectionTicks[i].first, +1 ) );
colTicks.append( qMakePair( colSelectionTicks[i+1].first, -1 ) );
}
}
Q_ASSERT( tally == 0 );
for (int i = 0; i < rowSelectionTicks.length(); ++i)
{
tally += rowSelectionTicks[i].second;
if ( tally == 0 && i + 1 < rowSelectionTicks.length() && rowSelectionTicks[i+1].first != rowSelectionTicks[i].first) {
rowTicks.append( qMakePair( rowSelectionTicks[i].first, +1 ) );
rowTicks.append( qMakePair( rowSelectionTicks[i+1].first, -1 ) );
}
}
Q_ASSERT( tally == 0 );
qSort( colTicks );
qSort( rowTicks );
for (int i = 0; i < colTicks.length(); ++i)
{
tally += colTicks[i].second;
if ( tally == 0 && i + 1 < colTicks.length() && colTicks[i+1].first != colTicks[i].first)
{
d->tableSelectionCols.append( (colTicks[i].first+colTicks[i+1].first) / 2 );
d->tableDividersGuessed = true;
}
}
Q_ASSERT( tally == 0 );
for (int i = 0; i < rowTicks.length(); ++i)
{
tally += rowTicks[i].second;
if ( tally == 0 && i + 1 < rowTicks.length() && rowTicks[i+1].first != rowTicks[i].first)
{
d->tableSelectionRows.append( (rowTicks[i].first+rowTicks[i+1].first) / 2 );
d->tableDividersGuessed = true;
}
}
Q_ASSERT( tally == 0 );
}
void PageView::mouseDoubleClickEvent( QMouseEvent * e )
{
if ( e->button() == Qt::LeftButton )
......@@ -3135,19 +3254,23 @@ Okular::RegularAreaRect * PageView::textSelectionForItem( PageViewItem * item, c
return selectionArea;
}
void PageView::selectionClear()
void PageView::selectionClear(const ClearMode mode)
{
QRect updatedRect = d->mouseSelectionRect.normalized().adjusted( 0, 0, 1, 1 );
d->mouseSelecting = false;
d->mouseSelectionRect.setCoords( 0, 0, 0, 0 );
d->tableSelectionCols.clear();
d->tableSelectionRows.clear();
d->tableDividersGuessed = false;
foreach (const TableSelectionPart &tsp, d->tableSelectionParts) {
QRect selectionPartRect = tsp.rectInItem.geometry(tsp.item->uncroppedWidth(), tsp.item->uncroppedHeight());
selectionPartRect.translate( tsp.item->uncroppedGeometry().topLeft () );
// should check whether this is on-screen here?
updatedRect = updatedRect.united(selectionPartRect);
}
if ( mode != ClearOnlyDividers ) {
d->tableSelectionParts.clear();
}
d->tableSelectionParts.clear();
updatedRect.translate( -contentAreaPosition() );
viewport()->update( updatedRect );
......
......@@ -60,6 +60,8 @@ Q_OBJECT
ZoomIn, ZoomOut, ZoomRefreshCurrent };