My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
schematic.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  schematic.cpp
3  ---------------
4  begin : Sat Mar 3 2006
5  copyright : (C) 2006 by Michael Margraf
6  email : michael.margraf@alumni.tu-berlin.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <stdlib.h>
23 #include <limits.h>
24 
25 #include <qimage.h>
26 #include <qaction.h>
27 #include <qregexp.h>
28 #include <qiconset.h>
29 #include <qprinter.h>
30 #include <qlineedit.h>
31 #include <qfileinfo.h>
32 #include <qiconview.h>
33 #include <qtabwidget.h>
34 #include <qdragobject.h>
35 #include <qpaintdevicemetrics.h>
36 #include <qdir.h>
37 
38 #include "qucs.h"
39 #include "main.h"
40 #include "node.h"
41 #include "schematic.h"
42 #include "textdoc.h"
43 #include "viewpainter.h"
44 #include "mouseactions.h"
45 #include "diagrams/diagrams.h"
46 #include "paintings/paintings.h"
47 #include "components/vhdlfile.h"
48 #include "components/verilogfile.h"
49 #include "components/vafile.h"
50 
51 // just dummies for empty lists
52 QPtrList<Wire> SymbolWires;
53 QPtrList<Node> SymbolNodes;
54 QPtrList<Diagram> SymbolDiags;
55 QPtrList<Component> SymbolComps;
56 
57 
58 Schematic::Schematic(QucsApp *App_, const QString& Name_)
59  : QucsDoc(App_, Name_)
60 {
61  symbolMode = false;
62 
63  // ...........................................................
64  GridX = GridY = 10;
65  ViewX1=ViewY1=0;
66  ViewX2=ViewY2=800;
67  UsedX1 = UsedY1 = INT_MAX;
68  UsedX2 = UsedY2 = INT_MIN;
69 
70  tmpPosX = tmpPosY = -100;
73  tmpScale = 1.0;
74 
75  DocComps.setAutoDelete(true);
76  DocWires.setAutoDelete(true);
77  DocNodes.setAutoDelete(true);
78  DocDiags.setAutoDelete(true);
79  DocPaints.setAutoDelete(true);
80  SymbolPaints.setAutoDelete(true);
81 
82  UndoStack.setAutoDelete(true);
83  UndoSymbol.setAutoDelete(true);
84  // The 'i' means state for being unchanged.
85  UndoStack.append(new QString(" i\n</>\n</>\n</>\n</>\n"));
86  UndoSymbol.append(new QString(" i\n</>\n</>\n</>\n</>\n"));
87 
88  isVerilog = false;
89  creatingLib = false;
90  QFileInfo Info(Name_);
91  if(App) {
92  if(Name_.isEmpty())
93  App->DocumentTab->addTab(this, QPixmap(empty_xpm),
94  QObject::tr("untitled"));
95  else
96  App->DocumentTab->addTab(this, QPixmap(empty_xpm),
97  Info.fileName());
98 
99  // calls indirectly "becomeCurrent"
100  App->DocumentTab->setCurrentPage(App->DocumentTab->indexOf(this));
101 
102  showFrame = 0; // don't show
103  Frame_Text0 = tr("Title");
104  Frame_Text1 = tr("Drawn By:");
105  Frame_Text2 = tr("Date:");
106  Frame_Text3 = tr("Revision:");
107 
108  setVScrollBarMode(QScrollView::AlwaysOn);
109  setHScrollBarMode(QScrollView::AlwaysOn);
110  viewport()->setPaletteBackgroundColor(QucsSettings.BGColor);
111  viewport()->setMouseTracking(true);
112  viewport()->setAcceptDrops(true); // enable drag'n drop
113 
114  connect(horizontalScrollBar(),
115  SIGNAL(prevLine()), SLOT(slotScrollLeft()));
116  connect(horizontalScrollBar(),
117  SIGNAL(nextLine()), SLOT(slotScrollRight()));
118  connect(verticalScrollBar(),
119  SIGNAL(prevLine()), SLOT(slotScrollUp()));
120  connect(verticalScrollBar(),
121  SIGNAL(nextLine()), SLOT(slotScrollDown()));
122 
123  // ...........................................................
124 
125  // to repair some strange scrolling artefacts
126  connect(this, SIGNAL(horizontalSliderReleased()),
127  viewport(), SLOT(update()));
128  connect(this, SIGNAL(verticalSliderReleased()),
129  viewport(), SLOT(update()));
130 
131  // to prevent user from editing something that he doesn't see
132  connect(this, SIGNAL(horizontalSliderPressed()), App, SLOT(slotHideEdit()));
133  connect(this, SIGNAL(verticalSliderPressed()), App, SLOT(slotHideEdit()));
134  } // of "if(App)"
135 }
136 
138 {
139  if(App) {
140  App->editText->reparent(App, 0, QPoint(0, 0));
141  App->DocumentTab->removePage(this); // delete tab in TabBar
142  }
143 }
144 
145 // ---------------------------------------------------
147 {
148  // If the number of ports is not equal, remove or add some.
149  unsigned int countPort = adjustPortNumbers();
150 
151  // If a symbol does not yet exist, create one.
152  if(SymbolPaints.count() != countPort)
153  return false;
154 
155  int h = 30*((countPort-1)/2) + 10;
156  SymbolPaints.prepend(new ID_Text(-20, h+4));
157 
158  SymbolPaints.append(
159  new GraphicLine(-20, -h, 40, 0, QPen(QPen::darkBlue,2)));
160  SymbolPaints.append(
161  new GraphicLine( 20, -h, 0,2*h, QPen(QPen::darkBlue,2)));
162  SymbolPaints.append(
163  new GraphicLine(-20, h, 40, 0, QPen(QPen::darkBlue,2)));
164  SymbolPaints.append(
165  new GraphicLine(-20, -h, 0,2*h, QPen(QPen::darkBlue,2)));
166 
167  unsigned int i=0, y = 10-h;
168  while(i<countPort) {
169  i++;
170  SymbolPaints.append(
171  new GraphicLine(-30, y, 10, 0, QPen(QPen::darkBlue,2)));
172  SymbolPaints.at(i)->setCenter(-30, y);
173 
174  if(i == countPort) break;
175  i++;
176  SymbolPaints.append(
177  new GraphicLine( 20, y, 10, 0, QPen(QPen::darkBlue,2)));
178  SymbolPaints.at(i)->setCenter(30, y);
179  y += 60;
180  }
181  return true;
182 }
183 
184 // ---------------------------------------------------
185 void Schematic::becomeCurrent(bool update)
186 {
187  QString *ps;
188  App->printCursorPosition(0, 0);
189 
190  // update appropriate menu entry
191  if (symbolMode) {
192  if (DocName.right(4) == ".sym") {
193  App->symEdit->setMenuText(tr("Edit Text"));
194  App->symEdit->setStatusTip(tr("Edits the Text"));
195  App->symEdit->setWhatsThis(tr("Edit Text\n\nEdits the text file"));
196  }
197  else {
198  App->symEdit->setMenuText(tr("Edit Schematic"));
199  App->symEdit->setStatusTip(tr("Edits the schematic"));
200  App->symEdit->setWhatsThis(tr("Edit Schematic\n\nEdits the schematic"));
201  }
202  }
203  else {
204  App->symEdit->setMenuText(tr("Edit Circuit Symbol"));
205  App->symEdit->setStatusTip(tr("Edits the symbol for this schematic"));
206  App->symEdit->setWhatsThis(
207  tr("Edit Circuit Symbol\n\nEdits the symbol for this schematic"));
208  }
209 
210  if(symbolMode) {
211  Nodes = &SymbolNodes;
212  Wires = &SymbolWires;
216 
217  // if no symbol yet exists -> create one
218  if(createSubcircuitSymbol()) {
220  setChanged(true, true);
221  }
222 
223  ps = UndoSymbol.current();
224  if(ps != UndoSymbol.getFirst()) App->undo->setEnabled(true);
225  else App->undo->setEnabled(false);
226  if(ps != UndoSymbol.getLast()) App->redo->setEnabled(true);
227  else App->redo->setEnabled(false);
228  }
229  else {
230  Nodes = &DocNodes;
231  Wires = &DocWires;
232  Diagrams = &DocDiags;
233  Paintings = &DocPaints;
234  Components = &DocComps;
235 
236  ps = UndoStack.current();
237  if(ps != UndoStack.getFirst()) App->undo->setEnabled(true);
238  else App->undo->setEnabled(false);
239  if(ps != UndoStack.getLast()) App->redo->setEnabled(true);
240  else App->redo->setEnabled(false);
241 
242  if(update)
243  reloadGraphs(); // load recent simulation data
244  }
245 }
246 
247 // ---------------------------------------------------
248 void Schematic::setName (const QString& Name_)
249 {
250  DocName = Name_;
251  QFileInfo Info (DocName);
252  if (App) App->DocumentTab->setTabLabel (this, Info.fileName ());
253 
254  QString base = Info.baseName (true);
255  QString ext = Info.extension (false);
256  DataSet = base + ".dat";
257  Script = base + ".m";
258  if (ext != "dpl")
259  DataDisplay = base + ".dpl";
260  else
261  DataDisplay = base + ".sch";
262 }
263 
264 // ---------------------------------------------------
265 // Sets the document to be changed or not to be changed.
266 void Schematic::setChanged(bool c, bool fillStack, char Op)
267 {
268  if((!DocChanged) && c)
269  App->DocumentTab->setTabIconSet(this, QPixmap(smallsave_xpm));
270  else if(DocChanged && (!c))
271  App->DocumentTab->setTabIconSet(this, QPixmap(empty_xpm));
272  DocChanged = c;
273 
274  showBias = -1; // schematic changed => bias points may be invalid
275 
276  if(!fillStack)
277  return;
278 
279 
280  // ................................................
281  if(symbolMode) { // for symbol edit mode
282  QString *Curr = UndoSymbol.current();
283  while(Curr != UndoSymbol.last())
284  UndoSymbol.remove(); // remove "Redo" items
285 
286  UndoSymbol.append(new QString(createSymbolUndoString(Op)));
287 
288  if(!App->undo->isEnabled()) App->undo->setEnabled(true);
289  if(App->redo->isEnabled()) App->redo->setEnabled(false);
290 
291  while(UndoSymbol.count() > QucsSettings.maxUndo) { // "while..." because
292  UndoSymbol.removeFirst(); // "maxUndo" could be decreased meanwhile
293  UndoSymbol.last();
294  }
295  return;
296  }
297 
298  // ................................................
299  // for schematic edit mode
300  QString *Curr = UndoStack.current();
301  while(Curr != UndoStack.last())
302  UndoStack.remove(); // remove "Redo" items
303 
304  if(Op == 'm') // only one for move marker
305  if(UndoStack.current()->at(0) == Op)
306  UndoStack.remove();
307 
308  UndoStack.append(new QString(createUndoString(Op)));
309 
310  if(!App->undo->isEnabled()) App->undo->setEnabled(true);
311  if(App->redo->isEnabled()) App->redo->setEnabled(false);
312 
313  while(UndoStack.count() > QucsSettings.maxUndo) { // "while..." because
314  UndoStack.removeFirst(); // "maxUndo" could be decreased meanwhile
315  UndoStack.last();
316  }
317 }
318 
319 // -----------------------------------------------------------
320 bool Schematic::sizeOfFrame(int& xall, int& yall)
321 {
322  // Values exclude border of 1.5cm at each side.
323  switch(showFrame) {
324  case 1: xall = 1020; yall = 765; break; // DIN A5 landscape
325  case 2: xall = 765; yall = 1020; break; // DIN A5 portrait
326  case 3: xall = 1530; yall = 1020; break; // DIN A4 landscape
327  case 4: xall = 1020; yall = 1530; break; // DIN A4 portrait
328  case 5: xall = 2295; yall = 1530; break; // DIN A3 landscape
329  case 6: xall = 1530; yall = 2295; break; // DIN A3 portrait
330  case 7: xall = 1414; yall = 1054; break; // letter landscape
331  case 8: xall = 1054; yall = 1414; break; // letter portrait
332  default: return false;
333  }
334 
335  return true;
336 }
337 
338 // -----------------------------------------------------------
340 {
341  // dimensions: X cm / 2.54 * 144
342  int xall, yall;
343  if(!sizeOfFrame(xall, yall))
344  return;
345 
346  p->Painter->setPen(QPen(Qt::black,0));
347  int d = p->LineSpacing + int(4.0 * p->Scale);
348  int x1_, y1_, x2_, y2_;
349  p->map(xall, yall, x1_, y1_);
350  x2_ = int(xall * p->Scale) + 1;
351  y2_ = int(yall * p->Scale) + 1;
352  p->Painter->drawRect(x1_, y1_, -x2_, -y2_);
353  p->Painter->drawRect(x1_-d, y1_-d, 2*d-x2_, 2*d-y2_);
354 
355  int z;
356  int step = xall / ((xall+127) / 255);
357  for(z=step; z<=xall-step; z+=step) {
358  p->map(z, 0, x2_, y2_);
359  p->Painter->drawLine(x2_, y2_, x2_, y2_+d);
360  p->Painter->drawLine(x2_, y1_-d, x2_, y1_);
361  }
362  char Letter[2] = "1";
363  for(z=step/2+5; z<xall; z+=step) {
364  p->drawText(Letter, z, 3, 0);
365  p->map(z, yall+3, x2_, y2_);
366  p->Painter->drawText(x2_, y2_-d, 0, 0, Qt::DontClip, Letter);
367  Letter[0]++;
368  }
369 
370  step = yall / ((yall+127) / 255);
371  for(z=step; z<=yall-step; z+=step) {
372  p->map(0, z, x2_, y2_);
373  p->Painter->drawLine(x2_, y2_, x2_+d, y2_);
374  p->Painter->drawLine(x1_-d, y2_, x1_, y2_);
375  }
376  Letter[0] = 'A';
377  for(z=step/2+5; z<yall; z+=step) {
378  p->drawText(Letter, 5, z, 0);
379  p->map(xall+5, z, x2_, y2_);
380  p->Painter->drawText(x2_-d, y2_, 0, 0, Qt::DontClip, Letter);
381  Letter[0]++;
382  }
383 
384  // draw text box with text
385  p->map(xall-340, yall-3, x1_, y1_);
386  p->map(xall-3, yall-3, x2_, y2_);
387  x1_ -= d; x2_ -= d;
388  y1_ -= d; y2_ -= d;
389  d = int(6.0 * p->Scale);
390  z = int(200.0 * p->Scale);
391  y1_ -= p->LineSpacing + d;
392  p->Painter->drawLine(x1_, y1_, x2_, y1_);
393  p->Painter->drawText(x1_+d, y1_+(d>>1), 0, 0, Qt::DontClip, Frame_Text2);
394  p->Painter->drawLine(x1_+z, y1_, x1_+z, y1_ + p->LineSpacing+d);
395  p->Painter->drawText(x1_+d+z, y1_+(d>>1), 0, 0, Qt::DontClip, Frame_Text3);
396  y1_ -= p->LineSpacing + d;
397  p->Painter->drawLine(x1_, y1_, x2_, y1_);
398  p->Painter->drawText(x1_+d, y1_+(d>>1), 0, 0, Qt::DontClip, Frame_Text1);
399  y1_ -= (Frame_Text0.contains('\n')+1) * p->LineSpacing + d;
400  p->Painter->drawRect(x2_, y2_, x1_-x2_-1, y1_-y2_-1);
401  p->Painter->drawText(x1_+d, y1_+(d>>1), 0, 0, Qt::DontClip, Frame_Text0);
402 }
403 
404 // -----------------------------------------------------------
405 // Is called when the content (schematic or data display) has to be drawn.
406 void Schematic::drawContents(QPainter *p, int, int, int, int)
407 {
408  ViewPainter Painter;
409  Painter.init(p, Scale, -ViewX1, -ViewY1, contentsX(), contentsY());
410 
411  paintGrid(&Painter, contentsX(), contentsY(),
412  visibleWidth(), visibleHeight());
413 
414  if(!symbolMode)
415  paintFrame(&Painter);
416 
417  for(Component *pc = Components->first(); pc != 0; pc = Components->next())
418  pc->paint(&Painter);
419 
420  for(Wire *pw = Wires->first(); pw != 0; pw = Wires->next()) {
421  pw->paint(&Painter);
422  if(pw->Label)
423  pw->Label->paint(&Painter); // separate because of paintSelected
424  }
425 
426  Node *pn;
427  for(pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
428  pn->paint(&Painter);
429  if(pn->Label)
430  pn->Label->paint(&Painter); // separate because of paintSelected
431  }
432 
433  for(Diagram *pd = Diagrams->first(); pd != 0; pd = Diagrams->next())
434  pd->paint(&Painter);
435 
436  for(Painting *pp = Paintings->first(); pp != 0; pp = Paintings->next())
437  pp->paint(&Painter);
438 
439  if(showBias > 0) { // show DC bias points in schematic ?
440  int x, y, z;
441  for(pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
442  if(pn->Name.isEmpty()) continue;
443  x = pn->cx;
444  y = pn->cy + 4;
445  z = pn->x1;
446  if(z & 1) x -= Painter.Painter->fontMetrics().width(pn->Name);
447  if(!(z & 2)) {
448  y -= (Painter.LineSpacing>>1) + 4;
449  if(z & 1) x -= 4;
450  else x += 4;
451  }
452  if(z & 0x10)
453  Painter.Painter->setPen(QPen::darkGreen); // green for currents
454  else
455  Painter.Painter->setPen(QPen::blue); // blue for voltages
456  Painter.drawText(pn->Name, x, y);
457  }
458  }
459 }
460 
461 // ---------------------------------------------------
462 void Schematic::contentsMouseMoveEvent(QMouseEvent *Event)
463 {
464  if(App->MouseMoveAction)
465  (App->view->*(App->MouseMoveAction))(this, Event);
466 }
467 
468 // -----------------------------------------------------------
469 void Schematic::contentsMousePressEvent(QMouseEvent *Event)
470 {
471  App->editText->setHidden(true); // disable text edit of component property
473  return;
474 
475  float x = float(Event->pos().x())/Scale + float(ViewX1);
476  float y = float(Event->pos().y())/Scale + float(ViewY1);
477 
478  if(Event->button() != Qt::LeftButton)
481  // show menu on right mouse button
482  App->view->rightPressMenu(this, Event, x, y);
484  // Is not called automatically because menu has focus.
485  (App->view->*(App->MouseReleaseAction))(this, Event);
486  return;
487  }
488 
489  if(App->MousePressAction)
490  (App->view->*(App->MousePressAction))(this, Event, x, y);
491 }
492 
493 // -----------------------------------------------------------
494 void Schematic::contentsMouseReleaseEvent(QMouseEvent *Event)
495 {
497  (App->view->*(App->MouseReleaseAction))(this, Event);
498 }
499 
500 // -----------------------------------------------------------
502 {
504  (App->view->*(App->MouseDoubleClickAction))(this, Event);
505 }
506 
507 // -----------------------------------------------------------
508 void Schematic::print(QPrinter*, QPainter *Painter, bool printAll, bool fitToPage)
509 {
510  QPaintDeviceMetrics pmetrics(Painter->device());
511  float printerDpiX = (float)pmetrics.logicalDpiX();
512  float printerDpiY = (float)pmetrics.logicalDpiY();
513  float printerW = (float)pmetrics.width();
514  float printerH = (float)pmetrics.height();
515  QPainter pa(viewport());
516  QPaintDeviceMetrics smetrics(pa.device());
517  float screenDpiX = (float)smetrics.logicalDpiX();
518  float screenDpiY = (float)smetrics.logicalDpiY();
519  float PrintScale = 0.5;
521  int marginX = (int)(40 * printerDpiX / screenDpiX);
522  int marginY = (int)(40 * printerDpiY / screenDpiY);
523 
524  if(fitToPage) {
525  float ScaleX = float(printerW - 2*marginX) /
526  float((UsedX2-UsedX1) * printerDpiX) * screenDpiX;
527  float ScaleY = float(printerH - 2*marginY) /
528  float((UsedY2-UsedY1) * printerDpiY) * screenDpiY;
529  if(ScaleX > ScaleY)
530  PrintScale = ScaleY;
531  else
532  PrintScale = ScaleX;
533  }
534 
535 
536  bool selected;
537  ViewPainter p;
538  int StartX = UsedX1;
539  int StartY = UsedY1;
540  if(showFrame) {
541  if(UsedX1 > 0) StartX = 0;
542  if(UsedY1 > 0) StartY = 0;
543  }
544 
545  float PrintRatio = printerDpiX / screenDpiX;
546  QFont oldFont = Painter->font();
547  p.init(Painter, PrintScale * PrintRatio,
548  -StartX, -StartY, -marginX, -marginY,
549  PrintScale, PrintRatio);
550 
551  if(!symbolMode)
552  paintFrame(&p);
553 
554  for(Component *pc = Components->first(); pc != 0; pc = Components->next())
555  if(pc->isSelected || printAll) {
556  selected = pc->isSelected;
557  pc->isSelected = false;
558 #ifdef __MINGW32__
559  pc->print(&p, 1.0);
560 #else
561  pc->print(&p, screenDpiX / printerDpiX);
562 #endif
563  pc->isSelected = selected;
564  }
565 
566  for(Wire *pw = Wires->first(); pw != 0; pw = Wires->next()) {
567  if(pw->isSelected || printAll) {
568  selected = pw->isSelected;
569  pw->isSelected = false;
570  pw->paint(&p); // paint all selected wires
571  pw->isSelected = selected;
572  }
573  if(pw->Label)
574  if(pw->Label->isSelected || printAll) {
575  selected = pw->Label->isSelected;
576  pw->Label->isSelected = false;
577  pw->Label->paint(&p);
578  pw->Label->isSelected = selected;
579  }
580  }
581 
582  Element *pe;
583  for(Node *pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
584  for(pe = pn->Connections.first(); pe != 0; pe = pn->Connections.next())
585  if(pe->isSelected || printAll) {
586  pn->paint(&p); // paint all nodes with selected elements
587  break;
588  }
589  if(pn->Label)
590  if(pn->Label->isSelected || printAll) {
591  selected = pn->Label->isSelected;
592  pn->Label->isSelected = false;
593  pn->Label->paint(&p);
594  pn->Label->isSelected = selected;
595  }
596  }
597 
598  Graph *pg;
599  Marker *pm;
600  for(Diagram *pd = Diagrams->first(); pd != 0; pd = Diagrams->next())
601  if(pd->isSelected || printAll) {
602  // if graph or marker is selected, deselect during printing
603  for(pg = pd->Graphs.first(); pg != 0; pg = pd->Graphs.next()) {
604  if(pg->isSelected) pg->Type |= 1; // remember selection
605  pg->isSelected = false;
606  for(pm = pg->Markers.first(); pm != 0; pm = pg->Markers.next()) {
607  if(pm->isSelected) pm->Type |= 1; // remember selection
608  pm->isSelected = false;
609  }
610  }
611 
612  selected = pd->isSelected;
613  pd->isSelected = false;
614  pd->paint(&p); // paint all selected diagrams with graphs and markers
615  pd->isSelected = selected;
616 
617  // revert selection of graphs and markers
618  for(pg = pd->Graphs.first(); pg != 0; pg = pd->Graphs.next()) {
619  if(pg->Type & 1) pg->isSelected = true;
620  pg->Type &= -2;
621  for(pm = pg->Markers.first(); pm != 0; pm = pg->Markers.next()) {
622  if(pm->Type & 1) pm->isSelected = true;
623  pm->Type &= -2;
624  }
625  }
626  }
627 
628  for(Painting *pp = Paintings->first(); pp != 0; pp = Paintings->next())
629  if(pp->isSelected || printAll) {
630  selected = pp->isSelected;
631  pp->isSelected = false;
632  pp->paint(&p); // paint all selected paintings
633  pp->isSelected = selected;
634  }
635 
636  Painter->setFont(oldFont);
637 }
638 
639 // -----------------------------------------------------------
640 float Schematic::zoom(float s)
641 {
642  Scale *= s;
643  if(Scale > 10.0) Scale = 10.0f;
644  if(Scale < 0.01) Scale = 0.01f;
645 
646  // "resizeContents()" performs an immediate repaint. So, set widget
647  // to hidden. This causes some flicker, but it is still nicer.
648  viewport()->setHidden(true);
649 // setHidden(true);
650  resizeContents(int(Scale*float(ViewX2 - ViewX1)),
651  int(Scale*float(ViewY2 - ViewY1)));
652 // setHidden(false);
653  viewport()->setHidden(false);
654 
655  viewport()->update();
656  App->view->drawn = false;
657  return Scale;
658 }
659 
660 // -----------------------------------------------------------
661 float Schematic::zoomBy(float s)
662 {
663  zoom(s);
664  s -= 1.0;
665  scrollBy( int(s * float(contentsX()+visibleWidth()/2)),
666  int(s * float(contentsY()+visibleHeight()/2)) );
667  return Scale;
668 }
669 
670 // ---------------------------------------------------
672 {
674  if(UsedX1 == 0)
675  if(UsedX2 == 0)
676  if(UsedY1 == 0)
677  if(UsedY2 == 0) {
678  UsedX1 = UsedY1 = INT_MAX;
679  UsedX2 = UsedY2 = INT_MIN;
680  return;
681  }
682 
683  float xScale = float(visibleWidth()) / float(UsedX2-UsedX1+80);
684  float yScale = float(visibleHeight()) / float(UsedY2-UsedY1+80);
685  if(xScale > yScale) xScale = yScale;
686  xScale /= Scale;
687 
688  ViewX1 = UsedX1 - 40;
689  ViewY1 = UsedY1 - 40;
690  ViewX2 = UsedX2 + 40;
691  ViewY2 = UsedY2 + 40;
692  zoom(xScale);
693 }
694 
695 // ---------------------------------------------------
697 {
698  Scale = 1.0;
699 
700  int x1 = UsedX1;
701  int y1 = UsedY1;
702  int x2 = UsedX2;
703  int y2 = UsedY2;
704 
705  if(x1 > x2) { // happens e.g. if untitled without changes
706  x1 = 0;
707  x2 = 800;
708  }
709  if(y1 > y2) {
710  y1 = 0;
711  y2 = 800;
712  }
713  if(x2==0) if(y2==0) if(x1==0) if(y1==0) x2 = y2 = 800;
714 
715  ViewX1 = x1-40;
716  ViewY1 = y1-40;
717  ViewX2 = x2+40;
718  ViewY2 = y2+40;
719  resizeContents(x2-x1+80, y2-y1+80);
720  viewport()->update();
721  App->view->drawn = false;
722 }
723 
724 // -----------------------------------------------------------
725 // Enlarge the viewport area if the coordinates x1-x2/y1-y2 exceed the
726 // visible area.
727 void Schematic::enlargeView(int x1, int y1, int x2, int y2)
728 {
729  int dx=0, dy=0;
730  if(x1 < UsedX1) UsedX1 = x1;
731  if(y1 < UsedY1) UsedY1 = y1;
732  if(x2 > UsedX2) UsedX2 = x2;
733  if(y2 > UsedY2) UsedY2 = y2;
734 
735  if(x1 < ViewX1) {
736  dx = int(Scale * float(ViewX1-x1+40));
737  ViewX1 = x1-40;
738  }
739  if(y1 < ViewY1) {
740  dy = int(Scale * float(ViewY1-y1+40));
741  ViewY1 = y1-40;
742  }
743  if(x2 > ViewX2) ViewX2 = x2+40;
744  if(y2 > ViewY2) ViewY2 = y2+40;
745 
746  resizeContents(int(Scale*float(ViewX2 - ViewX1)),
747  int(Scale*float(ViewY2 - ViewY1)));
748  scrollBy(dx,dy);
749 }
750 
751 // ---------------------------------------------------
752 // Sets an arbitrary coordinate onto the next grid coordinate.
753 void Schematic::setOnGrid(int& x, int& y)
754 {
755  if(x<0) x -= (GridX >> 1) - 1;
756  else x += GridX >> 1;
757  x -= x % GridX;
758 
759  if(y<0) y -= (GridY >> 1) - 1;
760  else y += GridY >> 1;
761  y -= y % GridY;
762 }
763 
764 // ---------------------------------------------------
765 void Schematic::paintGrid(ViewPainter *p, int cX, int cY, int Width, int Height)
766 {
767  if(!GridOn) return;
768 
769  p->Painter->setPen(QPen(QPen::black,0));
770  int dx = -int(Scale*float(ViewX1)) - cX;
771  int dy = -int(Scale*float(ViewY1)) - cY;
772  p->Painter->drawLine(-3+dx, dy, 4+dx, dy); // small cross at origin
773  p->Painter->drawLine( dx,-3+dy, dx, 4+dy);
774 
775 
776  int x1 = int(float(cX)/Scale) + ViewX1;
777  int y1 = int(float(cY)/Scale) + ViewY1;
778 
779  // setOnGrid(x1, y1) for 2*Grid
780  if(x1<0) x1 -= GridX - 1;
781  else x1 += GridX;
782  x1 -= x1 % (GridX << 1);
783 
784  if(y1<0) y1 -= GridY - 1;
785  else y1 += GridY;
786  y1 -= y1 % (GridY << 1);
787 
788  float X, Y, Y0, DX, DY;
789  X = float(x1)*Scale + p->DX;
790  Y = Y0 = float(y1)*Scale + p->DY;
791  x1 = X > 0.0 ? int(X + 0.5) : int(X - 0.5);
792  y1 = Y > 0.0 ? int(Y + 0.5) : int(Y - 0.5);
793 
794 
795  int xEnd = x1 + Width;
796  int yEnd = y1 + Height;
797  DX = float(GridX << 1) * Scale; // every second grid a point
798  DY = float(GridY << 1) * Scale;
799  while(DX <= 8.0) DX *= 1.5; // if too narrow, every third grid a point
800  while(DY <= 8.0) DY *= 1.5; // if too narrow, every third grid a point
801 
802  while(x1 < xEnd) {
803  Y = Y0;
804  y1 = Y > 0.0 ? int(Y + 0.5) : int(Y - 0.5);
805  while(y1 < yEnd) {
806  p->Painter->drawPoint(x1, y1); // paint grid
807  Y += DY;
808  y1 = Y > 0.0 ? int(Y + 0.5) : int(Y - 0.5);
809  }
810  X += DX;
811  x1 = X > 0.0 ? int(X + 0.5) : int(X - 0.5);
812  }
813 }
814 
815 // ---------------------------------------------------
816 // Correction factor for unproportional font scaling.
818 {
819  QFont Font = QucsSettings.font;
820  Font.setPointSizeFloat( Scale * float(Font.pointSize()) );
821  QFontMetrics metrics(Font);
822  return (Scale / float(metrics.lineSpacing()));
823 }
824 
825 // ---------------------------------------------------
826 void Schematic::sizeOfAll(int& xmin, int& ymin, int& xmax, int& ymax)
827 {
828  xmin=INT_MAX;
829  ymin=INT_MAX;
830  xmax=INT_MIN;
831  ymax=INT_MIN;
832  Component *pc;
833  Diagram *pd;
834  Wire *pw;
835  WireLabel *pl;
836  Painting *pp;
837 
838  if(Components->isEmpty())
839  if(Wires->isEmpty())
840  if(Diagrams->isEmpty())
841  if(Paintings->isEmpty()) {
842  xmin = xmax = 0;
843  ymin = ymax = 0;
844  return;
845  }
846 
847 
848  float Corr = textCorr();
849  int x1, y1, x2, y2;
850  // find boundings of all components
851  for(pc = Components->first(); pc != 0; pc = Components->next()) {
852  pc->entireBounds(x1, y1, x2, y2, Corr);
853  if(x1 < xmin) xmin = x1;
854  if(x2 > xmax) xmax = x2;
855  if(y1 < ymin) ymin = y1;
856  if(y2 > ymax) ymax = y2;
857  }
858 
859  // find boundings of all wires
860  for(pw = Wires->first(); pw != 0; pw = Wires->next()) {
861  if(pw->x1 < xmin) xmin = pw->x1;
862  if(pw->x2 > xmax) xmax = pw->x2;
863  if(pw->y1 < ymin) ymin = pw->y1;
864  if(pw->y2 > ymax) ymax = pw->y2;
865 
866  pl = pw->Label;
867  if(pl) { // check position of wire label
868  if(pl->x1 < xmin) xmin = pl->x1;
869  if((pl->x1+pl->x2) > xmax) xmax = pl->x1 + pl->x2;
870  if(pl->y1 > ymax) ymax = pl->y1;
871  if((pl->y1-pl->y2) < ymin) ymin = pl->y1 - pl->y2;
872  }
873  }
874 
875  // find boundings of all node labels
876  for(Node *pn = Nodes->first(); pn != 0; pn = Nodes->next()) {
877  pl = pn->Label;
878  if(pl) { // check position of node label
879  if(pl->x1 < xmin) xmin = pl->x1;
880  if((pl->x1+pl->x2) > xmax) xmax = pl->x1 + pl->x2;
881  if(pl->y1 > ymax) ymax = pl->y1;
882  if((pl->y1-pl->y2) < ymin) ymin = pl->y1 - pl->y2;
883  }
884  }
885 
886  // find boundings of all diagrams
887  for(pd = Diagrams->first(); pd != 0; pd = Diagrams->next()) {
888  pd->Bounding(x1, y1, x2, y2);
889  if(x1 < xmin) xmin = x1;
890  if(x2 > xmax) xmax = x2;
891  if(y1 < ymin) ymin = y1;
892  if(y2 > ymax) ymax = y2;
893 
894  for(Graph *pg = pd->Graphs.first(); pg!=0; pg = pd->Graphs.next())
895  // test all markers of diagram
896  for(Marker *pm = pg->Markers.first(); pm!=0; pm = pg->Markers.next()) {
897  pm->Bounding(x1, y1, x2, y2);
898  if(x1 < xmin) xmin = x1;
899  if(x2 > xmax) xmax = x2;
900  if(y1 < ymin) ymin = y1;
901  if(y2 > ymax) ymax = y2;
902  }
903  }
904 
905  // find boundings of all Paintings
906  for(pp = Paintings->first(); pp != 0; pp = Paintings->next()) {
907  pp->Bounding(x1, y1, x2, y2);
908  if(x1 < xmin) xmin = x1;
909  if(x2 > xmax) xmax = x2;
910  if(y1 < ymin) ymin = y1;
911  if(y2 > ymax) ymax = y2;
912  }
913 }
914 
915 // ---------------------------------------------------
916 // Rotates all selected components around their midpoint.
918 {
919  Wires->setAutoDelete(false);
920  Components->setAutoDelete(false);
921 
922  int x1=INT_MAX, y1=INT_MAX;
923  int x2=INT_MIN, y2=INT_MIN;
924  QPtrList<Element> ElementCache;
925  copyLabels(x1, y1, x2, y2, &ElementCache); // must be first of all !
926  copyComponents(x1, y1, x2, y2, &ElementCache);
927  copyWires(x1, y1, x2, y2, &ElementCache);
928  copyPaintings(x1, y1, x2, y2, &ElementCache);
929  if(y1 == INT_MAX) return false; // no element selected
930 
931  Wires->setAutoDelete(true);
932  Components->setAutoDelete(true);
933 
934  x1 = (x1+x2) >> 1; // center for rotation
935  y1 = (y1+y2) >> 1;
936  setOnGrid(x1, y1);
937 
938 
939  Wire *pw;
940  Painting *pp;
941  Component *pc;
942  WireLabel *pl;
943  // re-insert elements
944  for(Element *pe = ElementCache.first(); pe != 0; pe = ElementCache.next())
945  switch(pe->Type) {
946  case isComponent:
947  case isAnalogComponent:
948  case isDigitalComponent:
949  pc = (Component*)pe;
950  pc->rotate(); //rotate component !before! rotating its center
951  pc->setCenter(pc->cy - y1 + x1, x1 - pc->cx + y1);
952  insertRawComponent(pc);
953  break;
954 
955  case isWire:
956  pw = (Wire*)pe;
957  x2 = pw->x1;
958  pw->x1 = pw->y1 - y1 + x1;
959  pw->y1 = x1 - x2 + y1;
960  x2 = pw->x2;
961  pw->x2 = pw->y2 - y1 + x1;
962  pw->y2 = x1 - x2 + y1;
963  pl = pw->Label;
964  if(pl) {
965  x2 = pl->cx;
966  pl->cx = pl->cy - y1 + x1;
967  pl->cy = x1 - x2 + y1;
968  if(pl->Type == isHWireLabel)
969  pl->Type = isVWireLabel;
970  else pl->Type = isHWireLabel;
971  }
972  insertWire(pw);
973  break;
974 
975  case isHWireLabel:
976  case isVWireLabel:
977  pl = (WireLabel*)pe;
978  x2 = pl->x1;
979  pl->x1 = pl->y1 - y1 + x1;
980  pl->y1 = x1 - x2 + y1;
981  break;
982  case isNodeLabel:
983  pl = (WireLabel*)pe;
984  if(pl->pOwner == 0) {
985  x2 = pl->x1;
986  pl->x1 = pl->y1 - y1 + x1;
987  pl->y1 = x1 - x2 + y1;
988  }
989  x2 = pl->cx;
990  pl->cx = pl->cy - y1 + x1;
991  pl->cy = x1 - x2 + y1;
992  insertNodeLabel(pl);
993  break;
994 
995  case isPainting:
996  pp = (Painting*)pe;
997  pp->rotate(); // rotate painting !before! rotating its center
998  pp->getCenter(x2, y2);
999  pp->setCenter(y2-y1 + x1, x1-x2 + y1);
1000  Paintings->append(pp);
1001  break;
1002  default: ;
1003  }
1004 
1005  ElementCache.clear();
1006 
1007  setChanged(true, true);
1008  return true;
1009 }
1010 
1011 // ---------------------------------------------------
1012 // Mirrors all selected components.
1013 // First copy them to 'ElementCache', then mirror and insert again.
1015 {
1016  Wires->setAutoDelete(false);
1017  Components->setAutoDelete(false);
1018 
1019  int x1, y1, x2, y2;
1020  QPtrList<Element> ElementCache;
1021  if(!copyComps2WiresPaints(x1, y1, x2, y2, &ElementCache))
1022  return false;
1023  Wires->setAutoDelete(true);
1024  Components->setAutoDelete(true);
1025 
1026  y1 = (y1+y2) >> 1; // axis for mirroring
1027  setOnGrid(y2, y1);
1028  y1 <<= 1;
1029 
1030 
1031  Wire *pw;
1032  Painting *pp;
1033  Component *pc;
1034  WireLabel *pl;
1035  // re-insert elements
1036  for(Element *pe = ElementCache.first(); pe != 0; pe = ElementCache.next())
1037  switch(pe->Type) {
1038  case isComponent:
1039  case isAnalogComponent:
1040  case isDigitalComponent:
1041  pc = (Component*)pe;
1042  pc->mirrorX(); // mirror component !before! mirroring its center
1043  pc->setCenter(pc->cx, y1 - pc->cy);
1044  insertRawComponent(pc);
1045  break;
1046  case isWire:
1047  pw = (Wire*)pe;
1048  pw->y1 = y1 - pw->y1;
1049  pw->y2 = y1 - pw->y2;
1050  pl = pw->Label;
1051  if(pl) pl->cy = y1 - pl->cy;
1052  insertWire(pw);
1053  break;
1054  case isHWireLabel:
1055  case isVWireLabel:
1056  pl = (WireLabel*)pe;
1057  pl->y1 = y1 - pl->y1;
1058  break;
1059  case isNodeLabel:
1060  pl = (WireLabel*)pe;
1061  if(pl->pOwner == 0)
1062  pl->y1 = y1 - pl->y1;
1063  pl->cy = y1 - pl->cy;
1064  insertNodeLabel(pl);
1065  break;
1066  case isPainting:
1067  pp = (Painting*)pe;
1068  pp->getCenter(x2, y2);
1069  pp->mirrorX(); // mirror painting !before! mirroring its center
1070  pp->setCenter(x2, y1 - y2);
1071  Paintings->append(pp);
1072  break;
1073  default: ;
1074  }
1075 
1076  ElementCache.clear();
1077  setChanged(true, true);
1078  return true;
1079 }
1080 
1081 // ---------------------------------------------------
1082 // Mirrors all selected components. First copy them to 'ElementCache', then mirror and insert again.
1084 {
1085  Wires->setAutoDelete(false);
1086  Components->setAutoDelete(false);
1087 
1088  int x1, y1, x2, y2;
1089  QPtrList<Element> ElementCache;
1090  if(!copyComps2WiresPaints(x1, y1, x2, y2, &ElementCache))
1091  return false;
1092  Wires->setAutoDelete(true);
1093  Components->setAutoDelete(true);
1094 
1095  x1 = (x1+x2) >> 1; // axis for mirroring
1096  setOnGrid(x1, x2);
1097  x1 <<= 1;
1098 
1099  Wire *pw;
1100  Painting *pp;
1101  Component *pc;
1102  WireLabel *pl;
1103  // re-insert elements
1104  for(Element *pe = ElementCache.first(); pe != 0; pe = ElementCache.next())
1105  switch(pe->Type) {
1106  case isComponent:
1107  case isAnalogComponent:
1108  case isDigitalComponent:
1109  pc = (Component*)pe;
1110  pc->mirrorY(); // mirror component !before! mirroring its center
1111  pc->setCenter(x1 - pc->cx, pc->cy);
1112  insertRawComponent(pc);
1113  break;
1114  case isWire:
1115  pw = (Wire*)pe;
1116  pw->x1 = x1 - pw->x1;
1117  pw->x2 = x1 - pw->x2;
1118  pl = pw->Label;
1119  if(pl) pl->cx = x1 - pl->cx;
1120  insertWire(pw);
1121  break;
1122  case isHWireLabel:
1123  case isVWireLabel:
1124  pl = (WireLabel*)pe;
1125  pl->x1 = x1 - pl->x1;
1126  break;
1127  case isNodeLabel:
1128  pl = (WireLabel*)pe;
1129  if(pl->pOwner == 0)
1130  pl->x1 = x1 - pl->x1;
1131  pl->cx = x1 - pl->cx;
1132  insertNodeLabel(pl);
1133  break;
1134  case isPainting:
1135  pp = (Painting*)pe;
1136  pp->getCenter(x2, y2);
1137  pp->mirrorY(); // mirror painting !before! mirroring its center
1138  pp->setCenter(x1 - x2, y2);
1139  Paintings->append(pp);
1140  break;
1141  default: ;
1142  }
1143 
1144  ElementCache.clear();
1145  setChanged(true, true);
1146  return true;
1147 }
1148 
1149 // ---------------------------------------------------
1150 // Updates the graph data of all diagrams (load from data files).
1152 {
1153  QFileInfo Info(DocName);
1154  for(Diagram *pd = Diagrams->first(); pd != 0; pd = Diagrams->next())
1155  pd->loadGraphData(Info.dirPath()+QDir::separator()+DataSet);
1156 }
1157 
1158 // ---------------------------------------------------
1159 // Performs copy or cut functions for clipboard.
1160 QString Schematic::copySelected(bool cut)
1161 {
1162  QString s = createClipboardFile();
1163  if(cut) deleteElements(); // delete selected elements if wanted
1164  return s;
1165 }
1166 
1167 // ---------------------------------------------------
1168 // Performs paste function from clipboard
1169 bool Schematic::paste(QTextStream *stream, QPtrList<Element> *pe)
1170 {
1171  return pasteFromClipboard(stream, pe);
1172 }
1173 
1174 // ---------------------------------------------------
1175 // Loads this Qucs document.
1177 {
1178  DocComps.clear();
1179  DocWires.clear();
1180  DocNodes.clear();
1181  DocDiags.clear();
1182  DocPaints.clear();
1183  SymbolPaints.clear();
1184 
1185  if(!loadDocument()) return false;
1186  lastSaved = QDateTime::currentDateTime();
1187  UndoStack.clear();
1188  UndoSymbol.clear();
1189  symbolMode = true;
1190  setChanged(false, true); // "not changed" state, but put on undo stack
1191  UndoSymbol.current()->at(1) = 'i';
1192  symbolMode = false;
1193  setChanged(false, true); // "not changed" state, but put on undo stack
1194  UndoStack.current()->at(1) = 'i'; // state of being unchanged
1195 
1196  // The undo stack of the circuit symbol is initialized when first
1197  // entering its edit mode.
1198 
1200  if(ViewX1 > UsedX1) ViewX1 = UsedX1;
1201  if(ViewY1 > UsedY1) ViewY1 = UsedY1;
1202  if(ViewX2 < UsedX2) ViewX2 = UsedX2;
1203  if(ViewY2 < UsedY2) ViewY2 = UsedY2;
1204  zoom(1.0f);
1205  setContentsPos(tmpViewX1, tmpViewY1);
1206  tmpViewX1 = tmpViewY1 = -200; // was used as temporary cache
1207  return true;
1208 }
1209 
1210 // ---------------------------------------------------
1211 // Saves this Qucs document. Returns the number of subcircuit ports.
1213 {
1214  int result = adjustPortNumbers();// same port number for schematic and symbol
1215  if(saveDocument() < 0)
1216  return -1;
1217 
1218  QFileInfo Info(DocName);
1219  lastSaved = Info.lastModified();
1220 
1221  if(result >= 0) {
1222  setChanged(false);
1223  QString *p, *ps = UndoStack.current();
1224  for(p = UndoStack.first(); p != 0; p = UndoStack.next())
1225  p->at(1) = ' '; // state of being changed
1226  ps->at(1) = 'i'; // state of being unchanged
1227  UndoStack.findRef(ps); // back to current
1228 
1229  ps = UndoSymbol.current();
1230  for(p = UndoSymbol.first(); p != 0; p = UndoSymbol.next())
1231  p->at(1) = ' '; // state of being changed
1232  ps->at(1) = 'i'; // state of being unchanged
1233  UndoSymbol.findRef(ps); // back to current
1234  }
1235  return result;
1236 }
1237 
1238 // ---------------------------------------------------
1239 // If the port number of the schematic and of the symbol are not
1240 // equal add or remove some in the symbol.
1242 {
1243  int x1, x2, y1, y2;
1244  // get size of whole symbol to know where to place new ports
1245  if(symbolMode) sizeOfAll(x1, y1, x2, y2);
1246  else {
1248  Wires = &SymbolWires;
1249  Nodes = &SymbolNodes;
1250  Diagrams = &SymbolDiags;
1252  sizeOfAll(x1, y1, x2, y2);
1253  Components = &DocComps;
1254  Wires = &DocWires;
1255  Nodes = &DocNodes;
1256  Diagrams = &DocDiags;
1257  Paintings = &DocPaints;
1258  }
1259  x1 += 40;
1260  y2 += 20;
1261  setOnGrid(x1, y2);
1262 
1263 
1264  Painting *pp;
1265  // delete all port names in symbol
1266  for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
1267  if(pp->Name == ".PortSym ")
1268  ((PortSymbol*)pp)->nameStr = "";
1269 
1270  QString Str;
1271  int countPort = 0;
1272 
1273  QFileInfo Info (DataDisplay);
1274  QString Suffix = Info.extension (false);
1275 
1276  // handle VHDL file symbol
1277  if (Suffix == "vhd" || Suffix == "vhdl") {
1278  QStringList::iterator it;
1279  QStringList Names, GNames, GTypes, GDefs;
1280  int Number;
1281 
1282  // get ports from VHDL file
1283  QFileInfo Info(DocName);
1284  QString Name = Info.dirPath() + QDir::separator() + DataDisplay;
1285 
1286  // obtain VHDL information either from open text document or the
1287  // file directly
1288  VHDL_File_Info VInfo;
1289  TextDoc * d = (TextDoc*)App->findDoc (Name);
1290  if (d)
1291  VInfo = VHDL_File_Info (d->text());
1292  else
1293  VInfo = VHDL_File_Info (Name, true);
1294  Names = QStringList::split(",",VInfo.PortNames);
1295 
1296  for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
1297  if(pp->Name == ".ID ") {
1298  ID_Text * id = (ID_Text *) pp;
1299  id->Prefix = VInfo.EntityName.upper();
1300  id->Parameter.clear();
1301  GNames = QStringList::split(",",VInfo.GenNames);
1302  GTypes = QStringList::split(",",VInfo.GenTypes);
1303  GDefs = QStringList::split(",",VInfo.GenDefs);
1304  for(Number = 1, it = GNames.begin(); it != GNames.end(); ++it) {
1305  id->Parameter.append(new SubParameter(
1306  true,
1307  *it+"="+GDefs[Number-1],
1308  tr("generic")+" "+QString::number(Number),
1309  GTypes[Number-1]));
1310  Number++;
1311  }
1312  }
1313 
1314  for(Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
1315  countPort++;
1316 
1317  Str = QString::number(Number);
1318  // search for matching port symbol
1319  for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
1320  if(pp->Name == ".PortSym ")
1321  if(((PortSymbol*)pp)->numberStr == Str) break;
1322 
1323  if(pp)
1324  ((PortSymbol*)pp)->nameStr = *it;
1325  else {
1326  SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
1327  y2 += 40;
1328  }
1329  }
1330  }
1331  // handle Verilog-HDL file symbol
1332  else if (Suffix == "v") {
1333 
1334  QStringList::iterator it;
1335  QStringList Names;
1336  int Number;
1337 
1338  // get ports from Verilog-HDL file
1339  QFileInfo Info (DocName);
1340  QString Name = Info.dirPath() + QDir::separator() + DataDisplay;
1341 
1342  // obtain Verilog-HDL information either from open text document or the
1343  // file directly
1344  Verilog_File_Info VInfo;
1345  TextDoc * d = (TextDoc*)App->findDoc (Name);
1346  if (d)
1347  VInfo = Verilog_File_Info (d->text());
1348  else
1349  VInfo = Verilog_File_Info (Name, true);
1350  Names = QStringList::split(",",VInfo.PortNames);
1351 
1352  for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
1353  if(pp->Name == ".ID ") {
1354  ID_Text * id = (ID_Text *) pp;
1355  id->Prefix = VInfo.ModuleName.upper();
1356  id->Parameter.clear();
1357  }
1358 
1359  for(Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
1360  countPort++;
1361 
1362  Str = QString::number(Number);
1363  // search for matching port symbol
1364  for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
1365  if(pp->Name == ".PortSym ")
1366  if(((PortSymbol*)pp)->numberStr == Str) break;
1367 
1368  if(pp)
1369  ((PortSymbol*)pp)->nameStr = *it;
1370  else {
1371  SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
1372  y2 += 40;
1373  }
1374  }
1375  }
1376  // handle Verilog-A file symbol
1377  else if (Suffix == "va") {
1378 
1379  QStringList::iterator it;
1380  QStringList Names;
1381  int Number;
1382 
1383  // get ports from Verilog-A file
1384  QFileInfo Info (DocName);
1385  QString Name = Info.dirPath() + QDir::separator() + DataDisplay;
1386 
1387  // obtain Verilog-A information either from open text document or the
1388  // file directly
1389  VerilogA_File_Info VInfo;
1390  TextDoc * d = (TextDoc*)App->findDoc (Name);
1391  if (d)
1392  VInfo = VerilogA_File_Info (d->text());
1393  else
1394  VInfo = VerilogA_File_Info (Name, true);
1395  Names = QStringList::split(",",VInfo.PortNames);
1396 
1397  for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
1398  if(pp->Name == ".ID ") {
1399  ID_Text * id = (ID_Text *) pp;
1400  id->Prefix = VInfo.ModuleName.upper();
1401  id->Parameter.clear();
1402  }
1403 
1404  for(Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
1405  countPort++;
1406 
1407  Str = QString::number(Number);
1408  // search for matching port symbol
1409  for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
1410  if(pp->Name == ".PortSym ")
1411  if(((PortSymbol*)pp)->numberStr == Str) break;
1412 
1413  if(pp)
1414  ((PortSymbol*)pp)->nameStr = *it;
1415  else {
1416  SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
1417  y2 += 40;
1418  }
1419  }
1420  }
1421  // handle schematic symbol
1422  else {
1423  // go through all components in a schematic
1424  for(Component *pc = DocComps.first(); pc!=0; pc = DocComps.next())
1425  if(pc->Model == "Port") {
1426  countPort++;
1427 
1428  Str = pc->Props.getFirst()->Value;
1429  // search for matching port symbol
1430  for(pp = SymbolPaints.first(); pp!=0; pp = SymbolPaints.next())
1431  if(pp->Name == ".PortSym ")
1432  if(((PortSymbol*)pp)->numberStr == Str) break;
1433 
1434  if(pp)
1435  ((PortSymbol*)pp)->nameStr = pc->Name;
1436  else {
1437  SymbolPaints.append(new PortSymbol(x1, y2, Str, pc->Name));
1438  y2 += 40;
1439  }
1440  }
1441  }
1442 
1443  // delete not accounted port symbols
1444  for(pp = SymbolPaints.first(); pp!=0; ) {
1445  if(pp->Name == ".PortSym ")
1446  if(((PortSymbol*)pp)->nameStr.isEmpty()) {
1447  SymbolPaints.remove();
1448  pp = SymbolPaints.current();
1449  continue;
1450  }
1451  pp = SymbolPaints.next();
1452  }
1453 
1454  return countPort;
1455 }
1456 
1457 // ---------------------------------------------------
1459 {
1460  if(symbolMode) {
1461  if(UndoSymbol.current() == UndoSymbol.getFirst()) return false;
1462 
1463  rebuildSymbol(UndoSymbol.prev());
1464  adjustPortNumbers(); // set port names
1465 
1466  QString *ps = UndoSymbol.current();
1467  if(ps != UndoSymbol.getFirst()) App->undo->setEnabled(true);
1468  else App->undo->setEnabled(false);
1469  if(ps != UndoSymbol.getLast()) App->redo->setEnabled(true);
1470  else App->redo->setEnabled(false);
1471 
1472  if(ps->at(1) == 'i')
1473  if(UndoStack.current()->at(1) == 'i') {
1474  setChanged(false, false);
1475  return true;
1476  }
1477 
1478  setChanged(true, false);
1479  return true;
1480  }
1481 
1482 
1483  // ...... for schematic edit mode .......
1484  if(UndoStack.current() == UndoStack.getFirst()) return false;
1485 
1486  rebuild(UndoStack.prev());
1487  reloadGraphs(); // load recent simulation data
1488 
1489  QString *ps = UndoStack.current();
1490  if(ps != UndoStack.getFirst()) App->undo->setEnabled(true);
1491  else App->undo->setEnabled(false);
1492  if(ps != UndoStack.getLast()) App->redo->setEnabled(true);
1493  else App->redo->setEnabled(false);
1494 
1495  if(ps->at(1) == 'i') {
1496  if(UndoSymbol.isEmpty()) {
1497  setChanged(false, false);
1498  return true;
1499  }
1500  else if(UndoSymbol.current()->at(1) == 'i') {
1501  setChanged(false, false);
1502  return true;
1503  }
1504  }
1505 
1506  setChanged(true, false);
1507  return true;
1508 }
1509 
1510 // ---------------------------------------------------
1512 {
1513  if(symbolMode) {
1514  if(UndoSymbol.current() == UndoSymbol.getLast()) return false;
1515 
1516  rebuildSymbol(UndoSymbol.next());
1517  adjustPortNumbers(); // set port names
1518 
1519  QString *ps = UndoSymbol.current();
1520  if(ps != UndoSymbol.getFirst()) App->undo->setEnabled(true);
1521  else App->undo->setEnabled(false);
1522  if(ps != UndoSymbol.getLast()) App->redo->setEnabled(true);
1523  else App->redo->setEnabled(false);
1524 
1525  if(ps->at(1) == 'i')
1526  if(UndoStack.current()->at(1) == 'i') {
1527  setChanged(false, false);
1528  return true;
1529  }
1530 
1531  setChanged(true, false);
1532  return true;
1533  }
1534 
1535 
1536  // ...... for schematic edit mode .......
1537  if(UndoStack.current() == UndoStack.getLast()) return false;
1538 
1539  rebuild(UndoStack.next());
1540  reloadGraphs(); // load recent simulation data
1541 
1542  QString *ps = UndoStack.current();
1543  if(ps != UndoStack.getFirst()) App->undo->setEnabled(true);
1544  else App->undo->setEnabled(false);
1545  if(ps != UndoStack.getLast()) App->redo->setEnabled(true);
1546  else App->redo->setEnabled(false);
1547 
1548  if(ps->at(1) == 'i') {
1549  if(UndoSymbol.isEmpty()) {
1550  setChanged(false, false);
1551  return true;
1552  }
1553  else if(UndoSymbol.current()->at(1) == 'i') {
1554  setChanged(false, false);
1555  return true;
1556  }
1557  }
1558 
1559  setChanged(true, false);
1560  return true;
1561 }
1562 
1563 // ---------------------------------------------------
1564 // Sets selected elements on grid.
1566 {
1567  int x, y, No;
1568  bool count = false;
1569  Port *pp;
1570  WireLabel *pl, *pLabel;
1571  QPtrList<WireLabel> LabelCache;
1572 
1573  // test all components
1574  Components->setAutoDelete(false);
1575  for(Component *pc = Components->last(); pc != 0; pc = Components->prev())
1576  if(pc->isSelected) {
1577 
1578  // rescue non-selected node labels
1579  for(pp = pc->Ports.first(); pp != 0; pp = pc->Ports.next())
1580  if(pp->Connection->Label)
1581  if(pp->Connection->Connections.count() < 2) {
1582  LabelCache.append(pp->Connection->Label);
1583  pp->Connection->Label->pOwner = 0;
1584  pp->Connection->Label = 0;
1585  }
1586 
1587  x = pc->cx;
1588  y = pc->cy;
1589  No = Components->at();
1590  deleteComp(pc);
1591  setOnGrid(pc->cx, pc->cy);
1592  insertRawComponent(pc);
1593  Components->at(No); // restore current list position
1594  pc->isSelected = false;
1595  count = true;
1596 
1597  x -= pc->cx;
1598  y -= pc->cy; // re-insert node labels and correct position
1599  for(pl = LabelCache.first(); pl != 0; pl = LabelCache.next()) {
1600  pl->cx -= x;
1601  pl->cy -= y;
1602  insertNodeLabel(pl);
1603  }
1604  LabelCache.clear();
1605  }
1606  Components->setAutoDelete(true);
1607 
1608  Wires->setAutoDelete(false);
1609  // test all wires and wire labels
1610  for(Wire *pw = Wires->last(); pw != 0; pw = Wires->prev()) {
1611  pl = pw->Label;
1612  pw->Label = 0;
1613 
1614  if(pw->isSelected) {
1615  // rescue non-selected node label
1616  pLabel = 0;
1617  if(pw->Port1->Label) {
1618  if(pw->Port1->Connections.count() < 2) {
1619  pLabel = pw->Port1->Label;
1620  pw->Port1->Label = 0;
1621  }
1622  }
1623  else if(pw->Port2->Label) {
1624  if(pw->Port2->Connections.count() < 2) {
1625  pLabel = pw->Port2->Label;
1626  pw->Port2->Label = 0;
1627  }
1628  }
1629 
1630  No = Wires->at();
1631  deleteWire(pw);
1632  setOnGrid(pw->x1, pw->y1);
1633  setOnGrid(pw->x2, pw->y2);
1634  insertWire(pw);
1635  Wires->at(No); // restore current list position
1636  pw->isSelected = false;
1637  count = true;
1638  if(pl)
1639  setOnGrid(pl->cx, pl->cy);
1640 
1641  if(pLabel) {
1642  setOnGrid(pLabel->cx, pLabel->cy);
1643  insertNodeLabel(pLabel);
1644  }
1645  }
1646 
1647  if(pl) {
1648  pw->Label = pl;
1649  if(pl->isSelected) {
1650  setOnGrid(pl->x1, pl->y1);
1651  pl->isSelected = false;
1652  count = true;
1653  }
1654  }
1655  }
1656  Wires->setAutoDelete(true);
1657 
1658  // test all node labels
1659  for(Node *pn = Nodes->first(); pn != 0; pn = Nodes->next())
1660  if(pn->Label)
1661  if(pn->Label->isSelected) {
1662  setOnGrid(pn->Label->x1, pn->Label->y1);
1663  pn->Label->isSelected = false;
1664  count = true;
1665  }
1666 
1667  // test all diagrams
1668  for(Diagram *pd = Diagrams->last(); pd != 0; pd = Diagrams->prev()) {
1669  if(pd->isSelected) {
1670  setOnGrid(pd->cx, pd->cy);
1671  pd->isSelected = false;
1672  count = true;
1673  }
1674 
1675  for(Graph *pg = pd->Graphs.first(); pg != 0; pg = pd->Graphs.next())
1676  // test markers of diagram
1677  for(Marker *pm = pg->Markers.first(); pm != 0; pm = pg->Markers.next())
1678  if(pm->isSelected) {
1679  x = pm->x1 + pd->cx;
1680  y = pm->y1 + pd->cy;
1681  setOnGrid(x, y);
1682  pm->x1 = x - pd->cx;
1683  pm->y1 = y - pd->cy;
1684  pm->isSelected = false;
1685  count = true;
1686  }
1687  }
1688 
1689  // test all paintings
1690  for(Painting *pa = Paintings->last(); pa != 0; pa = Paintings->prev())
1691  if(pa->isSelected) {
1692  setOnGrid(pa->cx, pa->cy);
1693  pa->isSelected = false;
1694  count = true;
1695  }
1696 
1697  if(count) setChanged(true, true);
1698  return count;
1699 }
1700 
1701 // ---------------------------------------------------
1703 {
1704  symbolMode = !symbolMode; // change mode
1705 
1706  int tmp, t2;
1707  float temp;
1708  temp = Scale; Scale = tmpScale; tmpScale = temp;
1709  tmp = contentsX();
1710  t2 = contentsY();
1711  setContentsPos(tmpPosX, tmpPosY);
1712  tmpPosX = tmp;
1713  tmpPosY = t2;
1714  tmp = ViewX1; ViewX1 = tmpViewX1; tmpViewX1 = tmp;
1715  tmp = ViewY1; ViewY1 = tmpViewY1; tmpViewY1 = tmp;
1716  tmp = ViewX2; ViewX2 = tmpViewX2; tmpViewX2 = tmp;
1717  tmp = ViewY2; ViewY2 = tmpViewY2; tmpViewY2 = tmp;
1718  tmp = UsedX1; UsedX1 = tmpUsedX1; tmpUsedX1 = tmp;
1719  tmp = UsedY1; UsedY1 = tmpUsedY1; tmpUsedY1 = tmp;
1720  tmp = UsedX2; UsedX2 = tmpUsedX2; tmpUsedX2 = tmp;
1721  tmp = UsedY2; UsedY2 = tmpUsedY2; tmpUsedY2 = tmp;
1722 }
1723 
1724 
1725 // *********************************************************************
1726 // ********** **********
1727 // ********** Function for serving mouse wheel moving **********
1728 // ********** **********
1729 // *********************************************************************
1730 void Schematic::contentsWheelEvent(QWheelEvent *Event)
1731 {
1732  App->editText->setHidden(true); // disable edit of component property
1733  int delta = Event->delta() >> 1; // use smaller steps
1734 
1735  // ...................................................................
1736  if((Event->state() & Qt::ShiftButton) ||
1737  (Event->orientation() == Horizontal)) { // scroll horizontally ?
1738  if(delta > 0) { if(scrollLeft(delta)) scrollBy(-delta, 0); }
1739  else { if(scrollRight(delta)) scrollBy(-delta, 0); }
1740  viewport()->update(); // because QScrollView thinks nothing has changed
1741  App->view->drawn = false;
1742  }
1743  // ...................................................................
1744  else if(Event->state() & Qt::ControlButton) { // use mouse wheel to zoom ?
1745  float Scaling;
1746  if(delta < 0) Scaling = float(delta)/-60.0/1.1;
1747  else Scaling = 1.1*60.0/float(delta);
1748  zoom(Scaling);
1749  Scaling -= 1.0;
1750  scrollBy( int(Scaling * float(Event->pos().x())),
1751  int(Scaling * float(Event->pos().y())) );
1752  }
1753  // ...................................................................
1754  else { // scroll vertically !
1755  if(delta > 0) { if(scrollUp(delta)) scrollBy(0, -delta); }
1756  else { if(scrollDown(delta)) scrollBy(0, -delta); }
1757  viewport()->update(); // because QScrollView thinks nothing has changed
1758  App->view->drawn = false;
1759  }
1760 
1761  Event->accept(); // QScrollView must not handle this event
1762 }
1763 
1764 // -----------------------------------------------------------
1765 // Scrolls the visible area upwards and enlarges or reduces the view
1766 // area accordingly.
1768 {
1769  int diff;
1770 
1771  diff = contentsY() - step;
1772  if(diff < 0) { // scroll outside the active area ? (upwards)
1773  resizeContents(contentsWidth(), contentsHeight()-diff);
1774  ViewY1 += diff;
1775  scrollBy(0, diff);
1776  return false;
1777  }
1778 
1779  diff = ViewY2 - UsedY2 - 20; // keep border of 20
1780  if(diff > 0) { // make active area smaller ?
1781  if(step < diff) diff = step;
1782  resizeContents(contentsWidth(), contentsHeight()-diff);
1783  ViewY2 -= diff;
1784  }
1785 
1786  return true;
1787 }
1788 
1789 // -----------------------------------------------------------
1790 // Scrolls the visible area downwards and enlarges or reduces the view
1791 // area accordingly. ("step" must be negative!)
1793 {
1794  int diff;
1795 
1796  diff = contentsHeight() - contentsY()-visibleHeight() + step;
1797  if(diff < 0) { // scroll outside the active area ? (downwards)
1798  resizeContents(contentsWidth(), contentsHeight()-diff);
1799  ViewY2 -= diff;
1800  scrollBy(0, -step);
1801  return false;
1802  }
1803 
1804  diff = ViewY1 - UsedY1 + 20; // keep border of 20
1805  if(diff < 0) { // make active area smaller ?
1806  if(step > diff) diff = step;
1807  resizeContents(contentsWidth(), contentsHeight()+diff);
1808  ViewY1 -= diff;
1809  return false;
1810  }
1811 
1812  return true;
1813 }
1814 
1815 // -----------------------------------------------------------
1816 // Scrolls the visible area to the left and enlarges or reduces the view
1817 // area accordingly.
1819 {
1820  int diff;
1821 
1822  diff = contentsX() - step;
1823  if(diff < 0) { // scroll outside the active area ? (to the left)
1824  resizeContents(contentsWidth()-diff, contentsHeight());
1825  ViewX1 += diff;
1826  scrollBy(diff, 0);
1827  return false;
1828  }
1829 
1830  diff = ViewX2 - UsedX2 - 20; // keep border of 20
1831  if(diff > 0) { // make active area smaller ?
1832  if(step < diff) diff = step;
1833  resizeContents(contentsWidth()-diff, contentsHeight());
1834  ViewX2 -= diff;
1835  }
1836 
1837  return true;
1838 }
1839 
1840 // -----------------------------------------------------------
1841 // Scrolls the visible area to the right and enlarges or reduces the
1842 // view area accordingly. ("step" must be negative!)
1844 {
1845  int diff;
1846 
1847  diff = contentsWidth() - contentsX()-visibleWidth() + step;
1848  if(diff < 0) { // scroll outside the active area ? (to the right)
1849  resizeContents(contentsWidth()-diff, contentsHeight());
1850  ViewX2 -= diff;
1851  scrollBy(-step, 0);
1852  return false;
1853  }
1854 
1855  diff = ViewX1 - UsedX1 + 20; // keep border of 20
1856  if(diff < 0) { // make active area smaller ?
1857  if(step > diff) diff = step;
1858  resizeContents(contentsWidth()+diff, contentsHeight());
1859  ViewX1 -= diff;
1860  return false;
1861  }
1862 
1863  return true;
1864 }
1865 
1866 // -----------------------------------------------------------
1867 // Is called if the scroll arrow of the ScrollBar is pressed.
1869 {
1870  App->editText->setHidden(true); // disable edit of component property
1871  scrollUp(verticalScrollBar()->lineStep());
1872  viewport()->update(); // because QScrollView thinks nothing has changed
1873  App->view->drawn = false;
1874 }
1875 
1876 // -----------------------------------------------------------
1877 // Is called if the scroll arrow of the ScrollBar is pressed.
1879 {
1880  App->editText->setHidden(true); // disable edit of component property
1881  scrollDown(-verticalScrollBar()->lineStep());
1882  viewport()->update(); // because QScrollView thinks nothing has changed
1883  App->view->drawn = false;
1884 }
1885 
1886 // -----------------------------------------------------------
1887 // Is called if the scroll arrow of the ScrollBar is pressed.
1889 {
1890  App->editText->setHidden(true); // disable edit of component property
1891  scrollLeft(horizontalScrollBar()->lineStep());
1892  viewport()->update(); // because QScrollView thinks nothing has changed
1893  App->view->drawn = false;
1894 }
1895 
1896 // -----------------------------------------------------------
1897 // Is called if the scroll arrow of the ScrollBar is pressed.
1899 {
1900  App->editText->setHidden(true); // disable edit of component property
1901  scrollRight(-horizontalScrollBar()->lineStep());
1902  viewport()->update(); // because QScrollView thinks nothing has changed
1903  App->view->drawn = false;
1904 }
1905 
1906 
1907 // *********************************************************************
1908 // ********** **********
1909 // ********** Function for serving drag'n drop **********
1910 // ********** **********
1911 // *********************************************************************
1912 
1913 // Is called if an object is dropped (after drag'n drop).
1914 void Schematic::contentsDropEvent(QDropEvent *Event)
1915 {
1916  if(dragIsOkay) {
1917  QStrList List;
1918  QUriDrag::decode(Event, List);
1919 
1920  // do not close untitled document to avoid segfault
1921  QucsDoc *d = QucsMain->getDoc(0);
1922  bool changed = d->DocChanged;
1923  d->DocChanged = true;
1924 
1925  // URI: file:/home/linuxuser/Desktop/example.sch
1926  for(unsigned int i=0; i < List.count(); i++)
1927  App->gotoPage(QDir::convertSeparators(QUriDrag::uriToLocalFile(List.at(i))));
1928 
1929  d->DocChanged = changed;
1930  return;
1931  }
1932 
1933 
1934  QMouseEvent e(QEvent::MouseButtonPress, Event->pos(),
1935  Qt::LeftButton, Qt::NoButton);
1936  int x = int(Event->pos().x()/Scale) + ViewX1;
1937  int y = int(Event->pos().y()/Scale) + ViewY1;
1938 
1939  App->view->MPressElement(this, &e, x, y);
1940 
1941  if(App->view->selElem) delete App->view->selElem;
1942  App->view->selElem = 0; // no component selected
1943 
1944  if(formerAction)
1945  formerAction->setOn(true); // restore old action
1946 }
1947 
1948 // ---------------------------------------------------
1949 void Schematic::contentsDragEnterEvent(QDragEnterEvent *Event)
1950 {
1951  formerAction = 0;
1952  dragIsOkay = false;
1953 
1954  // file dragged in ?
1955  if(Event->provides("text/uri-list"))
1956  if(QUriDrag::canDecode(Event)) {
1957  dragIsOkay = true;
1958  Event->accept();
1959  return;
1960  }
1961 
1962  // drag library component
1963  if(Event->provides("text/plain")) {
1964  QString s;
1965  if(QTextDrag::decode(Event, s))
1966  if(s.left(15) == "QucsComponent:<") {
1967  s = s.mid(14);
1969  if(App->view->selElem) {
1970  Event->accept();
1971  return;
1972  }
1973  }
1974  Event->ignore();
1975  return;
1976  }
1977 
1978 
1979  if(Event->format(1) == 0) { // only one MIME type ?
1980 
1981  // drag component from listview
1982  if(Event->provides("application/x-qiconlist")) {
1983  QIconViewItem *Item = App->CompComps->currentItem();
1984  if(Item) {
1986  App->slotSelectComponent(Item); // also sets drawn=false
1987  App->MouseMoveAction = 0;
1988  App->MousePressAction = 0;
1989 
1990  Event->accept();
1991  return;
1992  }
1993  }
1994  }
1995 
1996  Event->ignore();
1997 }
1998 
1999 // ---------------------------------------------------
2001 {
2002  if(App->view->selElem)
2003  if(App->view->selElem->Type & isComponent)
2004  if(App->view->drawn) {
2005 
2006  QPainter painter(viewport());
2007  App->view->setPainter(this, &painter);
2008  ((Component*)App->view->selElem)->paintScheme(&painter);
2009  App->view->drawn = false;
2010  }
2011 
2012  if(formerAction)
2013  formerAction->setOn(true); // restore old action
2014 }
2015 
2016 // ---------------------------------------------------
2017 void Schematic::contentsDragMoveEvent(QDragMoveEvent *Event)
2018 {
2019  if(!dragIsOkay) {
2020  if(App->view->selElem == 0) {
2021  Event->ignore();
2022  return;
2023  }
2024 
2025  QMouseEvent e(QEvent::MouseMove, Event->pos(), Qt::NoButton, Qt::NoButton);
2026  App->view->MMoveElement(this, &e);
2027  }
2028 
2029  Event->accept();
2030 }