iiscell.cpp

Go to the documentation of this file.
00001 #include <qpainter.h>
00002 #include <qimage.h>
00003 #include <qlineedit.h>
00004 #include <qpalette.h>
00005 #include <typeinfo>
00006 #include "iis.h"
00007 #include "value.h"
00008 #include "vars.h"
00009 #include "refs.h"
00010 #include "ranges.h"
00011 #include "runtime.h"
00012 #include "spreadsheet.h"
00013 #include "text.h"
00014 #include "parser.h"
00015 #include "status.h"
00016 
00017 //---------------------------------------------------------------
00018 //                           Net stuff
00019 //---------------------------------------------------------------
00020 bool equals(QString a, QString b)
00021 {
00022   return a==b;
00023 }
00024 
00025 bool equals(bool a, bool b)
00026 {
00027   return a==b;
00028 }
00029 
00030 bool equals(QImage a, QImage b)
00031 {
00032   if (a.isNull() && b.isNull()) return true;
00033   if (a.isNull() || b.isNull()) return false;
00034   return a==b;
00035 }
00036 
00037 bool equals(Value * v1, Value * v2)
00038 {
00039   if (v1==v2) return true;
00040   if (v1==NULL || v2==NULL)
00041     return false;
00042   if (v1->equals(v2))
00043     return true;
00044   return false;
00045 }
00046 
00047 class CallMember: public PlaceListener
00048 {
00049 public:
00050   IisCell::member F;
00051   IisCell& o;
00052   CallMember(IisCell &ob, IisCell::member i) : o(ob) 
00053     {
00054     F=i;
00055   };
00056   virtual void setChanged()
00057   {
00058     if (!changed)
00059       {
00060         o.queueCall(this);
00061       }
00062     PlaceListener::setChanged();
00063   }
00064   void execute()
00065   {
00066     ((o).*(F))();
00067   };
00068 };
00069 
00070 void IisCell::link(AbstractPlace& l, IisCell::member m)
00071 {
00072   CallMember * em;
00073   em = new CallMember(*this,m);
00074   l.notify(em);
00075 };
00076 
00077 static vector<CallMember*> queued_calls;
00078 
00079 void IisCell::queueCall(CallMember * m)
00080 {
00081   queued_calls.push_back(m);
00082 }
00083 
00084 void IisCell::flushCalls()
00085 {
00086   static bool flushing = false;
00087   if (flushing) return;
00088   flushing = true;
00089   while(queued_calls.size())
00090     {
00091       CallMember * m = queued_calls.front();
00092       queued_calls.erase(queued_calls.begin());
00093       m->clearChanged();
00094       m->execute();
00095     };
00096   flushing = false;
00097 }
00098 
00099 //---------------------------------------------------------------
00100 //              The actual IisCell
00101 //---------------------------------------------------------------
00102 IisCell::IisCell(QTable * table, EditType et) :
00103   QTableItem(table,et)
00104 {
00105   link(entered,     &IisCell::entered_changed);
00106   link(value,       &IisCell::value_changed);
00107   link(formule,     &IisCell::formule_changed);
00108   link(image,       &IisCell::image_changed);
00109   link(show,        &IisCell::show_changed);
00110   link(needs_recalculation,&IisCell::recalc_changed);
00111 
00112   link(entered,     &IisCell::show_status_changed);
00113   link(show_status, &IisCell::show_status_changed);
00114 
00115   link(image,       &IisCell::visual_changed);
00116   link(show,        &IisCell::visual_changed);
00117   link(needs_recalculation,&IisCell::visual_changed);
00118   
00119   entered = "";
00120   formule = NULL;
00121   value = NULL;
00122   needs_recalculation = false;
00123   user_input_allowed  = false;
00124   image = QImage();
00125   scaled = QImage();
00126   scale_xs = 0;
00127   scale_ys = 0;
00128   setWordWrap(true);
00129   show_status = true;
00130   
00131   flushCalls();
00132 };
00133 
00134 void IisCell::load(Data initfrom) 
00135 {
00136   Token content = initfrom;
00137   String E = content["entered"];
00138   entered = E;
00139   Data F = content["formule"];
00140   if (!F.isNull())
00141     formule = Value::load(F);
00142   Data R = content["result"];
00143   if (!R.isNull())
00144     value = Value::load(R);
00145   // connect to the right cells
00146   set_inputs();
00147 
00148   flushCalls();
00149 };
00150 
00151 Data IisCell::saveFile(QString prefix)
00152 {
00153   Token content;
00154   content["entered"]=String(entered);
00155   Value * F = formule;
00156   if (F)
00157     content["formule"]=F->getSaveDescription(prefix);
00158   Value * result = value;
00159   if (result)
00160     content["result"]=result->getSaveDescription(prefix);
00161   return content;
00162 }
00163 
00164 QWidget * IisCell::createEditor () const
00165 {
00166   QWidget * res = QTableItem::createEditor();
00167   QLineEdit * le = (QLineEdit*)(void*)res;
00168   le->setText(entered);
00169   return le;
00170 };
00171 
00172 void IisCell::add_listener(IisCell * who)
00173 {
00174   for(list<IisCell*>::iterator it=listeners.begin() ; it!=listeners.end() ; it++)
00175     {
00176       IisCell * c = *it;
00177       if (c==who) return;
00178     }
00179   listeners.push_back(who);
00180 }
00181 
00182 void IisCell::image_changed()
00183 {
00184   QImage I = image;
00185   scaled = QImage();
00186   if (!I.isNull())
00187     show = NULL;
00188 }
00189 
00190 void IisCell::show_changed()
00191 {
00192   QString S = show;
00193   QTableItem::setText(S);
00194   if (!S.isEmpty())
00195     image = QImage();
00196 }
00197 
00198 void IisCell::visual_changed()
00199 {
00200   table()->updateCell(row(),col());
00201 }
00202 
00203 void IisCell::del_listener(IisCell * who)
00204 {
00205   for(list<IisCell*>::iterator it=listeners.begin() ; it!=listeners.end() ; it++)
00206     if ((*it)==who) 
00207       {
00208         listeners.erase(it);
00209         return;
00210       }
00211 }
00212 
00213 class CellShifter: public ReferenceFixer
00214 {
00215 protected: int from, nr;
00216 public:    CellShifter(int r, int c) : from(r), nr(c) {};
00217 };
00218 
00219 
00220 class CellDownShifter: public CellShifter
00221 {
00222 public:    
00223   CellDownShifter(int f, int c) : CellShifter(f,c) 
00224   {
00225   };
00226 protected: 
00227   Reference* copyRef(Reference * ref)
00228   {
00229     assert(ref);
00230     if (ref->row>=from) ref->row+=nr;
00231     return ref;
00232   }
00233 };
00234 
00235 class CellRightShifter: public CellShifter
00236 {
00237 public:   
00238   CellRightShifter(int f, int c) : CellShifter(f,c) 
00239   {
00240   };
00241 protected: 
00242   Reference* copyRef(Reference * ref)
00243   {
00244     if (ref->column>=from) ref->column+=nr;
00245     return ref;
00246   }
00247 };
00248 
00249 void IisCell::rowsInserted(int row, int nr)
00250 {
00251   if (formule)
00252     {
00253       CellDownShifter shifter(row,nr);
00254       shifter.copy(formule);
00255     }
00256 }
00257 
00258 void IisCell::colsInserted(int col, int nr)
00259 {
00260   if (formule)
00261     {
00262       CellRightShifter shifter(col,nr);
00263       shifter.copy(formule);
00264     }
00265 }
00266 
00267 class RelativeShifter: public ReferenceFixer
00268 {
00269   int x,y;
00270   public:    RelativeShifter(int X, int Y) : x(X), y(Y) {};
00271   protected: Reference* copyRef(Reference *ref)
00272   {
00273     Reference *result=new Reference(*ref);
00274     if (!ref->absolute_col) 
00275       result->column+=x;
00276     if (!ref->absolute_row)
00277       result->row+=y;
00278     return result;
00279   }
00280 };
00281 
00282 
00283 QString IisCell::getFormuleTextForCell(int x, int y)
00284 {
00285   if (!formule) return "";
00286   RelativeShifter shifter(x-col(),y-row());
00287   return shifter.copy(formule)->toString();
00288 }
00289 
00290 void IisCell::setText(const QString & str)
00291 {
00292   entered = str;
00293   flushCalls();
00294 }
00295 
00296 void IisCell::entered_changed()
00297 {
00298   QString e = entered;
00299   if (e.isEmpty())
00300     formule = NULL;
00301   else
00302     {
00303       try
00304         {
00305           ParseText p(e);
00306           formule = p.expr();
00307         }
00308       catch (Error * p)
00309         {
00310           formule = NULL;
00311           value = p;
00312         }
00313     }
00314 }
00315 
00316 void IisCell::formule_changed()
00317 {
00318   clear_inputs();
00319   set_inputs();
00320   if (will_loop())
00321     {
00322       clear_inputs(); 
00323       value = new Error("<loop>");
00324       formule = NULL;
00325       return;
00326     }
00327   //  value = NULL;
00328   mark_recalc();
00329   
00330   // backward, when the formule updates, the entered formule
00331   // should be updated as well
00332   Value * F = formule;
00333   if (F) 
00334     entered = F->toString();
00335 }
00336 
00337 Value * IisCell::fetch_value()
00338 {
00339   if (needs_recalculation) return NULL;
00340   Value * v = value;
00341   if (v &&
00342       (typeid(*v) == typeid(Status)
00343        || typeid(*v) == typeid(Error)
00344        || typeid(*v) == typeid(ParseError)))
00345     return NULL;
00346   return v;
00347 }
00348 
00349 void IisCell::updated()
00350 {
00351   printf("  Updated cell %d,%d\n",col(),row());
00352   for(list<IisCell*>::iterator it=listeners.begin(); it!=listeners.end(); it++)
00353     {
00354       assert(*it);
00355       printf("    Marking recalc at %d,%d\n",(*it)->col(),(*it)->row());
00356       (*it)->mark_recalc();
00357     }
00358 }
00359 
00360 void IisCell::value_changed()
00361 {
00362   if (!show_status)
00363     show_value();
00364   updated();
00365 }
00366 
00367 void IisCell::show_value()
00368 {
00369   Value * result = value;
00370   if (!result)
00371     {
00372       show_status = true; 
00373     }
00374   else
00375     {
00376       if (typeid(*result)==typeid(ImageValue))
00377         image = ((ImageValue*)result)->getQImage(true);
00378       else 
00379         show = result->toString();
00380     }
00381 }
00382 
00383 void IisCell::show_status_changed()
00384 {
00385   if (show_status)
00386     show = entered;
00387   else
00388     show_value();
00389 }
00390 
00391 void IisCell::paint(QPainter * p, const QColorGroup & cg, const QRect & cr, bool selected)
00392 {
00393   // determine color
00394   QColorGroup ncg(cg);
00395   Value * v = value;
00396   if (v && typeid(*v)==typeid(ParseError))
00397     ncg.setColor(QColorGroup::Base,QColor(255,0,0));
00398   else if (v && typeid(*v)==typeid(Error))
00399     ncg.setColor(QColorGroup::Base,QColor(255,192,192));
00400   else if (user_input_allowed)
00401     ncg.setColor(QColorGroup::Base,QColor(128,255,128));
00402   else if (needs_user_input())
00403     {
00404       if (needs_recalculation || show_status)
00405         ncg.setColor(QColorGroup::Base,QColor(255,192,128));
00406       else
00407         ncg.setColor(QColorGroup::Base,QColor(255,255,128));
00408     }
00409   else if (needs_recalculation || show_status)
00410     ncg.setColor(QColorGroup::Base,QColor(192,192,192));
00411 
00412   // paint text
00413   QImage I = image;
00414   if (I.isNull())
00415     {
00416       QTableItem::paint(p,ncg,cr,selected);
00417       return;
00418     }
00419 
00420   // if we have a pixmap we draw the content ourselves
00421   int height = table()->rowHeight(row());
00422   int width = table()->columnWidth(col());
00423   if (scaled.isNull() ||
00424       width!=scale_xs || 
00425       height!=scale_ys ||
00426       scaled.isNull())
00427     {
00428       scale_xs = width;
00429       scale_ys = height;
00430       scaled = I.smoothScale(scale_xs,scale_ys,QImage::ScaleMin);
00431 
00432       // draw the coordinate system
00433       if (value)
00434         {
00435           Value * v = value;
00436           if (typeid(*v)==typeid(ImageValue))
00437             {
00438               ImageValue* V = (ImageValue*)v;
00439               V->drawAxes(&scaled);
00440             }
00441         }
00442    }
00443   int xs = scaled.width();
00444   int ys = scaled.height();
00445   // top left corner
00446   int xl = (width - xs) / 2;
00447   int xr = xl + xs;
00448   int yt = (height - ys) / 2;
00449   int yb = yt + ys;
00450   QBrush b(ncg.color(QColorGroup::Base));
00451   p->fillRect(0,0,xl,height,b);
00452   p->fillRect(xr,0,width-xr+1,height,b);
00453   p->fillRect(xl,0,xs,yt,b);
00454   p->fillRect(xl,yb,xs,height-yb+1,b);
00455   p->drawImage(xl,yt,scaled);
00456 };
00457 
00458 void IisCell::recalc_changed()
00459 {
00460   if (needs_recalculation)
00461     {
00462       value = NULL;
00463       show_status = true;
00464       SpreadSheetTable * ss = (SpreadSheetTable*)table();
00465       assert(ss);
00466       printf("      Queueing recalc at %d,%d\n",col(),row());
00467       ss->queue_recalculation(this);
00468       updated();
00469     }
00470 }
00471 
00472 void IisCell::mark_recalc()
00473 {
00474   needs_recalculation = true;
00475   flushCalls();
00476 }
00477 
00478 void IisCell::mark_allow_ui()
00479 {
00480   if (needs_user_input())
00481     user_input_allowed=true;
00482 }
00483 
00484 void IisCell::clear_inputs() 
00485 {
00486   mark_inputs(false);
00487   inputs.clear();
00488 }
00489 
00490 void IisCell::set_inputs() 
00491 {
00492   gather_inputs(formule);
00493   mark_inputs(true);
00494 };
00495 
00496 void IisCell::gather_inputs(Value * v)
00497 {
00498   if (!v) return;
00499   if (typeid(*v)==typeid(TextValue)) return;
00500   if (typeid(*v)==typeid(ImageValue)) return;
00501   if (typeid(*v)==typeid(Reference))
00502     {
00503       // check position
00504       Reference *vv = (Reference*)v;
00505       assert(vv);
00506       SpreadSheetTable * ss = (SpreadSheetTable*)table();
00507       assert(ss);
00508       IisCell * sc = ss->findItem(vv->row,vv->column);
00509       assert(sc);
00510       inputs.push_back(sc);
00511       return;
00512     }
00513   if (typeid(*v)==typeid(Range))
00514     {
00515       // check position
00516       Range * r = (Range*)v;
00517       SpreadSheetTable * ss = (SpreadSheetTable*)table();
00518       assert(ss);
00519       for(int x = r->from->column ; x <= r->to->column ; x++)
00520         for(int y = r->from->row ; y <= r->to->row ; y++)
00521           {
00522             IisCell * sc = ss->findItem(y,x);
00523             assert(sc);
00524             inputs.push_back(sc);
00525           }
00526       return;
00527     }
00528   if (typeid(*v)==typeid(Expression))
00529     {
00530       Expression * e = (Expression*)v;
00531       for(int i = 0 ; i < e->argc() ; i++)
00532         gather_inputs(e->argv(i));
00533       return;
00534     }
00535   assert(0);
00536 }
00537 
00538 
00539 bool IisCell::needs_user_input()
00540 {
00541   return runtime.requires_user_input(formule);
00542 }
00543 
00544 bool IisCell::can_be_calculated_now()
00545 {
00546   // printf("Can cell %d,%d be recalculated ?\n",col(),row());
00547   for (list<IisCell*>::iterator it = inputs.begin();
00548        it!=inputs.end() ; it++)
00549     {
00550       bool nr = (*it)->needs_recalculation;
00551       // printf("  Input %d,%d: %d\n",(*it)->col(),(*it)->row(),nr);
00552       if ((*it)->needs_recalculation) 
00553         return false;
00554     }
00555   if (user_input_allowed) return true;
00556   return (!needs_user_input());
00557 }
00558 
00559 void IisCell::mark_inputs(bool add)
00560 {
00561   for (list<IisCell*>::iterator it = inputs.begin() ; 
00562        it!=inputs.end() ; it++)
00563     {
00564       IisCell * sc = *it;
00565       if (add) 
00566         sc->add_listener(this);
00567       else
00568         sc->del_listener(this);
00569     }
00570 }
00571 
00572 bool IisCell::will_loop() 
00573 {
00574   for (list<IisCell*>::iterator it = listeners.begin() ; 
00575        it!=listeners.end() ; it++)
00576     {
00577       IisCell * sc = *it;
00578       assert(sc);
00579       if (sc->will_influence(this)) return true;
00580     }
00581   return false;
00582 };
00583 
00584 bool IisCell::will_influence(IisCell * target)
00585 {
00586   if (this == target) return true;
00587   for (list<IisCell*>::iterator it = listeners.begin() ; 
00588        it!=listeners.end() ; it++)
00589     {
00590       IisCell * sc = *it;
00591       assert(sc);
00592       if (sc->will_influence(target)) return true;
00593     }
00594   return false;
00595 }
00596 
00597 void IisCell::recalc()
00598 {
00599   needs_recalculation = false;
00600   user_input_allowed = false;
00601   printf("Recalcing %d,%d\n",col(),row());
00602   if (formule != NULL)
00603     {
00604       try
00605         {
00606           value = evaluate(formule);
00607         }
00608       catch (Error * e)
00609         {
00610           value = e;
00611         }
00612     }
00613   else
00614     {
00615       value = NULL;
00616     }
00617   show_status = false;
00618   flushCalls();
00619 }
00620 
00621 Value * IisCell::lookup(Reference* var)
00622 {
00623   if (!var) return NULL;
00624   SpreadSheetTable * ss = (SpreadSheetTable*)table();
00625   IisCell * i = (IisCell*)(ss->item(var->row,var->column));
00626   if (!i) return new TextValue("");
00627   return i->fetch_value();
00628 }
00629 
00630 Value * IisCell::lookup(Range* var)
00631 {
00632   if (!var) return NULL;
00633   SpreadSheetTable * ss = (SpreadSheetTable*)table();
00634   int mx = var->from->column;
00635   int my = var->from->row;
00636   ValueArray * result = new ValueArray(var->to->column - var->from->column + 1,
00637                                        var->to->row    - var->from->row    + 1);
00638   for(int x = mx ; x <= var->to->column ; x++)
00639     for(int y = my ; y <= var->to->row ; y++)
00640       {
00641         Value * val;
00642         IisCell * i = (IisCell*)(ss->item(y,x));
00643         if (!i) val = new TextValue("");
00644         else val = i->fetch_value();
00645         if (!val) return NULL;
00646         result->set(x-mx,y-my,val);
00647       }
00648   return result;
00649 }
00650 
00651 Value *IisCell::evaluate(Value * what)
00652 {
00653   if (!what) throw new Error("Empty formule");
00654   if (typeid(*what)==typeid(TextValue))
00655     return what;
00656   if (typeid(*what)==typeid(ImageValue))
00657     return what;
00658   //  if (typeid(*what)==typeid(VarValue))
00659   //    return lookup((VarValue*)what);
00660   if (typeid(*what)==typeid(Reference))
00661     return lookup((Reference*)what);
00662   if (typeid(*what)==typeid(Range))
00663     return lookup((Range*)what);
00664   if (typeid(*what)==typeid(Expression))
00665     return apply((Expression*)what);
00666   assert(0);
00667   return NULL;
00668 }
00669 
00670 Value *IisCell::apply(Expression * app)
00671 {
00672   assert(app);
00673   // evaluate arguments
00674   vector<Value*> evaluated;
00675   for(int i = 0 ; i < app->argc() ; i++)
00676     evaluated.push_back(evaluate(app->argv(i)));
00677   // check operator
00678   return runtime.apply(app->op(),evaluated);
00679 }
00680 

Generated on Mon Jun 5 22:08:42 2006 for iis by  doxygen 1.4.6