00001 00002 // MathCore = a WYSIWYG equation editor + a powerful math engine // 00003 // Copyright (C) 2003 by Francesco Montorsi // 00004 // // 00005 // This library is free software; you can redistribute it and/or // 00006 // modify it under the terms of the GNU Lesser General Public // 00007 // License as published by the Free Software Foundation; either // 00008 // version 2.1 of the License, or (at your option) any later // 00009 // version. // 00010 // // 00011 // This library is distributed in the hope that it will be useful, // 00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of // 00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 00014 // GNU Lesser General Public License for more details. // 00015 // // 00016 // You should have received a copy of the GNU Lesser General Public // 00017 // License along with this program; if not, write to the Free // 00018 // Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, // 00019 // MA 02111-1307, USA. // 00020 // // 00021 // For any comment, suggestion or feature request, please contact // 00022 // the administrator of the project at frm@users.sourceforge.net // 00023 // // 00031 00032 00033 00034 // optimization for GCC compiler 00035 #ifdef __GNUG__ 00036 #pragma implementation "Element.cpp" 00037 #endif 00038 00039 // includes 00040 #include "mc/mcprec.h" 00041 #ifdef __BORLANDC__ 00042 #pragma hdrstop 00043 #endif 00044 00045 #ifndef mcPRECOMP 00046 #include <wx/dcscreen.h> 00047 00048 #include "mc/EmptyBox.h" 00049 #include "mc/Number.h" 00050 #include "mc/Bracket.h" 00051 #include "mc/Fraction.h" 00052 #include "mc/Function.h" 00053 #include "mc/Radical.h" 00054 #include "mc/Text.h" 00055 #include "mc/Symbol.h" 00056 #include "mc/Operator.h" 00057 #include "mc/AddSubOp.h" 00058 #include "mc/MultDivOp.h" 00059 #include "mc/Monomial.h" 00060 #include "mc/Parenthesis.h" 00061 #endif 00062 00063 00064 // setup customizable variables 00065 wxBrush *mcElementHelpers::sgui_pSelectionBrush = NULL; 00066 wxBrush *mcElementHelpers::sgui_pActivationBrush = NULL; 00067 mcStyleArray *mcElementHelpers::sgui_pDefaultStyles = NULL; 00068 int mcElementHelpers::sgui_nAdditionalActivationSpaceLeftRight = 2; 00069 mcElement *mcElementHelpers::sdata_pElem[]; 00070 00071 // global objects 00072 mcElement mcEmptyElement; 00073 00074 00075 00076 00077 00078 // ------------------------- 00079 // mcELEMENTHELPERS 00080 // ------------------------- 00081 00082 #ifdef __MCDEBUG__ 00083 00084 void mcElementHelpers::data_Check() const 00085 { 00086 //mcASSERT(m_nExpDepth >= 0, "Not valid"); 00087 mcASSERT(mdata_nProperties >= 0, wxT("Not valid")); 00088 mcASSERT(mdata_nID >= 0, wxT("Not valid")); 00089 00090 // now check childrens (recursive check)... 00091 for (int i=0,max=data_GetChildrenCount(); i < max; i++) { 00092 00093 const mcElement &tocheck = data_GetConstChild(i); 00094 00095 // be sure that this child has set this element as its parent 00096 /*mcASSERT(tocheck.GetParent() == this, 00097 wxT("One of our children is out of sync"));*/ 00098 00099 // recurse 00100 tocheck.data_Check(); 00101 } 00102 } 00103 00104 wxString mcElementHelpers::data_GetDebug(int indent, long flags) const 00105 { 00106 wxString str, tmp; 00107 00108 // get debug info from this element 00109 //tmp = data_Debug(); 00110 //tmp = Indent(tmp, indent); // indent it 00111 00112 // leave some spaces 00113 str += wxString(wxT(' '), indent); 00114 00115 // include some additional info if required 00116 if (flags & mcELEMENT_DBG_EXPDEPTH) 00117 str += wxString::Format(wxT("EXPDEPTH: %2d "), gui_GetExpDepth()); 00118 00119 if (flags & mcELEMENT_DBG_SELECTION && 00120 mcMathCore::Get()->isGUIEnabled()) 00121 str += wxString::Format(wxT("SELECTED: %s "), 00122 (gui_isSelected() ? wxT("YES") : wxT("NO "))); 00123 00124 if (flags & mcELEMENT_DBG_ID) 00125 str += wxString::Format(wxT("ID: %2d "), data_GetID()); 00126 00127 // add the string 00128 str += data_Debug(flags); 00129 str = wxIndent(str, indent); 00130 00131 // return the result 00132 return str; 00133 } 00134 00135 void mcElement::data_CheckContainer() const 00136 { 00137 // first of all, check if this is a valid container for the 00138 // current shared data 00139 if (data_GetRefData()) { 00140 mcElementHelpers *helpers = (mcElementHelpers *)data_GetRefData(); 00141 mcUNUSED(helpers); // just to avoid a stupid warning 00142 00143 // we cannot mix helpers of some type with elements of another 00144 // type: mcNumber cannot reference a mcBracketHelper class... 00145 mcASSERT(data_isValidContainerFor(helpers->data_GetType()), 00146 wxT("Invalid container (this is a ") + 00147 data_GetDebugName() + 00148 wxT(") for the given helper class (") + 00149 helpers->data_GetDebugName() + 00150 wxT("Helpers) !!")); 00151 } 00152 } 00153 00154 void mcElement::data_Check() const 00155 { 00156 // check if we contain valid data 00157 data_CheckContainer(); 00158 00159 // then, check the helper (if present) & its children 00160 if (chlp()) chlp()->data_Check(); 00161 } 00162 00163 #endif // __MCDEBUG__ 00164 00165 00166 mcElementHelpers::mcElementHelpers() 00167 { 00168 // this variable should be decided by derived classes 00169 //mdata_nType = mcET_INVALID; 00170 00171 // create the ID for this element 00172 mdata_nID = mcElementHelpers::data_GenID(); 00173 00174 // set up flags and member variables 00175 mdata_nProperties = mcEP_NOPROPERTIES; 00176 mgui_nExpDepth = 0; 00177 } 00178 00179 void mcElementHelpers::data_Init() 00180 { 00181 // init only required sections 00182 if (mcMathCore::Get()->isGUIEnabled()) gui_Init(); 00183 if (mcMathCore::Get()->isMathEnabled()) math_Init(); 00184 if (mcMathCore::Get()->isIOEnabled()) io_Init(); 00185 } 00186 00187 bool mcElementHelpers::io_Init(const wxString &str) 00188 { 00189 wxString err; 00190 int n; 00191 00192 // just check the string begins with the begin-char for this class 00193 if (!mcMathCore::Get()->isIOEnabled()) 00194 return FALSE; 00195 if (io_isBeginChar(str) == FALSE) 00196 return FALSE; 00197 return io_ImportInlinedExpr(str, &n, err); 00198 } 00199 00200 void mcElementHelpers::gui_OnRecalcSize(wxSize old) 00201 { 00202 // if there were no changes, do nothing... 00203 if (old == mgui_sz) 00204 return; 00205 00206 // if this object's size has changed, notify children 00207 // with the correct properties... 00208 //for (int i=0; i < data_GetChildrenCount(); i++) 00209 // data_GetChild(i).gui_OnParentSizeChange(); 00210 } 00211 00212 void mcElementHelpers::gui_SetStyleArray(const mcStyleArray *p) 00213 { 00214 // change the style for this class 00215 if (!mcMathCore::Get()->isGUIEnabled()) 00216 return; 00217 00218 mgui_pStyleArray = p; 00219 00220 // and its children... 00221 for (int i=0; i < data_GetChildrenCount(); i++) 00222 data_GetChild(i).gui_SetStyleArray(p); 00223 } 00224 00225 mcElement &mcElementHelpers::data_GetElemFromID(int id) const 00226 { 00227 // do not check for ourselves (since this is a CONST function) 00228 00229 // search among children 00230 for (int i=0; i < data_GetChildrenCount(); i++) { 00231 mcElement &p = data_GetConstChild(i).data_GetElemFromID(id); 00232 if (p != mcEmptyElement) 00233 return p; 00234 } 00235 00236 return mcEmptyElement; 00237 } 00238 00239 00240 void mcElementHelpers::data_AddRecursiveProperty(mcElementType t, int p, void *val1, void *val2) 00241 { 00242 if (data_GetType() == t) 00243 data_AddProperty(p, val1, val2); 00244 00245 // add this property to all the childrens... 00246 for (int i=0; i < data_GetChildrenCount(); i++) 00247 data_GetChild(i).data_AddRecursiveProperty(t, p, val1, val2); 00248 } 00249 00250 bool mcElementHelpers::data_hasRecursiveProperty(mcElementType t, int p) const 00251 { 00252 if (data_GetType() == t) 00253 return ((mdata_nProperties & p) != 0); 00254 00255 // check if at least one of the children has the property... 00256 for (int i=0; i < data_GetChildrenCount(); i++) 00257 if (data_GetConstChild(i).data_hasRecursiveProperty(t, p)) 00258 return TRUE; 00259 return FALSE; 00260 } 00261 00262 void mcElementHelpers::data_RemoveRecursiveProperty(mcElementType t, int p) 00263 { 00264 if (data_GetType() == t) 00265 mdata_nProperties &= ~p; 00266 00267 // remove this property from all the childrens... 00268 for (int i=0; i < data_GetChildrenCount(); i++) 00269 data_GetChild(i).data_RemoveRecursiveProperty(t, p); 00270 } 00271 00272 void mcElementHelpers::data_Update() 00273 { 00274 // nothing to do.... 00275 00276 // just update children 00277 for (int i=0; i < data_GetChildrenCount(); i++) 00278 data_GetChild(i).data_Update(); 00279 } 00280 00281 bool mcElementHelpers::data_AddChildrenTo(mcElement p) 00282 { 00283 if (p.data_GetType() != data_GetType()) 00284 return FALSE; 00285 00286 for (int i=0, max=data_GetChildrenCount(); i < max; i++) 00287 p.data_SetChild(i, data_GetChild(i)); 00288 return TRUE; // non container elements should have no child to add... 00289 } 00290 00291 int mcElementHelpers::data_GetChildIdx(const mcElement &p) const 00292 { 00293 for (int i=0,max=data_GetChildrenCount(); i < max; i++) 00294 if (data_GetConstChild(i) == p) 00295 return i; // found... 00296 00297 return -1; 00298 } 00299 00300 mcElement &mcElementHelpers::data_GetChild(int n) 00301 { return (mcElement &)data_GetConstChild(n);} 00302 00303 const mcElement &mcElementHelpers::data_GetConstChild(int) const 00304 { return mcEmptyElement;} 00305 00306 00307 00308 00309 00310 // ---------------------------------------- 00311 // mcELEMENT - various 00312 // ---------------------------------------- 00313 00314 mcElement::mcElement(const mcElementHelpers *helpers) 00315 { 00316 //mcLOG("mcElementHelpers::mcElement - wrapping"); 00317 data_SetRefData((mcObjectRefData *)helpers); 00318 00319 // increment the reference count since we've now wrapped the given class 00320 if (data_GetRefData()) 00321 data_GetRefData()->data_IncRefCount(); 00322 #ifndef mcENABLE_COW 00323 if (data_GetRefData()) 00324 data_MakePrivateCopy(); // no sharing allowed 00325 #endif 00326 } 00327 00328 mcElement::~mcElement() 00329 { 00330 #ifdef mcENABLE_COW_DEBUG 00331 if (data_GetRefData() && mcMathCore::isOk()) 00332 mcMathCore::Get()->m_nMaxSharing = 00333 mcMAX(mcMathCore::Get()->m_nMaxSharing, chlp()->data_GetRefCount()); 00334 #endif 00335 } 00336 00337 void mcElement::data_Ref(const mcObject &toclone) 00338 { 00339 mcObject::data_Ref(toclone); 00340 00341 // check this is a valid container for the just 00342 // referenced data.... we can do it here because 00343 // this function is called when the element 00344 // has been completely built and thus, if present, 00345 // the overloaded version of CheckContainer will be 00346 // called... 00347 #ifdef __MCDEBUG__ 00348 wxLogNull logNo; 00349 data_CheckContainer(); 00350 #endif 00351 } 00352 00353 void mcElementHelpers::Init() 00354 { 00355 for (int t=0; t < mcNUM_ELEMENT_TYPES; t++) 00356 sdata_pElem[t] = NULL; 00357 00358 // this will create an instance for each registered MathCore element 00359 data_UpdateElemArray(); 00360 } 00361 00362 void mcElementHelpers::Cleanup() 00363 { 00364 for (int t=0; t < mcNUM_ELEMENT_TYPES; t++) 00365 mcSAFE_DELETE(sdata_pElem[t]); 00366 } 00367 00368 void mcElementHelpers::data_UpdateElemArray() 00369 { 00370 // create one instance of each class; required by the 00371 // mcElementHelpers::isCharBeginChar & mcElementHelpers::isTagBeginTag 00372 // functions... 00373 for (int t=0; t < mcNUM_ELEMENT_TYPES; t++) { 00374 mcSAFE_DELETE(sdata_pElem[t]); 00375 00376 // if at least one of the mcElement sections is enabled, 00377 // build this element 00378 sdata_pElem[t] = new mcElement(data_NewElem((mcElementType)t)); 00379 } 00380 } 00381 00382 00383 #define mcDEFINE_ALLOCATOR(x) \ 00384 if (towrap) \ 00385 return x(towrap); \ 00386 else \ 00387 return x(); 00388 00389 #define mcDEFINE_SPECIAL_ALLOCATOR(x, param1) \ 00390 if (towrap) \ 00391 return x(towrap); \ 00392 else \ 00393 return x(param1); 00394 00395 mcElement mcElementHelpers::data_NewElem(mcElementType type, const mcElementHelpers *towrap) 00396 { 00397 //mcElement pnew = mcEmptyElement; 00398 00399 // create a new MathCore class; the following switch should contain 00400 // one case label for each value declared in mcElementType 00401 switch (type) { 00402 case mcET_NUMBER: 00403 mcDEFINE_ALLOCATOR(mcNumber) 00404 case mcET_PARENTHESIS: 00405 mcDEFINE_ALLOCATOR(mcParenthesis) 00406 case mcET_BRACKET: 00407 mcDEFINE_ALLOCATOR(mcBracket) 00408 case mcET_RADICAL: 00409 mcDEFINE_ALLOCATOR(mcRadical) 00410 case mcET_FRACTION: 00411 mcDEFINE_ALLOCATOR(mcFraction) 00412 case mcET_SYMBOL: 00413 mcDEFINE_ALLOCATOR(mcSymbol) 00414 case mcET_FUNCTION: 00415 mcDEFINE_ALLOCATOR(mcFunction) 00416 case mcET_TEXT: 00417 mcDEFINE_ALLOCATOR(mcText) 00418 case mcET_EMPTYBOX: 00419 mcDEFINE_ALLOCATOR(mcEmptyBox) 00420 case mcET_ADDOP: 00421 mcDEFINE_SPECIAL_ALLOCATOR(mcAddSubOp, mcET_ADDOP); 00422 case mcET_SUBOP: 00423 mcDEFINE_SPECIAL_ALLOCATOR(mcAddSubOp, mcET_SUBOP); 00424 case mcET_MULTOP: 00425 mcDEFINE_SPECIAL_ALLOCATOR(mcMultDivOp, mcET_MULTOP); 00426 case mcET_DIVOP: 00427 mcDEFINE_SPECIAL_ALLOCATOR(mcMultDivOp, mcET_DIVOP); 00428 case mcET_POLYNOMIAL: 00429 mcDEFINE_ALLOCATOR(mcPolynomial) 00430 case mcET_MONOMIAL: 00431 mcDEFINE_ALLOCATOR(mcMonomial) 00432 default: 00433 mcASSERT(0, wxT("The allocation of an undefined class was requested")); 00434 } 00435 00436 return NULL; 00437 } 00438 00439 // a macro used to avoid the repetion of specialized code in the 00440 // following functions 00441 #define SCAN_REGISTERED_CLASSES(fnc, params) \ 00442 /* for each MathCore registered element class */ \ 00443 for (int i=0; i < mcNUM_ELEMENT_TYPES; i++) { \ 00444 if (sdata_pElem[i]->fnc(params) != FALSE) { \ 00445 \ 00446 /* found the class we were searching for */ \ 00447 return (mcElementType)i; \ 00448 } \ 00449 } \ 00450 \ 00451 /* there is no class with that char as begin char */ \ 00452 return mcET_INVALID; 00453 00454 00455 00456 mcElementType mcElementHelpers::gui_isKeyBeginKey(const mcKey &key) { 00457 SCAN_REGISTERED_CLASSES(gui_isBeginKey, key); 00458 } 00459 00460 mcElementType mcElementHelpers::io_isCharBeginChar(const wxString &str) { 00461 SCAN_REGISTERED_CLASSES(io_isBeginChar, str); 00462 } 00463 00464 mcElementType mcElementHelpers::io_isTagBeginTag(const wxXml2Node &tag) { 00465 SCAN_REGISTERED_CLASSES(io_isBeginTag, tag); 00466 } 00467 00468 wxString mcElementHelpers::data_GetGlobalMemoryInfo() 00469 { 00470 wxString res; 00471 00472 for (int i=0; i < mcNUM_ELEMENT_TYPES; i++) { 00473 00474 res += wxString::Format(wxT("Class number #%d: %d\n"), 00475 i,//GetClassInfo()->GetClassName(), 00476 sdata_pElem[i]->data_GetMemoryInfo()); 00477 } 00478 00479 return res; 00480 } 00481 00482 int mcElementHelpers::data_GenID(bool bFirstMember) 00483 { 00484 // use both time and random generator to compute our ID 00485 static int l=0, r=0xFFFF; 00486 if (bFirstMember) 00487 return l++; 00488 return r++;//(rand()+wxGetLocalTime())/rand(); 00489 } 00490 00491 mcObjectRefData *mcElement::data_MakePrivateCopy() // implements COW 00492 { 00493 const mcElementHelpers *h = chlp(); 00494 00495 // if the mcElement-derived class we reference is owned only 00496 // by this mcElement, then we can just return it 00497 if (h->data_GetRefCount() == 1) 00498 return (mcElementHelpers *)h; 00499 00500 // we need to unshare: unreference the old data 00501 #ifdef mcENABLE_COW_DEEPDEBUG 00502 mcLOG(wxT("mcElementHelpers::data_MakePrivateCopy")); 00503 #endif 00504 data_UnRef(); 00505 00506 // set as reference data a clone of current referenced data 00507 data_SetRefData(h->data_Clone()); 00508 00509 // return our brand-new referenced data 00510 return (mcElementHelpers *)data_GetRefData(); 00511 } 00512 00513 void mcElementHelpers::gui_UpdateExpDepth() 00514 { 00515 // DO NOT UPDATE CHILDREN: 00516 // for (int i=0; i < data_GetChildrenCount(); i++) 00517 // data_GetChild(i).gui_UpdateExpDepth(); 00518 // default implementation does nothing because it doesn't know 00519 // anything about the relationships between this object and 00520 // its children... plus, the overloaded versions of this function 00521 // should have already updated (maybe calling the #gui_SetAsExpOf 00522 // or #gui_SetAtSameLevelOf functions) the expdepth of our 00523 // children; so, also UPDATE IS NOT NECESSARY ! 00524 00525 // we can just recalc our size: all the exponent updates 00526 // should have been already done !! 00527 gui_RecalcSize(); 00528 } 00529 00530 /* 00531 void mcElementHelpers::SetExpDepth(int n) 00532 { 00533 hlp()->m_nExpDepth = n; 00534 00535 // update derived class variables 00536 data_UpdateExpDepth(); 00537 00538 // size should be changed because the style is chosen 00539 // basing on exponent depth and, if exponent depth has 00540 // changed, also style has changed... 00541 // the following piece of code is used to avoid that 00542 // this function, called during the mcElement creation, 00543 // calls the pure virtual mcElement function #gui_RecalcSize()... 00544 // mcElement uses m_nType variable to check whether this 00545 // mcElement class has already been constructed.. so it's 00546 // essential to change the value of that variable as 00547 // the first thing in derived classes' constructors... 00548 if (data_GetType() != mcET_INVALID && mcMathCore::Get()->isGUIEnabled()) 00549 gui_RecalcSize(); 00550 } 00551 */ 00552 00553 00554 00555 00556 00557 00558 // ---------------------------------------- 00559 // mcELEMENTDATA 00560 // ---------------------------------------- 00561 00562 bool mcElementHelpers::data_isSameAs(const mcElementHelpers *p) const 00563 { 00564 bool b = TRUE; 00565 00566 // The ID must *not* be checked... 00567 // b &= (mdata_nID == p->mdata_nID); 00568 00569 // check basic data 00570 b &= (data_GetType() == p->data_GetType()); 00571 b &= (mdata_nProperties == p->mdata_nProperties); 00572 if (!b) return FALSE; 00573 00574 // check also the children, recursively 00575 for (int i=0; i < data_GetChildrenCount(); i++) 00576 if (!data_GetConstChild(i).data_isSameAs(p->data_GetConstChild(i))) 00577 return FALSE; 00578 00579 return TRUE; 00580 } 00581 00582 //bool mcElementHelpers::data_hasSameChildren(c 00583 00584 void mcElementHelpers::data_DeepCopy(const mcElementHelpers *p) 00585 { 00586 // types should already be the same (anyway, we could not copy them, since 00587 // they are not stored in mcElementHelpers) 00588 mcASSERT(data_GetType() == p->data_GetType(), wxT("Cannot perform deep copy !!")); 00589 00590 // copy basic data (except for ID !) 00591 mdata_nProperties = p->mdata_nProperties; 00592 00593 mgui_bSelected = p->mgui_bSelected; 00594 mgui_sz = p->mgui_sz; 00595 mgui_pStyleArray = p->mgui_pStyleArray; 00596 mgui_nExpDepth = p->mgui_nExpDepth; 00597 } 00598 00599 00600 00601 00602 00603 00604 // ---------------------------------------- 00605 // mcELEMENTGUI 00606 // ---------------------------------------- 00607 00608 void mcElementHelpers::gui_Init() 00609 { 00610 // mark the size as dirty: it will be recalculated as soon as 00611 // gui_GetSize() or directly gui_RecalcSize() is called 00612 mgui_sz.Set(-1, -1); 00613 00614 // as default, the element is not selected 00615 mgui_bSelected = FALSE; 00616 00617 // set the style array (if gui is enabled)... 00618 if (!mcMathCore::Get()->isGUIEnabled()) 00619 return; 00620 00621 // We cannot check our parent for a valid style since we are still 00622 // in mcElementGUI constructor (and accessing helper classes is 00623 // *not* allowed during construction), so we choose default styles, by now. 00624 // Anyway, our parent can call SyncStyleWithParent() once construction has been 00625 // completed... 00626 mcASSERT(sgui_pDefaultStyles != NULL, 00627 wxT("pDefaultStyles must always be a valid array !!!")); 00628 00629 // the style array *must* always be valid: mcElementGUI holds a static 00630 // array of styles which is the default for ALL elements... 00631 mgui_pStyleArray = sgui_pDefaultStyles; 00632 00633 gui_RecalcSize(); 00634 } 00635 /* 00636 bool mcElementHelpers::gui_SyncStyleWithParent() 00637 { 00638 const mcStyleArray *old = mgui_pStyleArray; 00639 00640 // climb the tree searching for a valid style... 00641 const mcElement &p = hlp()->GetConstParent(); 00642 if (p) { 00643 00644 // set this element's style to be the same of the parent's 00645 // NOTE: maybe that the caller of this constructor is 00646 // a mcElementData-derived class: in this case, 00647 // the parent GUI pointer could be not set yet... 00648 // but we need to set the style anyway, so we go 00649 // on searching for a valid GUI parent... 00650 while (p) { 00651 00652 if (p.gui()) { 00653 00654 // ok, we have found a valid pointer to a style array; 00655 // stop here; climbing further the tree is not useful... 00656 mgui_pStyleArray = p.gui_mgui_pStyleArray; 00657 if (mgui_pStyleArray) 00658 break; 00659 } 00660 00661 // go up one level... 00662 p = p.GetConstParent(); 00663 } 00664 } 00665 mcASSERT(0, wxT("")); 00666 00667 // if current style is different from the old one which was set 00668 // at the beginning of this function, then we managed to find a 00669 // valid style among parent's styles... 00670 return (old != mgui_pStyleArray); 00671 }*/ 00672 00673 mcElement mcElementHelpers::gui_GetSelection() const 00674 { 00675 // return a copy of the element which is assumed to be entirely selected: 00676 // this should work for all non-container classes 00677 return mcElement(this); 00678 } 00679 00680 mcElement &mcElementHelpers::gui_GetActiveElem(int x, int y, const wxPoint &pt) const 00681 { 00682 wxScreenDC tmp; 00683 00684 // use the gui_Draw() function to do the most important part of the work... 00685 int n = gui_Draw(tmp, x, y, mcDRW_USEPOINT, pt); 00686 00687 // and then, just convert the result of the gui_Draw() call to an element 00688 // pointer using the mcElementHelpers::data_GetElemFromID() 00689 if (n != mcDRW_NOACTIVEELEM && n != mcDRW_ONSELECTION) 00690 return data_GetElemFromID(n); 00691 return mcEmptyElement; 00692 } 00693 00694 void mcElementHelpers::gui_RecalcSize() 00695 { 00696 wxSize old = mgui_sz; 00697 gui_DoRecalcSize(); 00698 00699 // notify the element section... 00700 gui_OnRecalcSize(old); 00701 } 00702 00703 void mcElementHelpers::gui_DeepRecalcSize() 00704 { 00705 mcGUILOG(wxT("mcElementHelpers::gui_DeepRecalcSize [%s]"), mcTXTTHIS); 00706 00707 // recalc children sizes... 00708 for (int i=0; i < data_GetChildrenCount(); i++) 00709 data_GetChild(i).gui_DeepRecalcSize(); 00710 00711 // finally recalc also our size.... 00712 gui_RecalcSize(); 00713 } 00714 00715 void mcElementHelpers::gui_SelectAll() 00716 { 00717 // select our children 00718 for (int i=0; i < data_GetChildrenCount(); i++) 00719 data_GetChild(i).gui_SelectAll(); 00720 00721 // select ourselves 00722 gui_Select(); 00723 } 00724 00725 void mcElementHelpers::gui_DeSelect() 00726 { 00727 // deselect children 00728 for (int i=0; i < data_GetChildrenCount(); i++) 00729 data_GetChild(i).gui_DeSelect(); 00730 00731 // change also our selection state !!! 00732 mgui_bSelected = FALSE; 00733 } 00734 00735 bool mcElementHelpers::gui_isAllSelected() const 00736 { 00737 // are all our children selected ? 00738 for (int i=0; i < data_GetChildrenCount(); i++) 00739 if (!data_GetConstChild(i).gui_isAllSelected()) 00740 return FALSE; 00741 00742 // yes, they are... just check we are selected too... 00743 return gui_isSelected(); 00744 } 00745 00746 void mcElementHelpers::gui_DeleteSelection() 00747 { 00748 mcASSERT(!gui_isAllSelected(), 00749 wxT("This element is completely selected: it should be removed by the caller")); 00750 00751 // delete the selection inside our children 00752 for (int i=0; i < data_GetChildrenCount(); i++) 00753 data_GetChild(i).gui_DeleteSelection(); 00754 00755 // size should have changed... 00756 gui_RecalcSize(); 00757 } 00758 00759 00760 00761 00762 00763 // ---------------------------------------- 00764 // mcELEMENTGUI - styles functions 00765 // ---------------------------------------- 00766 00767 void mcElementHelpers::gui_SetAsExpOf(const mcElement &p) 00768 { 00769 if (p != mcEmptyElement) 00770 mgui_nExpDepth = p.gui_GetExpDepth()+1; 00771 00772 // update exponent depth of the children 00773 gui_UpdateExpDepth(); 00774 } 00775 00776 void mcElementHelpers::gui_SetAtSameLevelOf(const mcElement &p) 00777 { 00778 if (p != mcEmptyElement) 00779 mgui_nExpDepth = p.gui_GetExpDepth(); 00780 00781 // update exponent depth of the children 00782 gui_UpdateExpDepth(); 00783 } 00784 00785 void mcElementHelpers::gui_InitDefaultStyles() 00786 { 00787 mcGUILOG(wxT("mcElementHelpers::gui_InitDefaultStyles")); 00788 00789 // remove the previous existing styles (if present) 00790 gui_DeleteDefaultStyles(); 00791 00792 // create the style array 00793 sgui_pDefaultStyles = new mcStyleArray(); 00794 mcStyle *p = new mcStyle(); 00795 00796 // and init it 00797 for (int j=0; j < sgui_pDefaultStyles->GetCount(); j++) { 00798 00799 00800 // choose different facenames on different platforms 00801 // 00802 // VERY IMPORTANT: all the facenames should be chosen among 00803 // the facenames which are installed by default 00804 // on that plaftform. Otherwise a default (ugly) 00805 // font will be chosen... 00806 // 00807 // NOTE: these fontfaces are NOT the font faces you will see if 00808 // your program is using MathGUI library: it automatically 00809 // change them... 00810 // 00811 #if defined(__WXMSW__) 00812 00813 00814 wxString face1 = wxT("Lucida Sans Unicode"); //Trebuchet MS 00815 wxString face2 = wxT("Tahoma"); 00816 wxString face3 = wxT("Verdana"); 00817 const int size = 25; 00818 const int size2 = 20; 00819 /*wxString face1 = wxT("Times New Roman"); //Trebuchet MS 00820 wxString face2 = wxT("Tahoma"); 00821 wxString face3 = wxT("Verdana"); 00822 const int size = 12; 00823 const int size2 = 14;*/ 00824 00825 #else 00826 00827 wxString face1 = wxT("Lucida"); 00828 wxString face2 = wxT("Lucida"); 00829 wxString face3 = wxT("Lucida");//"Helvetica"; 00830 const int size = 25; 00831 const int size2 = 25; 00832 00833 #endif 00834 00835 // set up the font strings 00836 wxString num = wxString::Format(wxT("%d-%s-%d-%d-%d"), wxSWISS, 00837 face1.c_str(), size/(j+1), wxNORMAL, wxNORMAL); 00838 00839 wxString fnc = wxString::Format(wxT("%d-%s-%d-%d-%d"), wxSWISS, 00840 face2.c_str(), size/(j+1), wxNORMAL, wxITALIC); 00841 00842 wxString sym = wxString::Format(wxT("%d-%s-%d-%d-%d"), wxROMAN, 00843 face3.c_str(), size/(j+1), wxNORMAL, wxITALIC); 00844 00845 wxString text = wxString::Format(wxT("%d-%s-%d-%d-%d"), wxSWISS, 00846 face2.c_str(), size2/(j+1), wxBOLD, wxNORMAL); 00847 00848 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d"), wxSWISS, 00849 wxT("MathStudio Special"), size/(j+1), wxBOLD, wxNORMAL); 00850 00851 // sets the textsettings 00852 p->SetTextSettingsFor(mcET_NUMBER, mcINITWX(0xFF0000), *wxWHITE, num); 00853 p->SetTextSettingsFor(mcET_FUNCTION, mcINITWX(0x4080FF), *wxWHITE, fnc); 00854 p->SetTextSettingsFor(mcET_SYMBOL, mcINITWX(0x14A03C), *wxWHITE, sym); 00855 p->SetTextSettingsFor(mcET_TEXT, mcINITWX(0x329C7A), *wxWHITE, text); 00856 00857 // mcET_ADDOP and all other operators use the same style, so we can 00858 // avoid to call SetTextSettingsFor(mcET_SUBOP, .... 00859 p->SetTextSettingsFor(mcET_ADDOP, mcINITWX(0x0), *wxWHITE, num); 00860 00861 // these special settings are used to draw some special symbols... 00862 p->SetSpecialTextSettings(mcINITWX(0x0), *wxWHITE, spec); 00863 00864 // be sure we built correctly everything 00865 mcASSERT(p->Ok(), wxT("Problems in style creation")); 00866 00867 // store the style which has been just built 00868 sgui_pDefaultStyles->Set(p, j); // mcStyleArray::Set will *copy* the given style 00869 } 00870 00871 // now, we can delete the temporary class we created to make 00872 // styles initialization easier... 00873 delete p; 00874 } 00875 00876 double mcElementHelpers::gui_GetPointSize() 00877 { 00878 // pxperpoint contains the point size in pixels; this variable is 00879 // calculated only once, the first time it's required. 00880 static double pxperpoint = 0; 00881 00882 if (pxperpoint == 0) { 00883 00884 wxScreenDC dc; 00885 00886 // get the same result we would obtain calling GetDeviceCaps(), 00887 // using cross platform functions 00888 dc.SetMapMode(wxMM_POINTS); 00889 pxperpoint = dc.LogicalToDeviceYRel(72); 00890 } 00891 00892 return pxperpoint; 00893 } 00894 00895 void mcElementHelpers::gui_DeleteDefaultStyles() 00896 { 00897 // this is an useful macro... 00898 mcSAFE_DELETE(sgui_pDefaultStyles); 00899 } 00900 00901 double mcElementHelpers::gui_PointSize2Pixels(double pointsize) 00902 { 00903 // use the formula taken from Win32 Platform SDK under CreateFont help: 00904 // nHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72); 00905 // 00906 // here, we do not use the MulDiv & GetDeviceCaps functions because 00907 // they are Win32-specific; we need cross-platformness ... :-) 00908 return (pointsize * gui_GetPointSize()); 00909 } 00910 00911 double mcElementHelpers::gui_Pixels2PointSize(double pixels) 00912 { 00913 // see #gui_PointSize2Pixels for more info about the formula used here 00914 return (72.0 * pixels / gui_GetPointSize()); 00915 } 00916 00917 int mcElementHelpers::gui_GetBaseOffsety(int exph) const 00918 { 00919 // force the exponent to overlap the base (only on y axis) by half the 00920 // height of a character in exp style 00921 return (int)((exph/100.0)*50.); 00922 } 00923 00924 int mcElementHelpers::gui_GetSubscriptOffsety(int baseh) const 00925 { 00926 // force the exponent to overlap the base (only on y axis) by half the 00927 // height of a character in exp style 00928 return (int)((baseh/100.0)*60.); 00929 } 00930 00931 const mcStyle *mcElementHelpers::gui_GetStyleForThis() const { 00932 // just to avoid the following line repeated one million times... 00933 return mgui_pStyleArray->gui_GetStyleFor(this); 00934 } 00935 00936 const mcStyle *mcElementHelpers::gui_GetDefaultStyle(int n) { // static 00937 return sgui_pDefaultStyles->Get(n); 00938 } 00939 00940 const mcStyle *mcElementHelpers::gui_GetStyle(int n) const { 00941 // just to hide mgui_pStyleArray variable... 00942 return mgui_pStyleArray->Get(n); 00943 } 00944 /* 00945 void mcElementHelpers::gui_SetGlobalStyleArray(const mcStyleArray *p) { 00946 // just to hide mgui_pStyleArray variable... 00947 mgui_pStyleArray = p; 00948 }*/ 00949 00950 void mcElementHelpers::gui_SelectStyle(wxDC &hDC) const { 00951 const mcStyle *p = gui_GetStyleForThis(); 00952 00953 // in this function it doesn't make sense to use a 00954 // wxScreenDC to select the style; it would be destroyed 00955 // when this function returns and it could not be used 00956 mcASSERT(hDC.Ok(), wxT("Invalid DC")); 00957 00958 // select text fg/bk colors basing on element type 00959 p->gui_Select(hDC, this); 00960 } 00961 00962 int mcElementHelpers::gui_GetThickness(wxDC *dc) const { 00963 mcGUILOG(wxT("mcElementHelpers::gui_GetThickness")); 00964 bool bClean = FALSE; 00965 00966 if (dc == NULL) { 00967 dc = new wxScreenDC(); 00968 bClean = TRUE; 00969 } 00970 00971 // select the style for the given element and retrieve its attributes 00972 gui_SelectStyle(*dc); 00973 int nWeight = dc->GetFont().GetWeight(); 00974 int nHeight = dc->GetFont().GetPointSize(); 00975 00976 // clean up 00977 if (bClean) 00978 delete dc; 00979 00980 // empirical formula; it works good with "normal" fonts such as 00981 // Tahoma, Times New Roman... 00982 return (int)(nWeight/527.5 + nHeight/12.8); 00983 } 00984 00985 wxSize mcElementHelpers::gui_GetSizeOf(wxDC *hDC, const wxString &str) { 00986 mcGUILOG(wxT("mcElementHelpers::gui_GetSizeOf [%s]"), str.c_str()); 00987 bool bClean=FALSE; 00988 int w=0, h=0; 00989 00990 // if user doesn't provide a valid DC, use MathCore default one 00991 if (hDC == NULL) { 00992 hDC = new wxScreenDC(); 00993 bClean = TRUE; 00994 } 00995 00996 // if user provided an empty string, no problem 00997 if (str.IsEmpty()) return wxSize(0,0); 00998 00999 // be sure to get right size 01000 #ifdef __WXGTK20__ 01001 mcGUILOG(wxT("------------------ Using the buggy wxScreenDC function -----------------")); 01002 hDC->GetTextExtent(str, &w, &h);//, NULL, NULL, ¤t); 01003 #else 01004 hDC->GetTextExtent(str, &w, &h); 01005 #endif 01006 mcASSERT(w != 0 && h != 0, wxT("Cannot retrieve character size")); 01007 01008 // clean up 01009 if (bClean) 01010 delete hDC; 01011 01012 // and return 01013 return wxSize(w, h); 01014 } 01015 01016 int mcElementHelpers::gui_GetWidthOf(wxDC *h, const wxString &str) { 01017 return gui_GetSizeOf(h, str).GetWidth(); 01018 } 01019 01020 int mcElementHelpers::gui_GetHeightOf(wxDC *h, const wxString &str) { 01021 return gui_GetSizeOf(h, str).GetHeight(); 01022 } 01023 01024 wxSize mcElementHelpers::gui_GetSizeOfChar(wxDC *hDC, const mcElement &p) const { 01025 return wxSize(mcElementHelpers::gui_GetWidthOfChar(hDC, p), 01026 mcElementHelpers::gui_GetHeightOfChar(hDC, p)); 01027 } 01028 01029 int mcElementHelpers::gui_GetWidthOfChar(wxDC *hDC, const mcElement &p) const { 01030 mcGUILOG(wxT("mcElementHelpers::gui_GetWidthOfChar")); 01031 bool bClean=FALSE; 01032 int w; 01033 01034 // if user doesn't provide a valid DC, use MathCore default one 01035 if (hDC == NULL) { 01036 hDC = new wxScreenDC(); 01037 bClean = TRUE; 01038 } 01039 01040 // if the given pointer is NULL, use the font which is 01041 // already selected in the DC 01042 if (p != NULL) 01043 p.gui_SelectStyle(*hDC); 01044 01045 // be sure to get right size 01046 w = hDC->GetCharWidth(); 01047 mcASSERT(w != 0, wxT("Cannot retrieve character size")); 01048 01049 // clean up 01050 if (bClean) 01051 delete hDC; 01052 01053 // and return 01054 return w; 01055 } 01056 01057 int mcElementHelpers::gui_GetHeightOfChar(wxDC *hDC, const mcElement &p) const { 01058 mcGUILOG(wxT("mcElementHelpers::sgui_GetHeightOfChar")); 01059 bool bClean=FALSE; 01060 int h; 01061 01062 // if user doesn't provide a valid DC, use MathCore default one 01063 if (hDC == NULL) { 01064 hDC = new wxScreenDC(); 01065 bClean = TRUE; 01066 } 01067 01068 // if the given pointer is NULL, use the font which is 01069 // already selected in the DC 01070 if (p != NULL) 01071 p.gui_SelectStyle(*hDC); 01072 01073 // be sure to get right size 01074 h = hDC->GetCharHeight(); 01075 mcASSERT(h != 0, wxT("Cannot retrieve character size")); 01076 01077 // clean up 01078 if (bClean) 01079 delete hDC; 01080 01081 // and return 01082 return h; 01083 } 01084 01085 01086 01087 01088 01089 // ---------------------------------------- 01090 // mcELEMENTIO - miscellaneous 01091 // ---------------------------------------- 01092 01093 void mcElementHelpers::io_PostProcessChildren() 01094 { 01095 for (int i=0; i < data_GetChildrenCount(); i++) 01096 data_GetChild(i).hlp()->io_PostProcess(); 01097 } 01098 01099 01100 01101 01102 01103 // ---------------------------------------- 01104 // mcELEMENTMATH - basic functions 01105 // ---------------------------------------- 01106 01107 mcElement mcElement::operator^(const mcRealValue &r) const 01108 { mcElement ret(*this); ret.math_RaiseTo(mcPolynomial(r)); return ret; } 01109 01110 void mcElementHelpers::math_SimpleAdd(const mcElement &p, bool add) 01111 { 01112 mcMATHLOG(wxT("mcElementHelpers::math_SimpleAdd [%s] - simple adding with [%s]"), 01113 mcTXTTHIS, mcTXT(p)); 01114 mcBasicOpRes r = math_Add(p, NULL, add); 01115 mcASSERT(r == mcBOR_REMOVE_OPERAND, 01116 wxT("The operation could not be completely performed: ") 01117 wxT("the given operand has not been completely merged in *this")); 01118 mcUNUSED(r); // avoid dummy warnings 01119 } 01120 01121 void mcElementHelpers::math_SimpleMultiplyBy(const mcElement &p) 01122 { 01123 mcMATHLOG(wxT("mcElementHelpers::math_SimpleMultiplyBy [%s] - simple multiplying by [%s]"), 01124 mcTXTTHIS, mcTXT(p)); 01125 mcBasicOpRes r = math_MultiplyBy(p, NULL); 01126 mcASSERT(r == mcBOR_REMOVE_OPERAND, 01127 wxT("The operation could not be completely performed: ") 01128 wxT("the given operand has not been completely merged in *this")); 01129 mcUNUSED(r); // avoid dummy warnings 01130 } 01131 01132 void mcElementHelpers::math_SimpleDivideBy(const mcElement &p) 01133 { 01134 mcMATHLOG(wxT("mcElementHelpers::math_SimpleDivideBy [%s] - simple dividing by [%s]"), 01135 mcTXTTHIS, mcTXT(p)); 01136 mcBasicOpRes r = math_DivideBy(p, NULL); 01137 mcASSERT(r == mcBOR_REMOVE_OPERAND, 01138 wxT("The operation could not be completely performed: ") 01139 wxT("the given operand has not been completely merged in *this")); 01140 mcUNUSED(r); // avoid dummy warnings 01141 } 01142 01143 void mcElementHelpers::math_SimpleRaiseTo(const mcPolynomial &p) 01144 { 01145 mcMATHLOG(wxT("mcElementHelpers::math_SimpleRaiseTo [%s] - simple raising by [%s]"), 01146 mcTXTTHIS, mcTXT(p)); 01147 mcBasicOpRes r = math_RaiseTo(p); 01148 mcASSERT(r == mcBOR_REMOVE_OPERAND, 01149 wxT("The operation could not be completely performed: ") 01150 wxT("the given operand has not been completely merged in *this")); 01151 mcUNUSED(r); // avoid dummy warnings 01152 } 01153 01154 bool mcElementHelpers::math_isExpAllowed(mcElement p) 01155 { 01156 // check if the exponent level is under the limit 01157 //if (p.GetExpDepth()+1 < mcELEMENTMATH_MAX_EXPDEPTH) 01158 return TRUE; 01159 //return FALSE; 01160 } 01161 01162 bool mcElementHelpers::math_isSubAllowed(mcElement ) 01163 { 01164 // allow nested subscripts to a undefined depth... 01165 return TRUE; 01166 } 01167 01168 void mcElementHelpers::math_SetExp(const mcPolynomial &pol) 01169 { 01170 // just call recursively the math_SetExp on our children... 01171 // this behaviour is right for those elements not-derived 01172 // from mcExpElement and which does not own an exponent 01173 // and thus are obliged to set the exponents of their children 01174 for (int i=0; i<data_GetChildrenCount(); i++) 01175 data_GetChild(i).math_SetExp(pol); 01176 } 01177 01178 mcBasicOpRes mcElementHelpers::math_MakeReciprocal(mcElement *replacement) 01179 { 01180 mcFraction f; 01181 f.data_GetNum().math_WrapNumber(1); 01182 f.data_GetDen().math_WrapSimple(mcElement(this)); 01183 *replacement = f; 01184 01185 return mcBOR_REPLACE_BOTH; 01186 } 01187 01188 mcExpSimRes mcElementHelpers::math_MaxSimplify(long flags) 01189 { 01190 mcExpSimRes retflag = mcESR_NOTFINISHED; 01191 int cycles=0; 01192 01193 // call mcElementHelpers::math_Simplify(long flags) until everything is completely 01194 // simplified.... 01195 while (retflag != mcESR_DONE) { 01196 retflag = math_Simplify(flags, NULL); // NULL is just to compile but it's wrong 01197 cycles++; 01198 01199 if (cycles >= mcELEMENTMATH_MAX_PROCESS_CYCLES) // avoid to block the program 01200 return mcESR_NOTFINISHED; // cannot finish !!! 01201 } 01202 01203 return mcESR_DONE; 01204 } 01205 01206 bool mcElementHelpers::math_isMaxSimplified(long flags) const 01207 { 01208 mcExpSimRes retflag = mcESR_NOTFINISHED; 01209 int cycles=0; 01210 01211 // work on a backup copy 01212 mcElementHelpers *backup = data_Clone(); 01213 01214 // call mcElementHelpers::math_Simplify(long flags) until everything is completely 01215 // simplified.... 01216 while (retflag != mcESR_DONE) { 01217 retflag = backup->math_Simplify(flags, NULL); // NULL is just to compile but it's wrong 01218 cycles++; 01219 01220 if (cycles >= mcELEMENTMATH_MAX_PROCESS_CYCLES) // avoid to block the program 01221 break; 01222 } 01223 01224 // if the function returned mcESR_DONE at the first math_Simplify call, then 01225 // it should be completely simplified... 01226 return (cycles == 1); 01227 } 01228 01229 mcRealValue mcElementHelpers::math_GetTotalLenght() const 01230 { 01231 // to compute the lenght of this element we must take in count: 01232 // 1) the lenght of the children 01233 // 2) the lenght of this element itself (without its children) 01234 // using the #math_GetLenght() function 01235 mcRealValue l = math_GetLenght(); 01236 01237 for (int i=0; i < data_GetChildrenCount(); i++) 01238 l += data_GetConstChild(i).math_GetTotalLenght(); 01239 01240 return l; 01241 } 01242 01243 bool mcElementHelpers::math_isFinite() const 01244 { 01245 mcRealValue res = math_Evaluate(); 01246 if (res.math_isFinite()) 01247 return TRUE; 01248 return FALSE; 01249 } 01250 01251 bool mcElementHelpers::math_isValidMath() const 01252 { 01253 switch (data_GetType()) { 01254 case mcET_INVALID: 01255 case mcET_PARENTHESIS: 01256 case mcET_EMPTYBOX: 01257 /* case mcET_ADDOP: 01258 case mcET_SUBOP: 01259 case mcET_MULTOP: 01260 case mcET_DIVOP:*/ 01261 case mcET_TEXT: 01262 return FALSE; // a mcDecoration-derived class 01263 01264 default: 01265 break; 01266 } 01267 01268 // we are valid but we have to check our children... 01269 for (int i=0; i < data_GetChildrenCount(); i++) 01270 if (!data_GetConstChild(i).math_isValidMath()) 01271 return FALSE; 01272 01273 // ok; we are fully valid 01274 return TRUE; 01275 } 01276 01277 bool mcElementHelpers::math_isListedBeforeOf(const mcElement &p) const 01278 { 01279 if (math_GetOrderPos() < p.math_GetOrderPos()) 01280 return TRUE; 01281 return FALSE; 01282 } 01283 01284 mcMonomial mcElementHelpers::math_GetFactors() const 01285 { 01286 mcMonomial pol; 01287 pol.math_WrapSimple(this); 01288 01289 return pol; 01290 } 01291 01292 01293 01294 01295 // ------------------------------------------------------------------- 01296 // mcELEMENTMATH - search, comparison & replace functions 01297 // ------------------------------------------------------------------- 01298 01299 bool mcElementHelpers::math_Contains(const mcElement &p, long flags) const 01300 { return math_Find(0, p, flags) != mcEmptyElement; } 01301 01302 bool mcElementHelpers::math_ContainsNumber(const mcRealValue &num, 01303 const mcPolynomial &exp) const 01304 { 01305 mcNumber n(num); 01306 01307 // use the given polynomial as exponent, if required 01308 if (exp != mcEmptyPolynomial) 01309 n.data_SetExpSub(TRUE, exp); 01310 01311 return math_Contains(n); 01312 } 01313 01314 bool mcElementHelpers::math_ContainsSymbol(const mcSymbolProperties *prop, 01315 const mcPolynomial &exp) const 01316 { 01317 mcSymbol safe(prop->data_GetSafeLinkedSym()); 01318 mcASSERT(safe.data_isOk(), wxT("Invalid safe symbol ?")); 01319 01320 // use the given polynomial as exponent, if required 01321 if (exp != mcEmptyPolynomial) 01322 safe.data_SetExpSub(TRUE, exp); 01323 01324 return math_Contains(safe); 01325 } 01326 01327 bool mcElementHelpers::math_ContainsOneOf(const mcSymbolArray *arr) const 01328 { 01329 int max = arr->data_GetCount(); 01330 for (int i=0; i < max; i++) { 01331 01332 // this element will be used to contain the properties of 01333 // the symbol we are searching for in this element 01334 //mcSymbol sym; 01335 01336 // try to search all the symbols in the given array... 01337 const mcSymbolProperties *curr = arr->data_GetSymbol(i); 01338 01339 // BAD CAST FROM CONST TO NON-CONST !!!! 01340 //sym.data_LinkWith((mcSymbolProperties *)curr); 01341 01342 if (math_Contains(curr->data_GetSafeLinkedSym())) { 01343 01344 // ok; we contain one of the array's symbol 01345 return TRUE; 01346 } 01347 } 01348 01349 // we do not contain any of the symbol listed in arrUnknowns 01350 return FALSE; 01351 } 01352 01353 bool mcElementHelpers::math_ContainsUnknowns() const 01354 { return math_ContainsOneOf(&mcSymbol::arrUnknowns); } 01355 01356 bool mcElementHelpers::math_ContainsConstants() const 01357 { return math_ContainsOneOf(&mcSymbol::arrConstants); } 01358 01359 bool mcElementHelpers::math_ContainsParameters() const 01360 { return math_ContainsOneOf(&mcSymbol::arrParameters); } 01361 01362 bool mcElementHelpers::math_ContainsSymbols() const 01363 { 01364 // just check if contains any of the three supported type of symbols... 01365 return math_ContainsUnknowns() || 01366 math_ContainsConstants() || 01367 math_ContainsParameters(); 01368 } 01369 01370 bool mcElementHelpers::math_ContainsInvalidSymbols() const 01371 { 01372 // just scan for unregistered symbols... 01373 return math_ContainsOneOf(&mcSymbol::arrUnregistered); 01374 } 01375 01376 bool mcElementHelpers::math_isConstant() const 01377 { 01378 // since the only non-container elements are mcSymbols and mcNumbers 01379 // we just need to check them; since mcNumbers are allowed in any 01380 // case, we just have to check mcSymbols... 01381 return !math_ContainsParameters() && !math_ContainsUnknowns(); 01382 } 01383 01384 int mcElementHelpers::math_GetSymbolList(mcSymbol **arr, int size, const mcSymbol &tofind) const 01385 { 01386 int n = 0; 01387 //if (arr == NULL) return -math_GetCountOf(&tofind); 01388 01389 // start to search the first symbol... 01390 const mcElement p = math_Find(n, tofind); 01391 /* 01392 while (p) { 01393 01394 if (size <= n) return -math_GetCountOf(&tofind); 01395 01396 // store this entry 01397 arr[n] = (mcSymbol *)p; 01398 01399 // and search the next... 01400 p = math_Find(++n, tofind); 01401 }*/ 01402 01403 return n; 01404 } 01405 01406 int mcElementHelpers::math_GetSymbolList(mcSymbol **arr, int size, mcSymbolProperties &tofind) const 01407 { 01408 mcSymbol tmp; 01409 tmp.data_LinkWith(&tofind); 01410 01411 return math_GetSymbolList(arr, size, tmp); 01412 } 01413 01414 bool mcElementHelpers::math_CompareThisOnly(const mcElement &p, long) const 01415 { 01416 // we just need to compare types for simple elements 01417 // which are just containers of other elements (like 01418 // mcFraction) 01419 return (data_GetType() == p.data_GetType()); 01420 } 01421 01422 mcElement mcElementHelpers::math_Find(int n, const mcElement &p, long flags) const 01423 { 01424 mcASSERT(n >= 0, wxT("Invalid occurrence to find")); 01425 int k = n; 01426 01427 // search recursively 01428 return math_RecursiveFind(k, p, flags); 01429 } 01430 01431 mcElement mcElementHelpers::math_FindInChildrenOnly(int n, const mcElement &p, long flags) const 01432 { 01433 mcASSERT(n >= 0, wxT("Invalid occurrence to find")); 01434 int k = n; 01435 01436 // search recursively 01437 return math_RecursiveFindInChildrenOnly(k, p, flags); 01438 } 01439 01440 int mcElementHelpers::math_NonRecursiveFindInChildren(int n, 01441 const mcElement &p, long flags) const 01442 { 01443 mcASSERT(n >= 0, wxT("Invalid occurrence to find")); 01444 01445 // scan all the children 01446 for (int i=0,max=data_GetChildrenCount(); i<max; i++) { 01447 01448 if (data_GetConstChild(i).math_Compare(p, flags)) { 01449 01450 // did we find it ? 01451 if (--n == -1) 01452 return i; 01453 01454 // wait for next 01455 } 01456 } 01457 01458 // could not find anything 01459 return -1; 01460 } 01461 01462 mcElement mcElementHelpers::math_RecursiveFindInChildrenOnly(int &n, 01463 const mcElement &p, long flags) const 01464 { 01465 mcASSERT(n >= 0, wxT("Invalid occurrence to find")); 01466 01467 // scan all the children 01468 for (int i=0,max=data_GetChildrenCount(); i<max; i++) { 01469 01470 // and recursively search in each of them the element p 01471 const mcElement &res = data_GetConstChild(i).chlp()->math_RecursiveFind(n, p, flags); 01472 01473 // did we find it ? 01474 // NOTE: since we passed a reference of n, something like 01475 // if (res != mcEmptyElement) n--; 01476 // is not necessary 01477 if (n == -1) { 01478 01479 // if the given pointer is valid... 01480 if (res != mcEmptyElement) 01481 return res; 01482 01483 // if it's not it must be because the data_GetConstChild(i) 01484 // is the parent of the element to find and "flags" contain 01485 // the mcFIND_PARENT flag... 01486 mcASSERT(flags & mcFIND_PARENT, wxT("Something wrong")); 01487 01488 // NOTE: perform this check 01489 // 1) without the mcFIND_PARENT flag since we don't need it 01490 // 2) with "n+1" since n is now -1 and thus it's invalid. 01491 mcASSERT(math_NonRecursiveFindInChildren(n+1, p, flags & ~mcFIND_PARENT) != -1, 01492 wxT("Cannot find the element tofind in the children of *this...")); 01493 return mcElement(this); 01494 } 01495 } 01496 01497 return mcEmptyElement; 01498 } 01499 /* 01500 mcElement mcElementHelpers::math_FindParent(const mcElement &p, long flags) const 01501 { 01502 01503 }*/ 01504 01505 mcElement mcElementHelpers::math_RecursiveFind(int &n, 01506 const mcElement &p, long flags) const 01507 { 01508 mcASSERT(n >= 0, wxT("Invalid occurrence to find")); 01509 01510 // if we are of the same type of p and we have enough children, 01511 // the it maybe that we are identic to "p"... 01512 if (math_CompareThisOnly(p, flags) && 01513 data_GetChildrenCount() >= p.data_GetChildrenCount()) { 01514 01515 // to be sure that this element matches "p", we must 01516 // compare all p's children with our children in the right 01517 // order: if one or more child is different from the relative 01518 // p's child, then we can just try to search "p" in our children 01519 // recursively... 01520 for (int i=0,max=p.data_GetChildrenCount(); i<max; i++) 01521 if (!data_GetConstChild(i).math_Compare(p.data_GetConstChild(i), flags)) 01522 return math_RecursiveFindInChildrenOnly(n, p, flags); // we don't match p 01523 01524 // we are identic to p and this is true also for ours 01525 // first p.data_GetChildrenCount() children; we can thus 01526 // return ourselves as result or just decrement 'n' (it's a 01527 // reference, so the caller's "n" is automatically sync) 01528 // if this is not the occurrence we are searching for... 01529 if (--n == -1) { 01530 01531 // if we have to return not the element itself but its parent, 01532 // then we have to return with "n" set to -1 and with a NULL element: 01533 // math_RecursiveFindInChildrenOnly will then understand what 01534 // happened and will return the parent of "p" (which we do not hold 01535 // and thus we cannot return it directly). 01536 if (flags & mcFIND_PARENT) 01537 return mcEmptyElement; 01538 01539 return mcElement(this); 01540 } 01541 } 01542 01543 // *this does not match "p"; just search in our children 01544 return math_RecursiveFindInChildrenOnly(n, p, flags); 01545 } 01546 01547 int mcElementHelpers::math_GetCountOfChildrenOnly(const mcElement &p, long flags) const 01548 { 01549 int total = 0; 01550 01551 for (int i=0,max=data_GetChildrenCount(); i<max; i++) 01552 total += data_GetConstChild(i).math_GetCountOf(p, flags); 01553 01554 return total; 01555 } 01556 01557 int mcElementHelpers::math_GetCountOf(const mcElement &p, long flags) const 01558 { 01559 int total = 0; 01560 01561 if (math_CompareThisOnly(p, flags) && 01562 data_GetChildrenCount() >= p.data_GetChildrenCount()) { 01563 01564 for (int i=0,max=p.data_GetChildrenCount(); i<max; i++) 01565 if (!data_GetConstChild(i).math_Compare(p.data_GetConstChild(i), flags)) 01566 return math_GetCountOfChildrenOnly(p, flags); 01567 01568 total++; 01569 } 01570 01571 return total+math_GetCountOfChildrenOnly(p, flags); 01572 } 01573 01574 int mcElementHelpers::math_GetCountOf(const mcSymbolProperties *p, long flags) const 01575 { 01576 mcSymbol safe(p->data_GetSafeLinkedSym()); 01577 mcASSERT(safe.data_isOk(), wxT("Invalid safe symbol ?")); 01578 01579 return math_GetCountOf(safe, flags); 01580 } 01581 01582 int mcElementHelpers::math_GetCountOf(const mcSymbolArray *p, long flags) const 01583 { 01584 int total = 0; 01585 01586 // slow but easy to code... 01587 for (int i=0; i<p->data_GetCount(); i++) 01588 total += math_GetCountOf(p->data_GetSymbol(i), flags); 01589 01590 return total; 01591 } 01592 01593 int mcElementHelpers::math_GetCountOfSymbolsType(const mcSymbolArray *p, long flags) const 01594 { 01595 int total = 0; 01596 01597 // unlike math_GetCountOf(const mcSymbolArray *p, ...) we don't 01598 // want to take the total number of symbols contained: we want 01599 // to know how many *types* of symbols of the given array are 01600 // contained... 01601 for (int i=0; i<p->data_GetCount(); i++) 01602 total += (int)math_ContainsSymbol(p->data_GetSymbol(i)); 01603 01604 return total; 01605 } 01606 01607 int mcElementHelpers::math_Replace(const mcElement &tofind, int n, long flags, 01608 const mcElement &replacement, bool addchildren) 01609 { 01610 mcMATHLOG(wxT("mcElementHelpers::math_Replace [%s] - going to replace [%s] with [%s]"), 01611 mcTXTTHIS, mcTXT(tofind), mcTXT(replacement)); 01612 mcElement p(mcEmptyElement); 01613 int ntotal = 0; 01614 bool onlyone = (n != -1); 01615 int occ = (onlyone ? n : 0); 01616 01617 // we have to find the first unshared parent of the element tofind 01618 mcElement parent = math_Find(occ, tofind, flags | mcFIND_PARENT); 01619 if (parent == mcEmptyElement) return 0; 01620 const mcElement *currtofind = &parent; 01621 01622 mcElement currparent(mcEmptyElement); 01623 while (currtofind) { 01624 01625 // calling the math_Find() function, we ask "parent" to share 01626 // the same reference data of the actually found element... 01627 currparent = math_Find(0, *currtofind, flags | mcFIND_PARENT); 01628 if (currparent == mcEmptyElement) break; 01629 01630 // ... thus, if that element is shared only by an element, then 01631 // it should have a reference count == 2 01632 if (currparent.data_GetRefData()->data_GetRefCount() > 2) 01633 currtofind = &currparent; 01634 else 01635 currtofind = NULL; 01636 } 01637 01638 // "currparent" is not shared by anyone... 01639 /*while (currparent != parent) { 01640 currparent.math_Find( 01641 } 01642 01643 // if we have to replace all the occurrences, start from the first 01644 int occ = (onlyone ? n : 0); 01645 01646 // first of all, find the element 01647 mcElement p = math_Find(occ, tofind, flags); 01648 01649 mcElement &parent = p; 01650 while (parent 01651 mcElement parent = math_Find(occ, tofind, flags | mcFIND_PARENT); 01652 mcASSERT(parent != mcEmptyElement, 01653 wxT("Cannot replace an element which does not have a parent")); 01654 while (parent != mcEmptyElement) { 01655 01656 // now search in the parent we've just found, the element to find... 01657 int idx = parent.math_NonRecursiveFindInChildren(occ, tofind, flags); 01658 mcASSERT(idx != -1, wxT("We've found an invalid parent !") 01659 wxT("The parent does not have a pointer to its child ?")); 01660 p = parent.data_GetConstChild(idx); 01661 01662 mcMATHLOG(wxT("mcElementHelpers::math_Replace - the parent of [%s] is [%s]"), 01663 mcTXT(p), mcTXT(parent)); 01664 01665 // if we want to add the children of "p" to the replacement... 01666 mcElement rep(replacement); 01667 if (addchildren) { 01668 01669 // create a new instance of the replacement 01670 // with the children attached... 01671 mcElement tmp(replacement); 01672 bool b = p.data_AddChildrenTo(tmp); 01673 01674 // if we've been called with addchilren = TRUE, then this 01675 // operation should always work; otherwise, some elements 01676 // (the children of p) would be lost... 01677 mcASSERT(b, wxT("Cannot add children... check function call")); 01678 01679 // don't use normal replacement for this replace operation; 01680 // use the replacement+children 01681 rep = tmp; 01682 } 01683 01684 // then replace it (data_SetChild will make a copy) 01685 parent.data_SetChild(idx, rep); 01686 mcMATHLOG(wxT("mcElementHelpers::math_Replace - replaced occ #%d: [%s]"), 01687 ntotal, mcTXT(parent)); 01688 01689 // another substitution has been done... 01690 ntotal++; 01691 if (onlyone) break; 01692 01693 // find next first occurrence of TOFIND... 01694 // we'll search the first occurrence since the one we've just 01695 // found has been replaced and thus does not exist anymore. 01696 parent = math_Find(0, tofind, flags | mcFIND_PARENT); 01697 01698 }*/ 01699 01700 return ntotal; 01701 } 01702 01703 01704 // some wrappers which must be implemented in the source file... 01705 01706 mcMonomial mcElement::math_GetLCM(const mcElement &p) const 01707 { return hlp()->math_GetLCM(p); } 01708 01709 mcMonomial mcElement::math_GetGCD(const mcElement &p) const 01710 { return hlp()->math_GetGCD(p); } 01711 01712 mcMonomial mcElement::math_GetFactors() const 01713 { return hlp()->math_GetFactors(); } 01714 01715 01716 01717 01718 mcRealValue mcElementHelpers::math_EvaluateAt(const mcSymbolProperties *sym, 01719 const mcRealValue &symvalue) const 01720 { 01721 mcMATHLOG(wxT("mcElementHelpers::math_EvaluateAt [%s] - evaluating at [%s]=%s"), 01722 mcTXTTHIS, mcTXTP(sym), mcTXTV(symvalue)); 01723 01724 #if 0 01725 mcSymbol symtorep; 01726 symtorep.data_LinkWith(&sym); 01727 mcNumber replacement; 01728 replacement.math_Set(symvalue); 01729 01730 // or we should move sym to constants temporarily ? 01731 01732 mcMathMng *clone = this->Clone(); 01733 clone.math_Replace(&symtorep, -1, &replacement, TRUE); 01734 mcRealValue res = clone.math_Evaluate(); 01735 delete clone; 01736 #endif 01737 01738 // FIXME: thread unsafe ??? 01739 /* 01740 int entry = -1; 01741 const mcSymbolProperties *sym = psym; 01742 01743 // find the array containing the given symbol (should be arrParameters 01744 // or arrUnknowns)... 01745 mcSymbolArray *oldarr = mcSymbol::math_FindSymbol(mcSYMFIND_MATCH_ALL, *sym, 0, &entry); 01746 if (oldarr == NULL || entry == mcSYM_NOTFOUND) return *mcRealValue::pNAN; 01747 01748 // save its old value 01749 mcRealValue old = sym->m_fValue; 01750 01751 // temporary move it to mcSymbol::arrConstants 01752 mcSymbolProperties *newsym = oldarr->data_MoveSymbols(entry, &mcSymbol::arrConstants); 01753 if (newsym == NULL) return *mcRealValue::pNAN; 01754 01755 // and sets a new value 01756 newsym->m_fValue = symvalue; 01757 01758 // evaluate with the symbol's new value 01759 mcRealValue res = math_Evaluate(); 01760 01761 // now, undo what we did 01762 newsym->m_fValue = old; 01763 entry = mcSymbol::arrConstants.data_FindSymbol(mcSYMFIND_MATCH_ALL, *newsym); 01764 psym = mcSymbol::arrConstants.data_MoveSymbols(entry, oldarr); 01765 */ 01766 // NOTE: this should not be done, generally; however, here we can do 01767 // it because we are going to modify the given mcSymbolProperties 01768 // but we are also going to undo our changes... :-) 01769 mcSymbolProperties *psym = (mcSymbolProperties *)sym; 01770 01771 // temporary set the "evaluation mode" 01772 psym->m_bEvaluating = TRUE; 01773 psym->m_fValue = symvalue; 01774 01775 // evaluate ourselves 01776 mcRealValue res = math_Evaluate(); 01777 01778 // then revert everything to normal mode 01779 psym->m_bEvaluating = FALSE; 01780 psym->m_fValue = *mcRealValue::pNAN; 01781 01782 mcMATHLOG(wxT("mcElementHelpers::math_EvaluateAt - result is %s"), mcTXTV(res)); 01783 return res; 01784 }
[ Top ] |