20 #undef CLASSDESC_ARITIES    21 #define CLASSDESC_ARITIES 0x3F     27 #include <cairo_base.h>    31 #include "eventInterface.rcd"    34 #include "renderNativeWindow.xcd"    42   void Canvas::controlMouseDown(
float x, 
float y)
    45     if (itemFocus && itemFocus->group.lock() == model)
    46       selection.toggleItemMembership(itemFocus);
    49   void Canvas::mouseDown(
float x, 
float y)
    52     if (!itemFocus || !selection.contains(itemFocus))
    57   void Canvas::mouseDownCommon(
float x, 
float y)
    61     if ((itemFocus=itemAt(x,y)))
    63         clickType=itemFocus->clickType(x,y);
    66           case ClickType::onPort:
    68             if ((fromPort=itemFocus->closestOutPort(x,y)))
    75           case ClickType::onItem:
    76             moveOffsX=x-itemFocus->x();
    77             moveOffsY=y-itemFocus->y();
    79           case ClickType::outside:
    81             if (lassoMode==LassoMode::none)
    82               lassoMode=LassoMode::lasso;
    84           case ClickType::inItem:
    85             itemFocus->onMouseDown(x,y);
    87           case ClickType::onResize:
    88             lassoMode=LassoMode::itemResize;
    90             lasso.x0 = x>itemFocus->x()? itemFocus->left(): itemFocus->right();
    91             lasso.y0 = y>itemFocus->y()? itemFocus->top(): itemFocus->bottom();
    96           case ClickType::legendMove: 
