core/vgui/vgui_parent_child_link.cxx
Go to the documentation of this file.
00001 // This is core/vgui/vgui_parent_child_link.cxx
00002 #include "vgui_parent_child_link.h"
00003 //:
00004 // \file
00005 // \author fsm
00006 // \brief  See vgui_parent_child_link.h for a description of this file.
00007 
00008 #include <vcl_cassert.h>
00009 #include <vcl_iostream.h>
00010 #include <vcl_vector.h>
00011 #include <vcl_set.h>
00012 
00013 #include <vgui/vgui_event.h>
00014 #include <vgui/vgui_tableau.h>
00015 #include <vgui/vgui_tableau_sptr.h>
00016 #include <vgui/vgui_macro.h>
00017 
00018 // For efficiency (e.g. when posting redraws), the parents of a tableau
00019 // may be cached in the vgui_parent_child_link_data baseclass of vgui_tableau.
00020 // This macro enables that optimization. If caching is not enabled, parents
00021 // are computed by scanning the entire registry of parent_child_links in
00022 // existence which could obviously be quite slow.
00023 #define cache_parents 1
00024 
00025 //: Smart-pointer implementation of vgui_parent_child_link.
00026 //
00027 //  This container holds a single pointer for every impl object.
00028 //  The pointers are cast to and from void* to avoid (a) exposing the
00029 //  implementation class and (b) instantiating an extra class template.
00030 //
00031 //  Implementation notes:
00032 //
00033 //  [1]
00034 //  Since tableaux will hold parent_child_links and use them to refer to
00035 //  children, each parent_child_link must ref()erence its child.
00036 //
00037 //  [2]
00038 //  A parent_child_link should not ref()erence its parent as that would lead to
00039 //  cyclic dependencies and hence core leaks. Thus, we use a raw pointer to hold
00040 //  the parent.
00041 //
00042 //  [3]
00043 //  A vgui_tableau_sptr could be used to hold the child, but there is no real
00044 //  advantage in that.
00045 //
00046 //  [4]
00047 //  A parent_child_link's parent pointer must never be zero because a
00048 //  parent_child_link which does not have a parent is a useless
00049 //  parent_child_link (and so is most likely an error). Thus 'p' is a private
00050 //  data member of vgui_parent_child_link_impl and the constructor will
00051 //  cause assertion failure if given a null parent pointer.
00052 //
00053 //  [5]
00054 //  A parent_child_link's parent pointer cannot be changed because there is no
00055 //  legitimate use for that.
00056 struct vgui_parent_child_link_impl
00057 {
00058   // all is stored as a pointer as it must live longer than any static
00059   // parent_child_links
00060   typedef vcl_set<void *> all_t;
00061   static all_t* all;
00062   static int all_refs;
00063 
00064   inline vgui_parent_child_link_impl(vgui_tableau *p_, vgui_tableau *c_);
00065   inline ~vgui_parent_child_link_impl();
00066 
00067   // This changes the child, not the parent.
00068   inline void assign(vgui_tableau *t);
00069 
00070   // There's nothing tricky here. we just return the raw pointers.
00071   vgui_tableau *parent() const { return p; }
00072   vgui_tableau *child () const { return c; }
00073 
00074   // Reference counting. Deriving from vbl_ref_count would make the
00075   // methods virtual and hence non-inlineable.
00076   inline void acquire();
00077   inline void release();
00078 
00079  private:
00080   vgui_tableau *p; // parent
00081   vgui_tableau *c; // child
00082   int use_count;   // reference count
00083 
00084   // Helpers. the purpose of these functions is to perform
00085   // the double link/unlink required to cache the parent-child
00086   // relation on tableaux. they are static because they may be
00087   // invoked when the impl object is in a dubious state.
00088   static inline void link  (vgui_tableau *p, vgui_tableau *c);
00089   static inline void unlink(vgui_tableau *p, vgui_tableau *c);
00090 };
00091 
00092 // static data for impl class :
00093 vcl_set<void*>* vgui_parent_child_link_impl::all = 0;
00094 int vgui_parent_child_link_impl::all_refs = -1;
00095 
00096 void vgui_parent_child_link_impl::acquire()
00097 {
00098   ++ use_count;
00099 }
00100 
00101 void vgui_parent_child_link_impl::release()
00102 {
00103   assert(use_count > 0);
00104 
00105   if (-- use_count == 0)
00106     delete this;
00107 }
00108 
00109 void vgui_parent_child_link_impl::unlink(vgui_tableau *p, vgui_tableau *c)
00110 {
00111 #if cache_parents
00112   if (c) {
00113     vcl_vector<vgui_tableau*> &vec = c->vgui_parent_child_link_data::parents;
00114     for (vcl_vector<vgui_tableau*>::iterator i=vec.begin(); i!=vec.end(); ++i) {
00115       if (*i == p) {
00116         vec.erase(i);
00117         break;
00118       }
00119     }
00120   }
00121 #endif
00122 }
00123 
00124 void vgui_parent_child_link_impl::link(vgui_tableau *p, vgui_tableau *c)
00125 {
00126 #if cache_parents
00127   if (c)
00128     c->vgui_parent_child_link_data::parents.push_back(p);
00129 #endif
00130 }
00131 
00132 vgui_parent_child_link_impl::vgui_parent_child_link_impl(vgui_tableau *p_, vgui_tableau *c_)
00133   : p(p_)
00134   , c(c_)
00135   , use_count(0)
00136 {
00137   if (! p) {
00138     vgui_macro_warning << "parent is null\n";
00139     assert(false);
00140   }
00141 
00142   if (c)
00143     c->ref();
00144 
00145   // register.
00146   if (all == 0) {
00147 #ifdef DEBUG
00148     vcl_cerr << __FILE__ " : CREATING parent_child_link cache\n";
00149 #endif
00150     all = new all_t;
00151     all_refs = 0;
00152   }
00153   ++all_refs;
00154   all->insert(this);
00155 
00156   // parent and child are not allowed to be equal.
00157   if (p == c) {
00158     vgui_macro_warning << "parent and child are equal\n";
00159     assert(false);
00160   }
00161 
00162   link(p, c);
00163 }
00164 
00165 vgui_parent_child_link_impl::~vgui_parent_child_link_impl()
00166 {
00167   unlink(p, c);
00168 
00169   if (c)
00170     c->unref();
00171 
00172   // deregister.
00173   vcl_set<void*>::iterator i = all->find(this);
00174   assert(i != all->end());
00175   all->erase(i);
00176   if (--all_refs == 0) {
00177 #ifdef DEBUG
00178     vcl_cerr << __FILE__ " : DELETING parent_child_link cache\n";
00179 #endif
00180     delete all;
00181     all = 0;
00182   }
00183 }
00184 
00185 void vgui_parent_child_link_impl::assign(vgui_tableau *t)
00186 {
00187   if (t == c)
00188     return;
00189 
00190   if (t == p) {
00191     vgui_macro_warning << "cannot assign() a parent_child_link\'s parent to its child\n";
00192     assert(false);
00193   }
00194 
00195   unlink(p, c);
00196 
00197   if (t)
00198     t->ref();
00199 
00200   // remember the old 'c' so that it can be unref()fed below.
00201   // unreffing it here might cause '*this' to be deleted which
00202   // would be disastrous because we're about to assign to 'this->c'.
00203   vgui_tableau *old_c = c;
00204 
00205   c = t;
00206 
00207   link(p, c);
00208 
00209   // it's safe(r) to unref() now.
00210   if (old_c)
00211     old_c->unref();
00212 }
00213 
00214 //------------------------------------------------------------------------------
00215 
00216 vgui_parent_child_link::vgui_parent_child_link(vgui_tableau *p)
00217 {
00218   pimpl = new vgui_parent_child_link_impl(p, 0);
00219   pimpl->acquire();
00220 }
00221 
00222 vgui_parent_child_link::vgui_parent_child_link(vgui_tableau *p,
00223                                                vgui_tableau_sptr const &c)
00224 {
00225   pimpl = new vgui_parent_child_link_impl(p, c.operator->());
00226   pimpl->acquire();
00227 }
00228 
00229 vgui_parent_child_link::vgui_parent_child_link(vgui_parent_child_link const &that)
00230 {
00231   pimpl = that.pimpl;
00232 
00233   if (pimpl)
00234     pimpl->acquire();
00235 }
00236 
00237 vgui_parent_child_link::~vgui_parent_child_link()
00238 {
00239   if (pimpl)
00240     pimpl->release();
00241 
00242   pimpl = 0;
00243 }
00244 
00245 vgui_parent_child_link &vgui_parent_child_link::operator=(vgui_parent_child_link const &that)
00246 {
00247   if (pimpl != that.pimpl) {
00248     if (that.pimpl)
00249       that.pimpl->acquire();
00250 
00251     if (pimpl)
00252       pimpl->release();
00253 
00254     pimpl = that.pimpl;
00255   }
00256 
00257   return *this;
00258 }
00259 
00260 vgui_tableau_sptr vgui_parent_child_link::parent() const
00261 {
00262   return pimpl ? pimpl->parent() : 0;
00263 }
00264 
00265 vgui_tableau_sptr vgui_parent_child_link::child()  const
00266 {
00267   return pimpl ? pimpl->child () : 0;
00268 }
00269 
00270 bool vgui_parent_child_link::operator==(vgui_tableau_sptr const &t) const
00271 {
00272   return child() == t;
00273 }
00274 
00275 void vgui_parent_child_link::assign(vgui_tableau_sptr const &t)
00276 {
00277   if (pimpl)
00278     pimpl->assign(t.operator->());
00279   else
00280   {
00281     vgui_macro_warning << "attempted assign() to empty parent_child_link.\n"
00282                        << "t = " << t << vcl_endl;
00283     assert(false);
00284   }
00285 }
00286 
00287 bool vgui_parent_child_link::handle(vgui_event const &e)
00288 {
00289   if (!pimpl) return false;
00290   vgui_tableau* c = pimpl->child();
00291   if (!c) return false;
00292 
00293   return c->handle(e);
00294 }
00295 
00296 vgui_parent_child_link::operator vgui_parent_child_link::safe_bool() const
00297 {
00298   return (pimpl && (pimpl->child() != 0))? VCL_SAFE_BOOL_TRUE : 0;
00299 }
00300 
00301 bool vgui_parent_child_link::operator!() const
00302 {
00303   return (pimpl && (pimpl->child() != 0))? false : true;
00304 }
00305 
00306 vgui_parent_child_link::operator vgui_tableau_sptr() const
00307 {
00308   return pimpl ? pimpl->child() : 0;
00309 }
00310 
00311 vgui_tableau *vgui_parent_child_link::operator->() const
00312 {
00313   return pimpl ? pimpl->child() : 0;
00314 }
00315 
00316 vcl_ostream & operator<<(vcl_ostream &os, vgui_parent_child_link const &s)
00317 {
00318   // the reason for the flush() is to get as much stuff as
00319   // possible printed before an eventual segfault.
00320   return os << "vgui_parent_child_link("
00321             << vcl_flush
00322             << static_cast<void*>( s.parent().operator->() ) << ", "
00323             << vcl_flush
00324             << static_cast<void*>( s.child ().operator->() ) << ')'
00325             << vcl_flush;
00326 }
00327 
00328 //------------------------------------------------------------------------------
00329 
00330 void vgui_parent_child_link::get_children_of(vgui_tableau_sptr const& tab,
00331                                              vcl_vector<vgui_tableau_sptr> *children)
00332 {
00333   for (vcl_set<void*>::iterator i=vgui_parent_child_link_impl::all->begin();
00334        i!=vgui_parent_child_link_impl::all->end(); ++i)
00335   {
00336     vgui_parent_child_link_impl *ptr = static_cast<vgui_parent_child_link_impl*>(*i);
00337     if ( ptr->parent() == tab.operator->() )
00338       children->push_back( ptr->child() );
00339   }
00340 }
00341 
00342 void vgui_parent_child_link::get_parents_of (vgui_tableau_sptr const& tab,
00343                                              vcl_vector<vgui_tableau_sptr> *parents)
00344 {
00345 #if cache_parents
00346   vcl_vector<vgui_tableau*> const &vec
00347     = tab->vgui_parent_child_link_data::parents;
00348   for (unsigned i=0; i<vec.size(); ++i)
00349     parents->push_back(vec[i]);
00350 #else
00351   for (vcl_set<void*>::iterator i=vgui_parent_child_link_impl::all->begin();
00352        i!=vgui_parent_child_link_impl::all->end(); ++i)
00353   {
00354     vgui_parent_child_link_impl *ptr = static_cast<vgui_parent_child_link_impl*>(*i);
00355     if ( ptr->child() == tab.operator->() )
00356       children->push_back( ptr->parent() );
00357   }
00358 #endif
00359 }
00360 
00361 void vgui_parent_child_link::replace_child_everywhere(vgui_tableau_sptr const &old_child,
00362                                                       vgui_tableau_sptr const &new_child)
00363 {
00364 #ifdef DEBUG
00365   vcl_cerr << "vgui_parent_child_link::replace_child_everywhere\n"
00366            << "  old_child : " << old_child->pretty_name() << '\t'
00367            << "  new child : " << new_child->pretty_name() << '\n';
00368 #endif
00369 
00370   if (old_child == new_child)
00371     vcl_cerr << "vgui_parent_child_link::replace_child_everywhere: old_child == new_child\n";
00372 
00373   for (vcl_set<void*>::iterator i=vgui_parent_child_link_impl::all->begin();
00374        i!=vgui_parent_child_link_impl::all->end(); ++i)
00375   {
00376     vgui_parent_child_link_impl *ptr
00377       = static_cast<vgui_parent_child_link_impl*>(*i);
00378 
00379 #ifdef DEBUG
00380     vcl_cerr << "  parent_child_link\t"
00381              << "parent : " << ptr->parent()->pretty_name()
00382              << "\tchild : ";
00383     if (! ptr->child())
00384       vcl_cerr << "0\n";
00385     else
00386       vcl_cerr << ptr->child()->pretty_name() << '\n';
00387 #endif
00388 
00389     if ( ptr->child() == old_child.operator->() ) {
00390       assert(ptr->parent() != new_child.operator->() );
00391 #ifdef DEBUG
00392       vcl_cerr << "  replaced by: " << ptr->child() << '\n';
00393 #endif
00394       ptr->parent()->notify_replaced_child(old_child, new_child);
00395       ptr->assign(new_child.operator->());
00396     }
00397   }
00398 }