File indexing completed on 2024-04-28 13:39:36

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "canvasitemparts.h"
0012 #include "canvasmanipulator.h"
0013 #include "cells.h"
0014 #include "circuitdocument.h"
0015 #include "cnitem.h"
0016 #include "connector.h"
0017 #include "drawpart.h"
0018 #include "ecnode.h"
0019 #include "flowcodedocument.h"
0020 #include "icnview.h"
0021 #include "imageexportdlg.h"
0022 #include "itemdocumentdata.h"
0023 #include "itemgroup.h"
0024 #include "itemselector.h"
0025 #include "ktechlab.h"
0026 #include "pin.h"
0027 #include "resizeoverlay.h"
0028 #include "simulator.h"
0029 
0030 #include <KLocalizedString>
0031 #include <KMessageBox>
0032 // #include <k3popupmenu.h>
0033 //#include <kprinter.h>
0034 #include <KActionMenu>
0035 #include <KXMLGUIFactory>
0036 
0037 #include <QApplication>
0038 #include <QCheckBox>
0039 #include <QClipboard>
0040 #include <QCursor>
0041 #include <QImage>
0042 #include <QMenu>
0043 // #include <q3paintdevicemetrics.h>
0044 #include <QPainter>
0045 #include <QPicture>
0046 #include <QRegExp>
0047 // #include <q3simplerichtext.h> // 2018.08.13 - not needed
0048 #include <QFile>
0049 #include <QPrintDialog>
0050 #include <QPrinter>
0051 #include <QTextEdit>
0052 #include <QTimer>
0053 
0054 #include <cassert>
0055 #include <cmath>
0056 
0057 #include <ktlconfig.h>
0058 #include <ktechlab_debug.h>
0059 
0060 // BEGIN class ItemDocument
0061 int ItemDocument::m_nextActionTicket = 0;
0062 
0063 ItemDocument::ItemDocument(const QString &caption)
0064     : Document(caption)
0065 {
0066     m_queuedEvents = 0;
0067     m_nextIdNum = 1;
0068     m_savedState = nullptr;
0069     m_currentState = nullptr;
0070     m_bIsLoading = false;
0071 
0072     m_canvas = new Canvas(this);
0073     m_canvas->setObjectName("canvas");
0074     m_canvasTip = new CanvasTip(this, m_canvas);
0075     m_cmManager = new CMManager(this);
0076 
0077     updateBackground();
0078 
0079     m_pUpdateItemViewScrollbarsTimer = new QTimer(this);
0080     connect(m_pUpdateItemViewScrollbarsTimer, &QTimer::timeout, this, &ItemDocument::updateItemViewScrollbars);
0081 
0082     m_pEventTimer = new QTimer(this);
0083     connect(m_pEventTimer, &QTimer::timeout, this, &ItemDocument::processItemDocumentEvents);
0084 
0085     connect(this, &ItemDocument::selectionChanged, this, &ItemDocument::slotInitItemActions);
0086 
0087     connect(ComponentSelector::self(), qOverload<const QString &>(&ComponentSelector::itemClicked), this, &ItemDocument::slotUnsetRepeatedItemId);
0088     connect(FlowPartSelector::self(), qOverload<const QString &>(&FlowPartSelector::itemClicked), this, &ItemDocument::slotUnsetRepeatedItemId);
0089 
0090 #ifdef MECHANICS
0091     connect(MechanicsSelector::self(), SIGNAL(itemClicked(const QString &)), this, SLOT(slotUnsetRepeatedItemId()));
0092 #endif
0093 
0094     m_pAlignmentAction = new KActionMenu(i18n("Alignment") /*, "format-justify-right" */, this);
0095     m_pAlignmentAction->setObjectName("rightjust");
0096     m_pAlignmentAction->setIcon(QIcon::fromTheme("format-justify-right"));
0097 
0098     slotUpdateConfiguration();
0099 }
0100 
0101 ItemDocument::~ItemDocument()
0102 {
0103     m_bDeleted = true;
0104 
0105     //  ItemMap toDelete = m_itemList;
0106 
0107     const ItemMap::iterator end = m_itemList.end();
0108     for (ItemMap::iterator it = m_itemList.begin(); it != end; ++it) {
0109         qCDebug(KTL_LOG) << "ItemDocument::~ItemDocument: deleting [" << it.key() << "] " << it.value();
0110         // delete *it; // 2015.07.31 - this will crash
0111         it.value()->deleteLater();
0112     }
0113     m_itemList.clear();
0114 
0115     cleanClearStack(m_undoStack);
0116     cleanClearStack(m_redoStack);
0117 
0118     delete m_cmManager;
0119     delete m_currentState;
0120     delete m_canvasTip;
0121 }
0122 
0123 void ItemDocument::handleNewView(View *view)
0124 {
0125     Document::handleNewView(view);
0126     requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
0127 }
0128 
0129 bool ItemDocument::registerItem(KtlQCanvasItem *qcanvasItem)
0130 {
0131     if (!qcanvasItem)
0132         return false;
0133 
0134     requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
0135 
0136     if (Item *item = dynamic_cast<Item *>(qcanvasItem)) {
0137         m_itemList[item->id()] = item;
0138         connect(item, &Item::selectionChanged, this, &ItemDocument::selectionChanged);
0139         itemAdded(item);
0140         return true;
0141     }
0142 
0143     return false;
0144 }
0145 
0146 void ItemDocument::slotSetDrawAction(QAction *selected)
0147 {
0148     int drawAction = selected->data().toInt();
0149     m_cmManager->setDrawAction(drawAction);
0150 }
0151 
0152 void ItemDocument::cancelCurrentOperation()
0153 {
0154     m_cmManager->cancelCurrentManipulation();
0155 }
0156 
0157 void ItemDocument::slotSetRepeatedItemId(const QString &id)
0158 {
0159     m_cmManager->setCMState(CMManager::cms_repeated_add, true);
0160     m_cmManager->setRepeatedAddId(id);
0161 }
0162 
0163 void ItemDocument::slotUnsetRepeatedItemId()
0164 {
0165     m_cmManager->setCMState(CMManager::cms_repeated_add, false);
0166 }
0167 
0168 void ItemDocument::fileSave()
0169 {
0170     if (url().isEmpty() && !getURL(m_fileExtensionInfo, m_fileExtensionValue))
0171         return;
0172     writeFile();
0173 }
0174 
0175 void ItemDocument::fileSaveAs()
0176 {
0177     if (!getURL(m_fileExtensionInfo, m_fileExtensionValue))
0178         return;
0179     writeFile();
0180 
0181     // Our modified state may not have changed, but we emit this to force the
0182     // main window to update our caption.
0183     emit modifiedStateChanged();
0184 }
0185 
0186 void ItemDocument::writeFile()
0187 {
0188     ItemDocumentData data(type());
0189     data.saveDocumentState(this);
0190 
0191     if (data.saveData(url())) {
0192         m_savedState = m_currentState;
0193         setModified(false);
0194     }
0195 }
0196 
0197 bool ItemDocument::openURL(const QUrl &url)
0198 {
0199     ItemDocumentData data(type());
0200 
0201     if (!data.loadData(url))
0202         return false;
0203 
0204     // Why do we stop simulating while loading a document?
0205     // Crash possible when loading a circuit document, and the Qt event loop is
0206     // reentered (such as when a PIC component pops-up a message box), which
0207     // will then call the Simulator::step function, which might use components
0208     // that have not fully initialized themselves.
0209 
0210     m_bIsLoading = true;
0211     bool wasSimulating = Simulator::self()->isSimulating();
0212     Simulator::self()->slotSetSimulating(false);
0213     data.restoreDocument(this);
0214     Simulator::self()->slotSetSimulating(wasSimulating);
0215     m_bIsLoading = false;
0216 
0217     setURL(url);
0218     clearHistory();
0219     m_savedState = m_currentState;
0220     setModified(false);
0221 
0222     if (FlowCodeDocument *fcd = dynamic_cast<FlowCodeDocument *>(this)) {
0223         // We need to tell all pic-depedent components about what pic type is in use
0224         emit fcd->picTypeChanged();
0225     }
0226 
0227     requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
0228 
0229     // Load Z-position info
0230     m_zOrder.clear();
0231     ItemMap::iterator end = m_itemList.end();
0232     for (ItemMap::iterator it = m_itemList.begin(); it != end; ++it) {
0233         if (!*it || (*it)->parentItem())
0234             continue;
0235 
0236         m_zOrder[(*it)->baseZ()] = *it;
0237     }
0238     slotUpdateZOrdering();
0239 
0240     return true;
0241 }
0242 
0243 void ItemDocument::print()
0244 {
0245     static QPrinter *printer = new QPrinter;
0246 
0247     // if ( ! printer->setup( KTechlab::self() ) )
0248     //  return;
0249     QPrintDialog printDialog(printer, KTechlab::self());
0250     if (!printDialog.exec()) {
0251         return;
0252     }
0253 
0254     // setup the printer.  with Qt, you always "print" to a
0255     // QPainter.. whether the output medium is a pixmap, a screen,
0256     // or paper
0257     QPainter p;
0258     p.begin(printer);
0259 
0260     // we let our view do the actual printing
0261     // Q3PaintDeviceMetrics metrics( printer ); // 2018.08.13 - replaced with method call
0262     QRect pageRect = printer->pageLayout().paintRectPixels(printer->resolution());
0263 
0264     // Round to 16 so that we cut in the middle of squares
0265     int w = pageRect.width();
0266     w = (w & 0xFFFFFFF0) + ((w << 1) & 0x10);
0267 
0268     int h = pageRect.height();
0269     h = (h & 0xFFFFFFF0) + ((h << 1) & 0x10);
0270 
0271     p.setClipping(true);
0272     p.setClipRect(0, 0, w, h, /* QPainter::CoordPainter */ Qt::ReplaceClip); // TODO is this correct?
0273 
0274     // Send off the painter for drawing
0275     // note: What was this doing?? // set "null" background, so the background horiznotal and vertial lines are not visible
0276     m_canvas->setBackgroundPixmap(QPixmap(0, 0) /* 0 */);
0277 
0278     QRect bounding = canvasBoundingRect();
0279     unsigned int rows = unsigned(std::ceil(double(bounding.height()) / double(h)));
0280     unsigned int cols = unsigned(std::ceil(double(bounding.width()) / double(w)));
0281     int offset_x = bounding.x();
0282     int offset_y = bounding.y();
0283 
0284     for (unsigned row = 0; row < rows; ++row) {
0285         for (unsigned col = 0; col < cols; ++col) {
0286             if (row != 0 || col != 0)
0287                 printer->newPage();
0288 
0289             QRect drawArea(offset_x + (col * w), offset_y + (row * h), w, h);
0290             m_canvas->drawArea(drawArea, &p);
0291 
0292             p.translate(-w, 0);
0293         }
0294         p.translate(w * cols, -h);
0295     }
0296 
0297     updateBackground();
0298 
0299     // and send the result to the printer
0300     p.end();
0301 }
0302 
0303 void ItemDocument::requestStateSave(int actionTicket)
0304 {
0305     if (m_bIsLoading)
0306         return;
0307 
0308     cleanClearStack(m_redoStack);
0309 
0310     if ((actionTicket >= 0) && (actionTicket == m_currentActionTicket)) {
0311         delete m_currentState;
0312         m_currentState = nullptr;
0313     }
0314 
0315     m_currentActionTicket = actionTicket;
0316 
0317     // FIXME: it is possible, that we push something here, also nothing has changed, yet.
0318     // to reproduce do:
0319     // 1. select an item -> something is pushed onto undoStack, but nothing changed
0320     // 2. select Undo -> pushed on redoStack, pop from undoStack
0321     // 3. deselect item -> there is still something on the redoStack
0322     //
0323     // this way you can fill up the redoStack, as you like :-/
0324     if (m_currentState)
0325         m_undoStack.push(m_currentState);
0326 
0327     m_currentState = new ItemDocumentData(type());
0328     m_currentState->saveDocumentState(this);
0329 
0330     if (!m_savedState)
0331         m_savedState = m_currentState;
0332 
0333     setModified(m_savedState != m_currentState);
0334 
0335     emit undoRedoStateChanged();
0336 
0337     // FIXME To resize undo queue, have to pop and push everything
0338     // In Qt4 QStack is used and QStack inherits QVector, that should
0339     // make it a bit more easy
0340     int maxUndo = KTLConfig::maxUndo();
0341     if (maxUndo <= 0 || m_undoStack.count() < maxUndo)
0342         return;
0343     IDDStack tempStack;
0344     int pushed = 0;
0345     while (!m_undoStack.isEmpty() && pushed < maxUndo) {
0346         tempStack.push(m_undoStack.pop());
0347         pushed++;
0348     }
0349     cleanClearStack(m_undoStack);
0350     while (!tempStack.isEmpty())
0351         m_undoStack.push(tempStack.pop());
0352 }
0353 
0354 void ItemDocument::cleanClearStack(IDDStack &stack)
0355 {
0356     while (!stack.isEmpty()) {
0357         ItemDocumentData *idd = stack.pop();
0358         if (m_currentState != idd)
0359             delete idd;
0360     }
0361 }
0362 
0363 void ItemDocument::clearHistory()
0364 {
0365     cleanClearStack(m_undoStack);
0366     cleanClearStack(m_redoStack);
0367     delete m_currentState;
0368     m_currentState = nullptr;
0369     requestStateSave();
0370 }
0371 
0372 bool ItemDocument::isUndoAvailable() const
0373 {
0374     return !m_undoStack.isEmpty();
0375 }
0376 
0377 bool ItemDocument::isRedoAvailable() const
0378 {
0379     return !m_redoStack.isEmpty();
0380 }
0381 
0382 void ItemDocument::undo()
0383 {
0384     if (m_undoStack.empty()) {
0385         return;
0386     }
0387     ItemDocumentData *idd = m_undoStack.pop();
0388     if (!idd)
0389         return;
0390 
0391     if (m_currentState)
0392         m_redoStack.push(m_currentState);
0393 
0394     idd->restoreDocument(this);
0395     m_currentState = idd;
0396 
0397     setModified(m_savedState != m_currentState);
0398     emit undoRedoStateChanged();
0399 }
0400 
0401 void ItemDocument::redo()
0402 {
0403     if (m_redoStack.empty()) {
0404         return;
0405     }
0406     ItemDocumentData *idd = m_redoStack.pop();
0407     if (!idd)
0408         return;
0409 
0410     if (m_currentState)
0411         m_undoStack.push(m_currentState);
0412 
0413     idd->restoreDocument(this);
0414     m_currentState = idd;
0415 
0416     setModified(m_savedState != m_currentState);
0417     emit undoRedoStateChanged();
0418 }
0419 
0420 void ItemDocument::cut()
0421 {
0422     copy();
0423     deleteSelection();
0424 }
0425 
0426 void ItemDocument::paste()
0427 {
0428     QString xml = QApplication::clipboard()->text(QClipboard::Clipboard);
0429     if (xml.isEmpty())
0430         return;
0431 
0432     unselectAll();
0433 
0434     ItemDocumentData data(type());
0435 
0436     if (!data.fromXML(xml))
0437         return;
0438 
0439     data.generateUniqueIDs(this);
0440     //  data.translateContents( 64, 64 );
0441     data.mergeWithDocument(this, true);
0442 
0443     // Get rid of any garbage that shouldn't be around / merge connectors / etc
0444     flushDeleteList();
0445 
0446     requestStateSave();
0447 }
0448 
0449 Item *ItemDocument::itemWithID(const QString &id)
0450 {
0451     if (m_itemList.contains(id))
0452         return m_itemList[id];
0453     else
0454         return nullptr;
0455 }
0456 
0457 void ItemDocument::unselectAll()
0458 {
0459     selectList()->removeAllItems();
0460 }
0461 
0462 void ItemDocument::select(KtlQCanvasItem *item)
0463 {
0464     if (!item)
0465         return;
0466 
0467     item->setSelected(selectList()->contains(item) || selectList()->addQCanvasItem(item));
0468 }
0469 
0470 void ItemDocument::select(const KtlQCanvasItemList &list)
0471 {
0472     const KtlQCanvasItemList::const_iterator end = list.end();
0473     for (KtlQCanvasItemList::const_iterator it = list.begin(); it != end; ++it)
0474         selectList()->addQCanvasItem(*it);
0475 
0476     selectList()->setSelected(true);
0477 }
0478 
0479 void ItemDocument::unselect(KtlQCanvasItem *qcanvasItem)
0480 {
0481     selectList()->removeQCanvasItem(qcanvasItem);
0482     qcanvasItem->setSelected(false);
0483 }
0484 
0485 void ItemDocument::slotUpdateConfiguration()
0486 {
0487     updateBackground();
0488     m_canvas->setUpdatePeriod(int(1000. / KTLConfig::refreshRate()));
0489 }
0490 
0491 KtlQCanvasItem *ItemDocument::itemAtTop(const QPoint &pos) const
0492 {
0493     KtlQCanvasItemList list = m_canvas->collisions(QRect(pos.x() - 1, pos.y() - 1, 3, 3)); // note: m_canvas is actually modified here
0494     KtlQCanvasItemList::const_iterator it = list.begin();
0495     const KtlQCanvasItemList::const_iterator end = list.end();
0496 
0497     while (it != end) {
0498         KtlQCanvasItem *item = *it;
0499         if (!dynamic_cast<Item *>(item) && !dynamic_cast<ConnectorLine *>(item) && !dynamic_cast<Node *>(item) && !dynamic_cast<Widget *>(item) && !dynamic_cast<ResizeHandle *>(item)) {
0500             ++it;
0501         } else {
0502             if (ConnectorLine *l = dynamic_cast<ConnectorLine *>(item))
0503                 return l->parent();
0504 
0505             return item;
0506         }
0507     }
0508 
0509     return nullptr;
0510 }
0511 
0512 // these look dangerous., see todo in header file.
0513 void ItemDocument::alignHorizontally()
0514 {
0515     selectList()->slotAlignHorizontally();
0516     if (ICNDocument *icnd = dynamic_cast<ICNDocument *>(this))
0517         icnd->requestRerouteInvalidatedConnectors();
0518 }
0519 
0520 void ItemDocument::alignVertically()
0521 {
0522     selectList()->slotAlignVertically();
0523     if (ICNDocument *icnd = dynamic_cast<ICNDocument *>(this))
0524         icnd->requestRerouteInvalidatedConnectors();
0525 }
0526 
0527 void ItemDocument::distributeHorizontally()
0528 {
0529     selectList()->slotDistributeHorizontally();
0530     if (ICNDocument *icnd = dynamic_cast<ICNDocument *>(this))
0531         icnd->requestRerouteInvalidatedConnectors();
0532 }
0533 
0534 void ItemDocument::distributeVertically()
0535 {
0536     selectList()->slotDistributeVertically();
0537     if (ICNDocument *icnd = dynamic_cast<ICNDocument *>(this))
0538         icnd->requestRerouteInvalidatedConnectors();
0539 }
0540 // ###########################
0541 
0542 bool ItemDocument::registerUID(const QString &UID)
0543 {
0544     return m_idList.insert(UID).second;
0545 }
0546 
0547 void ItemDocument::unregisterUID(const QString &uid)
0548 {
0549     m_idList.erase(uid);
0550     m_itemList.remove(uid);
0551 }
0552 
0553 QString ItemDocument::generateUID(QString name)
0554 {
0555     name.remove(QRegExp("__.*")); // Change 'node__13' to 'node', for example
0556     QString idAttempt = name;
0557 
0558     while (!registerUID(idAttempt))
0559         idAttempt = name + "__" + QString::number(m_nextIdNum++);
0560 
0561     return idAttempt;
0562 }
0563 
0564 // FIXME: popup menu doesn't seem to work these days. =(
0565 void ItemDocument::canvasRightClick(const QPoint &pos, KtlQCanvasItem *item)
0566 {
0567     if (item) {
0568         if (dynamic_cast<CNItem *>(item) && !item->isSelected()) {
0569             unselectAll();
0570             select(item);
0571         }
0572     }
0573 
0574     KTechlab::self()->unplugActionList("alignment_actionlist");
0575     KTechlab::self()->unplugActionList("orientation_actionlist");
0576     fillContextMenu(pos);
0577 
0578     QMenu *pop = static_cast<QMenu *>(KTechlab::self()->factory()->container("item_popup", KTechlab::self()));
0579 
0580     if (!pop)
0581         return;
0582 
0583     pop->popup(pos);
0584 }
0585 
0586 void ItemDocument::fillContextMenu(const QPoint &pos)
0587 {
0588     Q_UNUSED(pos);
0589 
0590     ItemView *activeItemView = dynamic_cast<ItemView *>(activeView());
0591     if (!KTechlab::self() || !activeItemView)
0592         return;
0593 
0594     QAction *align_actions[] = {
0595         activeItemView->actionByName("align_horizontally"), activeItemView->actionByName("align_vertically"), activeItemView->actionByName("distribute_horizontally"), activeItemView->actionByName("distribute_vertically")};
0596 
0597     bool enableAlignment = selectList()->itemCount() > 1;
0598 
0599     if (!enableAlignment)
0600         return;
0601 
0602     for (unsigned i = 0; i < 4; ++i) {
0603         align_actions[i]->setEnabled(true);
0604         m_pAlignmentAction->removeAction(align_actions[i]);
0605         // m_pAlignmentAction->insert( align_actions[i] );
0606         m_pAlignmentAction->addAction(align_actions[i]);
0607     }
0608     QList<QAction *> alignment_actions;
0609     alignment_actions.append(m_pAlignmentAction);
0610     KTechlab::self()->plugActionList("alignment_actionlist", alignment_actions);
0611 }
0612 
0613 void ItemDocument::slotInitItemActions()
0614 {
0615     ItemView *activeItemView = dynamic_cast<ItemView *>(activeView());
0616     if (!KTechlab::self() || !activeItemView)
0617         return;
0618 
0619     QAction *align_actions[] = {
0620         activeItemView->actionByName("align_horizontally"), activeItemView->actionByName("align_vertically"), activeItemView->actionByName("distribute_horizontally"), activeItemView->actionByName("distribute_vertically")};
0621 
0622     bool enableAlignment = selectList()->itemCount() > 1;
0623     for (unsigned i = 0; i < 4; ++i)
0624         align_actions[i]->setEnabled(enableAlignment);
0625 }
0626 
0627 void ItemDocument::updateBackground()
0628 {
0629     // Also used in the constructor to make the background initially.
0630 
0631     // Thoughts.
0632     // ~The pixmap could be done somehow with 1bpp. It might save some waste
0633     // I expect it won't hurt for now.
0634     // ~This is all rather static, only works with square etc... should be no prob. for most uses. IMO.
0635     // ~If you want, decide what maximum and minimum spacing should be, then enforce them
0636     // in the Config (I suppose you can use <max></max> tags?)
0637     // ~Defaults based on the existing grid background png. It should produce identical results, to your
0638     // original png.
0639 
0640     // **** Below where it says "interval * 10", that decides how big the pixmap will be (always square)
0641     // Originally I set this to 32, which give 256x256 with 8 spacing, as that was the size of your pixmap
0642     // Are there any good reasons to make the a certain size? (i.e. big or small ?).
0643 
0644     int interval = 8;
0645     int bigness = interval * 10;
0646     QPixmap pm(bigness, bigness);
0647     //  pm.fill( KTLConfig::bgColor() ); // first fill the background colour in
0648     pm.fill(Qt::white);
0649 
0650     if (KTLConfig::showGrid()) {
0651         // QPainter p(&pm); // setup painter to draw on pixmap
0652         QPainter p;
0653         const bool isSuccess = p.begin(&pm);
0654         if (!isSuccess) {
0655             qCWarning(KTL_LOG) << " painter is not active";
0656         }
0657         p.setPen(KTLConfig::gridColor()); // set forecolour
0658         // note: anything other than 8 borks this
0659         for (int i = (interval / 2); i < bigness; i += interval) {
0660             p.drawLine(0, i, bigness, i); // horizontal
0661             p.drawLine(i, 0, i, bigness); // vertical
0662         }
0663         p.end(); // all done
0664     }
0665 
0666     // pm.setDefaultOptimization( QPixmap::BestOptim ); // TODO no longer available?
0667     m_canvas->setBackgroundPixmap(pm); // and the finale.
0668 }
0669 
0670 void ItemDocument::requestCanvasResize()
0671 {
0672     requestEvent(ItemDocumentEvent::ResizeCanvasToItems);
0673 }
0674 
0675 void ItemDocument::requestEvent(ItemDocumentEvent::type type)
0676 {
0677     m_queuedEvents |= type;
0678     m_pEventTimer->stop();
0679     m_pEventTimer->setSingleShot(true);
0680     m_pEventTimer->start(0 /*, true */);
0681 }
0682 
0683 void ItemDocument::processItemDocumentEvents()
0684 {
0685     // Copy it incase we have new events requested while doing this...
0686     unsigned queuedEvents = m_queuedEvents;
0687     m_queuedEvents = 0;
0688 
0689     if (queuedEvents & ItemDocumentEvent::ResizeCanvasToItems)
0690         resizeCanvasToItems();
0691 
0692     if (queuedEvents & ItemDocumentEvent::UpdateZOrdering)
0693         slotUpdateZOrdering();
0694 
0695     ICNDocument *icnd = dynamic_cast<ICNDocument *>(this);
0696 
0697     if (icnd && (queuedEvents & ItemDocumentEvent::UpdateNodeGroups))
0698         icnd->slotAssignNodeGroups();
0699 
0700     if (icnd && (queuedEvents & ItemDocumentEvent::RerouteInvalidatedConnectors))
0701         icnd->rerouteInvalidatedConnectors();
0702 }
0703 
0704 void ItemDocument::resizeCanvasToItems()
0705 {
0706     QRect bound = canvasBoundingRect();
0707 
0708     m_viewList.removeAll(static_cast<View *>(nullptr));
0709     const ViewList::iterator end = m_viewList.end();
0710     for (ViewList::iterator it = m_viewList.begin(); it != end; ++it) {
0711         ItemView *iv = static_cast<ItemView *>(static_cast<View *>(*it));
0712         CVBEditor *cvbEditor = iv->cvbEditor();
0713 
0714         QPoint topLeft = iv->mousePosToCanvasPos(QPoint(0, 0));
0715         int width = int(cvbEditor->visibleWidth() / iv->zoomLevel());
0716         int height = int(cvbEditor->visibleHeight() / iv->zoomLevel());
0717         QRect r(topLeft, QSize(width, height));
0718 
0719         bound |= r;
0720 
0721         //      qCDebug(KTL_LOG) << "r="<<r;
0722         //      qCDebug(KTL_LOG) << "bound="<<bound;
0723     }
0724 
0725     // Make it so that the rectangular offset is a multiple of 8
0726     bound.setLeft(bound.left() - (bound.left() % 8));
0727     bound.setTop(bound.top() - (bound.top() % 8));
0728 
0729     m_pUpdateItemViewScrollbarsTimer->setSingleShot(true);
0730     m_pUpdateItemViewScrollbarsTimer->start(10 /*, true */);
0731 
0732     bool changedSize = canvas()->rect() != bound;
0733     if (changedSize) {
0734         canvas()->resize(bound);
0735         requestEvent(ItemDocumentEvent::ResizeCanvasToItems);
0736     } else if (ICNDocument *icnd = dynamic_cast<ICNDocument *>(this)) {
0737         icnd->createCellMap();
0738     }
0739 }
0740 
0741 void ItemDocument::updateItemViewScrollbars()
0742 {
0743     int w = canvas()->width();
0744     int h = canvas()->height();
0745 
0746     const ViewList::iterator end = m_viewList.end();
0747     for (ViewList::iterator it = m_viewList.begin(); it != end; ++it) {
0748         ItemView *itemView = static_cast<ItemView *>(static_cast<View *>(*it));
0749         CVBEditor *cvbEditor = itemView->cvbEditor();
0750         // TODO QT3
0751         cvbEditor->setVScrollBarMode(((h * itemView->zoomLevel()) > cvbEditor->visibleHeight()) ? KtlQ3ScrollView::AlwaysOn : KtlQ3ScrollView::AlwaysOff);
0752         cvbEditor->setHScrollBarMode(((w * itemView->zoomLevel()) > cvbEditor->visibleWidth()) ? KtlQ3ScrollView::AlwaysOn : KtlQ3ScrollView::AlwaysOff);
0753     }
0754 }
0755 
0756 QRect ItemDocument::canvasBoundingRect() const
0757 {
0758     QRect bound;
0759 
0760     // Don't include items used for dragging
0761     Item *dragItem = nullptr;
0762     const ViewList::const_iterator viewsEnd = m_viewList.end();
0763     for (ViewList::const_iterator it = m_viewList.begin(); it != viewsEnd; ++it) {
0764         dragItem = (static_cast<ItemView *>(static_cast<View *>(*it)))->dragItem();
0765         if (dragItem)
0766             break;
0767     }
0768 
0769     const KtlQCanvasItemList allItems = canvas()->allItems();
0770     const KtlQCanvasItemList::const_iterator end = allItems.end();
0771 
0772     for (KtlQCanvasItemList::const_iterator it = allItems.begin(); it != end; ++it) {
0773         if (!(*it)->isVisible())
0774             continue;
0775 
0776         if (dragItem) {
0777             if (*it == dragItem)
0778                 continue;
0779 
0780             if (Node *n = dynamic_cast<Node *>(*it)) {
0781                 if (n->parentItem() == dragItem)
0782                     continue;
0783             }
0784 
0785             if (GuiPart *gp = dynamic_cast<GuiPart *>(*it)) {
0786                 if (gp->parent() == dragItem)
0787                     continue;
0788             }
0789         }
0790 
0791         bound |= (*it)->boundingRect();
0792     }
0793 
0794     if (!bound.isNull()) {
0795         bound.setLeft(bound.left() - 16);
0796         bound.setTop(bound.top() - 16);
0797         bound.setRight(bound.right() + 16);
0798         bound.setBottom(bound.bottom() + 16);
0799     }
0800 
0801     return bound;
0802 }
0803 
0804 void ItemDocument::exportToImage()
0805 {
0806     // scaralously copied from print.
0807     // this slot is called whenever the File->Export menu is selected,
0808     // the Export shortcut is pressed or the Export toolbar
0809     // button is clicked
0810 
0811     // we need an object so we can retrieve which image type was selected by the user
0812     // so setup the filedialog.
0813     ImageExportDialog exportDialog(KTechlab::self());
0814 
0815     // now actually show it
0816     if (exportDialog.exec() == QDialog::Rejected)
0817         return;
0818     const QString filePath = exportDialog.filePath();
0819 
0820     if (filePath.isEmpty())
0821         return;
0822 
0823     if (QFile::exists(filePath)) {
0824         int query = KMessageBox::warningYesNo(KTechlab::self(),
0825                                               i18n("A file named \"%1\" already exists. "
0826                                                    "Are you sure you want to overwrite it?",
0827                                                    filePath),
0828                                               i18n("Overwrite File?"));
0829 
0830         if (query == KMessageBox::No)
0831             return;
0832     }
0833 
0834     const bool crop = exportDialog.isCropSelected();
0835     // with Qt, you always "print" to a
0836     // QPainter.. whether the output medium is a pixmap, a screen,
0837     // or paper
0838 
0839     // needs to be something like QPicture to do SVG etc...
0840 
0841     QRect saveArea;
0842     QRect cropArea;
0843     QPaintDevice *outputImage;
0844     const QString type = exportDialog.formatType();
0845 
0846     // did have a switch here but seems you can't use that on strings
0847     if (type == "SVG") {
0848         KMessageBox::information(nullptr, i18n("SVG export is sub-functional"), i18n("Export As Image"));
0849     }
0850 
0851     if (crop) {
0852         cropArea = canvasBoundingRect();
0853         if (cropArea.isNull()) {
0854             KMessageBox::error(nullptr, i18n("There is nothing to crop"), i18n("Export As Image"));
0855             return;
0856         } else {
0857             cropArea &= canvas()->rect();
0858         }
0859     }
0860 
0861     saveArea = m_canvas->rect();
0862 
0863     if (type == "PNG" || type == "BMP")
0864         outputImage = new QPixmap(saveArea.size());
0865     else if (type == "SVG") {
0866         setSVGExport(true);
0867         outputImage = new QPicture();
0868         // svg can't be cropped using the qimage method.
0869         saveArea = cropArea;
0870     } else {
0871         qCWarning(KTL_LOG) << "Unknown type!";
0872         return;
0873     }
0874 
0875     // 2018.05.05 - extract to a method
0876     //  //QPainter p(outputImage); // 2016.05.03 - explicitly initialize painter
0877     //  QPainter p;
0878     //     const bool isBeginSuccess = p.begin(outputImage);
0879     //     if (!isBeginSuccess) {
0880     //         qCWarning(KTL_LOG) << " painter not active";
0881     //     }
0882     //
0883     //  m_canvas->setBackgroundPixmap(QPixmap());
0884     //  m_canvas->drawArea( saveArea, &p );
0885     //  updateBackground();
0886     //
0887     //  p.end();
0888     exportToImageDraw(saveArea, *outputImage);
0889 
0890     bool saveResult;
0891 
0892     // if cropping we need to convert to an image,
0893     // crop, then save.
0894     if (crop) {
0895         if (type == "SVG")
0896             saveResult = dynamic_cast<QPicture *>(outputImage)->save(filePath, type.toLatin1().data());
0897         else {
0898             QImage img = dynamic_cast<QPixmap *>(outputImage)->toImage();
0899             if (saveArea.x() < 0) {
0900                 cropArea.translate(-saveArea.x(), 0);
0901             }
0902             if (saveArea.y() < 0) {
0903                 cropArea.translate(0, -saveArea.y());
0904             }
0905             qCDebug(KTL_LOG) << " cropArea " << cropArea;
0906             QImage imgCropped = img.copy(cropArea);
0907             saveResult = imgCropped.save(filePath, type.toLatin1().data());
0908         }
0909     } else {
0910         if (type == "SVG")
0911             saveResult = dynamic_cast<QPicture *>(outputImage)->save(filePath, type.toLatin1().data());
0912         else
0913             saveResult = dynamic_cast<QPixmap *>(outputImage)->save(filePath, type.toLatin1().data());
0914     }
0915 
0916     // if(saveResult == true)   KMessageBox::information( this, i18n("Sucessfully exported to \"%1\"", url.filename() ), i18n("Image Export") );
0917     // else KMessageBox::information( this, i18n("Export failed"), i18n("Image Export") );
0918 
0919     if (type == "SVG")
0920         setSVGExport(false);
0921 
0922     if (saveResult == false)
0923         KMessageBox::information(KTechlab::self(), i18n("Export failed"), i18n("Image Export"));
0924 
0925     delete outputImage;
0926 }
0927 
0928 void ItemDocument::exportToImageDraw(const QRect &saveArea, QPaintDevice &pDev)
0929 {
0930     qCDebug(KTL_LOG) << " saveArea " << saveArea;
0931     // QPainter p(outputImage); // 2016.05.03 - explicitly initialize painter
0932     QPainter p;
0933     const bool isBeginSuccess = p.begin(&pDev);
0934     if (!isBeginSuccess) {
0935         qCWarning(KTL_LOG) << " painter not active";
0936     }
0937 
0938     QTransform transf;
0939     transf.translate(-saveArea.x(), -saveArea.y());
0940     p.setTransform(transf);
0941 
0942     m_canvas->setBackgroundPixmap(QPixmap());
0943     m_canvas->drawArea(saveArea, &p);
0944     updateBackground();
0945 
0946     p.end();
0947 }
0948 
0949 void ItemDocument::setSVGExport(bool svgExport)
0950 {
0951     // Find any items and tell them not to draw buttons or sliders
0952     KtlQCanvasItemList items = m_canvas->allItems();
0953     const KtlQCanvasItemList::iterator end = items.end();
0954     for (KtlQCanvasItemList::Iterator it = items.begin(); it != end; ++it) {
0955         if (CNItem *cnItem = dynamic_cast<CNItem *>(*it))
0956             cnItem->setDrawWidgets(!svgExport);
0957     }
0958 }
0959 
0960 void ItemDocument::raiseZ()
0961 {
0962     raiseZ(selectList()->items(true));
0963 }
0964 void ItemDocument::raiseZ(const ItemList &itemList)
0965 {
0966     if (m_zOrder.isEmpty())
0967         slotUpdateZOrdering();
0968 
0969     if (m_zOrder.isEmpty())
0970         return;
0971 
0972     IntItemMap::iterator begin = m_zOrder.begin();
0973     IntItemMap::iterator previous = m_zOrder.end();
0974     IntItemMap::iterator it = --m_zOrder.end();
0975     do {
0976         Item *previousData = (previous == m_zOrder.end()) ? nullptr : previous.value();
0977         Item *currentData = it.value();
0978 
0979         if (currentData && previousData && itemList.contains(currentData) && !itemList.contains(previousData)) {
0980             previous.value() = currentData;
0981             it.value() = previousData;
0982         }
0983 
0984         previous = it;
0985         --it;
0986     } while (previous != begin);
0987 
0988     slotUpdateZOrdering();
0989 }
0990 
0991 void ItemDocument::lowerZ()
0992 {
0993     lowerZ(selectList()->items(true));
0994 }
0995 
0996 void ItemDocument::lowerZ(const ItemList &itemList)
0997 {
0998     if (m_zOrder.isEmpty())
0999         slotUpdateZOrdering();
1000 
1001     if (m_zOrder.isEmpty())
1002         return;
1003 
1004     IntItemMap::iterator previous = m_zOrder.begin();
1005     IntItemMap::iterator end = m_zOrder.end();
1006     for (IntItemMap::iterator it = m_zOrder.begin(); it != end; ++it) {
1007         Item *previousData = previous.value();
1008         Item *currentData = it.value();
1009 
1010         if (currentData && previousData && itemList.contains(currentData) && !itemList.contains(previousData)) {
1011             previous.value() = currentData;
1012             it.value() = previousData;
1013         }
1014 
1015         previous = it;
1016     }
1017 
1018     slotUpdateZOrdering();
1019 }
1020 
1021 void ItemDocument::itemAdded(Item *)
1022 {
1023     requestEvent(ItemDocument::ItemDocumentEvent::UpdateZOrdering);
1024 }
1025 
1026 void ItemDocument::slotUpdateZOrdering()
1027 {
1028     ItemMap toAdd = m_itemList;
1029 
1030     IntItemMap newZOrder;
1031     int atLevel = 0;
1032 
1033     IntItemMap::iterator zEnd = m_zOrder.end();
1034     for (IntItemMap::iterator it = m_zOrder.begin(); it != zEnd; ++it) {
1035         Item *item = it.value();
1036         if (!item)
1037             continue;
1038 
1039         toAdd.remove(item->id());
1040 
1041         if (!item->parentItem() && item->isMovable())
1042             newZOrder[atLevel++] = item;
1043     }
1044 
1045     ItemMap::iterator addEnd = toAdd.end();
1046     for (ItemMap::iterator it = toAdd.begin(); it != addEnd; ++it) {
1047         Item *item = *it;
1048         if (item->parentItem() || !item->isMovable())
1049             continue;
1050 
1051         newZOrder[atLevel++] = item;
1052     }
1053 
1054     m_zOrder = newZOrder;
1055 
1056     zEnd = m_zOrder.end();
1057     for (IntItemMap::iterator it = m_zOrder.begin(); it != zEnd; ++it)
1058         it.value()->updateZ(it.key());
1059 }
1060 
1061 void ItemDocument::update()
1062 {
1063     ItemMap::iterator end = m_itemList.end();
1064     for (ItemMap::iterator it = m_itemList.begin(); it != end; ++it) {
1065         if ((*it)->contentChanged())
1066             (*it)->setChanged();
1067     }
1068 }
1069 
1070 ItemList ItemDocument::itemList() const
1071 {
1072     ItemList l;
1073 
1074     ItemMap::const_iterator end = m_itemList.end();
1075     for (ItemMap::const_iterator it = m_itemList.begin(); it != end; ++it)
1076         l << it.value();
1077 
1078     return l;
1079 }
1080 // END class ItemDocument
1081 
1082 // BEGIN class CanvasTip
1083 CanvasTip::CanvasTip(ItemDocument *itemDocument, KtlQCanvas *qcanvas)
1084     : KtlQCanvasRectangle(qcanvas)
1085 {
1086     p_itemDocument = itemDocument;
1087 
1088     setZ(ICNDocument::Z::Tip);
1089 }
1090 
1091 CanvasTip::~CanvasTip()
1092 {
1093 }
1094 
1095 void CanvasTip::displayVI(ECNode *node, const QPoint &pos)
1096 {
1097     if (!node || !updateVI())
1098         return;
1099 
1100     unsigned num = node->numPins();
1101 
1102     m_v.resize(num);
1103     m_i.resize(num);
1104 
1105     for (unsigned i = 0; i < num; i++) {
1106         if (Pin *pin = node->pin(i)) {
1107             m_v[i] = pin->voltage();
1108             m_i[i] = pin->current();
1109         }
1110     }
1111 
1112     display(pos);
1113 }
1114 
1115 void CanvasTip::displayVI(Connector *connector, const QPoint &pos)
1116 {
1117     if (!connector || !updateVI())
1118         return;
1119 
1120     unsigned num = connector->numWires();
1121 
1122     m_v.resize(num);
1123     m_i.resize(num);
1124 
1125     for (unsigned i = 0; i < num; i++) {
1126         if (Wire *wire = connector->wire(i)) {
1127             m_v[i] = wire->voltage();
1128             m_i[i] = std::abs(wire->current());
1129         }
1130     }
1131 
1132     display(pos);
1133 }
1134 
1135 bool CanvasTip::updateVI()
1136 {
1137     CircuitDocument *circuitDocument = dynamic_cast<CircuitDocument *>(p_itemDocument);
1138     if (!circuitDocument || !Simulator::self()->isSimulating())
1139         return false;
1140 
1141     circuitDocument->calculateConnectorCurrents();
1142     return true;
1143 }
1144 
1145 void CanvasTip::display(const QPoint &pos)
1146 {
1147     unsigned num = m_v.size();
1148 
1149     for (unsigned i = 0; i < num; i++) {
1150         if (!std::isfinite(m_v[i]) || std::abs(m_v[i]) < 1e-9)
1151             m_v[i] = 0.;
1152 
1153         if (!std::isfinite(m_i[i]) || std::abs(m_i[i]) < 1e-9)
1154             m_i[i] = 0.;
1155     }
1156 
1157     move(pos.x() + 20, pos.y() + 4);
1158 
1159     if (num == 0)
1160         return;
1161 
1162     if (num == 1)
1163         setText(displayText(0));
1164     else {
1165         QString text;
1166         for (unsigned i = 0; i < num; i++)
1167             text += QString("%1: %2\n").arg(QString::number(i)).arg(displayText(i));
1168         setText(text);
1169     }
1170 }
1171 
1172 QString CanvasTip::displayText(unsigned num) const
1173 {
1174     if (m_v.size() <= int(num))
1175         return QString();
1176 
1177     return QString("%1%2V  %3%4A")
1178         .arg(QString::number(m_v[num] / CNItem::getMultiplier(m_v[num]), 'g', 3))
1179         .arg(CNItem::getNumberMag(m_v[num]))
1180         .arg(QString::number(m_i[num] / CNItem::getMultiplier(m_i[num]), 'g', 3))
1181         .arg(CNItem::getNumberMag(m_i[num]));
1182 }
1183 
1184 void CanvasTip::draw(QPainter &p)
1185 {
1186     CircuitDocument *circuitDocument = dynamic_cast<CircuitDocument *>(p_itemDocument);
1187     if (!circuitDocument || !Simulator::self()->isSimulating())
1188         return;
1189 
1190     p.setBrush(QColor(0xff, 0xff, 0xdc));
1191     p.setPen(Qt::black);
1192     p.drawRect(boundingRect());
1193 
1194     QRect textRect = boundingRect();
1195     textRect.setLeft(textRect.left() + 3);
1196     textRect.setTop(textRect.top() + 1);
1197     p.drawText(textRect, 0, m_text);
1198 }
1199 
1200 void CanvasTip::setText(const QString &text)
1201 {
1202     m_text = text;
1203     canvas()->setChanged(boundingRect());
1204 
1205     QRect r = QFontMetrics(qApp->font()).boundingRect(0, 0, 0, 0, 0, m_text);
1206     setSize(r.width() + 4, r.height() - 1);
1207 }
1208 // END class CanvasTip
1209 
1210 // BEGIN class Canvas
1211 Canvas::Canvas(ItemDocument *itemDocument)
1212     : KtlQCanvas(itemDocument)
1213 {
1214     p_itemDocument = itemDocument;
1215     m_pMessageTimeout = new QTimer(this);
1216     connect(m_pMessageTimeout, SIGNAL(timeout()), this, SLOT(slotSetAllChanged()));
1217 }
1218 
1219 void Canvas::resize(const QRect &size)
1220 {
1221     if (rect() == size)
1222         return;
1223     QRect oldSize = rect();
1224     KtlQCanvas::resize(size);
1225     emit resized(oldSize, size);
1226 }
1227 
1228 void Canvas::setMessage(const QString &message)
1229 {
1230     m_message = message;
1231 
1232     if (message.isEmpty()) {
1233         m_pMessageTimeout->stop();
1234     } else {
1235         m_pMessageTimeout->setSingleShot(true);
1236         m_pMessageTimeout->start(2000 /*, true */);
1237     }
1238 
1239     setAllChanged();
1240 }
1241 
1242 void Canvas::drawBackground(QPainter &p, const QRect &clip)
1243 {
1244     KtlQCanvas::drawBackground(p, clip);
1245 #if 0
1246     const int scx = (int)((clip.left()-4)/8);
1247     const int ecx = (int)((clip.right()+4)/8);
1248     const int scy = (int)((clip.top()-4)/8);
1249     const int ecy = (int)((clip.bottom()+4)/8);
1250 
1251     ICNDocument * icnd = dynamic_cast<ICNDocument*>(p_itemDocument);
1252     if ( !icnd )
1253         return;
1254 
1255     Cells * c = icnd->cells();
1256 
1257     if ( !c->haveCell( scx, scy ) || !c->haveCell( ecx, ecy ) )
1258         return;
1259 
1260     for ( int x=scx; x<=ecx; x++ )
1261     {
1262         for ( int y=scy; y<=ecy; y++ )
1263         {
1264             const double score = c->cell( x, y ).CIpenalty + c->cell( x, y ).Cpenalty;
1265             int value = (int)std::log(score)*20;
1266             if ( value>255 )
1267                 value=255;
1268             else if (value<0 )
1269                 value=0;
1270             p.setBrush( QColor( 255, (255-value), (255-value) ) );
1271             p.setPen( Qt::NoPen );
1272             p.drawRect( (x*8), (y*8), 8, 8 );
1273         }
1274     }
1275 #endif
1276 }
1277 
1278 void Canvas::drawForeground(QPainter &p, const QRect &clip)
1279 {
1280     KtlQCanvas::drawForeground(p, clip);
1281 
1282     if (!m_pMessageTimeout->isActive())
1283         return;
1284 
1285     // Following code stolen and adapted from amarok/src/playlist.cpp :)
1286 
1287     // Find out width of smallest view
1288     QSize minSize;
1289     const ViewList viewList = p_itemDocument->viewList();
1290     ViewList::const_iterator end = viewList.end();
1291     View *firstView = nullptr;
1292     for (ViewList::const_iterator it = viewList.begin(); it != end; ++it) {
1293         if (!*it)
1294             continue;
1295 
1296         if (!firstView) {
1297             firstView = *it;
1298             minSize = (*it)->size();
1299         } else
1300             minSize = minSize.boundedTo((*it)->size());
1301     }
1302 
1303     if (!firstView)
1304         return;
1305 
1306     //  Q3SimpleRichText * t = new Q3SimpleRichText( m_message, QApplication::font() );
1307     QTextEdit *t = new QTextEdit(m_message);
1308 
1309     {
1310         QFont tf = t->document()->defaultFont();
1311         QFontMetrics tfm(tf);
1312         QSize textSize = tfm.size(0, m_message);
1313 
1314         t->resize(textSize);
1315     }
1316 
1317     int w = t->width();
1318     int h = t->height();
1319     int x = rect().left() + 15;
1320     int y = rect().top() + 15;
1321     int b = 10; // text padding
1322 
1323     //  if ( w+2*b >= minSize.width() || h+2*b >= minSize.height() )
1324     //  {
1325     //         qCWarning(KTL_LOG) << "size not good w=" << w << " h=" << h << "b=" << b << " minSize=" << minSize;
1326     //      delete t;
1327     //      return;
1328     //  }
1329 
1330     // p.setBrush( firstView->colorGroup().background() ); // 2018.12.02
1331     p.setBrush(firstView->palette().window());
1332     p.drawRoundedRect(x, y, w + 2 * b, h + 2 * b, (8 * 200) / (w + 2 * b), (8 * 200) / (h + 2 * b), Qt::RelativeSize);
1333     //  t->draw( &p, x+b, y+b, QRect(), firstView->colorGroup() );
1334     t->resize(w + 2 * b, h + 2 * b);
1335     t->viewport()->setAutoFillBackground(false);
1336     t->setFrameStyle(QFrame::NoFrame);
1337     t->render(&p, QPoint(x, y), QRegion(), QWidget::DrawChildren);
1338     delete t;
1339 }
1340 
1341 void Canvas::update()
1342 {
1343     p_itemDocument->update();
1344     KtlQCanvas::update();
1345 }
1346 // END class Canvas
1347 
1348 #include "moc_itemdocument.cpp"