core/vgui/vgui_poly_tableau.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 //
00004 // Class vgui_poly_tableau is a tableau which renders its children into sub-rectangles
00005 // of its given viewport. The subrectangles are given as relative coordinates
00006 // on [0,1]x[0,1], with (0,0) being the lower left corner and (1,1) the upper
00007 // right corner. vgui_poly_tableau has a concept of which child is 'current', meaning
00008 // roughly which child is getting the mouse events.
00009 //
00010 // Class vgui_poly_tableau is derived from vgui_poly_tableau and automatically switches current
00011 // child, according to where the pointer is, in a sensible way.
00012 //
00013 // This can be used to emulate two adaptors side by side.
00014 
00015 // \par Implementation notes:
00016 // Many methods take an argument "GLint const vp[4]", which is the viewport (in
00017 // the format returned by OpenGL) as it was when the last event reached the
00018 // tableau. For example, it is not possible to switch 'current' child without
00019 // knowing the viewport, because a LEAVE/ENTER pair have to be sent to the old
00020 // and new child and the viewport must be set correctly before dispatching these
00021 // events.
00022 //
00023 // \author fsm
00024 
00025 #include "vgui_poly_tableau.h"
00026 
00027 #include <vcl_iostream.h>
00028 #include <vcl_cassert.h>
00029 
00030 #include <vgui/vgui_gl.h>
00031 #include <vgui/vgui_macro.h>
00032 #include <vgui/vgui_event.h>
00033 #include <vgui/vgui_matrix_state.h>
00034 #include <vgui/vgui_popup_params.h>
00035 
00036 //-----------------------------------------------------------------------------
00037 vgui_poly_tableau::item::item(vgui_tableau* p, vgui_tableau_sptr const&c,
00038                               float x_, float y_, float w_, float h_,
00039                               int id_)
00040   : tab(p, c)
00041   , x(x_), y(y_)
00042   , w(w_), h(h_)
00043   , id(id_)
00044 {
00045   outline_color[0] = outline_color[1] = outline_color[2] = 1; // outline in white
00046 }
00047 
00048 //-----------------------------------------------------------------------------
00049 void vgui_poly_tableau::item::set_vp(GLint const vp[4])
00050 {
00051   int region[4]={
00052     int(vp[0] + x*vp[2]), // x
00053     int(vp[1] + y*vp[3]), // y
00054     int(w*vp[2]),  // w
00055     int(h*vp[3])   // h
00056   };
00057 
00058   glViewport( region[0], region[1], region[2], region[3] );
00059   glScissor ( region[0], region[1], region[2], region[3] );
00060 }
00061 
00062 //-----------------------------------------------------------------------------
00063 //: Returns true if the given position is inside the boundaries of this item.
00064 bool vgui_poly_tableau::item::inside(GLint const vp[4],int vx, int vy) const
00065 {
00066   float rx = float(vx-vp[0])/vp[2];
00067   float ry = float(vy-vp[1])/vp[3];
00068   bool ans = (x<=rx && rx<x+w) && (y<=ry && ry<y+h);
00069   #ifdef DEBUG
00070   if (ans)
00071     vcl_cerr << "Point "<< vx << ' '<< vy <<" inside sub-window: "<< id << '\n';
00072   #endif
00073   return ans;
00074 }
00075 
00076 //-----------------------------------------------------------------------------
00077 //: Constructor - don't use this, use vgui_poly_tableau_new.
00078 vgui_poly_tableau::vgui_poly_tableau()
00079   : vgui_tableau()
00080   , current(-1)
00081   , may_switch_child(true)
00082 {
00083 }
00084 
00085 //-----------------------------------------------------------------------------
00086 //: Destructor - called by vgui_poly_tableau_sptr.
00087 vgui_poly_tableau::~vgui_poly_tableau()
00088 {
00089 }
00090 
00091 //-----------------------------------------------------------------------------
00092 //: Erase the item at the given position from the list of items.
00093 void vgui_poly_tableau::erase(iterator i)
00094 {
00095   assert(sub.begin()<=i && i<sub.end()); // wrong iterator for this container.
00096 
00097   if (current == i-sub.begin())
00098     current = -1;
00099 
00100   sub.erase(i);
00101 }
00102 
00103 //-----------------------------------------------------------------------------
00104 //: Remove subtableau, referred to by handle.
00105 void vgui_poly_tableau::remove(int id)
00106 {
00107   for (iterator i=begin(); i!=end(); ++i)
00108     if (i->id == id) {
00109       erase(i);
00110       return;
00111     }
00112   vgui_macro_warning << "no such id " << id << vcl_endl;
00113 }
00114 
00115 //-----------------------------------------------------------------------------
00116 //:  Move subtableau to a new location.
00117 void vgui_poly_tableau::move(int id, float x, float y, float w, float h)
00118 {
00119   for (iterator i=begin(); i!=end(); ++i)
00120     if (i->id == id) {
00121       i->x = x;
00122       i->y = y;
00123       i->w = w;
00124       i->h = h;
00125       post_redraw();
00126       return;
00127     }
00128   vgui_macro_warning << "no such id " << id << vcl_endl;
00129 }
00130 
00131 //-----------------------------------------------------------------------------
00132 //: Replace the tableau with the given ID by the given tableau.
00133 //  Keep the same ID and do not change the value of 'current'.
00134 void vgui_poly_tableau::replace(int id, vgui_tableau_sptr const& tab)
00135 {
00136   for (iterator i=begin(); i!=end(); ++i)
00137     if (i->id == id) {
00138       i->tab.assign(tab);
00139       if (tab)
00140         tab->post_redraw();
00141       else
00142         post_redraw();
00143       return;
00144     }
00145   vgui_macro_warning << "no such id " << id << vcl_endl;
00146 }
00147 
00148 //-----------------------------------------------------------------------------
00149 //: Returns the tableau with the given ID.
00150 vgui_tableau_sptr vgui_poly_tableau::get(int id) const
00151 {
00152   for (const_iterator i=begin(); i!=end(); ++i)
00153     if (i->id == id)
00154       return i->tab;
00155   return vgui_tableau_sptr();
00156 }
00157 
00158 //-----------------------------------------------------------------------------
00159 //: Sets the color that the tableau is outlined with .
00160 void vgui_poly_tableau::set_outline_color(const int id, const int r,
00161                                           const int g, const int b)
00162 {
00163   for (unsigned i=0; i<sub.size(); ++i) {
00164     if (sub[i].id == id) {
00165       sub[i].outline_color[0] = r;
00166       sub[i].outline_color[1] = g;
00167       sub[i].outline_color[2] = b;
00168     }
00169   }
00170 }
00171 
00172 //-----------------------------------------------------------------------------
00173 //: Adds the given tableau to the given proportion of the viewport.
00174 //  x,y,w,h specify a portion of the vgui_poly_tableau's viewport in coordinates
00175 //  which go from 0 to 1.
00176 int vgui_poly_tableau::add(vgui_tableau_sptr const& t, float x, float y,
00177                            float w, float h)
00178 {
00179   static int counter = 0;
00180   assert(counter < 1000000); // a million. FIXME.
00181   item it(this, t, x, y, w, h, ++counter) ;
00182   sub.push_back(it);
00183 #ifdef DEBUG
00184   vcl_cerr << "id = " << sub.back().id << '\n'
00185            << "x  = " << sub.back().x << '\n'
00186            << "y  = " << sub.back().y << '\n'
00187            << "w  = " << sub.back().w << '\n'
00188            << "h  = " << sub.back().h << '\n';
00189 #endif
00190   return counter;
00191 }
00192 
00193 //-----------------------------------------------------------------------------
00194 //: Misnomer - gets index of the child currently under the pointer's position.
00195 int vgui_poly_tableau::get_active(GLint const vp[4], int wx, int wy) const
00196 {
00197   int act = -1;
00198   for (unsigned i=0; i<sub.size(); ++i)
00199     if (sub[i].inside(vp, wx, wy) )
00200       act = i;
00201   return act;
00202 }
00203 
00204 //-----------------------------------------------------------------------------
00205 //: Returns the ID of the current child.
00206 int vgui_poly_tableau::get_current_id()
00207 {
00208   return (current != -1) ? sub[current].id : -1;
00209 }
00210 
00211 //-----------------------------------------------------------------------------
00212 //: Sets the child under the pointer to current.
00213 void vgui_poly_tableau::set_current(GLint const vp[4], int index)
00214 {
00215   if (current == index)
00216     return;
00217 
00218   // send leave event to old current subtableau :
00219   if (current != -1) {
00220     vgui_event e(vgui_LEAVE);
00221     sub[current].set_vp(vp);
00222     sub[current].tab->handle(e);
00223   }
00224 
00225   // switch :
00226 #ifdef DEBUG
00227   vcl_cerr << "vgui_poly_tableau::set_current: switch from " << current << " to " << index << '\n';
00228 #endif
00229   current = index;
00230 
00231   // send enter event to new current subtableau :
00232   if (current != -1) {
00233     vgui_event e(vgui_ENTER);
00234     sub[current].set_vp(vp);
00235     sub[current].tab->handle(e);
00236   }
00237 }
00238 
00239 //-----------------------------------------------------------------------------
00240 //: Handles events for this tableau and passes unused ones to the correct child.
00241 bool vgui_poly_tableau::handle(GLint const vp[4], vgui_event const &e)
00242 {
00243   // Draw events must go to all children, in the right order.
00244   if (e.type==vgui_DRAW || e.type==vgui_DRAW_OVERLAY) {
00245     // save current matrix state so that we can restore it
00246     // after drawing each item (so that the next item will
00247     // have correct settings).
00248     vgui_matrix_state PM(false);
00249     PM.save();
00250 
00251     for (unsigned i=0; i<sub.size(); ++i) {
00252       PM.restore();
00253 
00254       // set viewport for child.
00255       sub[i].set_vp(vp);
00256 
00257       // let child handle the event.
00258       sub[i].tab && sub[i].tab->handle(e);
00259 
00260       if (e.type == vgui_DRAW) {
00261         // draw border of child.
00262         vgui_matrix_state::identity_gl_matrices();
00263         glColor3f(sub[i].outline_color[0],
00264                   sub[i].outline_color[1],
00265                   sub[i].outline_color[2]);
00266         glLineWidth(3);
00267         glMatrixMode(GL_PROJECTION);
00268         glLoadIdentity();
00269         glMatrixMode(GL_MODELVIEW);
00270         glLoadIdentity();
00271         glOrtho(0, vp[2], 0, vp[3], -1, +1);
00272         glBegin(GL_LINE_LOOP);
00273         glVertex2d(      0, 0);
00274         glVertex2d(vp[2]-1, 0);
00275         glVertex2d(vp[2]-1, vp[3]-1);
00276         glVertex2d(      0, vp[3]-1);
00277         glEnd();
00278       }
00279     }
00280 
00281     return true;
00282   }
00283 
00284   // Timer events go to each child, till handled.
00285   if (e.type == vgui_TIMER) {
00286     vgui_matrix_state PM(false);
00287     PM.save();
00288     bool handled = false;
00289     for (unsigned i=0; i<sub.size(); ++i) {
00290       PM.restore();
00291       sub[i].set_vp(vp);
00292       if (sub[i].tab && sub[i].tab->handle(e)) {
00293         handled = true;
00294         break;
00295       }
00296     }
00297     return handled;
00298   }
00299 
00300   // All other events (including leave and enter) are passed
00301   // to the current child, if there is one.
00302   if (current != -1) {
00303     sub[current].set_vp(vp);
00304     return sub[current].tab && sub[current].tab->handle(e);
00305   }
00306 
00307   // In case of doubt, or if there is no current child, return false.
00308   return false;
00309 }
00310 
00311 //-----------------------------------------------------------------------------
00312 //: Returns the type of this tableau ('vgui_poly_tableau').
00313 vcl_string vgui_poly_tableau::type_name() const
00314 {
00315   return "vgui_poly_tableau";
00316 }
00317 
00318 //-----------------------------------------------------------------------------
00319 //: Handle all events sent to this tableau.
00320 bool vgui_poly_tableau::handle(vgui_event const &e)
00321 {
00322   // Take snapshot of the viewport and scissor areas
00323   vgui_poly_tableau_vp_sc_snapshot snap;
00324   glEnable(GL_SCISSOR_TEST);
00325 
00326   // pointer motion
00327   if (e.type == vgui_MOTION)
00328   {
00329     // switch child, if necessary
00330     if (may_switch_child) {
00331       int active = get_active(snap.vp, e.wx, e.wy);
00332       if (active!=-1 && active!=get_current())
00333         set_current(snap.vp, active);
00334     }
00335     return handle(snap.vp, e);
00336   }
00337 
00338   // button down
00339   else if (e.type == vgui_BUTTON_DOWN)
00340   {
00341     // disallow child switch
00342     may_switch_child = false;
00343 
00344     return handle(snap.vp, e);
00345   }
00346 
00347   // button up
00348   else if (e.type == vgui_BUTTON_UP)
00349   {
00350     // Call this first because the button might be released over a
00351     // child other than the current one.
00352     bool f = handle(snap.vp, e);
00353 
00354     // allow child switch. (assumes all buttons are released now).
00355     may_switch_child = true;
00356 
00357     // switch child, if necessary
00358     int active = get_active(snap.vp, e.wx, e.wy);
00359     if (active!=-1 && active!=get_current())
00360       set_current(snap.vp, active);
00361 
00362      return f;
00363   }
00364   return handle(snap.vp, e);
00365 }
00366 
00367 //-----------------------------------------------------------------------------
00368 void vgui_poly_tableau::get_popup(vgui_popup_params const &params, vgui_menu &menu)
00369 {
00370   if (params.recurse) {
00371     int index = get_current();
00372     if (index >=0)
00373       sub[index].tab->get_popup(params, menu);
00374   }
00375 }