container.cpp

Go to the documentation of this file.
00001 /*
00002  * container.cpp
00003  *
00004  * Copyright (C) 2008-2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of basic container object.
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "element.h"                    // always include our own header first!
00036 
00037 #include "datahash/datahash_util.h"
00038 
00039 
00040 
00041 namespace dialog {
00042 
00043 static const int s_border                       = 2;
00044 
00045 
00046 struct element_record_t {
00047         rect_t                  bounds;
00048         smart_ptr<Element>      element;
00049 };
00050 
00051 
00052 typedef std::vector<element_record_t> vec_element_t;
00053 
00054 
00055 
00056 ////////////////////////////////////////////////////////////////////////////////
00057 //
00058 //      Container -- container implementation class
00059 //
00060 ////////////////////////////////////////////////////////////////////////////////
00061 
00062 class Container : public Element {
00063 public:
00064         Container(void) throw();
00065         ~Container(void) throw() { }
00066 
00067         // public class methods ------------------------------------------------
00068         void initialize(IN vec_element_t& children);
00069 
00070         // dialog::Element class interface methods -----------------------------
00071         int getWidth(void);
00072         int getHeight(void);
00073         void draw(IN const point_t& offset);
00074         void cursor(IN const point_t& pos);
00075         Element * getFocus(IN const point_t& pos);
00076         const char * button(IN int button, IN int state, IN const point_t& pos);
00077         void addData(IN crypto::DESKey * desKey, IN Datahash * data);
00078 
00079 private:
00080         // private helper methods ----------------------------------------------
00081         Element * getChild(IN const point_t& pos, OUT point_t& rel);
00082         void calculateExtent(void);
00083         rect_t getRect(IN const element_record_t& er);
00084 
00085         // private member data -------------------------------------------------
00086         int                     m_width;
00087         int                     m_height;
00088         vec_element_t           m_children;
00089 };
00090 
00091 
00092 
00093 Container::Container(void)
00094 throw()
00095 {
00096         m_width = -1;
00097         m_height = -1;
00098 }
00099 
00100 
00101 
00102 void
00103 Container::initialize
00104 (
00105 IN vec_element_t& children
00106 )
00107 {
00108         m_children = children;
00109         this->calculateExtent();
00110 }
00111 
00112 
00113 
00114 ////////////////////////////////////////////////////////////////////////////////
00115 //
00116 //      Container -- dialog::Element class interface methods
00117 //
00118 ////////////////////////////////////////////////////////////////////////////////
00119 
00120 int
00121 Container::getWidth
00122 (
00123 void
00124 )
00125 {
00126         return m_width;
00127 }
00128 
00129 
00130 int
00131 Container::getHeight
00132 (
00133 void
00134 )
00135 {
00136         return m_height;
00137 }
00138 
00139 
00140 
00141 void
00142 Container::draw
00143 (
00144 IN const point_t& offset
00145 )
00146 {
00147         for (vec_element_t::iterator i = m_children.begin();
00148              i != m_children.end(); ++i) {
00149                 element_record_t& er = *i;
00150                 ASSERT(er.element, "null element in vector");
00151 
00152                 // child is told to draw to correct global coordinates
00153                 point_t border(s_border, s_border);
00154                 er.element->draw(offset + er.bounds.getTopLeft() + border);
00155         }
00156 }
00157 
00158 
00159 
00160 void
00161 Container::cursor
00162 (
00163 IN const point_t& pos
00164 )
00165 {
00166         point_t rel;
00167         Element * e = this->getChild(pos, rel);
00168         if (e) {
00169                 e->cursor(rel);
00170         }
00171 }
00172 
00173 
00174 
00175 const char *
00176 Container::button
00177 (
00178 IN int button,
00179 IN int state,
00180 IN const point_t& pos
00181 )
00182 {
00183         point_t rel;
00184         Element * e = this->getChild(pos, rel);
00185         return e ? e->button(button, state, rel) : NULL;
00186 }
00187 
00188 
00189 
00190 Element *
00191 Container::getFocus
00192 (
00193 IN const point_t& pos
00194 )
00195 {
00196         //DPRINTF("Looking for focus at (%d, %d)", pos.x, pos.y);
00197         point_t rel;
00198         Element * e = this->getChild(pos, rel);
00199         //DPRINTF("  Found element: %p", e);
00200         return e ? e->getFocus(rel) : NULL;
00201 }
00202 
00203 
00204 
00205 void
00206 Container::addData
00207 (
00208 IN crypto::DESKey * key,
00209 IN Datahash * data
00210 )
00211 {
00212         // ASSERT(key) -- can be null
00213         ASSERT(data, "null");
00214 
00215         for (vec_element_t::iterator i = m_children.begin();
00216              i != m_children.end(); ++i) {
00217                 element_record_t& er = *i;
00218                 ASSERT(er.element, "null element in vector");
00219 
00220                 // child can attach data
00221                 er.element->addData(key, data);
00222         }
00223 }
00224 
00225 
00226 
00227 ////////////////////////////////////////////////////////////////////////////////
00228 //
00229 //      Container -- private helper methods
00230 //
00231 ////////////////////////////////////////////////////////////////////////////////
00232 
00233 Element *
00234 Container::getChild
00235 (
00236 IN const point_t& pos,
00237 OUT point_t& rel                        ///< child-relative coordinates
00238 )
00239 {
00240         rel.clear();
00241 
00242         for (vec_element_t::iterator i = m_children.begin();
00243              i != m_children.end(); ++i) {
00244                 element_record_t& er = *i;
00245                 ASSERT(er.element, "null element in vector");
00246 
00247                 if (!er.bounds.contains(pos))
00248                         continue;               // skip this one
00249 
00250                 // convert to child-relative coordinates
00251                 point_t border(s_border, s_border);
00252                 rel = pos - er.bounds.getTopLeft() - border;
00253                 return er.element;
00254         }
00255 
00256         // nothing found!
00257         return NULL;
00258 }
00259 
00260 
00261 
00262 void
00263 Container::calculateExtent
00264 (
00265 void
00266 )
00267 {
00268         int xmax = 0;
00269         int ymax = 0;
00270 
00271         for (vec_element_t::iterator i = m_children.begin();
00272              i != m_children.end(); ++i) {
00273                 const element_record_t& er = *i;
00274                 ASSERT(er.bounds.isValid(), "Invalid bounding rect?");
00275                 ASSERT(er.element, "null element in vector");
00276 
00277                 int x = er.bounds.right;
00278                 int y = er.bounds.bottom;
00279 
00280                 if (x > xmax) {
00281                         xmax = x;
00282                 }
00283                 if (y > ymax) {
00284                         ymax = y;
00285                 }
00286         }
00287 
00288         m_width = xmax;
00289         m_height = ymax;
00290 
00291         // account for border
00292         m_width += 2* s_border;
00293         m_height += 2 * s_border;
00294 }
00295 
00296 
00297 
00298 ////////////////////////////////////////////////////////////////////////////////
00299 //
00300 //      public API
00301 //
00302 ////////////////////////////////////////////////////////////////////////////////
00303 
00304 smart_ptr<Element>
00305 createContainerElement
00306 (
00307 IN Manager * mgr,
00308 IN const Datahash * hash
00309 )
00310 {
00311         ASSERT(mgr, "null");
00312         ASSERT(hash, "null");
00313 
00314         vec_element_t children;
00315 
00316         // first, construct all child elements
00317         int maxW = 0;
00318         int maxH = 0;
00319         Datahash::iterator_t i;
00320         hash->getIterator("element", i);
00321         while (const hash_value_t * phv = hash->getNextElementUnsafe(i)) {
00322                 ASSERT_THROW(eHashDataType_Hash == phv->type,
00323                     "Bad datahash datatype (expected hash)");
00324                 smart_ptr<Datahash> subhash = phv->hash;
00325                 ASSERT(subhash, "null subhash");
00326 
00327                 // construct this child tree
00328                 element_record_t er;
00329                 er.element = constructElementTree(mgr, subhash);
00330                 ASSERT(er.element, "failed to create element subtree");
00331 
00332                 // stash known size
00333                 int w = er.element->getWidth();
00334                 int h = er.element->getHeight();
00335                 //DPRINTF("Element claims width is %d", w);
00336                 if (w > maxW) {
00337                         maxW = w;
00338                 }
00339                 if (h > maxH) {
00340                         maxH = h;
00341                 }
00342                 er.bounds.set(0, 0, w, h);
00343 
00344                 children.push_back(er);
00345         }
00346 
00347         // vertically or horizontally aligned?
00348         std::string s_align = getOptionalString(hash, "axis", "v");
00349         char align = 'v';
00350         if ("h" == s_align) {
00351                 align = 'h';
00352         }
00353 
00354         // border settings?
00355         int borderX = getOptionalLong(hash, "borderX", s_border);
00356         int borderY = getOptionalLong(hash, "borderY", s_border);
00357 
00358         // now loop again and determine proper x,y values
00359         int oldX = 0;
00360         int oldY = 0;
00361         for (vec_element_t::iterator i = children.begin(); i != children.end();
00362              ++i) {
00363                 element_record_t& er = *i;
00364 
00365                 if ('v' == align) {
00366                         int dx = (int) (0.5 * (maxW - er.bounds.right) + 0.5);
00367                         er.bounds.set(dx, oldY, er.bounds.right + dx,
00368                             er.bounds.bottom + oldY);
00369                         oldY = er.bounds.bottom;
00370                         oldY += borderY;
00371                 } else {
00372                         int dy = (int) (0.5 * (maxH - er.bounds.bottom) + 0.5);
00373                         er.bounds.set(oldX, dy, er.bounds.right + oldX,
00374                             er.bounds.bottom + dy);
00375                         oldX = er.bounds.right;
00376                         oldX += borderX;
00377                 }
00378 
00379                 //er.bounds.dump("Child element");
00380         }
00381 
00382         // create container
00383         smart_ptr<Container> local = new Container;
00384         ASSERT(local, "out of memory");
00385 
00386         local->initialize(children);
00387 
00388         return local;
00389 }
00390 
00391 
00392 
00393 };      // dialog namespace
00394