Qucs-GUI  0.0.19
/home/travis/build/Qucs/qucs/qucs/qucs/schematic.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                               schematic.cpp
00003                              ---------------
00004     begin                : Sat Mar 3 2006
00005     copyright            : (C) 2006 by Michael Margraf
00006     email                : michael.margraf@alumni.tu-berlin.de
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include <stdlib.h>
00019 #include <limits.h>
00020 
00021 #include <QFileInfo>
00022 #include <QPrinter>
00023 #include <QPaintDevice>
00024 #include <QDir>
00025 #include <QTextStream>
00026 #include <QDragLeaveEvent>
00027 #include <Q3PtrList>
00028 #include <QPixmap>
00029 #include <QDragEnterEvent>
00030 #include <QDragMoveEvent>
00031 #include <QDropEvent>
00032 #include <QMouseEvent>
00033 #include <QEvent>
00034 #include <QWheelEvent>
00035 #include <QPainter>
00036 #include <QAction>
00037 #include <QLineEdit>
00038 #include <QUrl>
00039 #include <QListWidget>
00040 #include <QDebug>
00041 #include <QApplication>
00042 #include <QClipboard>
00043 
00044 #include "qucs.h"
00045 #include "schematic.h"
00046 #include "main.h"
00047 #include "node.h"
00048 #include "textdoc.h"
00049 #include "viewpainter.h"
00050 #include "mouseactions.h"
00051 #include "diagrams/diagrams.h"
00052 #include "paintings/paintings.h"
00053 #include "components/vhdlfile.h"
00054 #include "components/verilogfile.h"
00055 #include "components/vafile.h"
00056 
00057 // just dummies for empty lists
00058 Q3PtrList<Wire>      SymbolWires;
00059 Q3PtrList<Node>      SymbolNodes;
00060 Q3PtrList<Diagram>   SymbolDiags;
00061 Q3PtrList<Component> SymbolComps;
00062 
00063 
00064 Schematic::Schematic(QucsApp *App_, const QString& Name_)
00065     : QucsDoc(App_, Name_)
00066 {
00067   symbolMode = false;
00068 
00069   // ...........................................................
00070   GridX  = GridY  = 10;
00071   ViewX1=ViewY1=0;
00072   ViewX2=ViewY2=800;
00073   UsedX1 = UsedY1 = INT_MAX;
00074   UsedX2 = UsedY2 = INT_MIN;
00075 
00076   tmpPosX = tmpPosY = -100;
00077   tmpUsedX1 = tmpUsedY1 = tmpViewX1 = tmpViewY1 = -200;
00078   tmpUsedX2 = tmpUsedY2 = tmpViewX2 = tmpViewY2 =  200;
00079   tmpScale = 1.0;
00080 
00081   DocComps.setAutoDelete(true);
00082   DocWires.setAutoDelete(true);
00083   DocNodes.setAutoDelete(true);
00084   DocDiags.setAutoDelete(true);
00085   DocPaints.setAutoDelete(true);
00086   SymbolPaints.setAutoDelete(true);
00087 
00088   // The 'i' means state for being unchanged.
00089   undoActionIdx = 0;
00090   undoAction.append(new QString(" i\n</>\n</>\n</>\n</>\n"));
00091   undoSymbolIdx = 0;
00092   undoSymbol.append(new QString(" i\n</>\n</>\n</>\n</>\n"));
00093 
00094   isVerilog = false;
00095   creatingLib = false;
00096 
00097   showFrame = 0;  // don't show
00098   Frame_Text0 = tr("Title");
00099   Frame_Text1 = tr("Drawn By:");
00100   Frame_Text2 = tr("Date:");
00101   Frame_Text3 = tr("Revision:");
00102 
00103   setVScrollBarMode(Q3ScrollView::AlwaysOn);
00104   setHScrollBarMode(Q3ScrollView::AlwaysOn);
00105   viewport()->setPaletteBackgroundColor(QucsSettings.BGColor);
00106   viewport()->setMouseTracking(true);
00107   viewport()->setAcceptDrops(true);  // enable drag'n drop
00108 
00109   // to repair some strange  scrolling artefacts
00110   connect(this, SIGNAL(horizontalSliderReleased()),
00111       viewport(), SLOT(update()));
00112   connect(this, SIGNAL(verticalSliderReleased()),
00113       viewport(), SLOT(update()));
00114   if (App_) {
00115     connect(this, SIGNAL(signalCursorPosChanged(int, int)), 
00116         App_, SLOT(printCursorPosition(int, int)));
00117     connect(this, SIGNAL(horizontalSliderPressed()), 
00118         App_, SLOT(slotHideEdit()));
00119     connect(this, SIGNAL(verticalSliderPressed()),
00120         App_, SLOT(slotHideEdit()));
00121     connect(this, SIGNAL(signalUndoState(bool)),
00122         App_, SLOT(slotUpdateUndo(bool)));
00123     connect(this, SIGNAL(signalRedoState(bool)),
00124         App_, SLOT(slotUpdateRedo(bool)));
00125     connect(this, SIGNAL(signalFileChanged(bool)),
00126         App_, SLOT(slotFileChanged(bool)));
00127   }
00128 }
00129 
00130 Schematic::~Schematic()
00131 {
00132 }
00133 
00134 // ---------------------------------------------------
00135 bool Schematic::createSubcircuitSymbol()
00136 {
00137   // If the number of ports is not equal, remove or add some.
00138   unsigned int countPort = adjustPortNumbers();
00139 
00140   // If a symbol does not yet exist, create one.
00141   if(SymbolPaints.count() != countPort)
00142     return false;
00143 
00144   int h = 30*((countPort-1)/2) + 10;
00145   SymbolPaints.prepend(new ID_Text(-20, h+4));
00146 
00147   SymbolPaints.append(
00148      new GraphicLine(-20, -h, 40,  0, QPen(Qt::darkBlue,2)));
00149   SymbolPaints.append(
00150      new GraphicLine( 20, -h,  0,2*h, QPen(Qt::darkBlue,2)));
00151   SymbolPaints.append(
00152      new GraphicLine(-20,  h, 40,  0, QPen(Qt::darkBlue,2)));
00153   SymbolPaints.append(
00154      new GraphicLine(-20, -h,  0,2*h, QPen(Qt::darkBlue,2)));
00155 
00156   unsigned int i=0, y = 10-h;
00157   while(i<countPort) {
00158     i++;
00159     SymbolPaints.append(
00160        new GraphicLine(-30, y, 10, 0, QPen(Qt::darkBlue,2)));
00161     SymbolPaints.at(i)->setCenter(-30,  y);
00162 
00163     if(i == countPort)  break;
00164     i++;
00165     SymbolPaints.append(
00166        new GraphicLine( 20, y, 10, 0, QPen(Qt::darkBlue,2)));
00167     SymbolPaints.at(i)->setCenter(30,  y);
00168     y += 60;
00169   }
00170   return true;
00171 }
00172 
00173 // ---------------------------------------------------
00174 void Schematic::becomeCurrent(bool update)
00175 {
00176   emit signalCursorPosChanged(0, 0);
00177 
00178   // update appropriate menu entry
00179   if (symbolMode) {
00180     if (DocName.right(4) == ".sym") {
00181       App->symEdit->setText(tr("Edit Text"));
00182       App->symEdit->setStatusTip(tr("Edits the Text"));
00183       App->symEdit->setWhatsThis(tr("Edit Text\n\nEdits the text file"));
00184     }
00185     else {
00186       App->symEdit->setText(tr("Edit Schematic"));
00187       App->symEdit->setStatusTip(tr("Edits the schematic"));
00188       App->symEdit->setWhatsThis(tr("Edit Schematic\n\nEdits the schematic"));
00189     }
00190   }
00191   else {
00192     App->symEdit->setText(tr("Edit Circuit Symbol"));
00193     App->symEdit->setStatusTip(tr("Edits the symbol for this schematic"));
00194     App->symEdit->setWhatsThis(
00195   tr("Edit Circuit Symbol\n\nEdits the symbol for this schematic"));
00196   }
00197 
00198   if(symbolMode) {
00199     Nodes = &SymbolNodes;
00200     Wires = &SymbolWires;
00201     Diagrams = &SymbolDiags;
00202     Paintings = &SymbolPaints;
00203     Components = &SymbolComps;
00204 
00205     // if no symbol yet exists -> create one
00206     if(createSubcircuitSymbol()) {
00207       sizeOfAll(UsedX1, UsedY1, UsedX2, UsedY2);
00208       setChanged(true, true);
00209     }
00210 
00211     emit signalUndoState(undoSymbolIdx != 0);
00212     emit signalRedoState(undoSymbolIdx != undoSymbol.size()-1);
00213   }
00214   else {
00215     Nodes = &DocNodes;
00216     Wires = &DocWires;
00217     Diagrams = &DocDiags;
00218     Paintings = &DocPaints;
00219     Components = &DocComps;
00220 
00221     emit signalUndoState(undoActionIdx != 0);
00222     emit signalRedoState(undoActionIdx != undoAction.size()-1);
00223     if(update)
00224       reloadGraphs();   // load recent simulation data
00225   }
00226 }
00227 
00228 // ---------------------------------------------------
00229 void Schematic::setName (const QString& Name_)
00230 {
00231   DocName = Name_;
00232   QFileInfo Info (DocName);
00233   QString base = Info.completeBaseName ();
00234   QString ext = Info.suffix();
00235   DataSet = base + ".dat";
00236   Script = base + ".m";
00237   if (ext != "dpl")
00238     DataDisplay = base + ".dpl";
00239   else
00240     DataDisplay = base + ".sch";
00241 }
00242 
00243 // ---------------------------------------------------
00244 // Sets the document to be changed or not to be changed.
00245 void Schematic::setChanged(bool c, bool fillStack, char Op)
00246 {
00247   if((!DocChanged) && c)
00248     emit signalFileChanged(true);
00249   else if(DocChanged && (!c))
00250     emit signalFileChanged(false);
00251   DocChanged = c;
00252 
00253   showBias = -1;   // schematic changed => bias points may be invalid
00254 
00255   if(!fillStack)
00256     return;
00257 
00258 
00259   // ................................................
00260   if(symbolMode) {  // for symbol edit mode
00261     while(undoSymbol.size() > undoSymbolIdx + 1) {
00262       delete undoSymbol.last();
00263       undoSymbol.pop_back();
00264     }
00265 
00266     undoSymbol.append(new QString(createSymbolUndoString(Op)));
00267     undoSymbolIdx++;
00268 
00269     emit signalUndoState(true);
00270     emit signalRedoState(false);
00271 
00272     while(static_cast<unsigned int>(undoSymbol.size()) > QucsSettings.maxUndo) { // "while..." because
00273       delete undoSymbol.first();
00274       undoSymbol.pop_front();
00275       undoSymbolIdx--;
00276     }
00277     return;
00278   }
00279 
00280   // ................................................
00281   // for schematic edit mode
00282   while(undoAction.size() > undoActionIdx + 1) {
00283     delete undoAction.last();
00284     undoAction.pop_back();
00285   }
00286 
00287   if(Op == 'm') {   // only one for move marker
00288     if (undoAction.at(undoActionIdx)->at(0) == Op) {
00289       delete undoAction.last();
00290       undoAction.pop_back();
00291       undoActionIdx--;
00292     }
00293   }
00294 
00295   undoAction.append(new QString(createUndoString(Op)));
00296   undoActionIdx++;
00297 
00298   emit signalUndoState(true);
00299   emit signalRedoState(false);
00300 
00301   while(static_cast<unsigned int>(undoAction.size()) > QucsSettings.maxUndo) { // "while..." because
00302     delete undoAction.first(); // "maxUndo" could be decreased meanwhile
00303     undoAction.pop_front();
00304     undoActionIdx--;
00305   }
00306   return;
00307 }
00308 
00309 // -----------------------------------------------------------
00310 bool Schematic::sizeOfFrame(int& xall, int& yall)
00311 {
00312   // Values exclude border of 1.5cm at each side.
00313   switch(showFrame) {
00314     case 1:  xall = 1020; yall =  765; break;  // DIN A5 landscape
00315     case 2:  xall =  765; yall = 1020; break;  // DIN A5 portrait
00316     case 3:  xall = 1530; yall = 1020; break;  // DIN A4 landscape
00317     case 4:  xall = 1020; yall = 1530; break;  // DIN A4 portrait
00318     case 5:  xall = 2295; yall = 1530; break;  // DIN A3 landscape
00319     case 6:  xall = 1530; yall = 2295; break;  // DIN A3 portrait
00320     case 7:  xall = 1414; yall = 1054; break;  // letter landscape
00321     case 8:  xall = 1054; yall = 1414; break;  // letter portrait
00322     default:  return false;
00323   }
00324 
00325   return true;
00326 }
00327 
00328 // -----------------------------------------------------------
00329 void Schematic::paintFrame(ViewPainter *p)
00330 {
00331   // dimensions:  X cm / 2.54 * 144
00332   int xall, yall;
00333   if(!sizeOfFrame(xall, yall))
00334     return;
00335   p->Painter->setPen(QPen(Qt::darkGray,1));
00336   //p->Painter->setPen(QPen(Qt::black,0));
00337   int d = p->LineSpacing + int(4.0 * p->Scale);
00338   int x1_, y1_, x2_, y2_;
00339   p->map(xall, yall, x1_, y1_);
00340   x2_ = int(xall * p->Scale) + 1;
00341   y2_ = int(yall * p->Scale) + 1;
00342   p->Painter->drawRect(x1_, y1_, -x2_, -y2_);
00343   p->Painter->drawRect(x1_-d, y1_-d, 2*d-x2_, 2*d-y2_);
00344 
00345   int z;
00346   int step = xall / ((xall+127) / 255);
00347   for(z=step; z<=xall-step; z+=step) {
00348     p->map(z, 0, x2_, y2_);
00349     p->Painter->drawLine(x2_, y2_, x2_, y2_+d);
00350     p->Painter->drawLine(x2_, y1_-d, x2_, y1_);
00351   }
00352   char Letter[2] = "1";
00353   for(z=step/2+5; z<xall; z+=step) {
00354     p->drawText(Letter, z, 3, 0);
00355     p->map(z, yall+3, x2_, y2_);
00356     p->Painter->drawText(x2_, y2_-d, 0, 0, Qt::TextDontClip, Letter);
00357     Letter[0]++;
00358   }
00359 
00360   step = yall / ((yall+127) / 255);
00361   for(z=step; z<=yall-step; z+=step) {
00362     p->map(0, z, x2_, y2_);
00363     p->Painter->drawLine(x2_, y2_, x2_+d, y2_);
00364     p->Painter->drawLine(x1_-d, y2_, x1_, y2_);
00365   }
00366   Letter[0] = 'A';
00367   for(z=step/2+5; z<yall; z+=step) {
00368     p->drawText(Letter, 5, z, 0);
00369     p->map(xall+5, z, x2_, y2_);
00370     p->Painter->drawText(x2_-d, y2_, 0, 0, Qt::TextDontClip, Letter);
00371     Letter[0]++;
00372   }
00373 
00374   // draw text box with text
00375   p->map(xall-340, yall-3, x1_, y1_);
00376   p->map(xall-3,   yall-3, x2_, y2_);
00377   x1_ -= d;  x2_ -= d;
00378   y1_ -= d;  y2_ -= d;
00379   d = int(6.0 * p->Scale);
00380   z = int(200.0 * p->Scale);
00381   y1_ -= p->LineSpacing + d;
00382   p->Painter->drawLine(x1_, y1_, x2_, y1_);
00383   p->Painter->drawText(x1_+d, y1_+(d>>1), 0, 0, Qt::TextDontClip, Frame_Text2);
00384   p->Painter->drawLine(x1_+z, y1_, x1_+z, y1_ + p->LineSpacing+d);
00385   p->Painter->drawText(x1_+d+z, y1_+(d>>1), 0, 0, Qt::TextDontClip, Frame_Text3);
00386   y1_ -= p->LineSpacing + d;
00387   p->Painter->drawLine(x1_, y1_, x2_, y1_);
00388   p->Painter->drawText(x1_+d, y1_+(d>>1), 0, 0, Qt::TextDontClip, Frame_Text1);
00389   y1_ -= (Frame_Text0.count('\n')+1) * p->LineSpacing + d;
00390   p->Painter->drawRect(x2_, y2_, x1_-x2_-1, y1_-y2_-1);
00391   p->Painter->drawText(x1_+d, y1_+(d>>1), 0, 0, Qt::TextDontClip, Frame_Text0);
00392 }
00393 
00394 // -----------------------------------------------------------
00395 // Is called when the content (schematic or data display) has to be drawn.
00396 void Schematic::drawContents(QPainter *p, int, int, int, int)
00397 {
00398   ViewPainter Painter;
00399 
00400   Painter.init(p, Scale, -ViewX1, -ViewY1, contentsX(), contentsY());
00401 
00402   paintGrid(&Painter, contentsX(), contentsY(),
00403             visibleWidth(), visibleHeight());
00404 
00405   if(!symbolMode)
00406     paintFrame(&Painter);
00407 
00408   for(Component *pc = Components->first(); pc != 0; pc = Components->next())
00409     pc->paint(&Painter);
00410 
00411   for(Wire *pw = Wires->first(); pw != 0; pw = Wires->next()) {
00412     pw->paint(&Painter);
00413     if(pw->Label)
00414       pw->Label->paint(&Painter);  // separate because of paintSelected
00415   }
00416 
00417   Node *pn;
00418   for(pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
00419     pn->paint(&Painter);
00420     if(pn->Label)
00421       pn->Label->paint(&Painter);  // separate because of paintSelected
00422   }
00423 
00424   // FIXME disable here, issue with select box goes away
00425   // also, instead of red, line turns blue
00426   for(Diagram *pd = Diagrams->first(); pd != 0; pd = Diagrams->next())
00427     pd->paint(&Painter);
00428 
00429   for(Painting *pp = Paintings->first(); pp != 0; pp = Paintings->next())
00430     pp->paint(&Painter);
00431 
00432   if(showBias > 0) {  // show DC bias points in schematic ?
00433     int x, y, z;
00434     for(pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
00435       if(pn->Name.isEmpty()) continue;
00436       x = pn->cx;
00437       y = pn->cy + 4;
00438       z = pn->x1;
00439       if(z & 1) x -= Painter.Painter->fontMetrics().width(pn->Name);
00440       if(!(z & 2)) {
00441         y -= (Painter.LineSpacing>>1) + 4;
00442         if(z & 1) x -= 4;
00443         else x += 4;
00444       }
00445       if(z & 0x10)
00446         Painter.Painter->setPen(Qt::darkGreen);  // green for currents
00447       else
00448         Painter.Painter->setPen(Qt::blue);   // blue for voltages
00449       Painter.drawText(pn->Name, x, y);
00450     }
00451   }
00452 
00453   /*
00454    * The following events used to be drawn from mouseactions.cpp, but since Qt4
00455    * Paint actions can only be called from within the paint event, so they
00456    * are put into a QList (PostedPaintEvents) and processed here
00457    */
00458   for(int i=0;i<PostedPaintEvents.size();i++)
00459   {
00460     PostedPaintEvent p = PostedPaintEvents[i];
00461     QPainter painter2(viewport());
00462 
00463     switch(p.pe)
00464     {
00465       case _NotRop:
00466         if(p.PaintOnViewport)
00467           painter2.setCompositionMode(QPainter::RasterOp_SourceAndNotDestination);
00468         else
00469           Painter.Painter->setCompositionMode(QPainter::RasterOp_SourceAndNotDestination);
00470         break;
00471       case _Rect:
00472         if(p.PaintOnViewport)
00473           painter2.drawRect(p.x1, p.y1, p.x2, p.y2);
00474         else
00475           Painter.drawRect(p.x1, p.y1, p.x2, p.y2);
00476         break;
00477       case _Line:
00478         if(p.PaintOnViewport)
00479           painter2.drawLine(p.x1, p.y1, p.x2, p.y2);
00480         else
00481           Painter.drawLine(p.x1, p.y1, p.x2, p.y2);
00482         break;
00483       case _Ellipse:
00484         if(p.PaintOnViewport)
00485           painter2.drawEllipse(p.x1, p.y1, p.x2, p.y2);
00486         else
00487           Painter.drawEllipse(p.x1, p.y1, p.x2, p.y2);
00488         break;
00489       case _Arc:
00490         if(p.PaintOnViewport)
00491           painter2.drawArc(p.x1, p.y1, p.x2, p.y2, p.a, p.b);
00492         else
00493           Painter.drawArc(p.x1, p.y1, p.x2, p.y2, p.a, p.b);
00494         break;
00495       case _DotLine:
00496         Painter.Painter->setPen(Qt::DotLine);
00497         break;
00498       case _Translate:
00499 
00500         painter2.translate(p.x1, p.y1);
00501         break;
00502       case _Scale:
00503         painter2.scale(p.x1,p.y1);
00504         break;
00505     }
00506 
00507   }
00508   PostedPaintEvents.clear();
00509 
00510 }
00511 
00512 void Schematic::PostPaintEvent (PE pe, int x1, int y1, int x2, int y2, int a, int b, bool PaintOnViewport)
00513 {
00514   PostedPaintEvent p = {pe, x1,y1,x2,y2,a,b,PaintOnViewport};
00515   PostedPaintEvents.push_back(p);
00516   viewport()->update();
00517   update();
00518 }
00519 
00520 
00521 // ---------------------------------------------------
00522 void Schematic::contentsMouseMoveEvent(QMouseEvent *Event)
00523 {
00524   emit signalCursorPosChanged(Event->pos().x(), Event->pos().y());
00525   if(App->MouseMoveAction)
00526     (App->view->*(App->MouseMoveAction))(this, Event);
00527 }
00528 
00529 // -----------------------------------------------------------
00530 void Schematic::contentsMousePressEvent(QMouseEvent *Event)
00531 {
00532   App->editText->setHidden(true); // disable text edit of component property
00533   if(App->MouseReleaseAction == &MouseActions::MReleasePaste)
00534     return;
00535 
00536   float x = float(Event->pos().x())/Scale + float(ViewX1);
00537   float y = float(Event->pos().y())/Scale + float(ViewY1);
00538 
00539   if(Event->button() != Qt::LeftButton)
00540     if(App->MousePressAction != &MouseActions::MPressElement)
00541       if(App->MousePressAction != &MouseActions::MPressWire2) {
00542         // show menu on right mouse button
00543         App->view->rightPressMenu(this, Event, x, y);
00544         if(App->MouseReleaseAction)
00545            // Is not called automatically because menu has focus.
00546           (App->view->*(App->MouseReleaseAction))(this, Event);
00547         return;
00548       }
00549 
00550   if(App->MousePressAction)
00551     (App->view->*(App->MousePressAction))(this, Event, x, y);
00552 }
00553 
00554 // -----------------------------------------------------------
00555 void Schematic::contentsMouseReleaseEvent(QMouseEvent *Event)
00556 {
00557   if(App->MouseReleaseAction)
00558     (App->view->*(App->MouseReleaseAction))(this, Event);
00559 }
00560 
00561 // -----------------------------------------------------------
00562 void Schematic::contentsMouseDoubleClickEvent(QMouseEvent *Event)
00563 {
00564   if(App->MouseDoubleClickAction)
00565     (App->view->*(App->MouseDoubleClickAction))(this, Event);
00566 }
00567 
00568 // -----------------------------------------------------------
00569 void Schematic::print(QPrinter*, QPainter *Painter, bool printAll, bool fitToPage)
00570 {
00571   QPaintDevice *pdevice = Painter->device();
00572   float printerDpiX = (float)pdevice->logicalDpiX();
00573   float printerDpiY = (float)pdevice->logicalDpiY();
00574   float printerW = (float)pdevice->width();
00575   float printerH = (float)pdevice->height();
00576   QPainter pa(viewport());
00577   float screenDpiX = (float)pa.device()->logicalDpiX();
00578   float screenDpiY = (float)pa.device()->logicalDpiY();
00579   float PrintScale = 0.5;
00580   sizeOfAll(UsedX1, UsedY1, UsedX2, UsedY2);
00581   int marginX = (int)(40 * printerDpiX / screenDpiX);
00582   int marginY = (int)(40 * printerDpiY / screenDpiY);
00583 
00584   if(fitToPage) {
00585 
00586     float ScaleX = float((printerW - 2*marginX) /
00587                    float((UsedX2-UsedX1) * printerDpiX)) * screenDpiX;
00588     float ScaleY = float((printerH - 2*marginY) /
00589                    float((UsedY2-UsedY1) * printerDpiY)) * screenDpiY;
00590 
00591     if(showFrame){
00592         int xall, yall;
00593         sizeOfFrame(xall, yall);
00594         ScaleX = ((float)(printerW - 2*marginX) /
00595                        (float)(xall * printerDpiX)) * screenDpiX;
00596         ScaleY = ((float)(printerH - 2*marginY) /
00597                        (float)(yall * printerDpiY)) * screenDpiY;
00598     }
00599 
00600     if(ScaleX > ScaleY)
00601       PrintScale = ScaleY;
00602     else
00603       PrintScale = ScaleX;
00604   }
00605 
00606 
00607   //bool selected;
00608   ViewPainter p;
00609   int StartX = UsedX1;
00610   int StartY = UsedY1;
00611   if(showFrame) {
00612     if(UsedX1 > 0)  StartX = 0;
00613     if(UsedY1 > 0)  StartY = 0;
00614   }
00615 
00616   float PrintRatio = printerDpiX / screenDpiX;
00617   QFont oldFont = Painter->font();
00618   QFont printFont = Painter->font();
00619 #ifdef __MINGW32__
00620   printFont.setPointSizeF(printFont.pointSizeF()/PrintRatio);
00621   Painter->setFont(printFont);
00622 #endif
00623   p.init(Painter, PrintScale * PrintRatio,
00624          -StartX, -StartY, -marginX, -marginY, PrintScale, PrintRatio);
00625 
00626   if(!symbolMode)
00627     paintFrame(&p);
00628 
00629   paintSchToViewpainter(&p,printAll,false,screenDpiX,printerDpiX);
00630 
00631   Painter->setFont(oldFont);
00632 }
00633 
00634 
00635 void Schematic::paintSchToViewpainter(ViewPainter *p, bool printAll, bool toImage, int screenDpiX, int printerDpiX)
00636 {
00637     bool selected;
00638 
00639     if (printAll) {
00640         int x2,y2;
00641         if (sizeOfFrame(x2,y2)) paintFrame(p);
00642     }
00643 
00644     for(Component *pc = Components->first(); pc != 0; pc = Components->next())
00645       if(pc->isSelected || printAll) {
00646         selected = pc->isSelected;
00647         pc->isSelected = false;
00648         if (toImage) {
00649             pc->paint(p);
00650         } else {
00651             pc->print(p, (float)screenDpiX / (float)printerDpiX);
00652         }
00653         pc->isSelected = selected;
00654       }
00655 
00656     for(Wire *pw = Wires->first(); pw != 0; pw = Wires->next()) {
00657       if(pw->isSelected || printAll) {
00658         selected = pw->isSelected;
00659         pw->isSelected = false;
00660         pw->paint(p);   // paint all selected wires
00661         pw->isSelected = selected;
00662       }
00663       if(pw->Label)
00664         if(pw->Label->isSelected || printAll) {
00665           selected = pw->Label->isSelected;
00666           pw->Label->isSelected = false;
00667           pw->Label->paint(p);
00668           pw->Label->isSelected = selected;
00669         }
00670     }
00671 
00672     Element *pe;
00673     for(Node *pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
00674       for(pe = pn->Connections.first(); pe != 0; pe = pn->Connections.next())
00675         if(pe->isSelected || printAll) {
00676           pn->paint(p); // paint all nodes with selected elements
00677           break;
00678         }
00679       if(pn->Label)
00680         if(pn->Label->isSelected || printAll) {
00681           selected = pn->Label->isSelected;
00682           pn->Label->isSelected = false;
00683           pn->Label->paint(p);
00684           pn->Label->isSelected = selected;
00685         }
00686     }
00687 
00688     for(Painting *pp = Paintings->first(); pp != 0; pp = Paintings->next())
00689       if(pp->isSelected || printAll) {
00690         selected = pp->isSelected;
00691         pp->isSelected = false;
00692         pp->paint(p);   // paint all selected paintings
00693         pp->isSelected = selected;
00694       }
00695 
00696     for(Diagram *pd = Diagrams->first(); pd != 0; pd = Diagrams->next())
00697       if(pd->isSelected || printAll) {
00698         // if graph or marker is selected, deselect during printing
00699         foreach(Graph *pg, pd->Graphs) {
00700       if(pg->isSelected)  pg->Type |= 1;  // remember selection
00701       pg->isSelected = false;
00702       foreach(Marker *pm, pg->Markers) {
00703         if(pm->isSelected)  pm->Type |= 1;  // remember selection
00704         pm->isSelected = false;
00705       }
00706         }
00707 
00708         selected = pd->isSelected;
00709         pd->isSelected = false;
00710         pd->paint(p);  // paint all selected diagrams with graphs and markers
00711         pd->isSelected = selected;
00712 
00713         // revert selection of graphs and markers
00714         foreach(Graph *pg, pd->Graphs) {
00715       if(pg->Type & 1)  pg->isSelected = true;
00716       pg->Type &= -2;
00717       foreach(Marker *pm, pg->Markers) {
00718         if(pm->Type & 1)  pm->isSelected = true;
00719         pm->Type &= -2;
00720       }
00721         }
00722       }
00723 
00724     if(showBias > 0) {  // show DC bias points in schematic ?
00725       int x, y, z;
00726       for(Node* pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
00727         if(pn->Name.isEmpty()) continue;
00728         x = pn->cx;
00729         y = pn->cy + 4;
00730         z = pn->x1;
00731         if(z & 1) x -= p->Painter->fontMetrics().width(pn->Name);
00732         if(!(z & 2)) {
00733           y -= (p->LineSpacing>>1) + 4;
00734           if(z & 1) x -= 4;
00735           else x += 4;
00736         }
00737         if(z & 0x10)
00738           p->Painter->setPen(Qt::darkGreen);  // green for currents
00739         else
00740           p->Painter->setPen(Qt::blue);   // blue for voltages
00741         p->drawText(pn->Name, x, y);
00742       }
00743     }
00744 }
00745 
00746 // -----------------------------------------------------------
00747 float Schematic::zoom(float s)
00748 {
00749   Scale *= s;
00750   if(Scale > 10.0) Scale = 10.0f;
00751   if(Scale < 0.1) Scale = 0.1f;
00752 
00753   // "resizeContents()" performs an immediate repaint. So, set widget
00754   // to hidden. This causes some flicker, but it is still nicer.
00755   viewport()->setHidden(true);
00756 //  setHidden(true);
00757   resizeContents(int(Scale*float(ViewX2 - ViewX1)),
00758                  int(Scale*float(ViewY2 - ViewY1)));
00759 //  setHidden(false);
00760   viewport()->setHidden(false);
00761 
00762   viewport()->update();
00763   App->view->drawn = false;
00764   return Scale;
00765 }
00766 
00767 // -----------------------------------------------------------
00768 float Schematic::zoomBy(float s)
00769 {
00770   zoom(s);
00771   s -= 1.0;
00772   scrollBy( int(s * float(contentsX()+visibleWidth()/2)),
00773             int(s * float(contentsY()+visibleHeight()/2)) );
00774   return Scale;
00775 }
00776 
00777 // ---------------------------------------------------
00778 void Schematic::showAll()
00779 {
00780   sizeOfAll(UsedX1, UsedY1, UsedX2, UsedY2);
00781   if(UsedX1 == 0)
00782     if(UsedX2 == 0)
00783       if(UsedY1 == 0)
00784         if(UsedY2 == 0) {
00785     UsedX1 = UsedY1 = INT_MAX;
00786     UsedX2 = UsedY2 = INT_MIN;
00787     return;
00788   }
00789 
00790   float xScale = float(visibleWidth()) / float(UsedX2-UsedX1+80);
00791   float yScale = float(visibleHeight()) / float(UsedY2-UsedY1+80);
00792   if(xScale > yScale) xScale = yScale;
00793   xScale /= Scale;
00794 
00795   ViewX1 = UsedX1 - 40;
00796   ViewY1 = UsedY1 - 40;
00797   ViewX2 = UsedX2 + 40;
00798   ViewY2 = UsedY2 + 40;
00799   zoom(xScale);
00800 }
00801 
00802 // ---------------------------------------------------
00803 void Schematic::showNoZoom()
00804 {
00805   Scale = 1.0;
00806 
00807   int x1 = UsedX1;
00808   int y1 = UsedY1;
00809   int x2 = UsedX2;
00810   int y2 = UsedY2;
00811 
00812   if(x1 > x2) {  // happens e.g. if untitled without changes
00813     x1 = 0;
00814     x2 = 800;
00815   }
00816   if(y1 > y2) {
00817     y1 = 0;
00818     y2 = 800;
00819   }
00820   if(x2==0) if(y2==0) if(x1==0) if(y1==0) x2 = y2 = 800;
00821 
00822   ViewX1 = x1-40;
00823   ViewY1 = y1-40;
00824   ViewX2 = x2+40;
00825   ViewY2 = y2+40;
00826   resizeContents(x2-x1+80, y2-y1+80);
00827   viewport()->update();
00828   App->view->drawn = false;
00829 }
00830 
00831 // -----------------------------------------------------------
00832 // Enlarge the viewport area if the coordinates x1-x2/y1-y2 exceed the
00833 // visible area.
00834 void Schematic::enlargeView(int x1, int y1, int x2, int y2)
00835 {
00836   int dx=0, dy=0;
00837   if(x1 < UsedX1) UsedX1 = x1;
00838   if(y1 < UsedY1) UsedY1 = y1;
00839   if(x2 > UsedX2) UsedX2 = x2;
00840   if(y2 > UsedY2) UsedY2 = y2;
00841 
00842   if(x1 < ViewX1) {
00843     dx = int(Scale * float(ViewX1-x1+40));
00844     ViewX1 = x1-40;
00845   }
00846   if(y1 < ViewY1) {
00847     dy = int(Scale * float(ViewY1-y1+40));
00848     ViewY1 = y1-40;
00849   }
00850   if(x2 > ViewX2) ViewX2 = x2+40;
00851   if(y2 > ViewY2) ViewY2 = y2+40;
00852 
00853   resizeContents(int(Scale*float(ViewX2 - ViewX1)),
00854     int(Scale*float(ViewY2 - ViewY1)));
00855   scrollBy(dx,dy);
00856 }
00857 
00858 // ---------------------------------------------------
00859 // Sets an arbitrary coordinate onto the next grid coordinate.
00860 void Schematic::setOnGrid(int& x, int& y)
00861 {
00862   if(x<0) x -= (GridX >> 1) - 1;
00863   else x += GridX >> 1;
00864   x -= x % GridX;
00865 
00866   if(y<0) y -= (GridY >> 1) - 1;
00867   else y += GridY >> 1;
00868   y -= y % GridY;
00869 }
00870 
00871 // ---------------------------------------------------
00872 void Schematic::paintGrid(ViewPainter *p, int cX, int cY, int Width, int Height)
00873 {
00874   if(!GridOn) return;
00875 
00876   p->Painter->setPen(QPen(Qt::black,0));
00877   int dx = -int(Scale*float(ViewX1)) - cX;
00878   int dy = -int(Scale*float(ViewY1)) - cY;
00879   p->Painter->drawLine(-3+dx, dy, 4+dx, dy); // small cross at origin
00880   p->Painter->drawLine( dx,-3+dy, dx, 4+dy);
00881 
00882 
00883   int x1  = int(float(cX)/Scale) + ViewX1;
00884   int y1  = int(float(cY)/Scale) + ViewY1;
00885 
00887   setOnGrid(x1, y1);
00888   if(x1<0) x1 -= GridX - 1;
00889   else x1 += GridX;
00890   x1 -= x1 % (GridX << 1);
00891 
00892   if(y1<0) y1 -= GridY - 1;
00893   else y1 += GridY;
00894   y1 -= y1 % (GridY << 1);
00895 
00896   float X, Y, Y0, DX, DY;
00897   X = float(x1)*Scale + p->DX;
00898   Y = Y0 = float(y1)*Scale + p->DY;
00899   x1 = X > 0.0 ? int(X + 0.5) : int(X - 0.5);
00900   y1 = Y > 0.0 ? int(Y + 0.5) : int(Y - 0.5);
00901 
00902 
00903   int xEnd = x1 + Width;
00904   int yEnd = y1 + Height;
00905   DX = float(GridX << 1) * Scale;   // every second grid a point
00906   DY = float(GridY << 1) * Scale;
00907   while(DX <= 8.0)  DX *= 1.5;  // if too narrow, every third grid a point
00908   while(DY <= 8.0)  DY *= 1.5;  // if too narrow, every third grid a point
00909 
00910   while(x1 < xEnd) {
00911     Y = Y0;
00912     y1 = Y > 0.0 ? int(Y + 0.5) : int(Y - 0.5);
00913     while(y1 < yEnd) {
00914       p->Painter->drawPoint(x1, y1);    // paint grid
00915       Y += DY;
00916       y1 = Y > 0.0 ? int(Y + 0.5) : int(Y - 0.5);
00917     }
00918     X += DX;
00919     x1 = X > 0.0 ? int(X + 0.5) : int(X - 0.5);
00920   }
00921 }
00922 
00923 // ---------------------------------------------------
00924 // Correction factor for unproportional font scaling.
00925 float Schematic::textCorr()
00926 {
00927   QFont Font = QucsSettings.font;
00928   Font.setPointSizeF( Scale * float(Font.pointSize()) );
00929   // use the screen-compatible metric
00930   QFontMetrics  metrics(Font, 0);
00931   return (Scale / float(metrics.lineSpacing()));
00932 }
00933 
00934 // ---------------------------------------------------
00935 void Schematic::sizeOfAll(int& xmin, int& ymin, int& xmax, int& ymax)
00936 {
00937   xmin=INT_MAX;
00938   ymin=INT_MAX;
00939   xmax=INT_MIN;
00940   ymax=INT_MIN;
00941   Component *pc;
00942   Diagram *pd;
00943   Wire *pw;
00944   WireLabel *pl;
00945   Painting *pp;
00946 
00947   if(Components->isEmpty())
00948     if(Wires->isEmpty())
00949       if(Diagrams->isEmpty())
00950         if(Paintings->isEmpty()) {
00951           xmin = xmax = 0;
00952           ymin = ymax = 0;
00953           return;
00954         }
00955 
00956 
00957   float Corr = textCorr();
00958   int x1, y1, x2, y2;
00959   // find boundings of all components
00960   for(pc = Components->first(); pc != 0; pc = Components->next()) {
00961     pc->entireBounds(x1, y1, x2, y2, Corr);
00962     if(x1 < xmin) xmin = x1;
00963     if(x2 > xmax) xmax = x2;
00964     if(y1 < ymin) ymin = y1;
00965     if(y2 > ymax) ymax = y2;
00966   }
00967 
00968   // find boundings of all wires
00969   for(pw = Wires->first(); pw != 0; pw = Wires->next()) {
00970     if(pw->x1 < xmin) xmin = pw->x1;
00971     if(pw->x2 > xmax) xmax = pw->x2;
00972     if(pw->y1 < ymin) ymin = pw->y1;
00973     if(pw->y2 > ymax) ymax = pw->y2;
00974 
00975     pl = pw->Label;
00976     if(pl) {     // check position of wire label
00977       if(pl->x1 < xmin)  xmin = pl->x1;
00978       if((pl->x1+pl->x2) > xmax)  xmax = pl->x1 + pl->x2;
00979       if(pl->y1 > ymax)  ymax = pl->y1;
00980       if((pl->y1-pl->y2) < ymin)  ymin = pl->y1 - pl->y2;
00981     }
00982   }
00983 
00984   // find boundings of all node labels
00985   for(Node *pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
00986     pl = pn->Label;
00987     if(pl) {     // check position of node label
00988       if(pl->x1 < xmin)  xmin = pl->x1;
00989       if((pl->x1+pl->x2) > xmax)  xmax = pl->x1 + pl->x2;
00990       if(pl->y1 > ymax)  ymax = pl->y1;
00991       if((pl->y1-pl->y2) < ymin)  ymin = pl->y1 - pl->y2;
00992     }
00993   }
00994 
00995   // find boundings of all diagrams
00996   for(pd = Diagrams->first(); pd != 0; pd = Diagrams->next()) {
00997     pd->Bounding(x1, y1, x2, y2);
00998     if(x1 < xmin) xmin = x1;
00999     if(x2 > xmax) xmax = x2;
01000     if(y1 < ymin) ymin = y1;
01001     if(y2 > ymax) ymax = y2;
01002 
01003     foreach(Graph *pg, pd->Graphs)
01004       // test all markers of diagram
01005       foreach(Marker *pm, pg->Markers) {
01006         pm->Bounding(x1, y1, x2, y2);
01007         if(x1 < xmin) xmin = x1;
01008         if(x2 > xmax) xmax = x2;
01009         if(y1 < ymin) ymin = y1;
01010         if(y2 > ymax) ymax = y2;
01011       }
01012   }
01013 
01014   // find boundings of all Paintings
01015   for(pp = Paintings->first(); pp != 0; pp = Paintings->next()) {
01016     pp->Bounding(x1, y1, x2, y2);
01017     if(x1 < xmin) xmin = x1;
01018     if(x2 > xmax) xmax = x2;
01019     if(y1 < ymin) ymin = y1;
01020     if(y2 > ymax) ymax = y2;
01021   }
01022 }
01023 
01024 // ---------------------------------------------------
01025 // Rotates all selected components around their midpoint.
01026 bool Schematic::rotateElements()
01027 {
01028   Wires->setAutoDelete(false);
01029   Components->setAutoDelete(false);
01030 
01031   int x1=INT_MAX, y1=INT_MAX;
01032   int x2=INT_MIN, y2=INT_MIN;
01033   QList<Element *> ElementCache;
01034   copyLabels(x1, y1, x2, y2, &ElementCache);   // must be first of all !
01035   copyComponents(x1, y1, x2, y2, &ElementCache);
01036   copyWires(x1, y1, x2, y2, &ElementCache);
01037   copyPaintings(x1, y1, x2, y2, &ElementCache);
01038   if(y1 == INT_MAX) return false;   // no element selected
01039 
01040   Wires->setAutoDelete(true);
01041   Components->setAutoDelete(true);
01042 
01043   x1 = (x1+x2) >> 1;   // center for rotation
01044   y1 = (y1+y2) >> 1;
01045   //setOnGrid(x1, y1);
01046 
01047 
01048   Wire *pw;
01049   Painting  *pp;
01050   Component *pc;
01051   WireLabel *pl;
01052   // re-insert elements
01053   foreach(Element *pe, ElementCache)
01054     switch(pe->Type) {
01055       case isComponent:
01056       case isAnalogComponent:
01057       case isDigitalComponent:
01058         pc = (Component*)pe;
01059         pc->rotate();   //rotate component !before! rotating its center
01060         pc->setCenter(pc->cy - y1 + x1, x1 - pc->cx + y1);
01061         insertRawComponent(pc);
01062         break;
01063 
01064       case isWire:
01065         pw = (Wire*)pe;
01066         x2 = pw->x1;
01067         pw->x1 = pw->y1 - y1 + x1;
01068         pw->y1 = x1 - x2 + y1;
01069         x2 = pw->x2;
01070         pw->x2 = pw->y2 - y1 + x1;
01071         pw->y2 = x1 - x2 + y1;
01072         pl = pw->Label;
01073         if(pl) {
01074           x2 = pl->cx;
01075           pl->cx = pl->cy - y1 + x1;
01076           pl->cy = x1 - x2 + y1;
01077           if(pl->Type == isHWireLabel)
01078             pl->Type = isVWireLabel;
01079           else pl->Type = isHWireLabel;
01080         }
01081         insertWire(pw);
01082         break;
01083 
01084       case isHWireLabel:
01085       case isVWireLabel:
01086   pl = (WireLabel*)pe;
01087   x2 = pl->x1;
01088   pl->x1 = pl->y1 - y1 + x1;
01089   pl->y1 = x1 - x2 + y1;
01090   break;
01091       case isNodeLabel:
01092   pl = (WireLabel*)pe;
01093   if(pl->pOwner == 0) {
01094     x2 = pl->x1;
01095     pl->x1 = pl->y1 - y1 + x1;
01096     pl->y1 = x1 - x2 + y1;
01097   }
01098   x2 = pl->cx;
01099   pl->cx = pl->cy - y1 + x1;
01100   pl->cy = x1 - x2 + y1;
01101   insertNodeLabel(pl);
01102   break;
01103 
01104       case isPainting:
01105         pp = (Painting*)pe;
01106         pp->rotate();   // rotate painting !before! rotating its center
01107         pp->getCenter(x2, y2);
01108         //qDebug("pp->getCenter(x2, y2): (%i,%i)\n", x2, y2);
01109         //qDebug("(x1,y1) (x2,y2): (%i,%i) (%i,%i)\n", x1,y1,x2,y2);
01110         pp->setCenter(y2-y1 + x1, x1-x2 + y1);
01111         Paintings->append(pp);
01112         break;
01113       default: ;
01114     }
01115 
01116   ElementCache.clear();
01117 
01118   setChanged(true, true);
01119   return true;
01120 }
01121 
01122 // ---------------------------------------------------
01123 // Mirrors all selected components.
01124 // First copy them to 'ElementCache', then mirror and insert again.
01125 bool Schematic::mirrorXComponents()
01126 {
01127   Wires->setAutoDelete(false);
01128   Components->setAutoDelete(false);
01129 
01130   int x1, y1, x2, y2;
01131   QList<Element *> ElementCache;
01132   if(!copyComps2WiresPaints(x1, y1, x2, y2, &ElementCache))
01133     return false;
01134   Wires->setAutoDelete(true);
01135   Components->setAutoDelete(true);
01136 
01137   y1 = (y1+y2) >> 1;   // axis for mirroring
01138   setOnGrid(y2, y1);
01139   y1 <<= 1;
01140 
01141 
01142   Wire *pw;
01143   Painting  *pp;
01144   Component *pc;
01145   WireLabel *pl;
01146   // re-insert elements
01147   foreach(Element *pe, ElementCache)
01148     switch(pe->Type) {
01149       case isComponent:
01150       case isAnalogComponent:
01151       case isDigitalComponent:
01152   pc = (Component*)pe;
01153   pc->mirrorX();   // mirror component !before! mirroring its center
01154   pc->setCenter(pc->cx, y1 - pc->cy);
01155   insertRawComponent(pc);
01156   break;
01157       case isWire:
01158   pw = (Wire*)pe;
01159   pw->y1 = y1 - pw->y1;
01160   pw->y2 = y1 - pw->y2;
01161   pl = pw->Label;
01162   if(pl)  pl->cy = y1 - pl->cy;
01163   insertWire(pw);
01164   break;
01165       case isHWireLabel:
01166       case isVWireLabel:
01167   pl = (WireLabel*)pe;
01168   pl->y1 = y1 - pl->y1;
01169   break;
01170       case isNodeLabel:
01171   pl = (WireLabel*)pe;
01172   if(pl->pOwner == 0)
01173     pl->y1 = y1 - pl->y1;
01174   pl->cy = y1 - pl->cy;
01175   insertNodeLabel(pl);
01176   break;
01177       case isPainting:
01178   pp = (Painting*)pe;
01179   pp->getCenter(x2, y2);
01180   pp->mirrorX();   // mirror painting !before! mirroring its center
01181   pp->setCenter(x2, y1 - y2);
01182   Paintings->append(pp);
01183   break;
01184       default: ;
01185     }
01186 
01187   ElementCache.clear();
01188   setChanged(true, true);
01189   return true;
01190 }
01191 
01192 // ---------------------------------------------------
01193 // Mirrors all selected components. First copy them to 'ElementCache', then mirror and insert again.
01194 bool Schematic::mirrorYComponents()
01195 {
01196   Wires->setAutoDelete(false);
01197   Components->setAutoDelete(false);
01198 
01199   int x1, y1, x2, y2;
01200   QList<Element *> ElementCache;
01201   if(!copyComps2WiresPaints(x1, y1, x2, y2, &ElementCache))
01202     return false;
01203   Wires->setAutoDelete(true);
01204   Components->setAutoDelete(true);
01205 
01206   x1 = (x1+x2) >> 1;   // axis for mirroring
01207   setOnGrid(x1, x2);
01208   x1 <<= 1;
01209 
01210   Wire *pw;
01211   Painting  *pp;
01212   Component *pc;
01213   WireLabel *pl;
01214   // re-insert elements
01215   foreach(Element *pe, ElementCache)
01216     switch(pe->Type) {
01217       case isComponent:
01218       case isAnalogComponent:
01219       case isDigitalComponent:
01220         pc = (Component*)pe;
01221         pc->mirrorY();   // mirror component !before! mirroring its center
01222         pc->setCenter(x1 - pc->cx, pc->cy);
01223         insertRawComponent(pc);
01224         break;
01225       case isWire:
01226         pw = (Wire*)pe;
01227         pw->x1 = x1 - pw->x1;
01228         pw->x2 = x1 - pw->x2;
01229         pl = pw->Label;
01230         if(pl)  pl->cx = x1 - pl->cx;
01231         insertWire(pw);
01232         break;
01233       case isHWireLabel:
01234       case isVWireLabel:
01235         pl = (WireLabel*)pe;
01236         pl->x1 = x1 - pl->x1;
01237         break;
01238       case isNodeLabel:
01239         pl = (WireLabel*)pe;
01240         if(pl->pOwner == 0)
01241           pl->x1 = x1 - pl->x1;
01242         pl->cx = x1 - pl->cx;
01243         insertNodeLabel(pl);
01244         break;
01245       case isPainting:
01246         pp = (Painting*)pe;
01247         pp->getCenter(x2, y2);
01248         pp->mirrorY();   // mirror painting !before! mirroring its center
01249         pp->setCenter(x1 - x2, y2);
01250         Paintings->append(pp);
01251         break;
01252       default: ;
01253     }
01254 
01255   ElementCache.clear();
01256   setChanged(true, true);
01257   return true;
01258 }
01259 
01260 // ---------------------------------------------------
01261 // Updates the graph data of all diagrams (load from data files).
01262 void Schematic::reloadGraphs()
01263 {
01264   QFileInfo Info(DocName);
01265   for(Diagram *pd = Diagrams->first(); pd != 0; pd = Diagrams->next())
01266     pd->loadGraphData(Info.path()+QDir::separator()+DataSet);
01267 }
01268 
01269 // Copy function, 
01270 void Schematic::copy()
01271 {
01272   QString s = createClipboardFile();
01273   QClipboard *cb = QApplication::clipboard();  // get system clipboard
01274   if (!s.isEmpty()) {
01275     cb->setText(s, QClipboard::Clipboard);
01276   }
01277 }
01278 
01279 // ---------------------------------------------------
01280 // Cut function, copy followed by deletion
01281 void Schematic::cut()
01282 {
01283   copy();
01284   deleteElements(); //delete selected elements
01285   viewport()->update();
01286 }
01287 
01288 // ---------------------------------------------------
01289 // Performs paste function from clipboard
01290 bool Schematic::paste(QTextStream *stream, Q3PtrList<Element> *pe)
01291 {
01292   return pasteFromClipboard(stream, pe);
01293 }
01294 
01295 // ---------------------------------------------------
01296 // Loads this Qucs document.
01297 bool Schematic::load()
01298 {
01299   DocComps.clear();
01300   DocWires.clear();
01301   DocNodes.clear();
01302   DocDiags.clear();
01303   DocPaints.clear();
01304   SymbolPaints.clear();
01305 
01306   if(!loadDocument()) return false;
01307   lastSaved = QDateTime::currentDateTime();
01308 
01309   while(!undoAction.isEmpty()) {
01310     delete undoAction.last();
01311     undoAction.pop_back();
01312   }
01313   undoActionIdx = 0;
01314   while(!undoSymbol.isEmpty()) {
01315     delete undoSymbol.last();
01316     undoSymbol.pop_back();
01317   }
01318   symbolMode = true;
01319   setChanged(false, true); // "not changed" state, but put on undo stack
01320   undoSymbolIdx = 0;
01321   undoSymbol.at(undoSymbolIdx)->replace(1, 1, 'i');
01322   symbolMode = false;
01323   setChanged(false, true); // "not changed" state, but put on undo stack
01324   undoActionIdx = 0;
01325   undoAction.at(undoActionIdx)->replace(1, 1, 'i');
01326 
01327   // The undo stack of the circuit symbol is initialized when first
01328   // entering its edit mode.
01329   
01330   // have to call this to avoid crash at sizeOfAll
01331   becomeCurrent(false);
01332 
01333   sizeOfAll(UsedX1, UsedY1, UsedX2, UsedY2);
01334   if(ViewX1 > UsedX1)  ViewX1 = UsedX1;
01335   if(ViewY1 > UsedY1)  ViewY1 = UsedY1;
01336   if(ViewX2 < UsedX2)  ViewX2 = UsedX2;
01337   if(ViewY2 < UsedY2)  ViewY2 = UsedY2;
01338   zoom(1.0f);
01339   setContentsPos(tmpViewX1, tmpViewY1);
01340   tmpViewX1 = tmpViewY1 = -200;   // was used as temporary cache
01341   return true;
01342 }
01343 
01344 // ---------------------------------------------------
01345 // Saves this Qucs document. Returns the number of subcircuit ports.
01346 int Schematic::save()
01347 {
01348   int result = adjustPortNumbers();// same port number for schematic and symbol
01349   if(saveDocument() < 0)
01350      return -1;
01351 
01352   QFileInfo Info(DocName);
01353   lastSaved = Info.lastModified();
01354 
01355   if(result >= 0) {
01356     setChanged(false);
01357 
01358     QVector<QString *>::iterator it;
01359     for (it = undoAction.begin(); it != undoAction.end(); it++) {
01360       (*it)->replace(1, 1, ' '); //at(1) = ' '; state of being changed
01361     }
01362     //(1) = 'i';   // state of being unchanged
01363     undoAction.at(undoActionIdx)->replace(1, 1, 'i');
01364 
01365     for (it = undoSymbol.begin(); it != undoSymbol.end(); it++) {
01366       (*it)->replace(1, 1, ' '); //at(1) = ' '; state of being changed
01367     }
01368     //at(1) = 'i';   // state of being unchanged
01369     undoSymbol.at(undoSymbolIdx)->replace(1, 1, 'i');
01370   }
01371   // update the subcircuit file lookup hashes
01372   QucsMain->updateSchNameHash();
01373   QucsMain->updateSpiceNameHash();
01374 
01375   return result;
01376 }
01377 
01378 // ---------------------------------------------------
01379 // If the port number of the schematic and of the symbol are not
01380 // equal add or remove some in the symbol.
01381 int Schematic::adjustPortNumbers()
01382 {
01383   int x1, x2, y1, y2;
01384   // get size of whole symbol to know where to place new ports
01385   if(symbolMode)  sizeOfAll(x1, y1, x2, y2);
01386   else {
01387     Components = &SymbolComps;
01388     Wires      = &SymbolWires;
01389     Nodes      = &SymbolNodes;
01390     Diagrams   = &SymbolDiags;
01391     Paintings  = &SymbolPaints;
01392     sizeOfAll(x1, y1, x2, y2);
01393     Components = &DocComps;
01394     Wires      = &DocWires;
01395     Nodes      = &DocNodes;
01396     Diagrams   = &DocDiags;
01397     Paintings  = &DocPaints;
01398   }
01399   x1 += 40;
01400   y2 += 20;
01401   setOnGrid(x1, y2);
01402 
01403 
01404   Painting *pp;
01405   // delete all port names in symbol
01406   for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
01407     if(pp->Name == ".PortSym ")
01408       ((PortSymbol*)pp)->nameStr = "";
01409 
01410   QString Str;
01411   int countPort = 0;
01412 
01413   QFileInfo Info (DataDisplay);
01414   QString Suffix = Info.suffix();
01415 
01416   // handle VHDL file symbol
01417   if (Suffix == "vhd" || Suffix == "vhdl") {
01418     QStringList::iterator it;
01419     QStringList Names, GNames, GTypes, GDefs;
01420     int Number;
01421 
01422     // get ports from VHDL file
01423     QFileInfo Info(DocName);
01424     QString Name = Info.path() + QDir::separator() + DataDisplay;
01425 
01426     // obtain VHDL information either from open text document or the
01427     // file directly
01428     VHDL_File_Info VInfo;
01429     TextDoc * d = (TextDoc*)App->findDoc (Name);
01430     if (d)
01431       VInfo = VHDL_File_Info (d->document()->toPlainText());
01432     else
01433       VInfo = VHDL_File_Info (Name, true);
01434 
01435     if (!VInfo.PortNames.isEmpty())
01436       Names = VInfo.PortNames.split(",", QString::SkipEmptyParts);
01437 
01438     for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
01439       if(pp->Name == ".ID ") {
01440   ID_Text * id = (ID_Text *) pp;
01441   id->Prefix = VInfo.EntityName.toUpper();
01442   id->Parameter.clear();
01443   if (!VInfo.GenNames.isEmpty())
01444     GNames = VInfo.GenNames.split(",", QString::SkipEmptyParts);
01445   if (!VInfo.GenTypes.isEmpty())
01446     GTypes = VInfo.GenTypes.split(",", QString::SkipEmptyParts);
01447   if (!VInfo.GenDefs.isEmpty())
01448     GDefs = VInfo.GenDefs.split(",", QString::SkipEmptyParts);;
01449   for(Number = 1, it = GNames.begin(); it != GNames.end(); ++it) {
01450     id->Parameter.append(new SubParameter(
01451       true,
01452       *it+"="+GDefs[Number-1],
01453       tr("generic")+" "+QString::number(Number),
01454       GTypes[Number-1]));
01455     Number++;
01456   }
01457       }
01458 
01459     for(Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
01460       countPort++;
01461 
01462       Str = QString::number(Number);
01463       // search for matching port symbol
01464       for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
01465   if(pp->Name == ".PortSym ")
01466     if(((PortSymbol*)pp)->numberStr == Str) break;
01467 
01468       if(pp)
01469   ((PortSymbol*)pp)->nameStr = *it;
01470       else {
01471   SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
01472   y2 += 40;
01473       }
01474     }
01475   }
01476   // handle Verilog-HDL file symbol
01477   else if (Suffix == "v") {
01478 
01479     QStringList::iterator it;
01480     QStringList Names;
01481     int Number;
01482 
01483     // get ports from Verilog-HDL file
01484     QFileInfo Info (DocName);
01485     QString Name = Info.path() + QDir::separator() + DataDisplay;
01486 
01487     // obtain Verilog-HDL information either from open text document or the
01488     // file directly
01489     Verilog_File_Info VInfo;
01490     TextDoc * d = (TextDoc*)App->findDoc (Name);
01491     if (d)
01492       VInfo = Verilog_File_Info (d->document()->toPlainText());
01493     else
01494       VInfo = Verilog_File_Info (Name, true);
01495     if (!VInfo.PortNames.isEmpty())
01496       Names = VInfo.PortNames.split(",", QString::SkipEmptyParts);
01497 
01498     for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
01499       if(pp->Name == ".ID ") {
01500   ID_Text * id = (ID_Text *) pp;
01501   id->Prefix = VInfo.ModuleName.toUpper();
01502   id->Parameter.clear();
01503       }
01504 
01505     for(Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
01506       countPort++;
01507 
01508       Str = QString::number(Number);
01509       // search for matching port symbol
01510       for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
01511   if(pp->Name == ".PortSym ")
01512     if(((PortSymbol*)pp)->numberStr == Str) break;
01513 
01514       if(pp)
01515   ((PortSymbol*)pp)->nameStr = *it;
01516       else {
01517   SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
01518   y2 += 40;
01519       }
01520     }
01521   }
01522   // handle Verilog-A file symbol
01523   else if (Suffix == "va") {
01524 
01525     QStringList::iterator it;
01526     QStringList Names;
01527     int Number;
01528 
01529     // get ports from Verilog-A file
01530     QFileInfo Info (DocName);
01531     QString Name = Info.path() + QDir::separator() + DataDisplay;
01532 
01533     // obtain Verilog-A information either from open text document or the
01534     // file directly
01535     VerilogA_File_Info VInfo;
01536     TextDoc * d = (TextDoc*)App->findDoc (Name);
01537     if (d)
01538       VInfo = VerilogA_File_Info (d->toPlainText());
01539     else
01540       VInfo = VerilogA_File_Info (Name, true);
01541 
01542     if (!VInfo.PortNames.isEmpty())
01543       Names = VInfo.PortNames.split(",", QString::SkipEmptyParts);
01544 
01545     for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
01546       if(pp->Name == ".ID ") {
01547   ID_Text * id = (ID_Text *) pp;
01548   id->Prefix = VInfo.ModuleName.toUpper();
01549   id->Parameter.clear();
01550       }
01551 
01552     for(Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
01553       countPort++;
01554 
01555       Str = QString::number(Number);
01556       // search for matching port symbol
01557       for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
01558   if(pp->Name == ".PortSym ")
01559     if(((PortSymbol*)pp)->numberStr == Str) break;
01560 
01561       if(pp)
01562   ((PortSymbol*)pp)->nameStr = *it;
01563       else {
01564   SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
01565   y2 += 40;
01566       }
01567     }
01568   }
01569   // handle schematic symbol
01570   else
01571   {
01572       // go through all components in a schematic
01573       for(Component *pc = DocComps.first(); pc!=0; pc = DocComps.next())
01574       {
01575          if(pc->Model == "Port")
01576          {
01577              countPort++;
01578 
01579              Str = pc->Props.getFirst()->Value;
01580              // search for matching port symbol
01581              for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
01582              {
01583                  if(pp->Name == ".PortSym ")
01584                  {
01585                    if(((PortSymbol*)pp)->numberStr == Str) break;
01586                  }
01587              }
01588 
01589              if(pp)
01590              {
01591                  ((PortSymbol*)pp)->nameStr = pc->Name;
01592              }
01593              else
01594              {
01595                  SymbolPaints.append(new PortSymbol(x1, y2, Str, pc->Name));
01596                  y2 += 40;
01597              }
01598           }
01599       }
01600   }
01601 
01602   // delete not accounted port symbols
01603   for(pp = SymbolPaints.first(); pp!=0; ) {
01604     if(pp->Name == ".PortSym ")
01605       if(((PortSymbol*)pp)->nameStr.isEmpty()) {
01606         SymbolPaints.remove();
01607         pp = SymbolPaints.current();
01608         continue;
01609       }
01610     pp = SymbolPaints.next();
01611   }
01612 
01613   return countPort;
01614 }
01615 
01616 // ---------------------------------------------------
01617 bool Schematic::undo()
01618 {
01619   if(symbolMode) {
01620     if (undoSymbolIdx == 0) { return false; }
01621 
01622     rebuildSymbol(undoSymbol.at(--undoSymbolIdx));
01623     adjustPortNumbers();  // set port names
01624 
01625     emit signalUndoState(undoSymbolIdx != 0);
01626     emit signalRedoState(undoSymbolIdx != undoSymbol.size()-1);
01627 
01628     if(undoSymbol.at(undoSymbolIdx)->at(1) == 'i' && 
01629         undoAction.at(undoActionIdx)->at(1) == 'i') {
01630       setChanged(false, false);
01631       return true;
01632     }
01633 
01634     setChanged(true, false);
01635     return true;
01636   }
01637 
01638 
01639   // ...... for schematic edit mode .......
01640   if (undoActionIdx == 0) { return false; }
01641 
01642   rebuild(undoAction.at(--undoActionIdx));
01643   reloadGraphs();  // load recent simulation data
01644 
01645   emit signalUndoState(undoActionIdx != 0);
01646   emit signalRedoState(undoActionIdx != undoAction.size()-1);
01647 
01648   if(undoAction.at(undoActionIdx)->at(1) == 'i') {
01649     if(undoSymbol.isEmpty()) {
01650       setChanged(false, false);
01651       return true;
01652     }
01653     else if(undoSymbol.at(undoSymbolIdx)->at(1) == 'i') {
01654       setChanged(false, false);
01655       return true;
01656     }
01657   }
01658 
01659   setChanged(true, false);
01660   return true;
01661 }
01662 
01663 // ---------------------------------------------------
01664 bool Schematic::redo()
01665 {
01666   if(symbolMode) {
01667     if (undoSymbolIdx == undoSymbol.size() - 1) { return false; }
01668 
01669     rebuildSymbol(undoSymbol.at(++undoSymbolIdx));
01670     adjustPortNumbers();  // set port names
01671 
01672     emit signalUndoState(undoSymbolIdx != 0);
01673     emit signalRedoState(undoSymbolIdx != undoSymbol.size()-1);
01674 
01675     if(undoSymbol.at(undoSymbolIdx)->at(1) == 'i'
01676         && undoAction.at(undoActionIdx)->at(1) == 'i') {
01677       setChanged(false, false);
01678       return true;
01679     }
01680 
01681     setChanged(true, false);
01682     return true;
01683   }
01684 
01685 
01686   //
01687   // ...... for schematic edit mode .......
01688   if (undoActionIdx == undoAction.size()-1) { return false; }
01689 
01690   rebuild(undoAction.at(++undoActionIdx));
01691   reloadGraphs();  // load recent simulation data
01692 
01693   emit signalUndoState(undoActionIdx != 0);
01694   emit signalRedoState(undoActionIdx != undoAction.size()-1);
01695 
01696   if (undoAction.at(undoActionIdx)->at(1) == 'i') {
01697     if(undoSymbol.isEmpty()) {
01698       setChanged(false, false);
01699       return true;
01700     }
01701     else if(undoSymbol.at(undoSymbolIdx)->at(1) == 'i') {
01702       setChanged(false, false);
01703       return true;
01704     }
01705     
01706   }
01707 
01708   setChanged(true, false);
01709   return true;
01710 }
01711 
01712 // ---------------------------------------------------
01713 // Sets selected elements on grid.
01714 bool Schematic::elementsOnGrid()
01715 {
01716   int x, y, No;
01717   bool count = false;
01718   WireLabel *pl, *pLabel;
01719   Q3PtrList<WireLabel> LabelCache;
01720 
01721   // test all components
01722   Components->setAutoDelete(false);
01723   for(Component *pc = Components->last(); pc != 0; pc = Components->prev())
01724     if(pc->isSelected) {
01725 
01726       // rescue non-selected node labels
01727       foreach(Port *pp, pc->Ports)
01728         if(pp->Connection->Label)
01729           if(pp->Connection->Connections.count() < 2) {
01730             LabelCache.append(pp->Connection->Label);
01731             pp->Connection->Label->pOwner = 0;
01732             pp->Connection->Label = 0;
01733           }
01734 
01735       x = pc->cx;
01736       y = pc->cy;
01737       No = Components->at();
01738       deleteComp(pc);
01739       setOnGrid(pc->cx, pc->cy);
01740       insertRawComponent(pc);
01741       Components->at(No);   // restore current list position
01742       pc->isSelected = false;
01743       count = true;
01744 
01745       x -= pc->cx;
01746       y -= pc->cy;    // re-insert node labels and correct position
01747       for(pl = LabelCache.first(); pl != 0; pl = LabelCache.next()) {
01748         pl->cx -= x;
01749         pl->cy -= y;
01750         insertNodeLabel(pl);
01751       }
01752       LabelCache.clear();
01753     }
01754   Components->setAutoDelete(true);
01755 
01756   Wires->setAutoDelete(false);
01757   // test all wires and wire labels
01758   for(Wire *pw = Wires->last(); pw != 0; pw = Wires->prev()) {
01759     pl = pw->Label;
01760     pw->Label = 0;
01761 
01762     if(pw->isSelected) {
01763       // rescue non-selected node label
01764       pLabel = 0;
01765       if(pw->Port1->Label) {
01766         if(pw->Port1->Connections.count() < 2) {
01767             pLabel = pw->Port1->Label;
01768             pw->Port1->Label = 0;
01769         }
01770       }
01771       else if(pw->Port2->Label) {
01772         if(pw->Port2->Connections.count() < 2) {
01773             pLabel = pw->Port2->Label;
01774             pw->Port2->Label = 0;
01775         }
01776       }
01777 
01778       No = Wires->at();
01779       deleteWire(pw);
01780       setOnGrid(pw->x1, pw->y1);
01781       setOnGrid(pw->x2, pw->y2);
01782       insertWire(pw);
01783       Wires->at(No);   // restore current list position
01784       pw->isSelected = false;
01785       count = true;
01786       if(pl)
01787         setOnGrid(pl->cx, pl->cy);
01788 
01789       if(pLabel) {
01790         setOnGrid(pLabel->cx, pLabel->cy);
01791         insertNodeLabel(pLabel);
01792       }
01793     }
01794 
01795     if(pl) {
01796       pw->Label = pl;
01797       if(pl->isSelected) {
01798         setOnGrid(pl->x1, pl->y1);
01799         pl->isSelected = false;
01800         count = true;
01801       }
01802     }
01803   }
01804   Wires->setAutoDelete(true);
01805 
01806   // test all node labels
01807   for(Node *pn = Nodes->first(); pn != 0; pn = Nodes->next())
01808     if(pn->Label)
01809       if(pn->Label->isSelected) {
01810         setOnGrid(pn->Label->x1, pn->Label->y1);
01811         pn->Label->isSelected = false;
01812         count = true;
01813       }
01814 
01815   // test all diagrams
01816   for(Diagram *pd = Diagrams->last(); pd != 0; pd = Diagrams->prev()) {
01817     if(pd->isSelected) {
01818       setOnGrid(pd->cx, pd->cy);
01819       pd->isSelected = false;
01820       count = true;
01821     }
01822 
01823     foreach(Graph *pg,pd->Graphs)
01824       // test markers of diagram
01825       foreach(Marker *pm, pg->Markers)
01826         if(pm->isSelected) {
01827     x = pm->x1 + pd->cx;
01828     y = pm->y1 + pd->cy;
01829     setOnGrid(x, y);
01830     pm->x1 = x - pd->cx;
01831     pm->y1 = y - pd->cy;
01832     pm->isSelected = false;
01833     count = true;
01834         }
01835   }
01836 
01837   // test all paintings
01838   for(Painting *pa = Paintings->last(); pa != 0; pa = Paintings->prev())
01839     if(pa->isSelected) {
01840       setOnGrid(pa->cx, pa->cy);
01841       pa->isSelected = false;
01842       count = true;
01843     }
01844 
01845   if(count) setChanged(true, true);
01846   return count;
01847 }
01848 
01849 // ---------------------------------------------------
01850 void Schematic::switchPaintMode()
01851 {
01852   symbolMode = !symbolMode;  // change mode
01853 
01854   int tmp, t2;
01855   float temp;
01856   temp = Scale; Scale  = tmpScale;  tmpScale  = temp;
01857   tmp = contentsX();
01858   t2  = contentsY();
01859   setContentsPos(tmpPosX, tmpPosY);
01860   tmpPosX = tmp;
01861   tmpPosY = t2;
01862   tmp = ViewX1; ViewX1 = tmpViewX1; tmpViewX1 = tmp;
01863   tmp = ViewY1; ViewY1 = tmpViewY1; tmpViewY1 = tmp;
01864   tmp = ViewX2; ViewX2 = tmpViewX2; tmpViewX2 = tmp;
01865   tmp = ViewY2; ViewY2 = tmpViewY2; tmpViewY2 = tmp;
01866   tmp = UsedX1; UsedX1 = tmpUsedX1; tmpUsedX1 = tmp;
01867   tmp = UsedY1; UsedY1 = tmpUsedY1; tmpUsedY1 = tmp;
01868   tmp = UsedX2; UsedX2 = tmpUsedX2; tmpUsedX2 = tmp;
01869   tmp = UsedY2; UsedY2 = tmpUsedY2; tmpUsedY2 = tmp;
01870 }
01871 
01872 
01873 // *********************************************************************
01874 // **********                                                 **********
01875 // **********      Function for serving mouse wheel moving    **********
01876 // **********                                                 **********
01877 // *********************************************************************
01878 void Schematic::contentsWheelEvent(QWheelEvent *Event)
01879 {
01880   App->editText->setHidden(true);  // disable edit of component property
01881   int delta = Event->delta() >> 1;     // use smaller steps
01882 
01883   // ...................................................................
01884   if((Event->modifiers() & Qt::ShiftModifier) ||
01885      (Event->orientation() == Qt::Horizontal)) { // scroll horizontally ?
01886       if(delta > 0) { if(scrollLeft(delta)) scrollBy(-delta, 0); }
01887       else { if(scrollRight(delta)) scrollBy(-delta, 0); }
01888       viewport()->update(); // because QScrollView thinks nothing has changed
01889       App->view->drawn = false;
01890   }
01891   // ...................................................................
01892   else if(Event->modifiers() & Qt::ControlModifier) {  // use mouse wheel to zoom ?
01893       float Scaling;
01894       if(delta < 0) Scaling = float(delta)/-60.0/1.1;
01895       else Scaling = 1.1*60.0/float(delta);
01896       zoom(Scaling);
01897       Scaling -= 1.0;
01898       scrollBy( int(Scaling * float(Event->pos().x())),
01899                 int(Scaling * float(Event->pos().y())) );
01900   }
01901   // ...................................................................
01902   else {     // scroll vertically !
01903       if(delta > 0) { if(scrollUp(delta)) scrollBy(0, -delta); }
01904       else { if(scrollDown(delta)) scrollBy(0, -delta); }
01905       viewport()->update(); // because QScrollView thinks nothing has changed
01906       App->view->drawn = false;
01907   }
01908 
01909   Event->accept();   // QScrollView must not handle this event
01910 }
01911 
01912 // -----------------------------------------------------------
01913 // Scrolls the visible area upwards and enlarges or reduces the view
01914 // area accordingly.
01915 bool Schematic::scrollUp(int step)
01916 {
01917   int diff;
01918 
01919   diff = contentsY() - step;
01920   if(diff < 0) {     // scroll outside the active area ?  (upwards)
01921     resizeContents(contentsWidth(), contentsHeight()-diff);
01922     ViewY1 += diff;
01923     scrollBy(0, diff);
01924     return false;
01925   }
01926 
01927   diff = ViewY2 - UsedY2 - 20;    // keep border of 20
01928   if(diff > 0) {      // make active area smaller ?
01929     if(step < diff) diff = step;
01930     resizeContents(contentsWidth(), contentsHeight()-diff);
01931     ViewY2 -= diff;
01932   }
01933 
01934   return true;
01935 }
01936 
01937 // -----------------------------------------------------------
01938 // Scrolls the visible area downwards and enlarges or reduces the view
01939 // area accordingly. ("step" must be negative!)
01940 bool Schematic::scrollDown(int step)
01941 {
01942   int diff;
01943 
01944   diff = contentsHeight() - contentsY()-visibleHeight() + step;
01945   if(diff < 0) {     // scroll outside the active area ?  (downwards)
01946     resizeContents(contentsWidth(), contentsHeight()-diff);
01947     ViewY2 -= diff;
01948     scrollBy(0, -step);
01949     return false;
01950   }
01951 
01952   diff = ViewY1 - UsedY1 + 20;    // keep border of 20
01953   if(diff < 0) {      // make active area smaller ?
01954     if(step > diff) diff = step;
01955     resizeContents(contentsWidth(), contentsHeight()+diff);
01956     ViewY1 -= diff;
01957     return false;
01958   }
01959 
01960   return true;
01961 }
01962 
01963 // -----------------------------------------------------------
01964 // Scrolls the visible area to the left and enlarges or reduces the view
01965 // area accordingly.
01966 bool Schematic::scrollLeft(int step)
01967 {
01968   int diff;
01969 
01970   diff = contentsX() - step;
01971   if(diff < 0) {     // scroll outside the active area ?  (to the left)
01972     resizeContents(contentsWidth()-diff, contentsHeight());
01973     ViewX1 += diff;
01974     scrollBy(diff, 0);
01975     return false;
01976   }
01977 
01978   diff = ViewX2 - UsedX2 - 20;    // keep border of 20
01979   if(diff > 0) {      // make active area smaller ?
01980     if(step < diff) diff = step;
01981     resizeContents(contentsWidth()-diff, contentsHeight());
01982     ViewX2 -= diff;
01983   }
01984 
01985   return true;
01986 }
01987 
01988 // -----------------------------------------------------------
01989 // Scrolls the visible area to the right and enlarges or reduces the
01990 // view area accordingly. ("step" must be negative!)
01991 bool Schematic::scrollRight(int step)
01992 {
01993   int diff;
01994 
01995   diff = contentsWidth() - contentsX()-visibleWidth() + step;
01996   if(diff < 0) {     // scroll outside the active area ?  (to the right)
01997     resizeContents(contentsWidth()-diff, contentsHeight());
01998     ViewX2 -= diff;
01999     scrollBy(-step, 0);
02000     return false;
02001   }
02002 
02003   diff = ViewX1 - UsedX1 + 20;    // keep border of 20
02004   if(diff < 0) {      // make active area smaller ?
02005     if(step > diff) diff = step;
02006     resizeContents(contentsWidth()+diff, contentsHeight());
02007     ViewX1 -= diff;
02008     return false;
02009   }
02010 
02011   return true;
02012 }
02013 
02014 // -----------------------------------------------------------
02015 // Is called if the scroll arrow of the ScrollBar is pressed.
02016 void Schematic::slotScrollUp()
02017 {
02018   App->editText->setHidden(true);  // disable edit of component property
02019   scrollUp(verticalScrollBar()->singleStep());
02020   viewport()->update(); // because QScrollView thinks nothing has changed
02021   App->view->drawn = false;
02022 }
02023 
02024 // -----------------------------------------------------------
02025 // Is called if the scroll arrow of the ScrollBar is pressed.
02026 void Schematic::slotScrollDown()
02027 {
02028   App->editText->setHidden(true);  // disable edit of component property
02029   scrollDown(-verticalScrollBar()->singleStep());
02030   viewport()->update(); // because QScrollView thinks nothing has changed
02031   App->view->drawn = false;
02032 }
02033 
02034 // -----------------------------------------------------------
02035 // Is called if the scroll arrow of the ScrollBar is pressed.
02036 void Schematic::slotScrollLeft()
02037 {
02038   App->editText->setHidden(true);  // disable edit of component property
02039   scrollLeft(horizontalScrollBar()->singleStep());
02040   viewport()->update(); // because QScrollView thinks nothing has changed
02041   App->view->drawn = false;
02042 }
02043 
02044 // -----------------------------------------------------------
02045 // Is called if the scroll arrow of the ScrollBar is pressed.
02046 void Schematic::slotScrollRight()
02047 {
02048   App->editText->setHidden(true);  // disable edit of component property
02049   scrollRight(-horizontalScrollBar()->singleStep());
02050   viewport()->update(); // because QScrollView thinks nothing has changed
02051   App->view->drawn = false;
02052 }
02053 
02054 
02055 // *********************************************************************
02056 // **********                                                 **********
02057 // **********        Function for serving drag'n drop         **********
02058 // **********                                                 **********
02059 // *********************************************************************
02060 
02061 // Is called if an object is dropped (after drag'n drop).
02062 void Schematic::contentsDropEvent(QDropEvent *Event)
02063 {
02064   if(dragIsOkay) {
02065     QList<QUrl> urls = Event->mimeData()->urls();
02066     if (urls.isEmpty()) {
02067       return;
02068     }
02069 
02070     // do not close untitled document to avoid segfault
02071     QucsDoc *d = QucsMain->getDoc(0);
02072     bool changed = d->DocChanged;
02073     d->DocChanged = true;
02074 
02075     // URI:  file:/home/linuxuser/Desktop/example.sch
02076     foreach(QUrl url, urls) {
02077       App->gotoPage(QDir::convertSeparators(url.toLocalFile()));
02078     }
02079 
02080     d->DocChanged = changed;
02081     return;
02082   }
02083 
02084 
02085   QMouseEvent e(QEvent::MouseButtonPress, Event->pos(),
02086                 Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
02087   int x = int(Event->pos().x()/Scale) + ViewX1;
02088   int y = int(Event->pos().y()/Scale) + ViewY1;
02089 
02090   App->view->MPressElement(this, &e, x, y);
02091 
02092   if(App->view->selElem) delete App->view->selElem;
02093   App->view->selElem = 0;  // no component selected
02094 
02095   if(formerAction)
02096     formerAction->setChecked(true);  // restore old action
02097 }
02098 
02099 // ---------------------------------------------------
02100 void Schematic::contentsDragEnterEvent(QDragEnterEvent *Event)
02101 {
02102   //FIXME: the function of drag library component seems not working?
02103   formerAction = 0;
02104   dragIsOkay = false;
02105 
02106   // file dragged in ?
02107   if(Event->mimeData()->hasUrls()) {
02108     dragIsOkay = true;
02109     Event->accept();
02110     return;
02111   }
02112 
02113   // drag library component
02114   if(Event->mimeData()->hasText()) {
02115     QString s = Event->mimeData()->text();
02116     if(s.left(15) == "QucsComponent:<") {
02117       s = s.mid(14);
02118       App->view->selElem = getComponentFromName(s);
02119       if(App->view->selElem) {
02120         Event->accept();
02121         return;
02122       }
02123     }
02124     Event->ignore();
02125     return;
02126   }
02127 
02128 
02129   if(Event->format(1) == 0) {  // only one MIME type ?
02130 
02131     // drag component from listview
02132     if(Event->provides("application/x-qabstractitemmodeldatalist")) {
02133       QListWidgetItem *Item = App->CompComps->currentItem();
02134       if(Item) {
02135         formerAction = App->activeAction;
02136         App->slotSelectComponent(Item);  // also sets drawn=false
02137         App->MouseMoveAction = 0;
02138         App->MousePressAction = 0;
02139 
02140         Event->accept();
02141         return;
02142       }
02143     }
02144   }
02145 
02146   Event->ignore();
02147 }
02148 
02149 // ---------------------------------------------------
02150 void Schematic::contentsDragLeaveEvent(QDragLeaveEvent*)
02151 {
02152   if(App->view->selElem)
02153     if(App->view->selElem->Type & isComponent)
02154       if(App->view->drawn) {
02155 
02156         QPainter painter(viewport());
02157         App->view->setPainter(this);
02158         ((Component*)App->view->selElem)->paintScheme(this);
02159         App->view->drawn = false;
02160       }
02161 
02162   if(formerAction)
02163     formerAction->setChecked(true);  // restore old action
02164 }
02165 
02166 // ---------------------------------------------------
02167 void Schematic::contentsDragMoveEvent(QDragMoveEvent *Event)
02168 {
02169   if(!dragIsOkay) {
02170     if(App->view->selElem == 0) {
02171       Event->ignore();
02172       return;
02173     }
02174 
02175     QMouseEvent e(QEvent::MouseMove, Event->pos(), Qt::NoButton, 
02176       Qt::NoButton, Qt::NoModifier);
02177     App->view->MMoveElement(this, &e);
02178   }
02179 
02180   Event->accept();
02181 }
02182 
02183 void Schematic::getSelAreaWidthAndHeight(int &wsel, int &hsel, int& xmin_sel_, int& ymin_sel_)
02184 {
02185     int xmin= INT_MAX,
02186         ymin= INT_MAX,
02187         xmax= INT_MIN,
02188         ymax= INT_MIN;
02189 
02190      for(Component *pc = Components->first(); pc != 0; pc = Components->next()) {
02191          if (pc->isSelected) {
02192            int x1,y1,x2,y2,d1,d2,d3,d4;
02193            pc->entireBounds(x1,y1,x2,y2,this->textCorr());
02194            d1 = std::min(x1,x2);
02195            if (d1<xmin) xmin = d1;
02196            d2 = std::max(x2,x1);
02197            if (d2>xmax) xmax = d2;
02198            d3 = std::min(y1,y2);
02199            if (d3<ymin) ymin = d3;
02200            d4 = std::max(y2,y1);
02201            if (d4>ymax) ymax = d4;
02202          }
02203     }
02204 
02205     for(Wire *pw = Wires->first(); pw != 0; pw = Wires->next()) {
02206 
02207         if (pw->isSelected) {
02208             int xc,yc;
02209             pw->getCenter(xc,yc);
02210 
02211             if (xc<xmin) xmin = xc;
02212             if (xc>xmax) xmax = xc;
02213             if (yc<ymin) ymin = yc;
02214             if (yc>ymax) ymax = yc;
02215         }
02216     }
02217 
02218     for(Diagram *pd = Diagrams->first(); pd != 0; pd = Diagrams->next()) {
02219 
02220 
02221 
02222         if (pd->isSelected) {
02223             int x1,y1,x2,y2,d1,d2,d3,d4;
02224             pd->Bounding(x1,y1,x2,y2);
02225 
02226             d1 = std::min(x1,x2);
02227             if (d1<xmin) xmin = d1;
02228             d2 = std::max(x2,x1);
02229             if (d2>xmax) xmax = d2;
02230             d3 = std::min(y1,y2);
02231             if (d3<ymin) ymin = d3;
02232             d4 = std::max(y2,y1);
02233             if (d4>ymax) ymax = d4;
02234         }
02235     }
02236 
02237     for(Painting *pp = Paintings->first(); pp != 0; pp = Paintings->next()) {
02238 
02239        if (pp->isSelected) {
02240            int x1,y1,x2,y2,d1,d2,d3,d4;
02241            pp->Bounding(x1,y1,x2,y2);
02242            d1 = std::min(x1,x2);
02243            if (d1<xmin) xmin = d1;
02244            d2 = std::max(x2,x1);
02245            if (d2>xmax) xmax = d2;
02246            d3 = std::min(y1,y2);
02247            if (d3<ymin) ymin = d3;
02248            d4 = std::max(y2,y1);
02249            if (d4>ymax) ymax = d4;
02250        }
02251     }
02252 
02253     wsel = abs(xmax - xmin);
02254     hsel = abs(ymax - ymin);
02255     xmin_sel_ = xmin;
02256     ymin_sel_ = ymin;
02257 }
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines