home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1996 February / PCWK0296.iso / po7_win / object10 / obound.cpp < prev    next >
C/C++ Source or Header  |  1995-04-13  |  26KB  |  1,004 lines

  1. /* Copyright (c) Oracle Corporation 1994.  All Rights Reserved */
  2.  
  3. /*
  4.     This source code is provided as a debugging aid for developers
  5.     who have purchased Oracle Objects for OLE    .  Please see the
  6.     online help for documentation of these classes.
  7. */
  8.  
  9. /*
  10.     Oracle Objects for OLE     C++ Classes
  11.     
  12.     This file implements the OBinder and OBound classes
  13.                            
  14.     CREATED    ********   11/22/94
  15.     RWOOLARD    MODIFIED    03/20/95
  16.                 bug#    262162    IsChanged() not correct following Refresh()
  17.                         266266    m_fieldname being initialized to 0
  18.                         266270    BindToBinder(NULL, "ename") causes GPF 
  19.                         266277    BindToBinder(&bind, NULL) returns success
  20.                         267021    UnBindObj(NULL, TRUE) causes GPF
  21. */
  22.  
  23. #include "windows.h"
  24. #include <ole2.h>
  25. #include <olenls.h>       
  26. #include <dispatch.h>  
  27.  
  28. #ifndef OBOUND_ORACLE
  29. #include "OBound.h"
  30. #endif
  31.  
  32. #ifndef ORAOBJI_ORACLE
  33. #include "oraobji.h"
  34. #endif
  35.  
  36. // ----- OBinderAdvise -----------------------------------------------
  37. // this is a helper for OBinder
  38.  
  39. class OBinderAdvise : public OAdvise
  40. {
  41. public:
  42.     OBinderAdvise(OBinder *parent = NULL) {m_binder = parent;}
  43.     
  44.     // on requests and notifys call trigger routines in OBinder and OBound
  45.     oboolean ActionRequest(int movekind);
  46.     void    ActionNotify(int movekind);
  47.     
  48. private:
  49.     OBinder *m_binder;
  50.        
  51. };
  52.  
  53. // ----- OBound -----------------------------------------------
  54.  
  55. OBound::OBound(void)
  56. {
  57. // BUG #266266
  58.     m_fieldname = OObjectAllocString ("");
  59.     m_changed = FALSE;
  60.     m_binder = 0;
  61.     m_fieldindex = -1;  // invalid index
  62. }
  63.  
  64. OBound::~OBound(void)
  65. {
  66.     if (m_fieldname)
  67.         OObjectFreeString(m_fieldname);
  68.     
  69.     // make sure our binder knows we're gone
  70.     if (m_binder)
  71.         m_binder->UnbindObj(this, TRUE);
  72. }
  73.  
  74. oresult OBound::Unbind(void)
  75.     if (m_binder)
  76.     {
  77.         oresult ores = m_binder->UnbindObj(this, FALSE);
  78.         if (ores == OSUCCESS)
  79.         {
  80.             m_binder = 0;
  81.             return(OSUCCESS);
  82.         }
  83.         else
  84.             return(OFAILURE);
  85.     }
  86.     else    
  87.         return(OSUCCESS); // already unbound
  88. }
  89.  
  90. oresult OBound::GetValue(OValue *val)
  91. {
  92.     if (m_binder)
  93.         return(m_binder->GetFieldValue(m_fieldindex, val));
  94.     else
  95.         return(OFAILURE);
  96. }
  97.  
  98. oresult OBound::SetValue(const OValue &val)
  99. {
  100.     if (m_binder)
  101.         return(m_binder->SetFieldValue(m_fieldindex, val));
  102.     else
  103.         return(OFAILURE);
  104. }
  105.  
  106. const char *OBound::GetName(void) const
  107. {
  108.     return(m_fieldname);  // this is NULL if we haven't done BindToBinder yet
  109. }
  110.  
  111. ODatabase OBound::GetDatabase(void) const
  112. {
  113.     if (m_binder)
  114.         return(m_binder->GetDatabase());
  115.     else
  116.     { // return closed database
  117.         ODatabase odb;
  118.         return(odb);
  119.     }
  120. }
  121.  
  122. ODynaset OBound::GetDynaset(void) const
  123. {
  124.     if (m_binder)
  125.         return(m_binder->GetDynaset());
  126.     else
  127.     { // return closed dynaset
  128.         ODynaset dyn;
  129.         return(dyn);
  130.     }
  131. }
  132.     
  133. oresult OBound::GetFieldIndex(void)
  134. {
  135.     if (m_binder)
  136.         return(m_binder->GetFieldIndex(&m_fieldindex, m_fieldname));
  137.     else
  138.         return(OFAILURE);
  139. }
  140.  
  141. oresult OBound::BindToBinder(OBinder *binder, const char *fieldname)
  142. {   
  143. // BUG #266270 and 266277
  144.     if (!binder || !fieldname)
  145.     {
  146.         // Set error here when error handling code is available
  147.         return(OFAILURE);
  148.     }
  149.         
  150.     if (m_binder)
  151.     { // we're already bound - try to unbind
  152.         if (m_binder->UnbindObj(this, FALSE) != OSUCCESS)
  153.             return(OFAILURE);
  154.         m_binder = 0;
  155.     }
  156.     
  157.     // remember field name
  158.     if (m_fieldname)
  159.         OObjectFreeString(m_fieldname);
  160.     m_fieldname = OObjectAllocString(fieldname);
  161.     
  162.     // remember containing binder
  163.     m_binder = binder;
  164.     
  165.     return(m_binder->AddObjectToOBinder(this));
  166. }
  167.  
  168. oresult OBound::SaveChangeBound(void)
  169. {
  170.     if (!m_changed)
  171.         return(OSUCCESS);  // don't even call subclass
  172.     
  173.     return(SaveChange());  // do the real work
  174. }
  175.  
  176. oresult OBound::RefreshBound(void)
  177. {
  178.     if (!m_binder || m_fieldindex < 0)
  179.         return(OFAILURE);  // can't get the value
  180.     
  181.     // get the new data
  182.     OValue val;
  183.     m_binder->GetFieldValue(m_fieldindex,&val);
  184.     
  185.     // call subclass to do the work
  186.     oresult ores = Refresh(val);
  187.     
  188.     if (ores == OSUCCESS)
  189.         m_changed = FALSE;  // new value means we haven't got any changes
  190.     
  191.     return(ores);
  192. }
  193.  
  194. oresult OBound::Changed(oboolean changed) 
  195. {
  196.     if ((changed && m_changed) || (!changed && !m_changed))
  197.         return(OSUCCESS);  // we already know this
  198.     
  199.     if (changed && m_binder)
  200.     { // try to start change in dynaset (will do a StartEdit, which may fail)
  201.         if (m_binder->Changed(TRUE) != OSUCCESS)
  202.             return(OFAILURE);
  203.     }
  204.     
  205.     m_changed = changed;  // remember changed-ness
  206.         
  207.     return(OSUCCESS);
  208.  
  209. oboolean OBound::IsChanged(void) const
  210. {
  211.     return(m_changed);
  212. }
  213.  
  214. oresult OBound::UnbindNotify(void)
  215. {
  216.     // the binder is letting go of us
  217.     m_binder = 0;
  218.     
  219.     return(OSUCCESS);
  220. }
  221.  
  222. oresult OBound::ClearChange(void)
  223. {
  224.     // the binder is telling us that the change has been saved
  225.     Changed(FALSE);
  226.     return(OSUCCESS);
  227. }
  228.  
  229. // default trigger implementations
  230. oresult OBound::Startup(void)
  231. {return OSUCCESS;}
  232.  
  233. oresult OBound::Shutdown(void)
  234. {return OSUCCESS;}
  235.  
  236. oresult OBound::PreQuery(void)
  237. {return OSUCCESS;}
  238.  
  239. oresult OBound::PostQuery(void)
  240. {return RefreshBound();}
  241.  
  242. oresult OBound::PreDelete(void)
  243. {return OSUCCESS;}
  244.  
  245. oresult OBound::PostDelete(void)
  246. {return OSUCCESS;}
  247.  
  248. oresult OBound::PreAdd(void)
  249. {return OSUCCESS;}
  250.  
  251. oresult OBound::PostAdd(void)
  252. {return RefreshBound();}
  253.  
  254. oresult OBound::PreUpdate(void)
  255. {return OSUCCESS;}
  256.  
  257. oresult OBound::PostUpdate(void)
  258. {return OSUCCESS;}
  259.  
  260. oresult OBound::PreRollback(void)
  261. {return OSUCCESS;}
  262.  
  263. oresult OBound::PostRollback(void)
  264. {return RefreshBound();}
  265.  
  266. oresult OBound::PreMove(void)
  267. {return OSUCCESS;}
  268.  
  269. oresult OBound::PostMove(void)
  270. {return RefreshBound();}
  271.  
  272. // ----- OBinder -----------------------------------------------
  273.  
  274. // # of OBound pointers in each chunk
  275. #define OBinder_NBOUND 30
  276.  
  277. OBinder::OBinder(void)
  278. {
  279.     m_boundlist = 0;
  280.     m_advise = 0;
  281.    
  282.     m_changed = FALSE;
  283.     m_nbound = 0;   // # of bound objects
  284.  
  285.     m_schangeerr = 0;
  286.     m_changeerr = OERROR_NONE;
  287. }
  288.  
  289. OBinder::~OBinder(void)
  290. {
  291.     Close(FALSE);  // close, but don't fire shutdown triggers
  292. }
  293.  
  294. oresult OBinder::Close(oboolean doShutdown)
  295. {
  296.     if (doShutdown)
  297.     {  // fire shutdown triggers
  298.         if (OSUCCESS != Shutdown())
  299.             return(OFAILURE);
  300.         if (OSUCCESS != DoAllObjects(OBound::Shutdown))
  301.             return(OFAILURE);
  302.     }
  303.     
  304.     // tell all bound objects that they aren't bound anymore
  305.     DoAllObjects(OBound::UnbindNotify);
  306.     
  307.     // clean up the binder object
  308.     
  309.     // get rid of advisory sink
  310.     delete (OBinderAdvise *) m_advise;
  311.     m_advise = 0;
  312.     
  313.     // delete the list of bound objects
  314.     //  note that we are only deleting our pointers.  We don't delete the objects
  315.     OBound **curchunk;
  316.     OBound **nextchunk;
  317.     
  318.     curchunk = m_boundlist;
  319.     while (curchunk)
  320.     {
  321.         nextchunk = (OBound **) *(curchunk+(OBinder_NBOUND-1));
  322.         delete [] curchunk;
  323.         curchunk = nextchunk;
  324.     }
  325.     m_boundlist = 0;
  326.     m_nbound = 0;
  327.     m_changed = FALSE;
  328.     
  329.     return(OSUCCESS);      
  330. }
  331.  
  332. // routine to have all bound objects refresh their values
  333. oresult OBinder::Refresh(void)
  334. {
  335.     oresult ores = DoAllObjects(&OBound::RefreshBound);
  336. //BUG #262162    
  337.     if (ores == OSUCCESS)
  338.         m_changed = FALSE;
  339.     return ores;
  340. }
  341.  
  342. // routine called by bound object to get its field index
  343. oresult OBinder::GetFieldIndex(int *ofldi, const char *fieldname)
  344. {
  345.     // get field index
  346.     *ofldi = m_dynaset.GetFieldIndex(fieldname);
  347.     return(*ofldi >= 0 ? OSUCCESS : OFAILURE);   
  348. }
  349.  
  350. oresult OBinder::AddObjectToOBinder(OBound *object)
  351. {
  352.     /*
  353.         We keep pointers to the bound objects in a linked list
  354.         of chunks of pointers.  Each chunk contains OBinder_NBOUND-1 pointers
  355.         and a pointer to the next chunk.  This is a "fat" linked list.
  356.     */
  357.     
  358.     int ii;
  359.     OBound **op;
  360.     
  361.     if (!m_boundlist)
  362.     { // start the binder
  363.         
  364.         // allocate first chunk of bound object list
  365.         m_boundlist = new OBound *[OBinder_NBOUND];
  366.         if (!m_boundlist)
  367.             return(OFAILURE);  // no memory!
  368.         
  369.         // make sure all the pointers are 0
  370.         for (ii=0, op=m_boundlist; ii<OBinder_NBOUND; ii++, op++)
  371.             *op = 0;          
  372.  
  373.         // call the startup trigger
  374.         if (Startup() != OSUCCESS)
  375.             return(OFAILURE);       
  376.     }
  377.     
  378.     // scan through list to find all the objects.  We want to place ours after
  379.     //   all the already bound objects
  380.     op = m_boundlist;
  381.     int nfound = 0;
  382.     while(1)
  383.     {
  384.         for (ii=0; ii<OBinder_NBOUND-1; ii++, op++)
  385.         {
  386.             if (*op)
  387.                 nfound++;
  388.             else if (nfound >= m_nbound)
  389.                 break;  // found a zero pointer after all set pointers
  390.         }
  391.         
  392.         if (ii < OBinder_NBOUND-1)
  393.             break; // we found a spot
  394.         
  395.         // go to the next chunk
  396.         if (!*op)
  397.         { // next chunk isn't allocated yet - do it
  398.             *op = (OBound *) new OBound *[OBinder_NBOUND];
  399.             OBound **op2;
  400.             for (ii=0, op2 = (OBound **) (*op); ii<OBinder_NBOUND; ii++, op2++)
  401.                 *op2 = 0;  // make sure all the pointers are zeroed
  402.         }
  403.         
  404.         op = (OBound **) (*op); // point at beginning of next chunk
  405.     }
  406.     
  407.     // now op is pointing at an open spot
  408.     *op = object; 
  409.     m_nbound++;
  410.     
  411.     oresult ores = OSUCCESS;
  412.     if (m_dynaset.IsOpen())
  413.     {
  414.         ores = object->GetFieldIndex();  // have object get its field index
  415.         object->Startup();   // fire startup trigger for object
  416.         /*
  417.          * This line was commented out of the original code, but I think it should be
  418.          * there to give the value to an object that is being bound to an alraedy open dynaset.
  419.          * Suspect this line of code if OBinder starts to fail mysteriously
  420.          */
  421.         // object->RefreshBound();   // give the object its value
  422.     }
  423.     else
  424.         object->Startup();  // just fire startup trigger
  425.     
  426.     return (ores);    
  427. }
  428.  
  429. oresult OBinder::Open(const char *dbname, const char *username, const char *pwd, const char *sqls, long dynoptions)
  430. {
  431.     // open the database with the given arguments
  432.     oresult ores = m_database.Open(dbname, username, pwd);
  433.     
  434.     // and call the database version of the open method
  435.     if (ores == OSUCCESS)
  436.         ores = Open(m_database, sqls, dynoptions);
  437.     
  438.     return(ores);   
  439. }
  440.  
  441. oresult OBinder::Open(const ODatabase &odb, const char *sqls, long dynoptions)
  442. {
  443.     if (m_dynaset.IsOpen())
  444.     { // open on old database - close it
  445.         delete (OBinderAdvise *) m_advise;  // get rid of invalid advisory
  446.         m_advise = 0;
  447.         if (m_dynaset.Close() != OSUCCESS)
  448.             return(OFAILURE);
  449.     }
  450.     
  451.     // copy the database
  452.     m_database = odb;
  453.     if (!m_database.IsOpen())
  454.         return(OFAILURE);  // not a whole lot we can do
  455.     
  456.     if (!sqls)
  457.     { // caller hasn't specified a sql statement - we won't do a query
  458.         return(OSUCCESS);
  459.     }
  460.     
  461.     // call pre-action triggers
  462.     if (PreQuery() != OSUCCESS)
  463.         return(OFAILURE);
  464.     if (DoAllObjects(&OBound::PreQuery) != OSUCCESS)
  465.         return(OFAILURE);
  466.     
  467.     // create new dynaset and advice sink
  468.  
  469.     // open dynaset and move to first record
  470.     if (m_dynaset.Open(odb, sqls, dynoptions) != OSUCCESS)
  471.         return(OFAILURE); // couldn't open dynaset
  472.     
  473.     m_advise = (void *) new OBinderAdvise(this);
  474.     if (OSUCCESS != ((OBinderAdvise *) m_advise)->Open(m_dynaset))
  475.         return(OFAILURE);  // couldn't attach advisory
  476.     
  477.     // have all the bound objects get their field indices
  478.     DoAllObjects(&OBound::GetFieldIndex);        
  479.     
  480.     // post action triggers
  481.     if (PostQuery() != OSUCCESS)
  482.         return(OFAILURE);
  483.     if (DoAllObjects(&OBound::PostQuery) != OSUCCESS)   // by default, refreshes
  484.         return(OFAILURE);
  485.     
  486.     return(OSUCCESS);
  487. }
  488.  
  489. // navigational methods
  490. oresult OBinder::MoveFirst(void)
  491. {
  492.     return(m_dynaset.MoveFirst());
  493. }
  494.  
  495. oresult OBinder::MoveLast(void)
  496. {
  497.     return(m_dynaset.MoveLast());
  498. }
  499.  
  500. oresult OBinder::MoveNext(void)
  501. {
  502.     return(m_dynaset.MoveNext(FALSE));  // bounded move
  503. }
  504.  
  505. oresult OBinder::MovePrev(void)
  506. {
  507.     return(m_dynaset.MovePrev(FALSE));  // bounded move
  508. }
  509.  
  510. oresult OBinder::MoveToMark(const ODynasetMark &mark)
  511. {
  512.     return(m_dynaset.MoveToMark(mark));
  513. }
  514.  
  515. //BUG #262553
  516. oboolean OBinder::CanMark(void) const 
  517. {
  518.     return m_dynaset.CanMark();
  519. }
  520.  
  521. ODynasetMark OBinder::GetMark(void) const 
  522. {
  523.     return m_dynaset.GetMark();
  524. }  // bookmark at current position
  525.  
  526. ODynasetMark OBinder::GetLastModifiedMark(void) const 
  527. {
  528.     return m_dynaset.GetLastModifiedMark();    // get bookmark at last modified record
  529. }
  530.  
  531. // set SQL for the next query
  532. oresult OBinder::SetSQL(const char *sqls)
  533. {
  534.     if (!m_dynaset.IsOpen())
  535.     { // really open now that we have sql statement
  536.         return(Open(m_database, sqls));
  537.     }
  538.     else
  539.         return(m_dynaset.SetSQL(sqls));
  540. }
  541.  
  542. //BUG #262553
  543. const char * OBinder::GetSQL(void) const
  544. {
  545.     return m_dynaset.GetSQL();
  546. }  // gets sql statement
  547.  
  548. oresult OBinder::RefreshQuery(void)
  549. {
  550.     if (!m_dynaset.IsOpen())
  551.         return(OFAILURE);
  552.     return(m_dynaset.Refresh());
  553.     
  554.     // triggers fired in advisory
  555. }
  556.  
  557. const ODynaset OBinder::GetDynaset(void) const
  558. {
  559.     return(m_dynaset);
  560. }
  561.  
  562. const ODatabase OBinder::GetDatabase(void) const
  563. {
  564.     return(m_database);
  565. }
  566.  
  567. oboolean OBinder::IsChanged(void) const
  568. {
  569.     return m_changed;
  570. }
  571.  
  572. //BUG #262553
  573. oboolean OBinder::IsOpen(void) 
  574. {
  575.     return m_dynaset.IsOpen();
  576. }
  577.  
  578. oresult OBinder::GetFieldValue(int index, OValue *val) const
  579. {
  580.     return(m_dynaset.GetFieldValue(index, val));
  581. }
  582.  
  583. oresult OBinder::SetFieldValue(int index, const OValue &val)
  584. {
  585.     return(m_dynaset.SetFieldValue(index, val));
  586. }
  587.  
  588. oresult OBinder::DeleteRecord(void)
  589. {
  590.     if (!m_dynaset.IsOpen())
  591.         return(OFAILURE);  // can't delete record - no dynaset
  592.     
  593.     // delete the record
  594.     m_dynaset.DeleteRecord();
  595.     
  596.     // now move off the deleted record
  597.     m_dynaset.MoveNext();  // just to get off deleted record
  598.     if (m_dynaset.IsEOF())
  599.     { // whoops - we moved past last.  Backup
  600.         m_dynaset.MovePrev();
  601.         // if we've deleted the last record we're now on an invalid record
  602.     }
  603.     
  604.     return(OSUCCESS);
  605. }
  606.  
  607. oresult OBinder::AddNewRecord(void)
  608. {
  609.     if (!m_dynaset.IsOpen())
  610.         return(OFAILURE);
  611.    
  612.     if (m_dynaset.AddNewRecord() != OSUCCESS)
  613.         return(OFAILURE);
  614.     
  615.     // now call postadd triggers       
  616.     if (PostAdd() != OSUCCESS)
  617.         return(OFAILURE);
  618.     if (DoAllObjects(OBound::PostAdd) != OSUCCESS)
  619.         return(OFAILURE);
  620.     
  621.     return(OSUCCESS);
  622. }
  623.  
  624. oresult OBinder::DuplicateRecord(void)
  625. {
  626.     if (!m_dynaset.IsOpen())
  627.         return(OFAILURE);
  628.    
  629.     if (m_dynaset.DuplicateRecord() != OSUCCESS)
  630.         return(OFAILURE);
  631.     
  632.     // now call postadd triggers       
  633.     if (PostAdd() != OSUCCESS)
  634.         return(OFAILURE);
  635.     if (DoAllObjects(OBound::PostAdd) != OSUCCESS)
  636.         return(OFAILURE);
  637.     
  638.     return(OSUCCESS);
  639. }
  640.  
  641. oresult OBinder::Changed(oboolean cflag)
  642. {
  643.     // clear the change error state
  644.     m_schangeerr = 0;
  645.     m_changeerr = OERROR_NONE;
  646.     
  647.     if (cflag && m_changed || !cflag && !m_changed)
  648.         return(OSUCCESS);  // no change from current state
  649.     
  650.     if (cflag && (m_dynaset.GetEditMode() == ODYNASET_EDIT_NOEDIT))
  651.     { // we're starting a change
  652.         if (m_dynaset.StartEdit() != OSUCCESS)
  653.         {
  654.             OnChangedError();
  655.             return(OFAILURE);  // we can't start the edit, for some reason
  656.         }
  657.     }
  658.     
  659.     m_changed = cflag;
  660.     
  661.     return(OSUCCESS);     
  662. }
  663.  
  664. void OBinder::OnChangedError(void)
  665. {
  666.     // remember the error we have
  667.     m_changeerr = m_dynaset.ErrorNumber();
  668.     m_schangeerr = m_dynaset.GetSession().ServerErrorNumber();
  669.     
  670.     return;
  671. }
  672.  
  673. oboolean OBinder::GetChangedError(long *serr, long *cerr) const
  674. {
  675.     if (serr)
  676.         *serr = m_schangeerr;
  677.     if (cerr)
  678.         *cerr = m_changeerr;
  679.     
  680.     return(m_schangeerr != 0 || m_changeerr != OERROR_NONE);
  681. }
  682.  
  683. // public way to save changes
  684. oresult OBinder::Update(void)
  685. {
  686.     return(SaveRecordChanges());
  687. }
  688.  
  689. // the way we throw away changes to the current record
  690. oresult OBinder::DiscardChanges(void)
  691. {
  692.     if (!IsChanged())
  693.         return(OSUCCESS);  // nothing to do 
  694.     
  695.     m_changed = FALSE;
  696.     
  697.     // we have done a StartEdit, but we haven't changed any field values yet
  698.     // so cancel the edit
  699.     if (m_dynaset.CancelEdit() != OSUCCESS)
  700.         return(OFAILURE);
  701.     
  702.     // tell all the bound objects to refresh
  703.     return(Refresh());
  704. }
  705.  
  706. oresult OBinder::DoAllObjects(oresult (OBound::*ff)(void))
  707. {
  708.     // just go through the list of objects and refresh them all
  709.     OBound **op = m_boundlist;
  710.     int      ii;
  711.     int      ndone = 0;
  712.     
  713.     while (op && ndone < m_nbound)
  714.     { // for all chunks
  715.         for (ii=0; ii<OBinder_NBOUND-1; ii++, op++)
  716.         { // for every pointer in this chunk
  717.             if (*op)
  718.             {
  719.                 if ((**op.*ff)() != OSUCCESS)
  720.                     return(OFAILURE);
  721.                 ndone++;
  722.                 if (ndone >= m_nbound)
  723.                     break;  // note that this makes op calculation below into nonsense
  724.             }
  725.         }
  726.         
  727.         // move to the next chunk
  728.         op = (OBound **) (*op); // point at beginning of next chunk
  729.     }
  730.     
  731.     return(OSUCCESS); 
  732. }
  733.  
  734. oresult OBinder::UnbindObj(OBound *object, oboolean nofail)
  735. {   
  736. //BUG #267021    
  737.     if (! object)
  738.     {
  739.         // Set error here when error handling code is available
  740.         return(OFAILURE);
  741.     }
  742.  
  743.     // just go through the list of objects and find the object to be unbound
  744.     OBound **op = m_boundlist;
  745.     int      ii;
  746.     
  747.     while (op)
  748.     { // for all chunks
  749.         for (ii=0; ii<OBinder_NBOUND-1; ii++, op++)
  750.         { // for every pointer in this chunk
  751.             if (*op == object)
  752.             {
  753.                 if (OSUCCESS != (*op)->Shutdown() && !nofail)
  754.                     return(OFAILURE);  // object doesn't want to go away
  755.                 (*op)->UnbindNotify();  // tell object it isn't bound anymore
  756.                 *op = 0;  // we don't free the object, just clear our pointer to it
  757.                 m_nbound--;
  758.                 break;
  759.             }
  760.             // note that we never free any memory - we keep all the chunks
  761.         }
  762.         
  763.         if (ii < OBinder_NBOUND-1)
  764.             break;  // we found the object
  765.         
  766.         // move to the next chunk
  767.         op = (OBound **) (*op); // point at beginning of next chunk
  768.     }
  769.     
  770.     return(OSUCCESS); 
  771. }
  772.  
  773. // private way to save changes
  774. oresult OBinder::SaveRecordChanges(void)
  775. {
  776.     // if there have been changes, save them
  777.     if (m_changed && m_dynaset.IsOpen())
  778.     { // save the change before we move
  779.         if (DoAllObjects(&OBound::SaveChangeBound) != OSUCCESS)        
  780.             return(OFAILURE);  // somebody didn't want to change
  781.         
  782.         if (m_dynaset.Update() != OSUCCESS) // as side effect, fires the update triggers
  783.         { 
  784.             return(OFAILURE);
  785.         }
  786.         m_changed = FALSE;
  787.         
  788.         // tell all bound objects that the changes have been saved
  789.         DoAllObjects(OBound::ClearChange);       
  790.     }
  791.     
  792.     return(OSUCCESS);
  793. }
  794.  
  795. // default trigger routines
  796. oresult OBinder::Startup(void)
  797. {return OSUCCESS;}
  798.  
  799. oresult OBinder::Shutdown(void)
  800. {
  801.     return(SaveRecordChanges());
  802. }
  803.  
  804. oresult OBinder::PreQuery(void)
  805. {return OSUCCESS;}
  806.  
  807. oresult OBinder::PostQuery(void)
  808. {return OSUCCESS;}
  809.  
  810. oresult OBinder::PreDelete(void)
  811. {return OSUCCESS;}
  812.  
  813. oresult OBinder::PostDelete(void)
  814. {return OSUCCESS;}
  815.  
  816. oresult OBinder::PreAdd(void)
  817. {
  818.     return(SaveRecordChanges());
  819. }
  820.  
  821. oresult OBinder::PostAdd(void)
  822. {return OSUCCESS;}
  823.  
  824. oresult OBinder::PreUpdate(void)
  825. {return OSUCCESS;}
  826.  
  827. oresult OBinder::PostUpdate(void)
  828. {return OSUCCESS;}
  829.  
  830. oresult OBinder::PreRollback(void)
  831. {return OSUCCESS;}
  832.  
  833. oresult OBinder::PostRollback(void)
  834. {return OSUCCESS;}
  835.  
  836. oresult OBinder::PreMove(void)
  837.     return(SaveRecordChanges());
  838. }
  839.  
  840. oresult OBinder::PostMove(void)
  841. {return OSUCCESS;}
  842.  
  843. oboolean OBinder::IsFirst(void)
  844. {
  845.     return m_dynaset.IsFirst ();
  846. }
  847.  
  848. oboolean OBinder::IsLast(void)
  849. {
  850.     return m_dynaset.IsLast ();
  851. }
  852.  
  853. //BUG #262553
  854. long OBinder::ErrorNumber(void) const 
  855. {    // return error "number"
  856.     return m_dynaset.ErrorNumber();
  857. }
  858.     
  859. const char *OBinder::LookupErrorText(long errnum) const 
  860. {    // get error text for given error number
  861.     return m_dynaset.LookupErrorText(errnum);
  862. }
  863.                                           
  864. const char *OBinder::GetErrorText(void) const 
  865. {    // get description of last error
  866.     return m_dynaset.GetErrorText();
  867. }
  868.  
  869. void OBinder::ErrorReset(void) const 
  870. {    // reset error state to "no error"
  871.     m_dynaset.ErrorReset();
  872. }  
  873.  
  874. int OBinder::GetFieldCount(void) const 
  875. {    // returns # of fields in a record
  876.     return m_dynaset.GetFieldCount();
  877. }
  878.   
  879. long OBinder::GetRecordCount(void) const 
  880. {   // (dangerous!) gets total number of records in dynaset -- downloads the entire dynaset to the client
  881.     return m_dynaset.GetRecordCount();
  882. }
  883.    
  884. int OBinder::GetFieldIndex(const char *fieldname) const 
  885. {    // gets the index of a field by name
  886.     return m_dynaset.GetFieldIndex(fieldname);
  887. }
  888.                                                     
  889.                                                     
  890. // ----- OBinderAdvise -----------------------------------------------
  891.  
  892. oboolean OBinderAdvise::ActionRequest(int otherkind)
  893. {
  894.     // fire pre-action triggers
  895.     
  896.     switch (otherkind)
  897.     {
  898.     case OADVISE_MOVE_FIRST:
  899.     case OADVISE_MOVE_NEXT:
  900.     case OADVISE_MOVE_PREV:
  901.     case OADVISE_MOVE_LAST:
  902.     case OADVISE_MOVE_TOMARK:
  903.         if (m_binder->PreMove() != OSUCCESS)
  904.             return(FALSE);
  905.         if (m_binder->DoAllObjects(OBound::PreMove) != OSUCCESS)
  906.             return(FALSE);
  907.         break;   
  908.     case OADVISE_DELETE:
  909.         if (m_binder->PreDelete() != OSUCCESS)
  910.             return(FALSE);
  911.         if (m_binder->DoAllObjects(OBound::PreDelete) != OSUCCESS)
  912.             return(FALSE);
  913.         break;
  914.     case OADVISE_ADDNEW:
  915.         if (m_binder->PreAdd() != OSUCCESS)
  916.             return(FALSE);
  917.         if (m_binder->DoAllObjects(OBound::PreAdd) != OSUCCESS)
  918.             return(FALSE);
  919.         break;    
  920.     case OADVISE_UPDATE:
  921.         if (m_binder->PreUpdate() != OSUCCESS)
  922.             return(FALSE);
  923.         if (m_binder->DoAllObjects(OBound::PreUpdate) != OSUCCESS)
  924.             return(FALSE);
  925.         break; 
  926.     case OADVISE_ROLLBACK:
  927.         if (m_binder->PreRollback() != OSUCCESS)
  928.             return(FALSE);
  929.         if (m_binder->DoAllObjects(OBound::PreRollback) != OSUCCESS)
  930.             return(FALSE);
  931.         break;
  932.     case OADVISE_REFRESH:
  933.         if (m_binder->PreQuery() != OSUCCESS)
  934.             return(FALSE);
  935.         if (m_binder->DoAllObjects(OBound::PreQuery) != OSUCCESS)
  936.             return(FALSE);
  937.         break;
  938.     default:
  939.         // unknown triggers 
  940.         int ii = 3;
  941.         break;
  942.     }
  943.     
  944.     return(TRUE);
  945. }
  946.  
  947. void OBinderAdvise::ActionNotify(int otherkind)
  948. {
  949.     // fire post-action triggers
  950.     
  951.     switch (otherkind)
  952.     {
  953.     case OADVISE_MOVE_FIRST:
  954.     case OADVISE_MOVE_NEXT:
  955.     case OADVISE_MOVE_PREV:
  956.     case OADVISE_MOVE_LAST:
  957.     case OADVISE_MOVE_TOMARK:
  958.         if (m_binder->PostMove() != OSUCCESS)
  959.             return;
  960.         m_binder->DoAllObjects(OBound::PostMove);
  961.         break;   
  962.     case OADVISE_DELETE:
  963.         if (m_binder->PostDelete() != OSUCCESS)
  964.             return;
  965.         m_binder->DoAllObjects(OBound::PostDelete);
  966.         break;
  967.     case OADVISE_ADDNEW:
  968.         // the postadd triggers are called directly from AddNewRecord
  969.         //   and Duplicate record.  That's so the trigger is called AFTER
  970.         //   all the dynaset (and in the case of duplicate ODynaset) work
  971.         //   is completely done 
  972.         break;
  973.     case OADVISE_UPDATE:
  974.         if (m_binder->PostUpdate() != OSUCCESS)
  975.             return;
  976.         m_binder->DoAllObjects(OBound::PostUpdate);
  977.         break;
  978.     case OADVISE_ROLLBACK:
  979.         m_binder->Changed(FALSE);  // rollback means changes are gone
  980.         // tell all bound objects that the changes are gone
  981.         m_binder->DoAllObjects(OBound::ClearChange);
  982.                
  983.         if (m_binder->PostRollback() != OSUCCESS)
  984.             return;
  985.         m_binder->DoAllObjects(OBound::PostRollback);
  986.         break;
  987.     case OADVISE_REFRESH:
  988.         m_binder->DoAllObjects(&OBound::GetFieldIndex); // have all the bound objects get their field indices again 
  989.         // is a new field set needed?       
  990.         if (m_binder->PostQuery() != OSUCCESS)
  991.             return;
  992.         m_binder->DoAllObjects(OBound::PostQuery);
  993.         break;
  994.     default:
  995.         // unknown triggers
  996.         break;
  997.     }
  998.     
  999.     return;
  1000. }
  1001.