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 "dialog.h"
00038 
00039 #include "datahash/datahash_util.h"
00040 
00041 
00042 
00043 namespace dialog {
00044 
00045 static const int s_border                       = 2;
00046 
00047 
00048 struct element_record_t {
00049         rect_t                  bounds;
00050         smart_ptr<Element>      element;
00051 };
00052 
00053 
00054 typedef std::vector<element_record_t> vec_element_t;
00055 
00056 
00057 
00058 ////////////////////////////////////////////////////////////////////////////////
00059 //
00060 //      Container -- container implementation class
00061 //
00062 ////////////////////////////////////////////////////////////////////////////////
00063 
00064 class Container : public Element {
00065 public:
00066         Container(void) throw();
00067         ~Container(void) throw() { }
00068 
00069         // public class methods ------------------------------------------------
00070         void initialize(IN vec_element_t& children,
00071                                 IN int width, IN int height);
00072 
00073         // dialog::Element class interface methods -----------------------------
00074         int getWidth(void);
00075         int getHeight(void);
00076         void draw(IN const point_t& offset);
00077         void cursor(IN const point_t& pos);
00078         Element * getFocus(IN const point_t& pos);
00079         const char * button(IN int button, IN int state, IN const point_t& pos);
00080         void addData(IN crypto::DESKey * desKey, IN Datahash * data);
00081 
00082 private:
00083         // private helper methods ----------------------------------------------
00084         Element * getChild(IN const point_t& pos, OUT point_t& rel);
00085         rect_t getRect(IN const element_record_t& er);
00086 
00087         // private member data -------------------------------------------------
00088         int                     m_width;
00089         int                     m_height;
00090         vec_element_t           m_children;
00091         smart_ptr<Drawer>       m_drawer;
00092 };
00093 
00094 
00095 
00096 Container::Container(void)
00097 throw()
00098 {
00099         m_width = -1;
00100         m_height = -1;
00101 }
00102 
00103 
00104 
00105 void
00106 Container::initialize
00107 (
00108 IN vec_element_t& children,
00109 IN int width,
00110 IN int height
00111 )
00112 {
00113         ASSERT(width > 0, "empty?");
00114         ASSERT(height > 0, "empty?");
00115 
00116         m_children = children;
00117         m_width = width;
00118         m_height = height;
00119 }
00120 
00121 
00122 
00123 ////////////////////////////////////////////////////////////////////////////////
00124 //
00125 //      Container -- dialog::Element class interface methods
00126 //
00127 ////////////////////////////////////////////////////////////////////////////////
00128 
00129 int
00130 Container::getWidth
00131 (
00132 void
00133 )
00134 {
00135         return m_width;
00136 }
00137 
00138 
00139 int
00140 Container::getHeight
00141 (
00142 void
00143 )
00144 {
00145         return m_height;
00146 }
00147 
00148 
00149 
00150 void
00151 Container::draw
00152 (
00153 IN const point_t& offset
00154 )
00155 {
00156         for (vec_element_t::iterator i = m_children.begin();
00157              i != m_children.end(); ++i) {
00158                 element_record_t& er = *i;
00159                 ASSERT(er.element, "null element in vector");
00160 
00161                 // child is told to draw to correct global coordinates
00162                 point_t border(s_border, s_border);
00163                 er.element->draw(offset + er.bounds.getTopLeft() + border);
00164         }
00165 }
00166 
00167 
00168 
00169 void
00170 Container::cursor
00171 (
00172 IN const point_t& pos
00173 )
00174 {
00175         point_t rel;
00176         Element * e = this->getChild(pos, rel);
00177         if (e) {
00178                 e->cursor(rel);
00179         }
00180 }
00181 
00182 
00183 
00184 const char *
00185 Container::button
00186 (
00187 IN int button,
00188 IN int state,
00189 IN const point_t& pos
00190 )
00191 {
00192         point_t rel;
00193         Element * e = this->getChild(pos, rel);
00194         return e ? e->button(button, state, rel) : NULL;
00195 }
00196 
00197 
00198 
00199 Element *
00200 Container::getFocus
00201 (
00202 IN const point_t& pos
00203 )
00204 {
00205         //DPRINTF("Looking for focus at (%d, %d)", pos.x, pos.y);
00206         point_t rel;
00207         Element * e = this->getChild(pos, rel);
00208         //DPRINTF("  Found element: %p", e);
00209         return e ? e->getFocus(rel) : NULL;
00210 }
00211 
00212 
00213 
00214 void
00215 Container::addData
00216 (
00217 IN crypto::DESKey * key,
00218 IN Datahash * data
00219 )
00220 {
00221         // ASSERT(key) -- can be null
00222         ASSERT(data, "null");
00223 
00224         for (vec_element_t::iterator i = m_children.begin();
00225              i != m_children.end(); ++i) {
00226                 element_record_t& er = *i;
00227                 ASSERT(er.element, "null element in vector");
00228 
00229                 // child can attach data
00230                 er.element->addData(key, data);
00231         }
00232 }
00233 
00234 
00235 
00236 ////////////////////////////////////////////////////////////////////////////////
00237 //
00238 //      Container -- private helper methods
00239 //
00240 ////////////////////////////////////////////////////////////////////////////////
00241 
00242 Element *
00243 Container::getChild
00244 (
00245 IN const point_t& pos,
00246 OUT point_t& rel                        ///< child-relative coordinates
00247 )
00248 {
00249         rel.clear();
00250 
00251         for (vec_element_t::iterator i = m_children.begin();
00252              i != m_children.end(); ++i) {
00253                 element_record_t& er = *i;
00254                 ASSERT(er.element, "null element in vector");
00255 
00256                 if (!er.bounds.contains(pos))
00257                         continue;               // skip this one
00258 
00259                 // convert to child-relative coordinates
00260                 point_t border(s_border, s_border);
00261                 rel = pos - er.bounds.getTopLeft() - border;
00262                 return er.element;
00263         }
00264 
00265         // nothing found!
00266         return NULL;
00267 }
00268 
00269 
00270 
00271 ////////////////////////////////////////////////////////////////////////////////
00272 //
00273 //      public API
00274 //
00275 ////////////////////////////////////////////////////////////////////////////////
00276 
00277 smart_ptr<Element>
00278 createContainerElement
00279 (
00280 IN Manager * mgr,
00281 IN const Datahash * hash
00282 )
00283 {
00284         ASSERT(mgr, "null");
00285         ASSERT(hash, "null");
00286 
00287         vec_element_t children;
00288 
00289         // first, construct all child elements
00290         int maxW = 0;
00291         int maxH = 0;
00292         Datahash::iterator_t i;
00293         hash->getIterator("element", i);
00294         while (const hash_value_t * phv = hash->getNextElementUnsafe(i)) {
00295                 ASSERT_THROW(eHashDataType_Hash == phv->type,
00296                     "Bad datahash datatype (expected hash)");
00297                 smart_ptr<Datahash> subhash = phv->hash;
00298                 ASSERT(subhash, "null subhash");
00299 
00300                 // construct this child tree
00301                 element_record_t er;
00302                 er.element = constructElementTree(mgr, subhash);
00303                 ASSERT(er.element, "failed to create element subtree");
00304 
00305                 // stash known size
00306                 int w = er.element->getWidth();
00307                 int h = er.element->getHeight();
00308                 //DPRINTF("Element claims width is %d", w);
00309                 if (w > maxW) {
00310                         maxW = w;
00311                 }
00312                 if (h > maxH) {
00313                         maxH = h;
00314                 }
00315                 er.bounds.set(0, 0, w, h);
00316 
00317                 children.push_back(er);
00318         }
00319 
00320         // vertically or horizontally aligned?
00321         std::string s_align = getOptionalString(hash, "axis", "v");
00322         char align = 'v';
00323         if ("h" == s_align) {
00324                 align = 'h';
00325         }
00326 
00327         // border settings?
00328         int borderX = getOptionalLong(hash, "borderX", s_border);
00329         int borderY = getOptionalLong(hash, "borderY", s_border);
00330 
00331         // width string?
00332         const char * widthString =
00333             getOptionalString(hash, "widthString", NULL);
00334         if (widthString) {
00335                 // get drawer
00336                 smart_ptr<Drawer> drawer = mgr->getDrawer();
00337                 ASSERT(drawer, "null drawer?");
00338 
00339                 font_size_t fs =
00340                     drawer->getFontSizing(eElement_Label, widthString);
00341                 int width = fs.lead + fs.trail;
00342                 if (width > maxW) {
00343                         maxW = width;
00344                 }
00345         }
00346 
00347         // now loop again and determine proper x,y values
00348         int oldX = 0;
00349         int oldY = 0;
00350         for (vec_element_t::iterator i = children.begin(); i != children.end();
00351              ++i) {
00352                 element_record_t& er = *i;
00353 
00354                 if ('v' == align) {
00355                         int dx = (int) (0.5 * (maxW - er.bounds.right) + 0.5);
00356                         er.bounds.set(dx, oldY, er.bounds.right + dx,
00357                             er.bounds.bottom + oldY);
00358                         oldY = er.bounds.bottom;
00359                         oldY += borderY;
00360                 } else {
00361                         int dy = (int) (0.5 * (maxH - er.bounds.bottom) + 0.5);
00362                         er.bounds.set(oldX, dy, er.bounds.right + oldX,
00363                             er.bounds.bottom + dy);
00364                         oldX = er.bounds.right;
00365                         oldX += borderX;
00366                 }
00367 
00368                 //er.bounds.dump("Child element");
00369         }
00370 
00371         // update max width, height
00372         if ('v' == align) {
00373                 maxH = oldY;
00374                 maxW += borderX;
00375         } else {
00376                 maxW = oldX;
00377                 maxH += borderY;
00378         }
00379         maxW += borderX;
00380         maxH += borderY;
00381 
00382         // create container
00383         smart_ptr<Container> local = new Container;
00384         ASSERT(local, "out of memory");
00385 
00386         local->initialize(children, maxW, maxH);
00387 
00388         return local;
00389 }
00390 
00391 
00392 
00393 };      // dialog namespace
00394