/*************************************************************************** * Copyright (C) 2005 by David Saxton * * david@bluehaze.org * * * * 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 "canvasmanipulator.h" #include "cnitem.h" #include "connector.h" #include "docmanager.h" #include "drawpart.h" #include "ecnode.h" #include "itemdocument.h" #include "itemview.h" #include "ktechlab.h" #include "core/ktlconfig.h" #include #include #include #include #include #include #include #include #include //BEGIN class ItemView ItemView::ItemView( ItemDocument * itemDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) : View( itemDocument, viewContainer, viewAreaId, name ) { KActionCollection * ac = actionCollection(); KStdAction::selectAll( itemDocument, SLOT(selectAll()), ac ); KStdAction::zoomIn( this, SLOT(zoomIn()), ac ); KStdAction::zoomOut( this, SLOT(zoomOut()), ac ); KStdAction::actualSize( this, SLOT(actualSize()), ac )->setEnabled(false); KAccel *pAccel = new KAccel(this); pAccel->insert( "Cancel", i18n("Cancel"), i18n("Cancel the current operation"), Qt::Key_Escape, itemDocument, SLOT(cancelCurrentOperation()) ); pAccel->readSettings(); new KAction( i18n("Delete"), "editdelete", Qt::Key_Delete, itemDocument, SLOT(deleteSelection()), ac, "edit_delete" ); new KAction( i18n("Export as Image..."), 0, 0, itemDocument, SLOT(exportToImage()), ac, "file_export_image"); //BEGIN Item Alignment actions new KAction( i18n("Align Horizontally"), 0, 0, itemDocument, SLOT(alignHorizontally()), ac, "align_horizontally" ); new KAction( i18n("Align Vertically"), 0, 0, itemDocument, SLOT(alignVertically()), ac, "align_vertically" ); new KAction( i18n("Distribute Horizontally"), 0, 0, itemDocument, SLOT(distributeHorizontally()), ac, "distribute_horizontally" ); new KAction( i18n("Distribute Vertically"), 0, 0, itemDocument, SLOT(distributeVertically()), ac, "distribute_vertically" ); //END Item Alignment actions //BEGIN Draw actions KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Draw"), "paintbrush", 0, 0, 0, ac, "edit_draw" ); pa->setDelayed(false); KPopupMenu * m = pa->popupMenu(); m->insertTitle( i18n("Draw") ); m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_text", KIcon::Small ), i18n("Text"), DrawPart::da_text ); m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_line", KIcon::Small ), i18n("Line"), DrawPart::da_line ); m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_arrow", KIcon::Small ), i18n("Arrow"), DrawPart::da_arrow ); m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_ellipse", KIcon::Small ), i18n("Ellipse"), DrawPart::da_ellipse ); m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_rectangle", KIcon::Small ), i18n("Rectangle"), DrawPart::da_rectangle ); connect( m, SIGNAL(activated(int)), itemDocument, SLOT(slotSetDrawAction(int)) ); //END Draw actions //BEGIN Item Control actions new KAction( i18n("Raise Selection"), "1uparrow", Qt::Key_PageUp, itemDocument, SLOT(raiseZ()), ac, "edit_raise" ); new KAction( i18n("Lower Selection"), "1downarrow", Qt::Key_PageDown, itemDocument, SLOT(lowerZ()), ac, "edit_lower" ); //END Item Control actions KAction * na = new KAction( "", 0, 0, 0, 0, ac, "null_action" ); na->setEnabled(false); setXMLFile( "ktechlabitemviewui.rc" ); m_pUpdateStatusTmr = new QTimer(this); connect( m_pUpdateStatusTmr, SIGNAL(timeout()), this, SLOT(updateStatus()) ); connect( this, SIGNAL(viewUnfocused()), this, SLOT(stopUpdatingStatus()) ); p_itemDocument = itemDocument; m_zoomLevel = 1.; m_CVBEditor = new CVBEditor( p_itemDocument->canvas(), this, "cvbEditor" ); m_CVBEditor->setLineWidth(1); m_layout->insertWidget( 0, m_CVBEditor ); setAcceptDrops(true); } ItemView::~ItemView() { } bool ItemView::canZoomIn() const { return true; } bool ItemView::canZoomOut() const { return int(std::floor((100*m_zoomLevel)+0.5)) > 25; } void ItemView::zoomIn() { int currentZoomPercent = int(std::floor((100*m_zoomLevel)+0.5)); int newZoom = currentZoomPercent; if ( currentZoomPercent < 100 ) newZoom += 25; else if ( currentZoomPercent < 200 ) newZoom += 50; else newZoom += 100; m_zoomLevel = newZoom/100.; QWMatrix m( m_zoomLevel, 0.0, 0.0, m_zoomLevel, 1.0, 1.0 ); m_CVBEditor->setWorldMatrix(m); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); updateZoomActions(); } void ItemView::zoomOut() { int currentZoomPercent = int(std::floor((100*m_zoomLevel)+0.5)); int newZoom = currentZoomPercent; if ( currentZoomPercent <= 25 ) return; if ( currentZoomPercent <= 100 ) newZoom -= 25; else if ( currentZoomPercent <= 200 ) newZoom -= 50; else newZoom -= 100; m_zoomLevel = newZoom/100.; QWMatrix m( m_zoomLevel, 0.0, 0.0, m_zoomLevel, 1.0, 1.0 ); m_CVBEditor->setWorldMatrix(m); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); updateZoomActions(); } void ItemView::actualSize() { m_zoomLevel = 1.0; QWMatrix m( m_zoomLevel, 0.0, 0.0, m_zoomLevel, 1.0, 1.0 ); m_CVBEditor->setWorldMatrix(m); p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); updateZoomActions(); } void ItemView::updateZoomActions() { action("view_zoom_in")->setEnabled( canZoomIn() ); action("view_zoom_out")->setEnabled( canZoomOut() ); action("view_actual_size")->setEnabled( m_zoomLevel != 1.0 ); } void ItemView::dropEvent( QDropEvent *event ) { KURL::List urls; if ( KURLDrag::decode( event, urls ) ) { // Then it is URLs that we can decode :) const KURL::List::iterator end = urls.end(); for ( KURL::List::iterator it = urls.begin(); it != end; ++it ) { DocManager::self()->openURL(*it); } return; } if ( !QString(event->format()).startsWith("ktechlab/") ) return; QString text; QDataStream stream( event->encodedData(event->format()), IO_ReadOnly ); stream >> text; QPoint position = event->pos(); position.setX( int((position.x() + m_CVBEditor->contentsX())/m_zoomLevel) ); position.setY( int((position.y() + m_CVBEditor->contentsY())/m_zoomLevel) ); // Get a new component item p_itemDocument->addItem( text, position, true ); setFocus(); } void ItemView::scrollToMouse( const QPoint &pos ) { QPoint position = pos * m_zoomLevel; int left = m_CVBEditor->contentsX(); int top = m_CVBEditor->contentsY(); int right = left + m_CVBEditor->visibleWidth(); int bottom = top + m_CVBEditor->visibleHeight(); if( position.x() < left ) m_CVBEditor->scrollBy( position.x() - left, 0 ); else if( position.x() > right ) m_CVBEditor->scrollBy( position.x() - right, 0 ); if( position.y() < top ) m_CVBEditor->scrollBy( 0, position.y() - top ); else if( position.y() > bottom ) m_CVBEditor->scrollBy( 0, position.y() - bottom); } void ItemView::contentsMousePressEvent( QMouseEvent *e ) { if (!e) return; e->accept(); // For some reason, when we are initially unfocused, we only receive the // release event if the user drags the mouse - not very often. So see if we // were initially unfocused, and if so, do unclick as well. bool wasFocused = isFocused(); setFocused(); if ( !p_itemDocument ) return; p_itemDocument->canvas()->setMessage( QString::null ); p_itemDocument->m_cmManager->mousePressEvent( EventInfo( this, e ) ); if ( !wasFocused ) p_itemDocument->m_cmManager->mouseReleaseEvent( EventInfo( this, e ) ); } void ItemView::contentsMouseDoubleClickEvent( QMouseEvent *e ) { if (!e) return; e->accept(); //HACK: Pass this of as a single press event if widget underneath QCanvasItem * atTop = p_itemDocument->itemAtTop( e->pos()/zoomLevel() ); if ( atTop && atTop->rtti() == ItemDocument::RTTI::Widget ) contentsMousePressEvent(e); else p_itemDocument->m_cmManager->mouseDoubleClickEvent( EventInfo( this, e ) ); } void ItemView::contentsMouseMoveEvent( QMouseEvent *e ) { if ( !e || !p_itemDocument ) return; e->accept(); p_itemDocument->m_cmManager->mouseMoveEvent( EventInfo( this, e ) ); if ( !m_pUpdateStatusTmr->isActive() ) startUpdatingStatus(); } void ItemView::contentsMouseReleaseEvent( QMouseEvent *e ) { if (!e) return; e->accept(); p_itemDocument->m_cmManager->mouseReleaseEvent( EventInfo( this, e ) ); } void ItemView::contentsWheelEvent( QWheelEvent *e ) { if (!e) return; e->accept(); p_itemDocument->m_cmManager->wheelEvent( EventInfo( this, e ) ); } void ItemView::dragEnterEvent( QDragEnterEvent *event ) { startUpdatingStatus(); KURL::List urls; if ( KURLDrag::decode( event, urls ) ) { event->accept(true); // Then it is URLs that we can decode later :) return; } } void ItemView::enterEvent( QEvent * e ) { Q_UNUSED(e); startUpdatingStatus(); } void ItemView::leaveEvent( QEvent * e ) { Q_UNUSED(e); stopUpdatingStatus(); // Cleanup setCursor(Qt::ArrowCursor); if (p_ktechlab) p_ktechlab->slotChangeStatusbar(QString::null); if ( p_itemDocument ) p_itemDocument->m_canvasTip->setVisible(false); } void ItemView::slotUpdateConfiguration() { // m_CVBEditor->setEraseColor( KTLConfig::bgColor() ); m_CVBEditor->setEraseColor( Qt::white ); if ( m_pUpdateStatusTmr->isActive() ) startUpdatingStatus(); } void ItemView::startUpdatingStatus() { m_pUpdateStatusTmr->stop(); m_pUpdateStatusTmr->start( int(1000./KTLConfig::refreshRate()) ); } void ItemView::stopUpdatingStatus() { m_pUpdateStatusTmr->stop(); } void ItemView::updateStatus() { QPoint pos = (m_CVBEditor->mapFromGlobal( QCursor::pos() ) + QPoint( m_CVBEditor->contentsX(), m_CVBEditor->contentsY() )) / zoomLevel(); ItemDocument * itemDocument = static_cast(document()); if ( !itemDocument ) return; CMManager * cmManager = itemDocument->m_cmManager; CanvasTip * canvasTip = itemDocument->m_canvasTip; bool displayTip = false; QCursor cursor = Qt::ArrowCursor; QString statusbar; if ( cmManager->cmState() & CMManager::cms_repeated_add ) { cursor = Qt::CrossCursor; statusbar = i18n("Left click to add. Right click to resume normal editing"); } else if ( cmManager->cmState() & CMManager::cms_draw ) { cursor = Qt::CrossCursor; statusbar = i18n("Click and hold to start drawing."); } else if ( cmManager->currentManipulator()) { switch ( cmManager->currentManipulator()->type() ) { case CanvasManipulator::RepeatedItemAdd: cursor = Qt::CrossCursor; statusbar = i18n("Left click to add. Right click to resume normal editing"); break; case CanvasManipulator::ManualConnector: statusbar = i18n("Right click to cancel the connector"); // no break case CanvasManipulator::AutoConnector: cursor = Qt::CrossCursor; break; case CanvasManipulator::ItemMove: case CanvasManipulator::MechItemMove: cursor = Qt::SizeAllCursor; break; case CanvasManipulator::Draw: cursor = Qt::CrossCursor; break; default: break; } } else if ( QCanvasItem *qcanvasItem = itemDocument->itemAtTop(pos) ) { switch( qcanvasItem->rtti() ) { case ItemDocument::RTTI::Connector: { cursor = Qt::CrossCursor; if ( itemDocument->type() != Document::dt_circuit ) break; canvasTip->displayVI( static_cast(qcanvasItem), pos ); displayTip = true; break; } case ItemDocument::RTTI::Node: { cursor = Qt::CrossCursor; ECNode * ecnode = dynamic_cast(qcanvasItem); if ( !ecnode ) break; canvasTip->displayVI( ecnode, pos ); displayTip = true; break; } case ItemDocument::RTTI::CNItem: { statusbar = (static_cast(qcanvasItem))->name(); break; } default: { break; } } } setCursor(cursor); if (p_ktechlab) p_ktechlab->slotChangeStatusbar(statusbar); canvasTip->setVisible(displayTip); } //END class ItemView //BEGIN class CVBEditor CVBEditor::CVBEditor( QCanvas *canvas, ItemView *itemView, const char *name ) : QCanvasView( canvas, itemView, name, WNoAutoErase | WStaticContents ) { b_ignoreEvents = false; b_passEventsToView = true; p_itemView = itemView; viewport()->setMouseTracking(true); setAcceptDrops(true); setFrameShape(NoFrame); // setEraseColor( KTLConfig::bgColor() ); setEraseColor( Qt::white ); setPaletteBackgroundColor( Qt::white ); viewport()->setEraseColor( Qt::white ); viewport()->setPaletteBackgroundColor( Qt::white ); } void CVBEditor::contentsMousePressEvent( QMouseEvent* e ) { if (b_passEventsToView) p_itemView->contentsMousePressEvent(e); else QCanvasView::contentsMousePressEvent(e); } void CVBEditor::contentsMouseReleaseEvent( QMouseEvent* e ) { if (b_passEventsToView) p_itemView->contentsMouseReleaseEvent(e); else QCanvasView::contentsMouseReleaseEvent(e); } void CVBEditor::contentsMouseDoubleClickEvent( QMouseEvent* e ) { if (b_passEventsToView) p_itemView->contentsMouseDoubleClickEvent(e); else QCanvasView::contentsMouseDoubleClickEvent(e); } void CVBEditor::contentsMouseMoveEvent( QMouseEvent* e ) { if (b_passEventsToView) p_itemView->contentsMouseMoveEvent(e); else QCanvasView::contentsMouseMoveEvent(e); } void CVBEditor::dragEnterEvent( QDragEnterEvent* e ) { if (b_passEventsToView) p_itemView->dragEnterEvent(e); else QCanvasView::dragEnterEvent(e); } void CVBEditor::dropEvent( QDropEvent* e ) { if (b_passEventsToView) p_itemView->dropEvent(e); else QCanvasView::dropEvent(e); } void CVBEditor::enterEvent( QEvent * e ) { if (b_passEventsToView) p_itemView->enterEvent(e); else QCanvasView::enterEvent(e); } void CVBEditor::leaveEvent( QEvent* e ) { if (b_passEventsToView) p_itemView->leaveEvent(e); else QCanvasView::leaveEvent(e); } void CVBEditor::contentsWheelEvent( QWheelEvent *e ) { if (b_ignoreEvents) { e->ignore(); return; } if (b_passEventsToView) p_itemView->contentsWheelEvent(e); else { b_ignoreEvents = true; QCanvasView::wheelEvent(e); b_ignoreEvents = false; } } void CVBEditor::viewportResizeEvent( QResizeEvent * e ) { QCanvasView::viewportResizeEvent(e); p_itemView->p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); } //END class CVBEditor #include "itemview.moc"