case ClickType::legendResize:
    97             if (
auto p=itemFocus->plotWidgetCast())
   104         wireFocus=model->findAny(&Group::wires,
   105                        [&](
const WirePtr& i){
return i->near(x,y);});
   107           handleSelected=wireFocus->nearestHandle(x,y);
   109           if (lassoMode==LassoMode::none)
   110             lassoMode=LassoMode::lasso;
   113     if (lassoMode==LassoMode::lasso)
   120   shared_ptr<Port> Canvas::closestInPort(
float x, 
float y)
 const   122     shared_ptr<Port> closestPort;
   123     auto minD=numeric_limits<float>::max();
   124     model->recursiveDo(&GroupItems::items,
   125                        [&](
const Items&, Items::const_iterator i)
   127                          if ((*i)->group.lock()->displayContents())
   128                            for (
size_t pi=0; pi<(*i)->portsSize(); ++pi)
   130                                auto p=(*i)->ports(pi).lock();
   131                                const float d=
sqr(p->x()-x)+
sqr(p->y()-y);
   143   void Canvas::mouseUp(
float x, 
float y)
   147     if (itemFocus && clickType==ClickType::inItem)
   149         itemFocus->onMouseUp(x,y);
   155           if (
auto to=closestInPort(x,y)) {
   156             model->addWire(
static_cast<shared_ptr<Port>&
>(fromPort),to);
   159             if (to->item().tooltip().empty() && to->item().ravelCast())
   160               if (
auto v=fromPort->item().variableCast())
   161                 to->item().tooltip(v->name());
   169       wireFocus->editHandle(handleSelected,x,y);
   173       case LassoMode::lasso:
   174         select({lasso.x0,lasso.y0,x,y});
   177       case LassoMode::itemResize:
   186     lassoMode=LassoMode::none;
   189     itemIndicator.reset();
   195   void Canvas::mouseMoveOnItem(
float x, 
float y)
   197     updateRegion=
LassoBox(itemFocus->x(),itemFocus->y(),x,y);
   199     if (selection.empty() || !selection.contains(itemFocus))
   200       itemFocus->moveTo(x-moveOffsX, y-moveOffsY);
   204         auto deltaX=x-moveOffsX-itemFocus->x(), deltaY=y-moveOffsY-itemFocus->y();
   205         for (
auto& i: selection.items)
   206           i->moveTo(i->x()+deltaX, i->y()+deltaY);
   207         for (
auto& i: selection.groups)
   208           i->moveTo(i->x()+deltaX, i->y()+deltaY);
   212     if (
auto g=itemFocus->group.lock())
   213       if (g==model || !g->contains(itemFocus->x(),itemFocus->y()))
   215           if (
auto toGroup=model->minimalEnclosingGroup
   216               (itemFocus->x(),itemFocus->y(),itemFocus->x(),itemFocus->y(),itemFocus.get()))
   218               if (g.get()==toGroup) 
return;
   220               if (
auto g=dynamic_cast<Group*>(itemFocus.get()))
   221                 if (g->higher(*toGroup))
   224               toGroup->addItem(itemFocus);
   225               toGroup->splitBoundaryCrossingWires();
   226               g->splitBoundaryCrossingWires();
   231               model->addItem(itemFocus);
   232               model->splitBoundaryCrossingWires();
   233               g->splitBoundaryCrossingWires();
   236     if (
auto g=itemFocus->group.lock())
   238         g->checkAddIORegion(itemFocus);
   241   void Canvas::mouseMove(
float x, 
float y)
   244         if (rotatingItem && item)
   246             item->rotate(
Point{x,y},rotateOrigin);
   255               case ClickType::onItem:
   256                 mouseMoveOnItem(x,y);
   258               case ClickType::inItem:
   259                 if (itemFocus->onMouseMotion(x,y))
   262               case ClickType::legendMove: 
case ClickType::legendResize:
   263                 if (
auto p=itemFocus->plotWidgetCast())
   269               case ClickType::onResize:
   278         else if (fromPort.get())
   286             wireFocus->editHandle(handleSelected,x,y);
   289         else if (lassoMode==LassoMode::lasso || (lassoMode==LassoMode::itemResize && item.get()))
   297             auto setFlagAndRequestRedraw=[&](
bool& flag, 
bool cond) {
   298               if (flag==cond) 
return;
   303             bool mouseFocusSet=
false; 
   304             model->recursiveDo(&Group::items, [&](
Items&,Items::iterator& i)
   306                                                 (*i)->disableDelayedTooltip();
   311                                                 if (!(*i)->visible() && 
   313                                                   (*i)->mouseFocus=
false;
   316                                                     auto ct=(*i)->clickType(x,y);
   317                                                     setFlagAndRequestRedraw((*i)->mouseFocus, !mouseFocusSet && ct!=ClickType::outside);
   318                                                     if ((*i)->mouseFocus) mouseFocusSet=
true;
   319                                                     setFlagAndRequestRedraw((*i)->onResizeHandles, ct==ClickType::onResize);
   320                                                     setFlagAndRequestRedraw((*i)->onBorder, ct==ClickType::onItem);
   321                                                     if (ct==ClickType::outside)
   322                                                         (*i)->onMouseLeave();
   323                                                     else if ((*i)->onMouseOver(x,y))
   328             model->recursiveDo(&Group::groups, [&](
Groups&,Groups::iterator& i)
   330                                                  auto ct=(*i)->clickType(x,y);
   331                                                  setFlagAndRequestRedraw((*i)->mouseFocus, !mouseFocusSet && ct!=ClickType::outside);
   332                                                  if ((*i)->mouseFocus) mouseFocusSet=
true;
   333                                                  const bool onResize = ct==ClickType::onResize;
   334                                                  if (onResize!=(*i)->onResizeHandles)
   336                                                      (*i)->onResizeHandles=onResize;
   341             model->recursiveDo(&Group::wires, [&](
Wires&,Wires::iterator& i)
   343                                                 const bool mf=(*i)->near(x,y);
   344                                                 if (mf!=(*i)->mouseFocus)
   353           minsky().resetAt=std::chrono::system_clock::now()+std::chrono::milliseconds(1500); 
   359     if (
auto item=itemAt(args.
x,args.
y))
   370   void Canvas::displayDelayedTooltip(
float x, 
float y)
   372     if (
auto item=itemAt(x,y))
   374         item->displayDelayedTooltip(x,y);
   383     auto topLevel = model->minimalEnclosingGroup(lasso.
x0,lasso.
y0,lasso.
x1,lasso.
y1);
   385     if (!topLevel) topLevel=&*model;
   387     for (
auto& i: topLevel->items)
   389         selection.ensureItemInserted(i);
   391     for (
auto& i: topLevel->groups)
   393         selection.ensureGroupInserted(i);
   399   int Canvas::ravelsSelected()
 const   401     int ravelsSelected = 0;
   402     for (
auto& i: selection.items) {
   403       if (dynamic_pointer_cast<Ravel>(i))
   408     return ravelsSelected;
   415     auto minD=numeric_limits<float>::max();
   416     model->recursiveDo(&GroupItems::items,
   417                        [&](
const Items&, Items::const_iterator i)
   419                           const float d=
sqr((*i)->x()-x)+
sqr((*i)->y()-y);
   420                           if (d<
minD && (*i)->visible() && (*i)->contains(x,y))
   429         (&Group::groups, [&](
const GroupPtr& i)
   430                          {
return i->visible() && i->clickType(x,y)!=ClickType::outside;});
   434   bool Canvas::getWireAt(
float x, 
float y)
   436     wire=model->findAny(&Group::wires,
   437                         [&](
const WirePtr& i){
return i->near(x,y);});
   441   void Canvas::groupSelection()
   444     for (
auto& i: selection.items)
   446     for (
auto& i: selection.groups)
   448     r->splitBoundaryCrossingWires();
   449     r->resizeOnContents();
   452   void Canvas::lockRavelsInSelection()
   454     vector<shared_ptr<Ravel> > ravelsToLock;
   455     shared_ptr<RavelLockGroup> lockGroup;
   456     bool conflictingLockGroups=
false;
   457     for (
auto& i: selection.items)
   458       if (
auto r=dynamic_pointer_cast<Ravel>(i))
   460           ravelsToLock.push_back(r);
   462             lockGroup=r->lockGroup;
   463           if (lockGroup!=r->lockGroup)
   464             conflictingLockGroups=
true;
   466     if (ravelsToLock.size()<2)
   468     if (!lockGroup || conflictingLockGroups)
   470     for (
auto& r: ravelsToLock)
   472         lockGroup->addRavel(r);
   474         r->lockGroup=lockGroup;
   476     if (lockGroup) lockGroup->initialBroadcast();
   480   void Canvas::unlockRavelsInSelection()
   482     for (
auto& i: selection.items)
   483       if (
auto r=dynamic_cast<Ravel*>(i.get()))
   487   void Canvas::deleteItem()
   491       model->deleteItem(*item);
   496   void Canvas::deleteWire()
   500       model->removeWire(*
wire);
   507   void Canvas::delHandle(
float x, 
float y)
   509     wireFocus=model->findAny(&Group::wires,
   510                    [&](
const WirePtr& i){
return i->near(x,y);});
   513         wireFocus->deleteHandle(x,y);
   519   void Canvas::removeItemFromItsGroup()
   522       if (
auto g=item->group.lock())
   524           if (
auto parent=g->group.lock())
   526               itemFocus=parent->addItem(item);
   527               if (
auto v=itemFocus->variableCast())
   528                 v->controller.reset();
   529               g->splitBoundaryCrossingWires();
   535   void Canvas::selectAllVariables()
   538     auto var=item->variableCast();
   540       if (
auto i=dynamic_cast<IntOp*>(item.get()))
   545           (&GroupItems::items, [&](
const Items&,Items::const_iterator i)
   547              if (
auto v=(*i)->variableCast())
   548                if (v->valueId()==
var->valueId())
   550                    selection.ensureItemInserted(*i);
   557   void Canvas::renameAllInstances(
const string& newName)
   559     auto var=item->variableCast();
   561       if (
auto i=dynamic_cast<IntOp*>(item.get()))
   567         model->renameAllInstances(
valueId, newName);
   571   void Canvas::ungroupItem()
   573     if (
auto g=dynamic_cast<Group*>(item.get()))
   575         if (
auto p=g->group.lock())
   580                 map<string,string> existingParms; 
   581                 for (
auto& i: g->items) {
   582                   auto v=i->variableCast(); 
   583                   if (v && v->type()==VariableType::parameter) 
   584                     existingParms.emplace(v->valueId(),v->init());
   588                 auto scaleFactor=1/g->relZoom;
   589                 for (
auto& i: g->items)
   590                   i->moveTo((i->x()-g->x())*scaleFactor+g->x(), (i->y()-g->y())*scaleFactor+g->y());
   591                 for (
auto& i: g->groups)
   592                   i->moveTo((i->x()-g->x())*scaleFactor+g->x(), (i->y()-g->y())*scaleFactor+g->y());
   596                 existingParms.clear();    
   605   void Canvas::renameItem(
const std::string& newName)
   608     if (
auto var=item->variableCast())
   613           var->retype(VariableType::flow);
   617   void Canvas::copyItem()
   623         if (
auto intop=dynamic_cast<IntOp*>(item.get()))
   624           newItem.reset(intop->intVar->clone());
   625         else if (
auto group=dynamic_cast<Group*>(item.get()))
   626           newItem=
group->copy();
   630               if (
auto v=item->variableCast())
   631                 if (v->rawName()[0]!=
':')
   644             newItem.reset(item->clone());
   646             if (
auto v=item->variableCast())
   647               if (v->controller.lock())
   648                 newItem->rotation(defaultRotation);
   651         setItemFocus(model->addItem(newItem));
   652         model->normaliseGroupRefs(model);
   656   void Canvas::zoomToFit()
   658     if (frameArgs().parentWindowId.empty()) 
return; 
   660     for (
auto& i: model->items) i->updateBoundingBox();
   663     model->contentBounds(x0,y0,x1,y1);
   664     float inOffset=0, outOffset=0;
   665     const bool notFlipped=!
flipped(model->rotation());
   666     const float flip=notFlipped? 1: -1;
   669     for (
auto& v: model->inVariables)
   670       inOffset=std::max(inOffset, v->width());
   671     for (
auto& v: model->outVariables)
   672       outOffset=std::max(outOffset, v->width());
   674     const float zoomFactor=std::min(frameArgs().childWidth/(x1-x0+inOffset+outOffset),
   675                               frameArgs().childHeight/(y1-y0));
   677     model->zoom(model->x(),model->y(),zoomFactor);
   680     model->contentBounds(x0,y0,x1,y1);
   681     float ioOffset=notFlipped? x0: x1;
   684     for (
auto& v: model->inVariables)
   685       v->moveTo(ioOffset-flip*v->width(),v->y());
   686     ioOffset=notFlipped? x1: x0;                                               
   687     for (
auto& v: model->outVariables)
   688       v->moveTo(ioOffset+flip*v->width(),v->y());
   694   void Canvas::openGroupInCanvas(
const ItemPtr& item)
   696     if (
auto g=dynamic_pointer_cast<Group>(item))
   698         if (
auto parent=model->group.lock())
   699           model->setZoom(parent->zoomFactor());
   713   void Canvas::copyVars(
const std::vector<VariablePtr>& v)
   715     float maxWidth=0, totalHeight=0;
   716     vector<float> widths, heights;
   723           widths.push_back(rv.
width());
   724           heights.push_back(rv.
height());
   725           maxWidth=max(maxWidth, widths.back());
   726           totalHeight+=heights.back();
   728       float y=v[0]->y() - totalHeight;
   729       for (
size_t i=0; i<v.size(); ++i)
   732           const ItemPtr ni(v[i]->clone());
   733           (ni->variableCast())->rotation(0);
   734           ni->moveTo(v[0]->x()+maxWidth-v[i]->zoomFactor()*widths[i],
   737           if ((ni->variableCast())->name()[0]!=
':')
   738             (ni->variableCast())->name(
':'+(ni->variableCast())->name());
   739           y+=2*v[i]->zoomFactor()*heights[i];
   740           selection.insertItem(model->addItem(ni));              
   743         if (!selection.empty()) setItemFocus(selection.items[0]);
   744         else setItemFocus(
nullptr);  
   745     } 
else throw error(
"no flow or stock variables to copy");    
   748   void Canvas::zoomToDisplay()
   750     if (
auto g=dynamic_cast<Group*>(item.get()))
   752         model->zoom(g->x(),g->y(),1.1/(g->relZoom*g->zoomFactor()));
   757   bool Canvas::selectVar(
float x, 
float y) 
   761         if (
auto v=item->select(x,y))
   770   bool Canvas::findVariableDefinition()
   772     if (
auto iv=item->variableCast())
   774         if (iv->type()==VariableType::constant ||
   775             iv->type()==VariableType::parameter || iv->inputWired())
   776           return (itemIndicator=item).get();
   778         itemIndicator=model->findAny
   779           (&GroupItems::items, [&](
const ItemPtr& i) {
   780             if (
auto v=i->variableCast())
   781               return v->inputWired() && v->valueId()==iv->valueId();
   782             if (
auto g=dynamic_cast<GodleyIcon*>(i.get()))
   783               for (
auto& v: g->stockVars())
   785                   if (v->valueId()==iv->valueId())
   788             else if (
auto o=dynamic_cast<IntOp*>(i.get()))
   789               return o->intVar->valueId()==iv->valueId();
   792         return itemIndicator.get();
   797   bool Canvas::redraw(
int x0, 
int y0, 
int width, 
int height)
   801     updateRegion.x1=x0+width;
   802     updateRegion.y1=y0+height;
   806         return redrawUpdateRegion();
   809     catch (std::exception& ex)
   811         cerr << ex.what() << endl;
   823   bool Canvas::redraw()
   826     return redraw(-1e9,-1e9,2e9,2e9);
   829   bool Canvas::redrawUpdateRegion()
   831     bool didDrawSomething = 
false;
   832     if (!surface().
get()) {
   833       return didDrawSomething;
   835     m_redrawRequested=
false;
   836     auto cairo=surface()->cairo();
   837     const CairoSave cs(cairo);
   838     cairo_rectangle(cairo,updateRegion.x0,updateRegion.y0,updateRegion.x1-updateRegion.x0,updateRegion.y1-updateRegion.y0);
   840     cairo_set_line_width(cairo, 1);
   843       (&GroupItems::items, [&](
const Items&, Items::const_iterator i)
   846          if (it.visible() && updateRegion.intersects(it))
   848              didDrawSomething = 
true;
   849              const CairoSave cs(cairo);
   850              cairo_identity_matrix(cairo);
   851              cairo_translate(cairo,it.x(), it.y());
   856              catch (
const std::exception& ex)
   858                  cerr << ex.what() << endl;
   866       (&GroupItems::groups, [&](
const Groups&, Groups::const_iterator i)
   869          if (it.visible() && updateRegion.intersects(it))
   871              didDrawSomething = 
true;
   872              const CairoSave cs(cairo);
   873              cairo_identity_matrix(cairo);
   874              cairo_translate(cairo,it.x(), it.y());
   883       (&GroupItems::wires, [&](
const Wires&, Wires::const_iterator i)
   894         didDrawSomething = 
true;
   895         cairo_move_to(cairo,fromPort->x(),fromPort->y());
   896         cairo_line_to(cairo,termX,termY);
   899         const CairoSave cs(cairo);
   900         cairo_translate(cairo, termX,termY);
   901         cairo_rotate(cairo,atan2(termY-fromPort->y(), termX-fromPort->x()));
   902         cairo_move_to(cairo,0,0);
   903         cairo_line_to(cairo,-5,-3); 
   904         cairo_line_to(cairo,-3,0); 
   905         cairo_line_to(cairo,-5,3);
   906         cairo_close_path(cairo);
   910     if (lassoMode!=LassoMode::none)
   912         didDrawSomething = 
true;
   913         cairo_rectangle(cairo,lasso.x0,lasso.y0,lasso.x1-lasso.x0,lasso.y1-lasso.y0);
   919         const CairoSave cs(surface()->cairo());
   920         cairo_set_source_rgb(surface()->cairo(),1,0,0);
   921         cairo_arc(surface()->cairo(),itemIndicator->x(),itemIndicator->y(),15,0,2*
M_PI);
   922         cairo_stroke(surface()->cairo());
   926     return didDrawSomething;
   929   void Canvas::recentre()
   931     const SurfacePtr tmp(surface());
   932     surface().reset(
new Surface(cairo_recording_surface_create(CAIRO_CONTENT_COLOR,
nullptr)));
   934     model->moveTo(model->x()-surface()->left(), model->y()-surface()->top());
   939   void Canvas::reportDrawTime(
double t) 
   945     void Canvas::renderToPNGCropped(
const std::string& filename, 
const ZoomCrop& z)
   947     model->zoom(model->x(),model->y(),z.
zoom);
   948     model->moveTo(model->x()-z.
left,model->y()-z.
top);
   949     cairo::SurfacePtr tmp(
new cairo::Surface(cairo_image_surface_create(CAIRO_FORMAT_ARGB32,z.
width,z.
height)));
   953     cairo_surface_write_to_png(tmp->surface(),filename.c_str());
   954     model->moveTo(model->x()+z.
left,model->y()+z.
top);
   955     model->zoom(model->x(),model->y(),1/z.
zoom);
   960   void Canvas::applyDefaultPlotOptions() {
   961       if (
auto p=item->plotWidgetCast()) {
   963         const string title(p->title), xlabel(p->xlabel()),
   964           ylabel(p->ylabel()), y1label(p->y1label());
   965         defaultPlotOptions.applyPlotOptions(*p);
   966         p->title=title, p->xlabel(xlabel),
   967           p->ylabel(ylabel), p->y1label(y1label);
   971   void Canvas::setItemFromItemFocus()
 #define M_PI
some useful geometry types, defined from boost::geometry 
std::vector< WirePtr > Wires
void copy() const
copy items in current selection into clipboard 
bool intersects(const Item &item) const
returns whether item's icon overlaps the lasso 
bool pushHistory()
push current model state onto history if it differs from previous 
represents rectangular region of a lasso operation 
std::shared_ptr< Item > ItemPtr
std::shared_ptr< Wire > WirePtr
string valueId(const string &name)
construct a valueId from fully qualified name @ name should not be canonicalised 
void markEdited()
indicate model has been changed since last saved 
bool flipped(double rotation)
returns if the angle (in degrees) is in the second or third quadrant 
bool reset_flag() const
true if reset needs to be called prior to numerical integration 
void renameAllInstances(const std::string &newName)
rename all instances of variable as item to newName 
long undo(int changes=1)
restore model to state changes ago 
std::vector< ItemPtr > Items
virtual void bookmarkRefresh()
refresh the bookmark menu after changes 
std::vector< GroupPtr > Groups
boost::geometry::model::d2::point_xy< float > Point
VariablePtr definingVar(const std::string &valueId) const
returns reference to variable defining (ie input wired) for valueId 
const Minsky & cminsky()
const version to help in const correctness 
double minD(const Item &item1, const Item &item2)
virtual void resetScroll()
reset main window scroll bars after model has been panned 
std::shared_ptr< Group > GroupPtr
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::EventInterface)
int maxWaitMS
maximum wait in millisecond between redrawing canvas during simulation 
float height() const
half height of unrotated image 
float width() const
half width of unrotated image