/*************************************************************************** * 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 ) { TDEActionCollection * ac = actionCollection(); KStdAction::selectAll( itemDocument, TQT_SLOT(selectAll()), ac ); KStdAction::zoomIn( TQT_TQOBJECT(this), TQT_SLOT(zoomIn()), ac ); KStdAction::zoomOut( TQT_TQOBJECT(this), TQT_SLOT(zoomOut()), ac ); KStdAction::actualSize( TQT_TQOBJECT(this), TQT_SLOT(actualSize()), ac )->setEnabled(false); TDEAccel *pAccel = new TDEAccel(this); pAccel->insert( "Cancel", i18n("Cancel"), i18n("Cancel the current operation"), TQt::Key_Escape, itemDocument, TQT_SLOT(cancelCurrentOperation()) ); pAccel->readSettings(); new TDEAction( i18n("Delete"), "edit-delete", TQt::Key_Delete, itemDocument, TQT_SLOT(deleteSelection()), ac, "edit_delete" ); new TDEAction( i18n("Export as Image..."), 0, 0, itemDocument, TQT_SLOT(exportToImage()), ac, "file_export_image"); //BEGIN Item Alignment actions new TDEAction( i18n("Align Horizontally"), 0, 0, itemDocument, TQT_SLOT(alignHorizontally()), ac, "align_horizontally" ); new TDEAction( i18n("Align Vertically"), 0, 0, itemDocument, TQT_SLOT(alignVertically()), ac, "align_vertically" ); new TDEAction( i18n("Distribute Horizontally"), 0, 0, itemDocument, TQT_SLOT(distributeHorizontally()), ac, "distribute_horizontally" ); new TDEAction( i18n("Distribute Vertically"), 0, 0, itemDocument, TQT_SLOT(distributeVertically()), ac, "distribute_vertically" ); //END Item Alignment actions //BEGIN Draw actions TDEToolBarPopupAction * pa = new TDEToolBarPopupAction( i18n("Draw"), "paintbrush", 0, 0, 0, ac, "edit_draw" ); pa->setDelayed(false); TDEPopupMenu * m = pa->popupMenu(); m->insertTitle( i18n("Draw") ); m->insertItem( TDEGlobal::iconLoader()->loadIcon( "tool_text", TDEIcon::Small ), i18n("Text"), DrawPart::da_text ); m->insertItem( TDEGlobal::iconLoader()->loadIcon( "tool_line", TDEIcon::Small ), i18n("Line"), DrawPart::da_line ); m->insertItem( TDEGlobal::iconLoader()->loadIcon( "tool_arrow", TDEIcon::Small ), i18n("Arrow"), DrawPart::da_arrow ); m->insertItem( TDEGlobal::iconLoader()->loadIcon( "tool_ellipse", TDEIcon::Small ), i18n("Ellipse"), DrawPart::da_ellipse ); m->insertItem( TDEGlobal::iconLoader()->loadIcon( "tool_rectangle", TDEIcon::Small ), i18n("Rectangle"), DrawPart::da_rectangle ); connect( m, TQT_SIGNAL(activated(int)), itemDocument, TQT_SLOT(slotSetDrawAction(int)) ); //END Draw actions //BEGIN Item Control actions new TDEAction( i18n("Raise Selection"), "1uparrow", TQt::Key_PageUp, itemDocument, TQT_SLOT(raiseZ()), ac, "edit_raise" ); new TDEAction( i18n("Lower Selection"), "1downarrow", TQt::Key_PageDown, itemDocument, TQT_SLOT(lowerZ()), ac, "edit_lower" ); //END Item Control actions TDEAction * na = new TDEAction( "", 0, 0, 0, 0, ac, "null_action" ); na->setEnabled(false); setXMLFile( "ktechlabitemviewui.rc" ); m_pUpdateStatusTmr = new TQTimer(this); connect( m_pUpdateStatusTmr, TQT_SIGNAL(timeout()), this, TQT_SLOT(updateStatus()) ); connect( this, TQT_SIGNAL(viewUnfocused()), this, TQT_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.; TQWMatrix 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.; TQWMatrix 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; TQWMatrix 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( TQDropEvent *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 ( !TQString(event->format()).startsWith("ktechlab/") ) return; TQString text; TQDataStream stream( event->encodedData(event->format()), IO_ReadOnly ); stream >> text; TQPoint 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 TQPoint &pos ) { TQPoint 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( TQMouseEvent *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( TQString() ); p_itemDocument->m_cmManager->mousePressEvent( EventInfo( this, e ) ); if ( !wasFocused ) p_itemDocument->m_cmManager->mouseReleaseEvent( EventInfo( this, e ) ); } void ItemView::contentsMouseDoubleClickEvent( TQMouseEvent *e ) { if (!e) return; e->accept(); //HACK: Pass this of as a single press event if widget underneath TQCanvasItem * 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( TQMouseEvent *e ) { if ( !e || !p_itemDocument ) return; e->accept(); p_itemDocument->m_cmManager->mouseMoveEvent( EventInfo( this, e ) ); if ( !m_pUpdateStatusTmr->isActive() ) startUpdatingStatus(); } void ItemView::contentsMouseReleaseEvent( TQMouseEvent *e ) { if (!e) return; e->accept(); p_itemDocument->m_cmManager->mouseReleaseEvent( EventInfo( this, e ) ); } void ItemView::contentsWheelEvent( TQWheelEvent *e ) { if (!e) return; e->accept(); p_itemDocument->m_cmManager->wheelEvent( EventInfo( this, e ) ); } void ItemView::dragEnterEvent( TQDragEnterEvent *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( TQEvent * e ) { Q_UNUSED(e); startUpdatingStatus(); } void ItemView::leaveEvent( TQEvent * e ) { Q_UNUSED(e); stopUpdatingStatus(); // Cleanup setCursor(TQt::ArrowCursor); if (p_ktechlab) p_ktechlab->slotChangeStatusbar(TQString()); if ( p_itemDocument ) p_itemDocument->m_canvasTip->setVisible(false); } void ItemView::slotUpdateConfiguration() { // m_CVBEditor->setEraseColor( KTLConfig::bgColor() ); m_CVBEditor->setEraseColor( TQt::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() { TQPoint pos = (m_CVBEditor->mapFromGlobal( TQCursor::pos() ) + TQPoint( 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; TQCursor cursor = TQt::ArrowCursor; TQString statusbar; if ( cmManager->cmState() & CMManager::cms_repeated_add ) { cursor = TQt::CrossCursor; statusbar = i18n("Left click to add. Right click to resume normal editing"); } else if ( cmManager->cmState() & CMManager::cms_draw ) { cursor = TQt::CrossCursor; statusbar = i18n("Click and hold to start drawing."); } else if ( cmManager->currentManipulator()) { switch ( cmManager->currentManipulator()->type() ) { case CanvasManipulator::RepeatedItemAdd: cursor = TQt::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 = TQt::CrossCursor; break; case CanvasManipulator::ItemMove: case CanvasManipulator::MechItemMove: cursor = TQt::SizeAllCursor; break; case CanvasManipulator::Draw: cursor = TQt::CrossCursor; break; default: break; } } else if ( TQCanvasItem *qcanvasItem = itemDocument->itemAtTop(pos) ) { switch( qcanvasItem->rtti() ) { case ItemDocument::RTTI::Connector: { cursor = TQt::CrossCursor; if ( itemDocument->type() != Document::dt_circuit ) break; canvasTip->displayVI( static_cast(qcanvasItem), pos ); displayTip = true; break; } case ItemDocument::RTTI::Node: { cursor = TQt::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( TQCanvas *canvas, ItemView *itemView, const char *name ) : TQCanvasView( 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( TQt::white ); setPaletteBackgroundColor( TQt::white ); viewport()->setEraseColor( TQt::white ); viewport()->setPaletteBackgroundColor( TQt::white ); } void CVBEditor::contentsMousePressEvent( TQMouseEvent* e ) { if (b_passEventsToView) p_itemView->contentsMousePressEvent(e); else TQCanvasView::contentsMousePressEvent(e); } void CVBEditor::contentsMouseReleaseEvent( TQMouseEvent* e ) { if (b_passEventsToView) p_itemView->contentsMouseReleaseEvent(e); else TQCanvasView::contentsMouseReleaseEvent(e); } void CVBEditor::contentsMouseDoubleClickEvent( TQMouseEvent* e ) { if (b_passEventsToView) p_itemView->contentsMouseDoubleClickEvent(e); else TQCanvasView::contentsMouseDoubleClickEvent(e); } void CVBEditor::contentsMouseMoveEvent( TQMouseEvent* e ) { if (b_passEventsToView) p_itemView->contentsMouseMoveEvent(e); else TQCanvasView::contentsMouseMoveEvent(e); } void CVBEditor::dragEnterEvent( TQDragEnterEvent* e ) { if (b_passEventsToView) p_itemView->dragEnterEvent(e); else TQCanvasView::dragEnterEvent(e); } void CVBEditor::dropEvent( TQDropEvent* e ) { if (b_passEventsToView) p_itemView->dropEvent(e); else TQCanvasView::dropEvent(e); } void CVBEditor::enterEvent( TQEvent * e ) { if (b_passEventsToView) p_itemView->enterEvent(e); else TQCanvasView::enterEvent(e); } void CVBEditor::leaveEvent( TQEvent* e ) { if (b_passEventsToView) p_itemView->leaveEvent(e); else TQCanvasView::leaveEvent(e); } void CVBEditor::contentsWheelEvent( TQWheelEvent *e ) { if (b_ignoreEvents) { e->ignore(); return; } if (b_passEventsToView) p_itemView->contentsWheelEvent(e); else { b_ignoreEvents = true; TQCanvasView::wheelEvent(e); b_ignoreEvents = false; } } void CVBEditor::viewportResizeEvent( TQResizeEvent * e ) { TQCanvasView::viewportResizeEvent(e); p_itemView->p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); } //END class CVBEditor #include "itemview.moc"