/***************************************************************************
* 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 "circuitdocument.h"
# include "connector.h"
# include "cnitem.h"
# include "drawpart.h"
# include "ecnode.h"
# include "flowcodedocument.h"
# include "icnview.h"
# include "itemdocumentdata.h"
# include "itemgroup.h"
# include "itemselector.h"
# include "ktechlab.h"
# include "core/ktlconfig.h"
# include "pin.h"
# include "simulator.h"
# include <tdeapplication.h>
# include <kdebug.h>
# include <tdefiledialog.h>
# include <kiconloader.h>
# include <klocale.h>
# include <kmessagebox.h>
# include <tdepopupmenu.h>
# include <kprinter.h>
# include <tqcheckbox.h>
# include <tqclipboard.h>
# include <tqcursor.h>
# include <tqimage.h>
# include <tqpaintdevicemetrics.h>
# include <tqpainter.h>
# include <tqpicture.h>
# include <tqregexp.h>
# include <tqsimplerichtext.h>
# include <tqtimer.h>
# include <cmath>
//BEGIN class ItemDocument
int ItemDocument : : m_nextActionTicket = 0 ;
ItemDocument : : ItemDocument ( const TQString & caption , KTechlab * ktechlab , const char * name )
: Document ( caption , ktechlab , name )
{
m_queuedEvents = 0 ;
p_ktechlab = ktechlab ;
m_nextIdNum = 1 ;
m_savedState = 0l ;
m_currentState = 0l ;
m_bIsLoading = false ;
m_canvas = new Canvas ( this , " canvas " ) ;
m_canvasTip = new CanvasTip ( this , m_canvas ) ;
m_cmManager = new CMManager ( this ) ;
m_undoStack . setAutoDelete ( true ) ;
m_redoStack . setAutoDelete ( true ) ;
updateBackground ( ) ;
m_canvas - > resize ( 0 , 0 ) ;
m_canvas - > setDoubleBuffering ( true ) ;
m_pEventTimer = new TQTimer ( this ) ;
connect ( m_pEventTimer , TQT_SIGNAL ( timeout ( ) ) , this , TQT_SLOT ( processItemDocumentEvents ( ) ) ) ;
connect ( this , TQT_SIGNAL ( itemSelected ( Item * ) ) , this , TQT_SLOT ( slotInitItemActions ( Item * ) ) ) ;
connect ( this , TQT_SIGNAL ( itemUnselected ( Item * ) ) , this , TQT_SLOT ( slotInitItemActions ( Item * ) ) ) ;
connect ( ComponentSelector : : self ( ) , TQT_SIGNAL ( itemClicked ( const TQString & ) ) , this , TQT_SLOT ( slotUnsetRepeatedItemId ( ) ) ) ;
connect ( FlowPartSelector : : self ( ) , TQT_SIGNAL ( itemClicked ( const TQString & ) ) , this , TQT_SLOT ( slotUnsetRepeatedItemId ( ) ) ) ;
# ifdef MECHANICS
connect ( MechanicsSelector : : self ( ) , TQT_SIGNAL ( itemClicked ( const TQString & ) ) , this , TQT_SLOT ( slotUnsetRepeatedItemId ( ) ) ) ;
# endif
m_pAlignmentAction = new TDEActionMenu ( i18n ( " Alignment " ) , " rightjust " , this ) ;
slotUpdateConfiguration ( ) ;
}
ItemDocument : : ~ ItemDocument ( )
{
m_bDeleted = true ;
ItemList toDelete = m_itemList ;
const ItemList : : iterator end = toDelete . end ( ) ;
for ( ItemList : : iterator it = toDelete . begin ( ) ; it ! = end ; + + it )
delete * it ;
delete m_cmManager ;
m_cmManager = 0l ;
delete m_currentState ;
m_currentState = 0l ;
delete m_canvasTip ;
m_canvasTip = 0l ;
}
void ItemDocument : : handleNewView ( View * view )
{
Document : : handleNewView ( view ) ;
requestEvent ( ItemDocument : : ItemDocumentEvent : : ResizeCanvasToItems ) ;
}
bool ItemDocument : : registerItem ( TQCanvasItem * qcanvasItem )
{
if ( ! qcanvasItem )
return false ;
requestEvent ( ItemDocument : : ItemDocumentEvent : : ResizeCanvasToItems ) ;
switch ( qcanvasItem - > rtti ( ) )
{
case ItemDocument : : RTTI : : DrawPart :
case ItemDocument : : RTTI : : CNItem :
{
Item * item = dynamic_cast < Item * > ( qcanvasItem ) ;
m_itemList . append ( item ) ;
connect ( item , TQT_SIGNAL ( removed ( Item * ) ) , this , TQT_SLOT ( requestRerouteInvalidatedConnectors ( ) ) ) ;
connect ( item , TQT_SIGNAL ( selected ( Item * , bool ) ) , this , TQT_SIGNAL ( itemSelected ( Item * ) ) ) ;
connect ( item , TQT_SIGNAL ( unselected ( Item * , bool ) ) , this , TQT_SIGNAL ( itemUnselected ( Item * ) ) ) ;
itemAdded ( item ) ;
return true ;
}
default :
return false ;
}
}
void ItemDocument : : slotSetDrawAction ( int drawAction )
{
m_cmManager - > setDrawAction ( drawAction ) ;
}
void ItemDocument : : cancelCurrentOperation ( )
{
m_cmManager - > cancelCurrentManipulation ( ) ;
}
void ItemDocument : : slotSetRepeatedItemId ( const TQString & id )
{
m_cmManager - > setCMState ( CMManager : : cms_repeated_add , true ) ;
m_cmManager - > setRepeatedAddId ( id ) ;
}
void ItemDocument : : slotUnsetRepeatedItemId ( )
{
m_cmManager - > setCMState ( CMManager : : cms_repeated_add , false ) ;
}
void ItemDocument : : fileSave ( )
{
if ( url ( ) . isEmpty ( ) & & ! getURL ( m_fileExtensionInfo ) ) return ;
writeFile ( ) ;
}
void ItemDocument : : fileSaveAs ( )
{
if ( ! getURL ( m_fileExtensionInfo ) ) return ;
writeFile ( ) ;
// Our modified state may not have changed, but we emit this to force the
// main window to update our caption.
emit modifiedStateChanged ( ) ;
}
void ItemDocument : : writeFile ( )
{
ItemDocumentData data ( type ( ) ) ;
data . saveDocumentState ( this ) ;
if ( data . saveData ( url ( ) ) )
{
m_savedState = m_currentState ;
setModified ( false ) ;
}
}
bool ItemDocument : : openURL ( const KURL & url )
{
ItemDocumentData data ( type ( ) ) ;
if ( ! data . loadData ( url ) )
return false ;
// Why do we stop simulating while loading a document?
// Crash possible when loading a circuit document, and the TQt event loop is
// reentered (such as when a PIC component pops-up a message box), which
// will then call the Simulator::step function, which might use components
// that have not fully initialized themselves.
m_bIsLoading = true ;
bool wasSimulating = Simulator : : self ( ) - > isSimulating ( ) ;
Simulator : : self ( ) - > slotSetSimulating ( false ) ;
data . restoreDocument ( this ) ;
Simulator : : self ( ) - > slotSetSimulating ( wasSimulating ) ;
m_bIsLoading = false ;
setURL ( url ) ;
clearHistory ( ) ;
m_savedState = m_currentState ;
setModified ( false ) ;
if ( FlowCodeDocument * fcd = dynamic_cast < FlowCodeDocument * > ( this ) )
{
// We need to tell all pic-depedent components about what pic type is in use
emit fcd - > picTypeChanged ( ) ;
}
requestEvent ( ItemDocument : : ItemDocumentEvent : : ResizeCanvasToItems ) ;
// Load Z-position info
m_zOrder . clear ( ) ;
ItemList : : iterator end = m_itemList . end ( ) ;
for ( ItemList : : iterator it = m_itemList . begin ( ) ; it ! = end ; + + it )
{
if ( ! * it | | ( * it ) - > parentItem ( ) )
continue ;
m_zOrder [ ( * it ) - > baseZ ( ) ] = * it ;
}
slotUpdateZOrdering ( ) ;
return true ;
}
void ItemDocument : : print ( )
{
static KPrinter * printer = new KPrinter ;
if ( ! printer - > setup ( p_ktechlab ) )
return ;
// setup the printer. with TQt, you always "print" to a
// TQPainter.. whether the output medium is a pixmap, a screen,
// or paper
TQPainter p ;
p . begin ( printer ) ;
// we let our view do the actual printing
TQPaintDeviceMetrics metrics ( printer ) ;
// Round to 16 (= 2 * 8) so that we cut in the middle of squares
int w = 16 * int ( metrics . width ( ) / 16 ) ;
int h = 16 * int ( metrics . height ( ) / 16 ) ;
p . setClipping ( true ) ;
p . setClipRect ( 0 , 0 , w , h , TQPainter : : CoordPainter ) ;
// Send off the painter for drawing
m_canvas - > setBackgroundPixmap ( 0 ) ;
TQRect bounding = canvasBoundingRect ( ) ;
unsigned int rows = ( unsigned ) std : : ceil ( double ( bounding . height ( ) ) / double ( h ) ) ;
unsigned int cols = ( unsigned ) std : : ceil ( double ( bounding . width ( ) ) / double ( w ) ) ;
int offset_x = bounding . x ( ) ;
int offset_y = bounding . y ( ) ;
for ( unsigned row = 0 ; row < rows ; + + row )
{
for ( unsigned col = 0 ; col < cols ; + + col )
{
if ( row ! = 0 | | col ! = 0 )
printer - > newPage ( ) ;
TQRect drawArea ( offset_x + ( col * w ) , offset_y + ( row * h ) , w , h ) ;
m_canvas - > drawArea ( drawArea , & p ) ;
p . translate ( - w , 0 ) ;
}
p . translate ( w * cols , - h ) ;
}
updateBackground ( ) ;
// and send the result to the printer
p . end ( ) ;
}
void ItemDocument : : requestStateSave ( int actionTicket )
{
if ( m_bIsLoading )
return ;
m_redoStack . clear ( ) ;
if ( ( actionTicket > = 0 ) & & ( actionTicket = = m_currentActionTicket ) )
{
delete m_currentState ;
m_currentState = 0l ;
}
m_currentActionTicket = actionTicket ;
if ( m_currentState )
m_undoStack . push ( m_currentState ) ;
m_currentState = new ItemDocumentData ( type ( ) ) ;
m_currentState - > saveDocumentState ( this ) ;
if ( ! m_savedState )
m_savedState = m_currentState ;
setModified ( m_savedState ! = m_currentState ) ;
emit undoRedoStateChanged ( ) ;
//FIXME To resize undo queue, have to pop and push everything
int maxUndo = KTLConfig : : maxUndo ( ) ;
if ( maxUndo < = 0 | | m_undoStack . count ( ) < ( unsigned ) maxUndo )
return ;
IDDStack tempStack ;
int pushed = 0 ;
while ( ! m_undoStack . isEmpty ( ) & & pushed < maxUndo )
{
tempStack . push ( m_undoStack . pop ( ) ) ;
pushed + + ;
}
m_undoStack . clear ( ) ;
while ( ! tempStack . isEmpty ( ) )
m_undoStack . push ( tempStack . pop ( ) ) ;
}
void ItemDocument : : clearHistory ( )
{
m_undoStack . clear ( ) ;
m_redoStack . clear ( ) ;
delete m_currentState ;
m_currentState = 0l ;
requestStateSave ( ) ;
}
bool ItemDocument : : isUndoAvailable ( ) const
{
return ! m_undoStack . isEmpty ( ) ;
}
bool ItemDocument : : isRedoAvailable ( ) const
{
return ! m_redoStack . isEmpty ( ) ;
}
void ItemDocument : : undo ( )
{
ItemDocumentData * idd = m_undoStack . pop ( ) ;
if ( ! idd )
return ;
if ( m_currentState )
m_redoStack . push ( m_currentState ) ;
idd - > restoreDocument ( this ) ;
m_currentState = idd ;
setModified ( m_savedState ! = m_currentState ) ;
emit undoRedoStateChanged ( ) ;
}
void ItemDocument : : redo ( )
{
ItemDocumentData * idd = m_redoStack . pop ( ) ;
if ( ! idd )
return ;
if ( m_currentState )
m_undoStack . push ( m_currentState ) ;
idd - > restoreDocument ( this ) ;
m_currentState = idd ;
setModified ( m_savedState ! = m_currentState ) ;
emit undoRedoStateChanged ( ) ;
}
void ItemDocument : : cut ( )
{
copy ( ) ;
deleteSelection ( ) ;
}
void ItemDocument : : paste ( )
{
TQString xml = TDEApplication : : clipboard ( ) - > text ( TQClipboard : : Clipboard ) ;
if ( xml . isEmpty ( ) )
return ;
unselectAll ( ) ;
ItemDocumentData data ( type ( ) ) ;
data . fromXML ( xml ) ;
data . generateUniqueIDs ( this ) ;
data . translateContents ( 64 , 64 ) ;
data . mergeWithDocument ( this , true ) ;
// Get rid of any garbage that shouldn't be around / merge connectors / etc
flushDeleteList ( ) ;
requestStateSave ( ) ;
}
Item * ItemDocument : : itemWithID ( const TQString & id )
{
const ItemList : : iterator end = m_itemList . end ( ) ;
for ( ItemList : : iterator it = m_itemList . begin ( ) ; it ! = end ; + + it )
{
if ( ( * it ) - > id ( ) = = id )
return * it ;
}
return 0L ;
}
void ItemDocument : : unselectAll ( )
{
selectList ( ) - > removeAllItems ( ) ;
}
void ItemDocument : : select ( TQCanvasItem * item )
{
if ( ! item )
return ;
item - > setSelected ( selectList ( ) - > contains ( item ) | | selectList ( ) - > addTQCanvasItem ( item ) ) ;
}
void ItemDocument : : select ( const TQCanvasItemList & list )
{
const TQCanvasItemList : : const_iterator end = list . end ( ) ;
for ( TQCanvasItemList : : const_iterator it = list . begin ( ) ; it ! = end ; + + it )
selectList ( ) - > addTQCanvasItem ( * it ) ;
selectList ( ) - > setSelected ( true ) ;
}
void ItemDocument : : unselect ( TQCanvasItem * qcanvasItem )
{
selectList ( ) - > removeTQCanvasItem ( qcanvasItem ) ;
qcanvasItem - > setSelected ( false ) ;
}
void ItemDocument : : slotUpdateConfiguration ( )
{
updateBackground ( ) ;
m_canvas - > setUpdatePeriod ( int ( 1000. / KTLConfig : : refreshRate ( ) ) ) ;
}
TQCanvasItem * ItemDocument : : itemAtTop ( const TQPoint & pos ) const
{
TQCanvasItemList list = m_canvas - > collisions ( TQRect ( pos . x ( ) - 1 , pos . y ( ) - 1 , 3 , 3 ) ) ;
TQCanvasItemList : : const_iterator it = list . begin ( ) ;
const TQCanvasItemList : : const_iterator end = list . end ( ) ;
while ( it ! = end )
{
TQCanvasItem * item = * it ;
if ( item = = m_canvasTip | |
item - > rtti ( ) = = TQCanvasItem : : Rtti_Line | |
item - > rtti ( ) = = TQCanvasItem : : Rtti_Text | |
item - > rtti ( ) = = TQCanvasItem : : Rtti_Rectangle )
{
+ + it ;
}
else
{
if ( item - > rtti ( ) = = ItemDocument : : RTTI : : ConnectorLine )
return ( static_cast < ConnectorLine * > ( item ) ) - > parent ( ) ;
return item ;
}
}
return 0L ;
}
void ItemDocument : : alignHorizontally ( )
{
selectList ( ) - > slotAlignHorizontally ( ) ;
if ( ICNDocument * icnd = dynamic_cast < ICNDocument * > ( this ) )
icnd - > requestRerouteInvalidatedConnectors ( ) ;
}
void ItemDocument : : alignVertically ( )
{
selectList ( ) - > slotAlignVertically ( ) ;
if ( ICNDocument * icnd = dynamic_cast < ICNDocument * > ( this ) )
icnd - > requestRerouteInvalidatedConnectors ( ) ;
}
void ItemDocument : : distributeHorizontally ( )
{
selectList ( ) - > slotDistributeHorizontally ( ) ;
if ( ICNDocument * icnd = dynamic_cast < ICNDocument * > ( this ) )
icnd - > requestRerouteInvalidatedConnectors ( ) ;
}
void ItemDocument : : distributeVertically ( )
{
selectList ( ) - > slotDistributeVertically ( ) ;
if ( ICNDocument * icnd = dynamic_cast < ICNDocument * > ( this ) )
icnd - > requestRerouteInvalidatedConnectors ( ) ;
}
bool ItemDocument : : registerUID ( const TQString & UID )
{
if ( m_idList . findIndex ( UID ) = = - 1 )
{
m_idList . append ( UID ) ;
return true ;
}
return false ;
}
void ItemDocument : : unregisterUID ( const TQString & uid )
{
m_idList . remove ( uid ) ;
}
TQString ItemDocument : : generateUID ( TQString name )
{
name . remove ( TQRegExp ( " __.* " ) ) ; // Change 'node__13' to 'node', for example
TQString idAttempt = name ;
// if ( idAttempt.find("__") != -1 ) idAttempt.truncate( idAttempt.find("__") );
while ( ! registerUID ( idAttempt ) ) { idAttempt = name + " __ " + TQString : : number ( m_nextIdNum + + ) ; }
return idAttempt ;
}
void ItemDocument : : canvasRightClick ( const TQPoint & pos , TQCanvasItem * item )
{
if ( item )
{
if ( item - > rtti ( ) = = ItemDocument : : RTTI : : CNItem & &
! item - > isSelected ( ) )
{
unselectAll ( ) ;
select ( item ) ;
}
}
p_ktechlab - > unplugActionList ( " alignment_actionlist " ) ;
p_ktechlab - > unplugActionList ( " orientation_actionlist " ) ;
p_ktechlab - > unplugActionList ( " component_actionlist " ) ;
fillContextMenu ( pos ) ;
TQPopupMenu * pop = static_cast < TQPopupMenu * > ( p_ktechlab - > factory ( ) - > container ( " item_popup " , p_ktechlab ) ) ;
if ( ! pop )
return ;
pop - > popup ( pos ) ;
}
void ItemDocument : : fillContextMenu ( const TQPoint & pos )
{
Q_UNUSED ( pos ) ;
ItemView * activeItemView = dynamic_cast < ItemView * > ( activeView ( ) ) ;
if ( ! p_ktechlab | | ! activeItemView )
return ;
TDEAction * align_actions [ ] = {
activeItemView - > action ( " align_horizontally " ) ,
activeItemView - > action ( " align_vertically " ) ,
activeItemView - > action ( " distribute_horizontally " ) ,
activeItemView - > action ( " distribute_vertically " ) } ;
bool enableAlignment = selectList ( ) - > itemCount ( ) > 1 ;
if ( ! enableAlignment )
return ;
for ( unsigned i = 0 ; i < 4 ; + + i )
{
align_actions [ i ] - > setEnabled ( true ) ;
m_pAlignmentAction - > remove ( align_actions [ i ] ) ;
m_pAlignmentAction - > insert ( align_actions [ i ] ) ;
}
TQPtrList < TDEAction > alignment_actions ;
alignment_actions . append ( m_pAlignmentAction ) ;
p_ktechlab - > plugActionList ( " alignment_actionlist " , alignment_actions ) ;
}
void ItemDocument : : slotInitItemActions ( Item * item )
{
Q_UNUSED ( item ) ;
ItemView * activeItemView = dynamic_cast < ItemView * > ( activeView ( ) ) ;
if ( ! p_ktechlab | | ! activeItemView )
return ;
TDEAction * align_actions [ ] = {
activeItemView - > action ( " align_horizontally " ) ,
activeItemView - > action ( " align_vertically " ) ,
activeItemView - > action ( " distribute_horizontally " ) ,
activeItemView - > action ( " distribute_vertically " ) } ;
bool enableAlignment = selectList ( ) - > itemCount ( ) > 1 ;
for ( unsigned i = 0 ; i < 4 ; + + i )
align_actions [ i ] - > setEnabled ( enableAlignment ) ;
}
void ItemDocument : : updateBackground ( )
{
// Also used in the constructor to make the background initially.
// Thoughts.
// ~The pixmap could be done somehow with 1bpp. It might save some waste
// I expect it won't hurt for now.
// ~This is all rather static, only works with square etc... should be no prob. for most uses. IMO.
// ~If you want, decide what maximum and minimum spacing should be, then enforce them
// in the Config (I suppose you can use <max></max> tags?)
// ~Defaults based on the existing grid background png. It should produce identical results, to your
// original png.
// **** Below where it says "interval * 10", that decides how big the pixmap will be (always square)
// Originally I set this to 32, which give 256x256 with 8 spacing, as that was the size of your pixmap
// Are there any good reasons to make the a certain size? (i.e. big or small ?).
int interval = 8 ;
int bigness = interval * 10 ;
TQPixmap pm ( bigness , bigness ) ;
// pm.fill( KTLConfig::bgColor() ); // first fill the background colour in
pm . fill ( TQt : : white ) ;
if ( KTLConfig : : showGrid ( ) ) {
TQPainter p ( & pm ) ; // setup painter to draw on pixmap
p . setPen ( KTLConfig : : gridColor ( ) ) ; // set forecolour
// note: anything other than 8 borks this
for ( int i = ( interval / 2 ) ; i < bigness ; i + = interval ) {
p . drawLine ( 0 , i , bigness , i ) ; // horizontal
p . drawLine ( i , 0 , i , bigness ) ; // vertical
}
p . end ( ) ; // all done
}
pm . setDefaultOptimization ( TQPixmap : : BestOptim ) ;
m_canvas - > setBackgroundPixmap ( pm ) ; // and the finale.
}
void ItemDocument : : requestEvent ( ItemDocumentEvent : : type type )
{
m_queuedEvents | = type ;
m_pEventTimer - > stop ( ) ;
m_pEventTimer - > start ( 0 , true ) ;
}
void ItemDocument : : processItemDocumentEvents ( )
{
// Copy it incase we have new events requested while doing this...
unsigned queuedEvents = m_queuedEvents ;
m_queuedEvents = 0 ;
if ( queuedEvents & ItemDocumentEvent : : ResizeCanvasToItems )
resizeCanvasToItems ( ) ;
if ( queuedEvents & ItemDocumentEvent : : UpdateZOrdering )
slotUpdateZOrdering ( ) ;
ICNDocument * icnd = dynamic_cast < ICNDocument * > ( this ) ;
if ( icnd & & ( queuedEvents & ItemDocumentEvent : : UpdateNodeGroups ) )
icnd - > slotAssignNodeGroups ( ) ;
if ( icnd & & ( queuedEvents & ItemDocumentEvent : : RerouteInvalidatedConnectors ) )
icnd - > rerouteInvalidatedConnectors ( ) ;
}
void ItemDocument : : resizeCanvasToItems ( )
{
const ViewList : : iterator end = m_viewList . end ( ) ;
TQRect bound = canvasBoundingRect ( ) ;
TQSize size ( bound . right ( ) , bound . bottom ( ) ) ;
m_viewList . remove ( ( View * ) 0 ) ;
for ( ViewList : : iterator it = m_viewList . begin ( ) ; it ! = end ; + + it )
{
CVBEditor * cvbEditor = ( static_cast < ItemView * > ( ( View * ) * it ) ) - > cvbEditor ( ) ;
int contentsX , contentsY ;
int contentsWMX , contentsWMY ;
cvbEditor - > viewportToContents ( cvbEditor - > viewport ( ) - > width ( ) , cvbEditor - > viewport ( ) - > height ( ) , contentsX , contentsY ) ;
cvbEditor - > inverseWorldMatrix ( ) . map ( contentsX , contentsY , & contentsWMX , & contentsWMY ) ;
// Hack to fix a bug whereby when scrolled, but emoty gap before scrollbars,
// size slowly decreases one pixel at a time
if ( ( contentsX - contentsWMX ) = = 1 )
contentsWMX = contentsX ;
if ( ( contentsY - contentsWMY ) = = 1 )
contentsWMY = contentsY ;
size = size . expandedTo ( TQSize ( contentsWMX , contentsWMY ) ) ;
}
// We want to avoid flicker....
for ( ViewList : : iterator it = m_viewList . begin ( ) ; it ! = end ; + + it )
{
ItemView * itemView = static_cast < ItemView * > ( ( View * ) * it ) ;
CVBEditor * cvbEditor = itemView - > cvbEditor ( ) ;
cvbEditor - > setVScrollBarMode ( ( ( size . height ( ) * itemView - > zoomLevel ( ) ) > cvbEditor - > visibleHeight ( ) ) ? TQScrollView : : AlwaysOn : TQScrollView : : AlwaysOff ) ;
cvbEditor - > setHScrollBarMode ( ( ( size . width ( ) * itemView - > zoomLevel ( ) ) > cvbEditor - > visibleWidth ( ) ) ? TQScrollView : : AlwaysOn : TQScrollView : : AlwaysOff ) ;
}
bool changedSize = canvas ( ) - > size ( ) ! = size ;
canvas ( ) - > resize ( size . width ( ) , size . height ( ) ) ;
if ( changedSize )
requestEvent ( ItemDocumentEvent : : ResizeCanvasToItems ) ;
else if ( ICNDocument * icnd = dynamic_cast < ICNDocument * > ( this ) )
icnd - > createCellMap ( ) ;
}
TQRect ItemDocument : : canvasBoundingRect ( ) const
{
TQRect bound ;
const TQCanvasItemList allItems = canvas ( ) - > allItems ( ) ;
const TQCanvasItemList : : const_iterator end = allItems . end ( ) ;
for ( TQCanvasItemList : : const_iterator it = allItems . begin ( ) ; it ! = end ; + + it )
{
if ( ! ( * it ) - > isVisible ( ) )
continue ;
bound | = ( * it ) - > boundingRect ( ) ;
}
if ( ! bound . isNull ( ) )
{
bound . setLeft ( bound . left ( ) - 16 ) ;
bound . setTop ( bound . top ( ) - 16 ) ;
bound . setRight ( bound . right ( ) + 16 ) ;
bound . setBottom ( bound . bottom ( ) + 16 ) ;
}
return bound ;
}
void ItemDocument : : exportToImage ( )
{
// scaralously copied from print.
// this slot is called whenever the File->Export menu is selected,
// the Export shortcut is pressed or the Export toolbar
// button is clicked
// widget for the tdefiledialog
// It is the bit that says "Crop circuit?"
// Okay need to think of something way better to say here.
// gotme here, KFileDialog makes itself parent so tries to destroy cropCheck when it is deleted.
// therefore we use a pointer.
TQString cropMessage ;
if ( type ( ) = = Document : : dt_flowcode )
cropMessage = i18n ( " Crop image to program parts " ) ;
else if ( type ( ) = = Document : : dt_circuit )
cropMessage = i18n ( " Crop image to circuit components " ) ;
else
cropMessage = i18n ( " Crop image " ) ;
TQCheckBox * cropCheck = new TQCheckBox ( cropMessage , p_ktechlab , " cropCheck " ) ;
cropCheck - > setChecked ( true ) ; // yes by default?
// we need an object so we can retrieve which image type was selected by the user
// so setup the filedialog.
KFileDialog exportDialog ( TQString ( ) , " *.png|PNG Image \n *.bmp|BMP Image \n *.svg|SVG Image " , p_ktechlab , i18n ( " Export As Image " ) , true , cropCheck ) ;
exportDialog . setOperationMode ( KFileDialog : : Saving ) ;
// now actually show it
if ( exportDialog . exec ( ) = = TQDialog : : Rejected )
return ;
KURL url = exportDialog . selectedURL ( ) ;
if ( url . isEmpty ( ) )
return ;
if ( TQFile : : exists ( url . path ( ) ) )
{
int query = KMessageBox : : warningYesNo ( p_ktechlab , i18n ( " A file named \" %1 \" already exists. " " Are you sure you want to overwrite it? " ) . arg ( url . fileName ( ) ) , i18n ( " Overwrite File? " ) , i18n ( " Overwrite " ) , KStdGuiItem : : cancel ( ) ) ;
if ( query = = KMessageBox : : No ) return ;
}
// with TQt, you always "print" to a
// TQPainter.. whether the output medium is a pixmap, a screen,
// or paper
// needs to be something like TQPicture to do SVG etc...
// at the moment the pixmap is just as big as the canvas,
// intend to make some kind of cropping thing so it just
// takes the bit with the circuit on.
TQRect saveArea ;
TQString type ;
TQRect cropArea ;
TQPaintDevice * outputImage ;
TQString filter = exportDialog . currentFilter ( ) ;
filter = filter . lower ( ) ; // gently soften the appearance of the letters.
// did have a switch here but seems you can't use that on strings
if ( filter = = " *.png " )
type = " PNG " ;
else if ( filter = = " *.bmp " )
type = " BMP " ;
else if ( filter = = " *.svg " )
{
KMessageBox : : information ( NULL , i18n ( " SVG export is sub-functional " ) , i18n ( " Export As Image " ) ) ;
type = " SVG " ;
}
// I don't like forcing people to use the right extension (personally)
// but it is the easiest way to decide image type.
else
{
KMessageBox : : sorry ( NULL , i18n ( " Unknown extension, please select one from the filter list. " ) , i18n ( " Export As Image " ) ) ;
return ;
}
if ( cropCheck - > isChecked ( ) )
{
cropArea = canvasBoundingRect ( ) ;
if ( cropArea . isNull ( ) )
{
KMessageBox : : sorry ( 0l , i18n ( " There is nothing to crop " ) , i18n ( " Export As Image " ) ) ;
return ;
}
else
{
cropArea & = canvas ( ) - > rect ( ) ;
}
}
saveArea = m_canvas - > rect ( ) ;
if ( type = = " PNG " | | type = = " BMP " )
outputImage = TQT_TQPAINTDEVICE ( new TQPixmap ( saveArea . size ( ) ) ) ;
else if ( type = = " SVG " )
{
setSVGExport ( true ) ;
outputImage = TQT_TQPAINTDEVICE ( new TQPicture ( ) ) ;
// svg can't be cropped using the qimage method.
saveArea = cropArea ;
}
else
{
kdWarning ( ) < < " Unknown type! " < < endl ;
return ;
}
TQPainter p ( outputImage ) ;
m_canvas - > setBackgroundPixmap ( TQPixmap ( ) ) ;
m_canvas - > drawArea ( saveArea , & p ) ;
updateBackground ( ) ;
p . end ( ) ;
bool saveResult ;
// if cropping we need to convert to an image,
// crop, then save.
if ( cropCheck - > isChecked ( ) )
{
if ( type = = " SVG " )
saveResult = dynamic_cast < TQPicture * > ( outputImage ) - > save ( url . path ( ) , type ) ;
else
{
TQImage img = dynamic_cast < TQPixmap * > ( outputImage ) - > convertToImage ( ) ;
img = img . copy ( cropArea ) ;
saveResult = img . save ( url . path ( ) , type ) ;
}
}
else
{
if ( type = = " SVG " )
saveResult = dynamic_cast < TQPicture * > ( outputImage ) - > save ( url . path ( ) , type ) ;
else
saveResult = dynamic_cast < TQPixmap * > ( outputImage ) - > save ( url . path ( ) , type ) ;
}
//if(saveResult == true) KMessageBox::information( this, i18n("Sucessfully exported to \"%1\"").arg( url.filename() ), i18n("Image Export") );
//else KMessageBox::information( this, i18n("Export failed"), i18n("Image Export") );
if ( type = = " SVG " )
setSVGExport ( false ) ;
if ( saveResult = = false )
KMessageBox : : information ( p_ktechlab , i18n ( " Export failed " ) , i18n ( " Image Export " ) ) ;
delete outputImage ;
}
void ItemDocument : : setSVGExport ( bool svgExport )
{
// Find any items and tell them not to draw buttons or sliders
TQCanvasItemList items = m_canvas - > allItems ( ) ;
const TQCanvasItemList : : iterator end = items . end ( ) ;
for ( TQCanvasItemList : : Iterator it = items . begin ( ) ; it ! = end ; + + it )
{
if ( CNItem * cnItem = dynamic_cast < CNItem * > ( * it ) )
cnItem - > setDrawWidgets ( ! svgExport ) ;
}
}
void ItemDocument : : raiseZ ( )
{
raiseZ ( selectList ( ) - > items ( true ) ) ;
}
void ItemDocument : : raiseZ ( const ItemList & itemList )
{
if ( m_zOrder . isEmpty ( ) )
slotUpdateZOrdering ( ) ;
if ( m_zOrder . isEmpty ( ) )
return ;
IntItemMap : : iterator begin = m_zOrder . begin ( ) ;
IntItemMap : : iterator previous = m_zOrder . end ( ) ;
IntItemMap : : iterator it = - - m_zOrder . end ( ) ;
do
{
Item * previousData = ( previous = = m_zOrder . end ( ) ) ? 0l : previous . data ( ) ;
Item * currentData = it . data ( ) ;
if ( currentData & & previousData & & itemList . contains ( currentData ) & & ! itemList . contains ( previousData ) )
{
previous . data ( ) = currentData ;
it . data ( ) = previousData ;
}
previous = it ;
- - it ;
}
while ( previous ! = begin ) ;
slotUpdateZOrdering ( ) ;
}
void ItemDocument : : lowerZ ( )
{
lowerZ ( selectList ( ) - > items ( true ) ) ;
}
void ItemDocument : : lowerZ ( const ItemList & itemList )
{
if ( m_zOrder . isEmpty ( ) )
slotUpdateZOrdering ( ) ;
if ( m_zOrder . isEmpty ( ) )
return ;
IntItemMap : : iterator previous = m_zOrder . begin ( ) ;
IntItemMap : : iterator end = m_zOrder . end ( ) ;
for ( IntItemMap : : iterator it = m_zOrder . begin ( ) ; it ! = end ; + + it )
{
Item * previousData = previous . data ( ) ;
Item * currentData = it . data ( ) ;
if ( currentData & & previousData & & itemList . contains ( currentData ) & & ! itemList . contains ( previousData ) )
{
previous . data ( ) = currentData ;
it . data ( ) = previousData ;
}
previous = it ;
}
slotUpdateZOrdering ( ) ;
}
void ItemDocument : : itemAdded ( Item * )
{
requestEvent ( ItemDocument : : ItemDocumentEvent : : UpdateZOrdering ) ;
}
void ItemDocument : : slotUpdateZOrdering ( )
{
ItemList toAdd = m_itemList ;
toAdd . remove ( ( Item * ) 0l ) ;
IntItemMap newZOrder ;
int atLevel = 0 ;
IntItemMap : : iterator zEnd = m_zOrder . end ( ) ;
for ( IntItemMap : : iterator it = m_zOrder . begin ( ) ; it ! = zEnd ; + + it )
{
Item * item = it . data ( ) ;
if ( ! item )
continue ;
toAdd . remove ( item ) ;
if ( ! item - > parentItem ( ) & & item - > isMovable ( ) )
newZOrder [ atLevel + + ] = item ;
}
ItemList : : iterator addEnd = toAdd . end ( ) ;
for ( ItemList : : iterator it = toAdd . begin ( ) ; it ! = addEnd ; + + it )
{
Item * item = * it ;
if ( item - > parentItem ( ) | | ! item - > isMovable ( ) )
continue ;
newZOrder [ atLevel + + ] = item ;
}
m_zOrder = newZOrder ;
zEnd = m_zOrder . end ( ) ;
for ( IntItemMap : : iterator it = m_zOrder . begin ( ) ; it ! = zEnd ; + + it )
it . data ( ) - > updateZ ( it . key ( ) ) ;
}
void ItemDocument : : update ( )
{
ItemList : : iterator end = m_itemList . end ( ) ;
for ( ItemList : : iterator it = m_itemList . begin ( ) ; it ! = end ; + + it )
{
if ( ( * it ) - > hasDynamicContent ( ) )
( * it ) - > setChanged ( ) ;
}
}
//END class ItemDocument
//BEGIN class CanvasTip
CanvasTip : : CanvasTip ( ItemDocument * itemDocument , TQCanvas * qcanvas )
: TQCanvasText ( qcanvas )
{
p_itemDocument = itemDocument ;
setColor ( TQt : : black ) ;
setZ ( ICNDocument : : Z : : Tip ) ;
}
CanvasTip : : ~ CanvasTip ( )
{
}
void CanvasTip : : displayVI ( ECNode * node , const TQPoint & pos )
{
if ( ! node | | ! updateVI ( ) )
return ;
unsigned num = node - > numPins ( ) ;
m_v . resize ( num ) ;
m_i . resize ( num ) ;
for ( unsigned i = 0 ; i < num ; i + + )
{
if ( Pin * pin = node - > pin ( i ) )
{
m_v [ i ] = pin - > voltage ( ) ;
m_i [ i ] = pin - > current ( ) ;
}
}
display ( pos ) ;
}
void CanvasTip : : displayVI ( Connector * connector , const TQPoint & pos )
{
if ( ! connector | | ! updateVI ( ) )
return ;
unsigned num = connector - > numWires ( ) ;
m_v . resize ( num ) ;
m_i . resize ( num ) ;
for ( unsigned i = 0 ; i < num ; i + + )
{
if ( Wire * wire = connector - > wire ( i ) )
{
m_v [ i ] = wire - > voltage ( ) ;
m_i [ i ] = std : : abs ( wire - > current ( ) ) ;
}
}
display ( pos ) ;
}
bool CanvasTip : : updateVI ( )
{
CircuitDocument * circuitDocument = dynamic_cast < CircuitDocument * > ( p_itemDocument ) ;
if ( ! circuitDocument | | ! Simulator : : self ( ) - > isSimulating ( ) )
return false ;
circuitDocument - > calculateConnectorCurrents ( ) ;
return true ;
}
void CanvasTip : : display ( const TQPoint & pos )
{
unsigned num = m_v . size ( ) ;
for ( unsigned i = 0 ; i < num ; i + + )
{
if ( ! std : : isfinite ( m_v [ i ] ) | | std : : abs ( m_v [ i ] ) < 1e-9 )
m_v [ i ] = 0. ;
if ( ! std : : isfinite ( m_i [ i ] ) | | std : : abs ( m_i [ i ] ) < 1e-9 )
m_i [ i ] = 0. ;
}
move ( pos . x ( ) + 20 , pos . y ( ) + 4 ) ;
if ( num = = 0 )
return ;
if ( num = = 1 )
setText ( displayText ( 0 ) ) ;
else
{
TQString text ;
for ( unsigned i = 0 ; i < num ; i + + )
text + = TQString ( " %1: %2 \n " ) . arg ( TQString : : number ( i ) ) . arg ( displayText ( i ) ) ;
setText ( text ) ;
}
}
TQString CanvasTip : : displayText ( unsigned num ) const
{
if ( m_v . size ( ) < = num )
return TQString ( ) ;
return TQString ( " %1%2V %3%4A " )
. arg ( TQString : : number ( m_v [ num ] / CNItem : : getMultiplier ( m_v [ num ] ) , ' g ' , 3 ) )
. arg ( CNItem : : getNumberMag ( m_v [ num ] ) )
. arg ( TQString : : number ( m_i [ num ] / CNItem : : getMultiplier ( m_i [ num ] ) , ' g ' , 3 ) )
. arg ( CNItem : : getNumberMag ( m_i [ num ] ) ) ;
}
void CanvasTip : : draw ( TQPainter & p )
{
CircuitDocument * circuitDocument = dynamic_cast < CircuitDocument * > ( p_itemDocument ) ;
if ( ! circuitDocument | | ! Simulator : : self ( ) - > isSimulating ( ) )
return ;
p . setBrush ( TQColor ( 0xff , 0xff , 0xdc ) ) ;
p . setPen ( TQt : : black ) ;
p . drawRect ( boundingRect ( ) ) ;
TQCanvasText : : draw ( p ) ;
}
//END class CanvasTip
//BEGIN class Canvas
Canvas : : Canvas ( ItemDocument * itemDocument , const char * name )
: TQCanvas ( itemDocument , name )
{
p_itemDocument = itemDocument ;
m_pMessageTimeout = new TQTimer ( this ) ;
connect ( m_pMessageTimeout , TQT_SIGNAL ( timeout ( ) ) , this , TQT_SLOT ( slotSetAllChanged ( ) ) ) ;
}
void Canvas : : setMessage ( const TQString & message )
{
m_message = message ;
if ( message . isEmpty ( ) )
m_pMessageTimeout - > stop ( ) ;
else
m_pMessageTimeout - > start ( 2000 , true ) ;
setAllChanged ( ) ;
}
void Canvas : : drawBackground ( TQPainter & p , const TQRect & clip )
{
TQCanvas : : drawBackground ( p , clip ) ;
#if 0
const int scx = ( int ) ( ( clip . left ( ) - 4 ) / 8 ) ;
const int ecx = ( int ) ( ( clip . right ( ) + 4 ) / 8 ) ;
const int scy = ( int ) ( ( clip . top ( ) - 4 ) / 8 ) ;
const int ecy = ( int ) ( ( clip . bottom ( ) + 4 ) / 8 ) ;
if ( ! ( ( ICNDocument * ) ( p_itemDocument ) ) - > isValidCellReference ( scx , scy ) | |
! ( ( ICNDocument * ) ( p_itemDocument ) ) - > isValidCellReference ( ecx , ecy ) ) return ;
Cells * c = ( ( ICNDocument * ) ( p_itemDocument ) ) - > cells ( ) ;
for ( int x = scx ; x < = ecx ; x + + )
{
for ( int y = scy ; y < = ecy ; y + + )
{
const double score = ( * c ) [ x ] [ y ] . CIpenalty + ( * c ) [ x ] [ y ] . Cpenalty ;
int value = ( int ) std : : log ( score ) * 20 ;
if ( value > 255 ) value = 255 ;
else if ( value < 0 ) value = 0 ;
p . setBrush ( TQColor ( 255 , ( 255 - value ) , ( 255 - value ) ) ) ;
p . setPen ( TQt : : NoPen ) ;
p . drawRect ( ( x * 8 ) , ( y * 8 ) , 8 , 8 ) ;
}
}
# endif
}
void Canvas : : drawForeground ( TQPainter & p , const TQRect & clip )
{
TQCanvas : : drawForeground ( p , clip ) ;
if ( ! m_pMessageTimeout - > isActive ( ) )
return ;
// Following code stolen and adapted from amarok/src/playlist.cpp :)
// Find out width of smallest view
TQSize minSize ;
const ViewList viewList = p_itemDocument - > viewList ( ) ;
ViewList : : const_iterator end = viewList . end ( ) ;
View * firstView = 0l ;
for ( ViewList : : const_iterator it = viewList . begin ( ) ; it ! = end ; + + it )
{
if ( ! * it )
continue ;
if ( ! firstView )
{
firstView = * it ;
minSize = ( * it ) - > size ( ) ;
}
else
minSize = minSize . boundedTo ( ( * it ) - > size ( ) ) ;
}
if ( ! firstView )
return ;
TQSimpleRichText * t = new TQSimpleRichText ( m_message , TQApplication : : font ( ) ) ;
int w = t - > width ( ) ;
int h = t - > height ( ) ;
int x = 15 ;
int y = 15 ;
int b = 10 ; // text padding
if ( w + 2 * b > = minSize . width ( ) | | h + 2 * b > = minSize . height ( ) )
{
delete t ;
return ;
}
p . setBrush ( firstView - > colorGroup ( ) . background ( ) ) ;
p . drawRoundRect ( x , y , w + 2 * b , h + 2 * b , ( 8 * 200 ) / ( w + 2 * b ) , ( 8 * 200 ) / ( h + 2 * b ) ) ;
t - > draw ( & p , x + b , y + b , TQRect ( ) , firstView - > colorGroup ( ) ) ;
delete t ;
}
void Canvas : : update ( )
{
p_itemDocument - > update ( ) ;
TQCanvas : : update ( ) ;
}
//END class Canvas
# include "itemdocument.moc"