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 "ExpContainer.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 "mc/Element.h" 00047 #include "mc/ExpContainer.h" 00048 #endif 00049 00050 00051 00052 mcIMPLEMENT_ABSTRACT_CLASS(mcExpContainer, mcExpElement); 00053 00054 00055 00056 // ---------------------------------------- 00057 // mcExpContainerGUI 00058 // ---------------------------------------- 00059 00060 bool mcExpContainerHelpers::gui_isBaseEndKey(const mcKey &ev) const 00061 { 00062 // using cursor position, decide if the given character is okay 00063 switch (mgui_nCursorPos) { 00064 case mcEXPCONTAINER_LEFTMOST: 00065 case mcEXPCONTAINER_RIGHTMOST: 00066 if (mcMathCore::Get()->MatchEditKeys(ev)) 00067 return FALSE; 00068 return TRUE; 00069 } 00070 00071 // mgui_nCursorPos must be mcEXPCONTAINER_INSIDEEXPR 00072 mcASSERT(mgui_nCursorPos == mcEXPCONTAINER_INSIDEEXPR, wxT("Invalid cursor position")); 00073 return FALSE; 00074 } 00075 00076 void mcExpContainerHelpers::gui_EditBase() 00077 { 00078 // set cursor on the base 00079 mgui_nCursorPos = mcEXPCONTAINER_INSIDEEXPR; 00080 mgui_nCursorLoc = mcECL_INSIDEBASE; 00081 } 00082 00083 void mcExpContainerHelpers::gui_OnBaseSelect(wxDC &dc, wxRect &rc) 00084 { 00085 // be sure that the selection rectangle is not inside the left 00086 // or right bracket 00087 wxPoint orig(gui_GetContentOffsetX(), gui_GetContentOffsetY()); 00088 wxRect content(orig, data_GetContent().gui_GetSize()); 00089 00090 // the parent has got an extended selection; cannot select only a part of 00091 // the bracket.... 00092 /* if (hlp()->GetParent().data_GetType() == mcET_POLYNOMIAL) { 00093 if (((mcElementArray *)(hlp()->GetParent())).gui_GetSelElemCount() > 0) { 00094 gui_SelectAll(); 00095 return; 00096 } 00097 }*/ 00098 //mcASSERT(0, ""); 00099 00100 // just recall the mcPolynomial's gui_OnSelect function: it will find the 00101 // element whose bounding box intersects with the selection rectangle 00102 // and it will select it; ignore the selection if it intersects the 00103 // brackets only. 00104 if (content.Intersects(rc)) { 00105 00106 rc.Offset(-content.x, -content.y); 00107 data_GetContent().gui_OnSelect(dc, rc); 00108 } 00109 00110 // if our contents are selected, we are too 00111 if (data_GetContent().gui_isSelected()) 00112 gui_Select(); 00113 } 00114 00115 int mcExpContainerHelpers::gui_DrawBase(wxDC &dc, int x, int y, long flags, 00116 const wxPoint &pt) const 00117 { 00118 wxPoint org(x+gui_GetContentOffsetX(), y+gui_GetContentOffsetY()), mypt=pt; 00119 wxRect rc(org, data_GetContent().gui_GetSize()); 00120 bool ret = FALSE; 00121 int r; 00122 00123 if (flags & mcDRW_USEPOINT) { 00124 00125 if (rc.Inside(pt)) { 00126 00127 // the mouse cursor is inside the math content: we must return the ID 00128 // of the active element contained inside it, not our ID... 00129 ret = TRUE; 00130 00131 } else { 00132 00133 // the cursor is not inside the contents: it's useless to tell 00134 // them to check the point coords we give them... 00135 flags &= ~mcDRW_USEPOINT; 00136 } 00137 } 00138 00139 // should we allot to the contents to draw itself as entirely 00140 // selected, if it is ? 00141 flags &= ~mcDRW_ALLOW_TOTAL_SELECTION; 00142 if (!data_GetContent().gui_isAllSelected()) 00143 flags |= mcDRW_ALLOW_TOTAL_SELECTION; 00144 00145 // draw the content 00146 r = data_GetContent().gui_Draw(dc, org.x, org.y, flags, mypt); 00147 00148 // draw the container graphic 00149 gui_DrawContainer(dc, x, y, flags, pt); 00150 00151 // choose which ID must be returned 00152 return (ret == TRUE) ? r : data_GetID(); 00153 } 00154 00155 mcInputRes mcExpContainerHelpers::gui_BaseInput(const mcKey &key, mcElement *pnew) 00156 { 00157 // discard the begin character used to create this element 00158 if (gui_isBeginKey(key) != FALSE && 00159 !data_hasProperty(mcEP_INITIALIZED)) { 00160 00161 // create an empty box in the contained expression 00162 data_GetContent().gui_AddNewEmptyMonomial(); 00163 00164 // this element is now ready for input 00165 data_AddProperty(mcEP_INITIALIZED); 00166 gui_RecalcSize(); 00167 00168 return mcIR_OKAY; 00169 } 00170 00171 // handle the EDITEXP and EDITSUBSCRIPT keypresses, if the cursor 00172 // is not placed inside the contained expression... 00173 if (mgui_nCursorPos == mcEXPCONTAINER_LEFTMOST || 00174 mgui_nCursorPos == mcEXPCONTAINER_RIGHTMOST) { 00175 00176 if (gui_HandleSubExpEditKeys(key) == mcIR_OKAY) 00177 return mcIR_OKAY; 00178 } 00179 00180 switch (mgui_nCursorPos) { 00181 case mcEXPCONTAINER_LEFTMOST: 00182 00183 if (mcMathCore::Get()->m_pDeleteKey->MatchKey(key)) { 00184 00185 // delete previous elements 00186 return mcIR_DELETE_PREVIOUS; 00187 00188 } else if (mcMathCore::Get()->m_pCancelKey->MatchKey(key)) { 00189 00190 // delete this class 00191 return mcIR_DELETE_THIS; 00192 } 00193 00194 mcMathCore::Get()->SyntaxError(wxT("Cannot type here")); 00195 break; 00196 00197 case mcEXPCONTAINER_RIGHTMOST: 00198 00199 if (mcMathCore::Get()->m_pDeleteKey->MatchKey(key)) { 00200 00201 // delete this class 00202 return mcIR_DELETE_THIS; 00203 00204 } else if (mcMathCore::Get()->m_pCancelKey->MatchKey(key)) { 00205 00206 // delete next elements 00207 return mcIR_DELETE_NEXT; 00208 } 00209 00210 mcMathCore::Get()->SyntaxError(wxT("Cannot type here")); 00211 break; 00212 00213 case mcEXPCONTAINER_INSIDEEXPR: 00214 00215 // let the contained expression process the input 00216 mcInputRes r = data_GetContent().gui_Input(key, pnew); 00217 return gui_HandleContentInput(r, pnew); 00218 } 00219 00220 return mcIR_OKAY; 00221 } 00222 00223 mcInsertRes mcExpContainerHelpers::gui_BaseInsert(const mcElement &toinsert, mcElement *) 00224 { 00225 mcASSERT(mgui_nCursorPos != mcEXPCONTAINER_LEFTMOST && 00226 mgui_nCursorPos != mcEXPCONTAINER_RIGHTMOST, 00227 wxT("Invalid position")); 00228 00229 return data_GetContent().gui_Insert(toinsert, NULL); 00230 } 00231 00232 mcInputRes mcExpContainerHelpers::gui_HandleContentInput(mcInputRes r, mcElement *) 00233 { 00234 switch (r) { 00235 case mcIR_OKAY: 00236 break; 00237 00238 case mcIR_DELETE_THIS: 00239 case mcIR_DELETE_PREVIOUS: 00240 case mcIR_DELETE_NEXT: 00241 return mcIR_DELETE_THIS; 00242 00243 default: 00244 mcASSERT(0, wxT("Unhandled return flag")); 00245 } 00246 00247 // size should have changed 00248 gui_RecalcSize(); 00249 return mcIR_OKAY; 00250 } 00251 00252 mcMoveCursorRes mcExpContainerHelpers::gui_BaseMoveCursor(mcMoveCursorFlag flag, long modifiers) 00253 { 00254 mcMoveCursorRes result; 00255 00256 // handle up/down flags for both the cursor positions which 00257 // are completely under our control 00258 if (mgui_nCursorPos == mcEXPCONTAINER_LEFTMOST || 00259 mgui_nCursorPos == mcEXPCONTAINER_RIGHTMOST) { 00260 00261 if (flag == mcMCF_UP) 00262 return mcMCR_SETFOCUS_ABOVE; 00263 if (flag == mcMCF_DOWN) 00264 return mcMCR_SETFOCUS_BELOW; 00265 } 00266 00267 switch (mgui_nCursorPos) { 00268 case mcEXPCONTAINER_LEFTMOST: 00269 mcASSERT(gui_isLeftPosEnabled(), wxT("Cursor position is wrong")); 00270 00271 // cursor position (cursor=|): 00272 // ...+ | (....)+... 00273 if (flag == mcMCF_LEFT) { 00274 00275 // there is nothing on the left of this position, 00276 // which belongs to the element... 00277 return mcMCR_SETFOCUS_PREVIOUS; 00278 00279 } else if (flag == mcMCF_RIGHT) { 00280 00281 // set the cursor on the leftmost position inside the 00282 // content 00283 mgui_nCursorPos = mcEXPCONTAINER_INSIDEEXPR; 00284 data_GetContent().gui_SetCursorPos(mcCP_BEGIN); 00285 } 00286 break; 00287 00288 case mcEXPCONTAINER_INSIDEEXPR: 00289 // cursor position (cursor=|): 00290 // ...+(... | ...)+... 00291 00292 // drop request to the expression 00293 result = data_GetContent().gui_MoveCursor(flag, modifiers); 00294 00295 // check if cursor is still inside the expression 00296 if (result == mcMCR_SETFOCUS_PREVIOUS) { 00297 if (gui_isLeftPosEnabled()) 00298 mgui_nCursorPos = mcEXPCONTAINER_LEFTMOST; 00299 else 00300 return mcMCR_SETFOCUS_PREVIOUS; 00301 00302 } else if (result == mcMCR_SETFOCUS_NEXT) { 00303 if (gui_isRightPosEnabled()) 00304 mgui_nCursorPos = mcEXPCONTAINER_RIGHTMOST; 00305 else 00306 return mcMCR_SETFOCUS_NEXT; 00307 00308 } else if (result == mcMCR_SETFOCUS_BELOW || result == mcMCR_SETFOCUS_ABOVE) { 00309 00310 // return the setfocus request to the caller 00311 return result; 00312 } 00313 break; 00314 00315 case mcEXPCONTAINER_RIGHTMOST: 00316 mcASSERT(gui_isRightPosEnabled(), wxT("Cursor position is wrong")); 00317 00318 // cursor position (cursor=|): 00319 // ...+(....) | +... 00320 if (flag == mcMCF_RIGHT) { 00321 00322 // there is nothing beyond this cursor position... 00323 return mcMCR_SETFOCUS_NEXT; 00324 00325 } else if (flag == mcMCF_LEFT) { 00326 00327 // set the cursor on the rightmost position inside 00328 // the content 00329 mgui_nCursorPos = mcEXPCONTAINER_INSIDEEXPR; 00330 data_GetContent().gui_SetCursorPos(mcCP_END); 00331 } 00332 break; 00333 00334 default: 00335 mcASSERT(0, wxT("Unhandled cursor position")); 00336 } 00337 00338 return mcMCR_OKAY; 00339 } 00340 00341 int mcExpContainerHelpers::gui_BaseMoveCursorUsingPoint(wxDC &hDC, const wxPoint &pt) 00342 { 00343 wxRect rc(wxPoint(0, 0), data_GetContent().gui_GetSize()); 00344 wxPoint mypt(pt); 00345 00346 // user should have clicked on the content 00347 if (rc.Inside(mypt)) { 00348 00349 mypt.x -= gui_GetContentOffsetX(); 00350 mypt.y -= gui_GetContentOffsetY(); 00351 00352 // ask the contained polynomial to set the cursor 00353 int r = data_GetContent().gui_MoveCursorUsingPoint(hDC, mypt); 00354 if (r == mcMCR_CANNOT_SETFOCUS) 00355 return mcMCR_CANNOT_SETFOCUS; 00356 00357 // ok, now the cursor is inside content 00358 mgui_nCursorPos = mcEXPCONTAINER_INSIDEEXPR; 00359 return mcMCR_OKAY; 00360 } 00361 00362 // do we have to set cursor on the container symbols ? 00363 if (mypt.x < gui_GetContentOffsetX() && gui_isLeftPosEnabled()) { 00364 mgui_nCursorPos = mcEXPCONTAINER_LEFTMOST; 00365 return mcMCR_OKAY; 00366 } 00367 if (mypt.x > gui_GetContentOffsetX()+rc.GetWidth() && gui_isRightPosEnabled()) { 00368 mgui_nCursorPos = mcEXPCONTAINER_RIGHTMOST; 00369 return mcMCR_OKAY; 00370 } 00371 00372 return mcMCR_CANNOT_SETFOCUS; 00373 } 00374 00375 int mcExpContainerHelpers::gui_GetBaseRelCursorPos(wxDC &hDC, wxPoint *pt) const 00376 { 00377 int n; 00378 00379 switch (mgui_nCursorPos) { 00380 case mcEXPCONTAINER_LEFTMOST: 00381 pt->x = 0; 00382 pt->y = 0; 00383 00384 // use global obj height as cursor height 00385 return gui_GetBaseSize().GetHeight(); 00386 00387 case mcEXPCONTAINER_INSIDEEXPR: 00388 n = data_GetContent().gui_GetRelCursorPos(hDC, pt); 00389 pt->x += gui_GetContentOffsetX(); 00390 pt->y += gui_GetContentOffsetY(); 00391 00392 // use content height as cursor height 00393 return n; 00394 00395 case mcEXPCONTAINER_RIGHTMOST: 00396 pt->x = gui_GetBaseSize().GetWidth(); 00397 pt->y = 0; 00398 00399 // use global obj height as cursor height 00400 return gui_GetBaseSize().GetHeight(); 00401 } 00402 00403 // when cursor is inside exponent, mcExpElement functions 00404 // handle the gui_GetRelCursorPos function call 00405 mcASSERT(0, wxT("Unhandled cursor position")); 00406 00407 // just to make compiler happy 00408 return 0; 00409 } 00410 00411 void mcExpContainerHelpers::gui_GetBaseCursorPos(mcCursorPos &cp) const 00412 { 00413 if (mgui_nCursorPos == mcEXPCONTAINER_LEFTMOST) 00414 cp.gui_Push(mcCP_BEGIN); 00415 else if (mgui_nCursorPos == mcEXPCONTAINER_RIGHTMOST) 00416 cp.gui_Push(mcCP_END); 00417 else 00418 cp.gui_Push(mgui_nCursorPos); 00419 } 00420 00421 void mcExpContainerHelpers::gui_SetBaseCursorPos(const mcCursorPos &n) 00422 { 00423 if (n.isBegin()) { 00424 if (gui_isLeftPosEnabled()) 00425 mgui_nCursorPos = mcEXPCONTAINER_LEFTMOST; 00426 else { 00427 mgui_nCursorPos = mcEXPCONTAINER_INSIDEEXPR; 00428 data_GetContent().gui_SetCursorPos(mcCP_BEGIN); 00429 } 00430 } 00431 00432 if (n.isEnd()) { 00433 if (gui_isRightPosEnabled()) 00434 mgui_nCursorPos = mcEXPCONTAINER_RIGHTMOST; 00435 else { 00436 mgui_nCursorPos = mcEXPCONTAINER_INSIDEEXPR; 00437 data_GetContent().gui_SetCursorPos(mcCP_END); 00438 } 00439 } 00440 } 00441 00442 int mcExpContainerHelpers::gui_GetYAnchor() const 00443 { 00444 return gui_GetContentOffsetY() + gui_GetBaseOffsety() + 00445 data_GetContent().gui_GetYAnchor(); 00446 } 00447 00448 00449 00450 00451 // ---------------------------------------- 00452 // mcEXPCONTAINERIO 00453 // ---------------------------------------- 00454 00455 wxXml2Node mcExpContainerHelpers::io_GetBaseMathML(bool bGetPresentation) const 00456 { 00457 wxXml2Node maintag(wxXML_ELEMENT_NODE, wxXml2EmptyDoc, io_GetMathMLPresentationTag()); 00458 wxXml2Node child = data_GetContent().io_GetMathML(bGetPresentation); 00459 maintag.AddChild(child); 00460 00461 return maintag; 00462 } 00463 00464 bool mcExpContainerHelpers::io_ImportPresentationMathML(wxXml2Node tag, wxString &pErr) 00465 { 00466 mcASSERT(tag.GetName() == io_GetMathMLPresentationTag(), 00467 wxT("Error in mcExpContainerHelpers::io_isBeginTag")); 00468 00469 // the child of this node should be imported by m_eContent 00470 wxXml2Node child = tag.GetChildren(); 00471 if (child.GetNext() != wxXml2EmptyNode) { 00472 00473 // this tag contains more than one child; pack them in an MROW 00474 child.Encapsulate(wxT("mrow")); 00475 } 00476 00477 return data_GetContent().io_ImportPresentationMathML(child, pErr); 00478 } 00479 00480 00481 00482 00483 00484 // ---------------------------------------- 00485 // mcEXPCONTAINERMATH 00486 // ---------------------------------------- 00487 00488 bool mcExpContainerHelpers::math_CompareThisOnly(const mcElement &p, long flags) const 00489 { 00490 if (p.data_GetType() == data_GetType()) { 00491 00492 // ok, the given element has this same type... 00493 // now check contents 00494 const mcPolynomial &pcontents = mcExpContainer(p).data_GetConstContent(); 00495 00496 if (pcontents.math_Compare(data_GetContent(), flags)) 00497 return TRUE; 00498 } 00499 00500 return FALSE; 00501 } 00502 00503 bool mcExpContainerHelpers::math_CanBeAddedWith(const mcElement &p) const 00504 { 00505 // first of all, check the element type 00506 if (p.data_GetType() != data_GetType()) 00507 return FALSE; 00508 00509 mcExpContainer e(p); 00510 const mcPolynomial &content = e.data_GetContent(); 00511 return data_GetContent().math_CanBeAddedWith(content); 00512 } 00513 00514 bool mcExpContainerHelpers::math_CanBeDivBy(const mcElement &p) const 00515 { 00516 // first of all, check the element type 00517 if (p.data_GetType() != data_GetType()) 00518 return FALSE; 00519 00520 mcExpContainer e(p); 00521 const mcPolynomial &content = e.data_GetConstContent(); 00522 return data_GetContent().math_CanBeDivBy(content); 00523 } 00524 00525 bool mcExpContainerHelpers::math_CanBeMultWith(const mcElement &p) const 00526 { 00527 // first of all, check the element type 00528 if (p.data_GetType() != data_GetType()) 00529 return FALSE; 00530 00531 mcExpContainer e(p); 00532 const mcPolynomial &content = e.data_GetContent(); 00533 return data_GetContent().math_CanBeMultWith(content); 00534 } 00535 00536 mcPolynomial mcExpContainerHelpers::math_GetRaisedContents() const 00537 { 00538 mcPolynomial res(data_GetContent()); 00539 if (!math_hasExp()) return res; 00540 00541 // raise the polynomial to the exponent we contain: 00542 // mcPolynomial implements the full algorithm 00543 res.math_RaiseTo(math_GetExp()); 00544 00545 return res; 00546 } 00547 00548 mcExpSimRes mcExpContainerHelpers::math_SimplifyBaseExp(long flags, mcElement *pnew) 00549 { 00550 mcMATHLOG(wxT("mcExpContainerHelpers::math_SimplifyBaseExp [%s]"), mcTXTTHIS); 00551 00552 // it does make sense to raise our contents only if they can 00553 // be raised by our exponent: if they cannot and we still 00554 // call the mcPolynomialHelpers::math_RaiseTo function, 00555 // the polynomial would create a mcBracket inside it with 00556 // the exponent we gave it... this would be useless ! 00557 if (!data_GetContent().math_CanBeRaisedTo(math_GetExp()) || 00558 (flags & mcEXPSIM_KEEP_FACTORIZATION)) 00559 return mcESR_DONE; 00560 00561 // if contents are completely simplified, we can remove this 00562 // container and replace it with its contents raised to the 00563 // exponent of this container 00564 data_GetContent().math_RaiseTo(math_GetExp()); 00565 data_DestroyExpSub(TRUE); 00566 00567 return mcESR_NOTFINISHED; 00568 } 00569 00570 mcExpSimRes mcExpContainerHelpers::math_SimplifyBase(long flags, mcElement *newelem) 00571 { 00572 // simplify contents 00573 mcExpSimRes res = data_GetContent().math_Simplify(flags, newelem); 00574 if (res != mcESR_DONE) return res; 00575 00576 // now, try to simplify this element with the simplified contents... 00577 return math_SimplifyContents(flags, newelem); 00578 } 00579 00580 mcExpSimRes mcExpContainerHelpers::math_ExpandBase(long flags, mcElement *newelem) 00581 { 00582 // expand contents 00583 mcExpSimRes res = data_GetContent().math_Expand(flags, newelem); 00584 if (res != mcESR_DONE) return res; 00585 00586 // now, try to simplify this element with the simplified contents... 00587 return math_ExpandContents(flags, newelem); 00588 } 00589 00590 mcBasicOpRes mcExpContainerHelpers::math_DivideBaseOnlyBy(const mcElement &p, mcElement *pp) 00591 { 00592 if (p.data_GetType() != data_GetType()) return mcBOR_INVALID; 00593 mcExpContainer e(p); 00594 return data_GetContent().math_DivideBy(e.data_GetContent(), pp); 00595 } 00596 00597 mcBasicOpRes mcExpContainerHelpers::math_Add(const mcElement &p, mcElement *pp, bool add) 00598 { 00599 if (p.data_GetType() != data_GetType()) return mcBOR_INVALID; 00600 mcExpContainer e(p); 00601 return data_GetContent().math_Add(e.data_GetContent(), pp, add); 00602 } 00603 /* 00604 mcBasicOpRes mcExpContainerHelpers::math_Subtract(const mcElement &p, mcElement *pp) 00605 { 00606 if (p.data_GetType() != hlp().data_GetType()) return mcBOR_INVALID; 00607 mcExpContainer *e = (mcExpContainer *)p; 00608 return data_GetContent().math_Subtract(e.data_GetContent(), pp); 00609 }*/ 00610 00611 mcBasicOpRes mcExpContainerHelpers::math_MultiplyBaseOnlyBy(const mcElement &p, mcElement *pp) 00612 { 00613 if (p.data_GetType() != data_GetType()) return mcBOR_INVALID; 00614 mcExpContainer e(p); 00615 return data_GetContent().math_MultiplyBy(e.data_GetContent(), pp); 00616 } 00617 00618 mcMonomial mcExpContainerHelpers::math_GetFactors() const 00619 { return data_GetContent().math_GetFactors(); } 00620
[ Top ] |