Qucs-GUI
0.0.19
|
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 }