kword

KWFrameView.cpp

00001 /* This file is part of the KOffice project
00002  * Copyright (C) 2005 Thomas Zander <zander@kde.org>
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Library General Public
00006  * License as published by the Free Software Foundation; version 2.
00007 
00008  * This library is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  * Library General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU Library General Public License
00014  * along with this library; see the file COPYING.LIB.  If not, write to
00015  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016  * Boston, MA 02110-1301, USA.
00017  */
00018 #include "KWFrameView.h"
00019 #include "KWFrameViewManager.h"
00020 #include "KWFrame.h"
00021 #include "KWFrameSet.h"
00022 #include "KWTextFrameSet.h"
00023 #include "KWTableFrameSet.h"
00024 #include "KWPartFrameSet.h"
00025 #include "KWVariable.h"
00026 #include "KWPictureFrameSet.h"
00027 #include "KWViewMode.h"
00028 #include "KWDocument.h"
00029 #include "KWCanvas.h"
00030 
00031 #include "KoZoomHandler.h"
00032 
00033 #include <klocale.h>
00034 #include <kactionclasses.h>
00035 
00036 #include <qapplication.h>
00037 #include <qpopupmenu.h>
00038 
00039 static const double HORIZONTAL_SNAP = 6; // horizontal snap zone (in pt)
00040 static const double VERTICAL_SNAP = 6; // vertical snap zone (in pt)
00041 
00042 KWFrameView::KWFrameView(KWFrameViewManager *parent, KWFrame *frame) {
00043     m_manager = parent;
00044     Q_ASSERT(frame);
00045     Q_ASSERT(frame->frameSet());
00046     m_frame = frame;
00047     m_selected = false;
00048     if(frame->frameSet()->groupmanager() || dynamic_cast<KWTableFrameSet *>(frame->frameSet()) != 0)
00049         m_policy = new TableFramePolicy(this);
00050     else if(dynamic_cast<KWTextFrameSet *>(frame->frameSet()) != 0)
00051         m_policy = new TextFramePolicy(this);
00052     else if(dynamic_cast<KWPartFrameSet *>(frame->frameSet()) != 0)
00053         m_policy = new PartFramePolicy(this);
00054     else if(dynamic_cast<KWPictureFrameSet *>(frame->frameSet()) != 0)
00055         m_policy = new ImageFramePolicy(this);
00056     else {
00057         m_policy = new TextFramePolicy(this);
00058         kdWarning() << "Unknown frameset supplied!" << endl;
00059     }
00060 }
00061 
00062 KWFrameView::~KWFrameView() {
00063     delete m_policy;
00064 }
00065 
00066 bool KWFrameView::isBorderHit(const KoPoint &point) const {
00067     return hit(point, true, true);
00068 }
00069 
00070 bool KWFrameView::contains(const KoPoint &point, bool fuzzy) const {
00071     return hit(point, fuzzy, false);
00072 }
00073 
00074 bool KWFrameView::hit(const KoPoint &point, bool fuzzy , bool borderOnly) const {
00075     //kdDebug() << "hit " << point << " " << fuzzy << ", " << borderOnly << endl;
00076     double hs = 0, vs =0;
00077     if(fuzzy) {
00078         hs = HORIZONTAL_SNAP;
00079         if(frame()->width() < HORIZONTAL_SNAP * 3)
00080             hs = frame()->width() / 3;
00081         vs = VERTICAL_SNAP;
00082         if(frame()->height() < VERTICAL_SNAP * 3)
00083             vs = frame()->height() / 3;
00084     }
00085     if(point.x() < frame()->x() - hs)
00086         return false;
00087     if(point.x() > frame()->right() + hs)
00088         return false;
00089     if(point.y() < frame()->y() - vs)
00090         return false;
00091     if(point.y() > frame()->bottom() + vs)
00092         return false;
00093 
00094     if(borderOnly) { // also exclude inner part.
00095         if(point.x() > frame()->x() + hs && point.x() < frame()->right() - hs &&
00096                 point.y() > frame()->y() + vs && point.y() < frame()->bottom() - vs )
00097             return false;
00098     }
00099     return true;
00100 }
00101 
00102 MouseMeaning KWFrameView::mouseMeaning( const KoPoint &point, int keyState ) {
00103     if(isBorderHit(point)) {
00104         MouseMeaning mm = m_policy->mouseMeaningOnBorder(point, keyState);
00105         if(mm != MEANING_NONE && ( frame()->frameSet()->isProtectSize() ||
00106                 frame()->frameSet()->isMainFrameset() || frame()->frameSet()->isAHeader() ||
00107                 frame()->frameSet()->isAFooter() ))
00108             mm = MEANING_FORBIDDEN;
00109         return mm;
00110     }
00111     if(hit(point, false, false))
00112         return m_policy->mouseMeaning(point, keyState);
00113     return MEANING_NONE;
00114 }
00115 
00116 void KWFrameView::setSelected(bool selected, MouseMeaning selectPolicy) {
00117     m_policy->setSelected(selectPolicy);
00118     if( m_selected == selected ) return;
00119     m_manager->slotFrameSelectionChanged();
00120     m_selected = selected;
00121 }
00122 
00123 void KWFrameView::showPopup( const KoPoint &point, KWView *view, const QPoint &popupPoint) const {
00124     view->unplugActionList( "tableactions" );
00125     view->unplugActionList( "frameset_type_action" );
00126     QPopupMenu *popup = m_policy->createPopup(point, view);
00127     Q_ASSERT(popup);
00128     popup->popup(popupPoint);
00129 }
00130 
00131 void KWFrameView::paintFrameAttributes(QPainter *painter, const QRect &crect, KWViewMode *vm, KoZoomHandler *zh) {
00132     if( !m_selected )
00133         return;
00134 
00135     class ResizeHandle {
00136       private:
00137         const int GRIP_SIZE;
00138         bool readOnly;
00139       public:
00140         ResizeHandle(KWFrameView *fv) : GRIP_SIZE(6) {
00141             KWFrameSet *fs = fv->frame()->frameSet();
00142             readOnly = fs->isProtectSize() || fs->isMainFrameset() ||
00143                 fs->isAHeader() || fs->isAFooter() || fs->isFloating();
00144         }
00145         void paint(QPainter *p, int x, int y) {
00146             p->setPen( QPen( Qt::black, 1, QPen::SolidLine ) );
00147             p->setBrush( QApplication::palette().color( QPalette::Active, QColorGroup::Highlight ) );
00148             p->drawRect( x, y, GRIP_SIZE, GRIP_SIZE );
00149             if( readOnly ) { //protected frame
00150                 QBrush brush = QApplication::palette().color( QPalette::Active,QColorGroup::Base );
00151                 p->fillRect( x+1, y+1, GRIP_SIZE-2, GRIP_SIZE-2, brush );
00152             }
00153         }
00154     };
00155 
00156     const QRect frameRect = vm->normalToView( zh->zoomRect( *frame() ) );
00157     if ( crect.intersects( frameRect ) )
00158     {
00159         ResizeHandle handle(this);
00160 
00161         const int width = frameRect.width();
00162         const int height = frameRect.height();
00163         for(int y=0; y < 3; y++) {
00164             int offsetY = -1 + y + frameRect.y();
00165             if(y > 0)  offsetY += (height - 6) / (y == 1 ? 2:1);
00166             for(int x=0; x < 3; x++) {
00167                 if(x == 1 && y == 1) continue; // don't draw in the center of the frame.
00168                 int offsetX = -1 + x + frameRect.x();
00169                 if(x > 0)  offsetX += (width - 6) / (x == 1 ? 2:1);
00170                 handle.paint(painter, offsetX, offsetY);
00171             }
00172         }
00173     }
00174 }
00175 
00176 // *********** Policies *********
00177 FramePolicy::FramePolicy(KWFrameView *view) {
00178     m_separator = new KActionSeparator();
00179     m_view = view;
00180 }
00181 void FramePolicy::addFloatingAction(KWView *view, QPtrList<KAction> &actionList) {
00182     if(m_view->frame()->frameSet()->isMainFrameset())
00183         return;
00184     actionList.append(m_separator);
00185     KToggleAction *action = dynamic_cast<KToggleAction*> (view->actionCollection()->
00186             action("inline_frame"));
00187     Q_ASSERT(action);
00188     KWFrameSet *parentFs = m_view->frame()->frameSet()->groupmanager() ?
00189         m_view->frame()->frameSet()->groupmanager() : m_view->frame()->frameSet();
00190     action->setChecked(parentFs->isFloating());
00191     actionList.append(action);
00192 }
00193 MouseMeaning FramePolicy::mouseMeaningOnBorder( const KoPoint &point, int keyState ) {
00194     Q_UNUSED(keyState);
00195     double hs = HORIZONTAL_SNAP;
00196     KWFrame *frame = m_view->frame();
00197     if(frame->width() < HORIZONTAL_SNAP * 3)
00198         hs = frame->width() / 3;
00199     double vs = VERTICAL_SNAP;
00200     if(frame->height() < VERTICAL_SNAP * 3)
00201         vs = frame->height() / 3;
00202 
00203     // Corners
00204     if( point.x() <= frame->x() + hs ) { // left edge
00205         if(point.y() <= frame->y() + vs)
00206             return MEANING_TOPLEFT;
00207         if(point.y() >= frame->bottom() - vs)
00208             return MEANING_BOTTOMLEFT;
00209         if( QABS(frame->y() + frame->height() / 2 - point.y()) <= vs )
00210             return MEANING_LEFT;
00211         return MEANING_MOUSE_MOVE;
00212     }
00213     if( point.x() >= frame->right() - hs) { // right edge
00214         if(point.y() <= frame->y() + vs)
00215             return MEANING_TOPRIGHT;
00216         if(point.y() >= frame->bottom() - vs)
00217             return MEANING_BOTTOMRIGHT;
00218         if( QABS(frame->y() + frame->height() / 2 - point.y()) <= vs )
00219             return MEANING_RIGHT;
00220         return MEANING_MOUSE_MOVE;
00221     }
00222 
00223     if( frame->y() + vs >= point.y() ) { // top edge
00224         if( QABS(frame->x() + frame->width() / 2 - point.x() ) <= hs )
00225             return MEANING_TOP;
00226         return MEANING_MOUSE_MOVE;
00227     }
00228     if( frame->bottom() - vs <= point.y() ) { // bottom edge
00229         if( QABS(frame->x() + frame->width() / 2 - point.x() ) <= hs )
00230             return MEANING_BOTTOM;
00231         return MEANING_MOUSE_MOVE;
00232     }
00233     return MEANING_NONE;
00234 }
00235 
00236 TableFramePolicy::TableFramePolicy(KWFrameView *view) : FramePolicy (view) {
00237 }
00238 MouseMeaning TableFramePolicy::mouseMeaning( const KoPoint &point, int keyState ) {
00239     Q_UNUSED(point);
00240     // Found a frame under the cursor
00241     // Ctrl -> select
00242     if ( keyState & Qt::ControlButton )
00243         return MEANING_MOUSE_SELECT;
00244     // Shift _and_ at least a frame is selected already
00245     // (shift + no frame selected is used to select text)
00246     if ( (keyState & Qt::ShiftButton) && m_view->parent()->selectedFrame() != 0 )
00247         return MEANING_MOUSE_SELECT;
00248 
00249     return MEANING_MOUSE_INSIDE_TEXT;
00250 }
00251 QPopupMenu* TableFramePolicy::createPopup( const KoPoint &point, KWView *view ) {
00252     view->plugActionList( "tableactions", view->tableActions() );
00253     if( m_view->isBorderHit(point) )
00254         return view->popupMenu("frame_popup_table");
00255     return view->popupMenu("text_popup");
00256 }
00257 MouseMeaning TableFramePolicy::mouseMeaningOnBorder(const KoPoint &point, int keyState) {
00258     KWFrame *frame = m_view->frame();
00259     double hs = HORIZONTAL_SNAP; // horizontal snap zone (in pt)
00260     double vs = VERTICAL_SNAP; // vertical snap zone (in pt)
00261     bool ctrl = keyState & Qt::ControlButton;
00262 
00263     if ( QABS( frame->x() - point.x() ) < hs
00264             && point.y() >= frame->y() && point.y() <= frame->bottom() ) {
00265         if( static_cast<KWTableFrameSet::Cell *>(frame->frameSet())->firstColumn() == 0 )
00266             return MEANING_SELECT_ROW;
00267         if(!ctrl)
00268             return MEANING_RESIZE_COLUMN;
00269     }
00270     if ( QABS( frame->y() - point.y() ) < vs
00271             && point.x() >= frame->x() && point.x() <= frame->right() ) {
00272         if( static_cast<KWTableFrameSet::Cell *>(frame->frameSet())->firstRow() == 0 )
00273             return MEANING_SELECT_COLUMN;
00274         if(!ctrl)
00275             return MEANING_MOUSE_SELECT;
00276     }
00277 
00278     if (ctrl)
00279         return MEANING_MOUSE_SELECT;
00280     if ( QABS( frame->right() - point.x() ) < hs
00281             && point.y() >= frame->y() && point.y() <= frame->bottom() )
00282         return MEANING_RESIZE_COLUMN;
00283     if ( QABS( frame->bottom() - point.y() ) < vs
00284             && point.x() >= frame->x() && point.x() <= frame->right() )
00285         return MEANING_MOUSE_SELECT;
00286     return MEANING_NONE;
00287 }
00288 void TableFramePolicy::setSelected(MouseMeaning selectPolicy) {
00289     KWFrameSet *fs = m_view->frame()->frameSet();
00290     if( selectPolicy == MEANING_SELECT_COLUMN ) {
00291         unsigned int column = static_cast<KWTableFrameSet::Cell *>(fs)->firstColumn();
00292         for (KWTableFrameSet::TableIter cells(fs->groupmanager()); cells; ++cells) {
00293             if(cells->firstColumn() >= column && cells->lastColumn() <= column) {
00294                 KWFrameView *fv = m_view->parent()->view(cells->frame(0));
00295                 if(fv)
00296                     fv->setSelected(true);
00297             }
00298         }
00299     }
00300     else if( selectPolicy == MEANING_SELECT_ROW ) {
00301         unsigned int row = static_cast<KWTableFrameSet::Cell *>(fs)->firstRow();
00302         for (KWTableFrameSet::TableIter cells(fs->groupmanager()); cells; ++cells) {
00303             if(cells->firstRow() >= row && cells->lastRow() <= row) {
00304                 KWFrameView *fv = m_view->parent()->view(cells->frame(0));
00305                 if(fv)
00306                     fv->setSelected(true);
00307             }
00308         }
00309     }
00310     else if( selectPolicy == MEANING_SELECT_RANGE ) {
00311 kdDebug() << "not yet implemented; select table range\n"; // TODO
00312     }
00313 }
00314 
00315 PartFramePolicy::PartFramePolicy(KWFrameView *view) : FramePolicy (view) {
00316 }
00317 MouseMeaning PartFramePolicy::mouseMeaning( const KoPoint &point, int keyState ) {
00318     Q_UNUSED(point);
00319     // Clicking on a selected part frame, but not on its border -> either resize or "activate part"
00320     if( keyState & Qt::ControlButton ) {
00321         return m_view->selected() ? MEANING_MOUSE_MOVE : MEANING_MOUSE_SELECT;
00322     }
00323     if ( m_view->selected() )
00324         return MEANING_ACTIVATE_PART;
00325     return MEANING_MOUSE_SELECT;
00326 }
00327 QPopupMenu* PartFramePolicy::createPopup( const KoPoint &point, KWView *view ) {
00328     Q_UNUSED(point);
00329     KWPartFrameSet *part = static_cast<KWPartFrameSet *>(m_view->frame()->frameSet());
00330     KActionSeparator *separator=new KActionSeparator();
00331     KActionCollection *actionCollection = view->actionCollection();
00332     QPtrList<KAction> actionList;
00333     actionList.append(separator);
00334     if( !part->protectContent() ) {
00335         KToggleAction *action = dynamic_cast<KToggleAction*>(actionCollection->action("embedded_store_internal"));
00336         Q_ASSERT(action);
00337         action->setChecked(part->getChild()->document()->storeInternal());
00338         action->setEnabled(part->getChild()->document()->hasExternURL());
00339         actionList.append(action);
00340     }
00341     addFloatingAction(view, actionList);
00342     view->plugActionList( "frameset_type_action", actionList );
00343 
00344     return view->popupMenu("frame_popup");
00345 }
00346 
00347 TextFramePolicy::TextFramePolicy(KWFrameView *view) : FramePolicy (view) {
00348 }
00349 MouseMeaning TextFramePolicy::mouseMeaning( const KoPoint &point, int keyState ) {
00350     if( (keyState & Qt::ControlButton) == Qt::ControlButton )
00351         return MEANING_MOUSE_SELECT;
00352     KWTextFrameSet *fs = dynamic_cast<KWTextFrameSet*>(m_view->frame()->frameSet());
00353     if(fs == 0 || fs->kWordDocument() == 0)
00354         return MEANING_MOUSE_INSIDE_TEXT;
00355     KWVariableCollection *varCol = fs->kWordDocument()->variableCollection();
00356     if (varCol->variableSetting()->displayLink() && varCol->variableSetting()->underlineLink()
00357             && fs->linkVariableUnderMouse( point ) )
00358         return MEANING_MOUSE_OVER_LINK;
00359     KoVariable* var = fs->variableUnderMouse(point);
00360     if ( var ) {
00361         KWFootNoteVariable * footNoteVar = dynamic_cast<KWFootNoteVariable *>( var );
00362         if ( footNoteVar )
00363             return MEANING_MOUSE_OVER_FOOTNOTE;
00364     }
00365     return MEANING_MOUSE_INSIDE_TEXT;
00366 }
00367 QPopupMenu* TextFramePolicy::createPopup( const KoPoint &point, KWView *view ) {
00368     if( m_view->isBorderHit(point) ) {
00369         KWFrameSet *fs = m_view->frame()->frameSet();
00370         KActionSeparator *separator=new KActionSeparator();
00371         KActionCollection *actionCollection = view->actionCollection();
00372         QPtrList<KAction> actionList;
00373         if(fs->isHeaderOrFooter()) {
00374             actionList.append(separator);
00375             actionList.append(actionCollection->action("configure_headerfooter"));
00376         }
00377         else if (fs->isFootEndNote()) {
00378             actionList.append(separator);
00379             KAction *action = actionCollection->action("goto_footendnote");
00380             action->setText( fs->isFootNote() ?
00381                     i18n( "Go to Footnote" ) : i18n( "Go to Endnote" ) );
00382             actionList.append(action);
00383         }
00384         addFloatingAction(view, actionList);
00385         view->plugActionList( "frameset_type_action", actionList );
00386         return view->popupMenu("frame_popup");
00387     }
00388 
00389     if ( view->getGUI()->canvasWidget()->currentFrameSetEdit()->frameSet()->type() == FT_FORMULA )
00390             return view->popupMenu("Formula");
00391 
00392     // yeah yeah, this is not great and needs a second look. (TZ)
00393     KWTextFrameSetEdit *fse = static_cast<KWTextFrameSetEdit*> (view->getGUI()->
00394             canvasWidget()->currentFrameSetEdit());
00395 
00396     QString word = fse->wordUnderCursor( *fse->cursor() );
00397 
00398     // Remove previous stuff
00399     view->unplugActionList( "datatools" );
00400     view->unplugActionList( "variable_action" );
00401     view->unplugActionList( "spell_result_action" );
00402     view->unplugActionList( "datatools_link" );
00403 
00404     // Those lists are stored in the KWView. Grab a ref to them.
00405     QPtrList<KAction> &actionList = view->dataToolActionList();
00406     QPtrList<KAction> &variableList = view->variableActionList();
00407 
00408     actionList.clear();
00409     variableList.clear();
00410 
00411     bool singleWord= false;
00412     KWDocument * doc = m_view->frame()->frameSet()->kWordDocument();
00413     actionList = fse->dataToolActionList(doc->instance(), word, singleWord);
00414 
00415     KoVariable* var = fse->variable();
00416     doc->variableCollection()->setVariableSelected(var);
00417     if ( var )
00418         variableList = doc->variableCollection()->popupActionList();
00419 
00420     if( variableList.count()>0) {
00421         view->plugActionList( "variable_action", variableList );
00422         return view->popupMenu("variable_popup");
00423     }
00424 
00425     //kdDebug() << "TextFramePolicy::createPopup: plugging actionlist with " << actionList.count() << " actions" << endl;
00426     KoLinkVariable* linkVar = dynamic_cast<KoLinkVariable *>( var );
00427     if ( linkVar ) {
00428         view->plugActionList( "datatools_link", actionList );
00429         return view->popupMenu("text_popup_link");
00430     }
00431     view->plugActionList( "datatools", actionList );
00432     KoNoteVariable * noteVar = dynamic_cast<KoNoteVariable *>( var );
00433     if( noteVar )
00434         return view->popupMenu("comment_popup");
00435 
00436     KoCustomVariable * customVar = dynamic_cast<KoCustomVariable *>( var );
00437     if( customVar )
00438         return view->popupMenu("custom_var_popup");
00439 
00440     KWFootNoteVariable * footNoteVar = dynamic_cast<KWFootNoteVariable *>( var );
00441     if ( footNoteVar ) {
00442         view->changeFootNoteMenuItem( footNoteVar->noteType() == FootNote );
00443         return view->popupMenu("footnote_popup");
00444     }
00445     if ( singleWord ) {
00446         QPtrList<KAction> actionCheckSpellList = view->listOfResultOfCheckWord( word );
00447         if ( !actionCheckSpellList.isEmpty() ) {
00448             view->plugActionList( "spell_result_action", actionCheckSpellList );
00449             return view->popupMenu("text_popup_spell_with_result");
00450         }
00451         return view->popupMenu("text_popup_spell");
00452     }
00453 
00454     return view->popupMenu("text_popup");
00455 }
00456 
00457 
00458 ImageFramePolicy::ImageFramePolicy(KWFrameView *view) : FramePolicy (view) {
00459 }
00460 MouseMeaning ImageFramePolicy::mouseMeaning( const KoPoint &point, int keyState ) {
00461     Q_UNUSED(point);
00462     Q_UNUSED(keyState);
00463     return m_view->selected() ? MEANING_MOUSE_MOVE: MEANING_MOUSE_SELECT;
00464 }
00465 QPopupMenu* ImageFramePolicy::createPopup( const KoPoint &point, KWView *view ) {
00466     Q_UNUSED(point);
00467     KActionSeparator *separator=new KActionSeparator();
00468     KActionCollection *actionCollection = view->actionCollection();
00469     QPtrList<KAction> actionList;
00470     actionList.append(separator);
00471     KAction *action = actionCollection->action("change_picture");
00472     Q_ASSERT(action);
00473     actionList.append(action);
00474     action = actionCollection->action("save_picture");
00475     Q_ASSERT(action);
00476     actionList.append(action);
00477     addFloatingAction(view, actionList);
00478     view->plugActionList( "frameset_type_action", actionList );
00479 
00480     return view->popupMenu("frame_popup");
00481 }
KDE Home | KDE Accessibility Home | Description of Access Keys