Back to index

supertuxkart  0.5+dfsg1
widget.cpp
Go to the documentation of this file.
00001 //  $Id: widget_set.cpp 1094 2007-05-21 06:49:06Z hiker $
00002 //
00003 //  SuperTuxKart - a fun racing game with go-kart
00004 //  This code originally from Neverball copyright (C) 2003 Robert Kooima
00005 //
00006 //  This program is free software; you can redistribute it and/or
00007 //  modify it under the terms of the GNU General Public License
00008 //  as published by the Free Software Foundation; either version 2
00009 //  of the License, or (at your option) any later version.
00010 //
00011 //  This program is distributed in the hope that it will be useful,
00012 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //  GNU General Public License for more details.
00015 //
00016 //  You should have received a copy of the GNU General Public License
00017 //  along with this program; if not, write to the Free Software
00018 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00019 
00020 #include "widget.hpp"
00021 
00022 //FIXME: this should be removed when the scrolling is cleaned
00023 #include "user_config.hpp"
00024 
00025 #include "constants.hpp"
00026 #include "track_manager.hpp"
00027 #include "material_manager.hpp"
00028 #include "track.hpp"
00029 #include <cmath>
00030 #include <iostream>
00031 
00032 const int Widget::MAX_SCROLL = 1000000;
00033 
00034 const float Widget::MAX_TEXT_SCALE = 1.2f;
00035 const float Widget::MIN_TEXT_SCALE = 1.0f;
00036 
00037 const GLfloat WGT_WHITE  [4] = { 1.0f, 1.0f, 1.0f, 1.0f };
00038 const GLfloat WGT_GRAY   [4] = { 0.5f, 0.5f, 0.5f, 1.0f };
00039 const GLfloat WGT_BLACK  [4] = { 0.0f, 0.0f, 0.0f, 1.0f };
00040 const GLfloat WGT_YELLOW [4] = { 1.0f, 1.0f, 0.0f, 1.0f };
00041 const GLfloat WGT_RED    [4] = { 1.0f, 0.0f, 0.0f, 1.0f };
00042 const GLfloat WGT_GREEN  [4] = { 0.0f, 1.0f, 0.0f, 1.0f };
00043 const GLfloat WGT_BLUE   [4] = { 0.0f, 0.0f, 1.0f, 1.0f };
00044 const GLfloat WGT_TRANS_WHITE  [4] = { 1.0f, 1.0f, 1.0f, 0.5f };
00045 const GLfloat WGT_TRANS_GRAY   [4] = { 0.5f, 0.5f, 0.5f, 0.5f };
00046 const GLfloat WGT_TRANS_BLACK  [4] = { 0.0f, 0.0f, 0.0f, 0.5f };
00047 const GLfloat WGT_TRANS_YELLOW [4] = { 1.0f, 1.0f, 0.0f, 0.5f };
00048 const GLfloat WGT_TRANS_RED    [4] = { 1.0f, 0.0f, 0.0f, 0.5f };
00049 const GLfloat WGT_TRANS_GREEN  [4] = { 0.0f, 1.0f, 0.0f, 0.5f };
00050 const GLfloat WGT_TRANS_BLUE   [4] = { 0.0f, 0.0f, 1.0f, 0.5f };
00051 
00052 //FIXME: I should change 'LIGHT' for 'LIT'.
00053 const GLfloat WGT_LIGHT_GRAY   [4] = {1.0f, 1.0f, 1.0f, 1.0f};
00054 const GLfloat WGT_LIGHT_BLACK  [4] = {0.5f, 0.5f, 0.5f, 1.0f};
00055 const GLfloat WGT_LIGHT_YELLOW [4] = {1.0f, 1.0f, 0.5f, 1.0f};
00056 const GLfloat WGT_LIGHT_RED    [4] = {1.0f, 0.5f, 0.5f, 1.0f};
00057 const GLfloat WGT_LIGHT_GREEN  [4] = {0.5f, 1.0f, 0.5f, 1.0f};
00058 const GLfloat WGT_LIGHT_BLUE   [4] = {0.5f, 0.5f, 1.0f, 1.0f};
00059 const GLfloat WGT_LIGHT_TRANS_GRAY   [4] = {1.0f, 1.0f, 1.0f, 0.8f};
00060 const GLfloat WGT_LIGHT_TRANS_BLACK  [4] = {0.5f, 0.5f, 0.5f, 0.8f};
00061 const GLfloat WGT_LIGHT_TRANS_YELLOW [4] = {1.0f, 1.0f, 0.5f, 0.8f};
00062 const GLfloat WGT_LIGHT_TRANS_RED    [4] = {1.0f, 0.5f, 0.5f, 0.8f};
00063 const GLfloat WGT_LIGHT_TRANS_GREEN  [4] = {0.5f, 1.0f, 0.5f, 0.8f};
00064 const GLfloat WGT_LIGHT_TRANS_BLUE   [4] = {0.5f, 0.5f, 1.0f, 0.8f};
00065 
00066 const GLfloat WGT_TRANSPARENT [4] = {1.0f, 1.0f, 1.0f, 0.0f};
00067 
00068 Widget::Widget
00069 (
00070     const int X_,
00071     const int Y_,
00072     const int WIDTH_,
00073     const int HEIGHT_
00074 ) :
00075 //Switch features are not set here to sane defaults because the WidgetManager
00076 //handles that.
00077     m_x(X_), m_y(Y_),
00078     m_width(WIDTH_), m_height(HEIGHT_),
00079     m_rect_list(0),
00080     m_round_corners(WGT_AREA_ALL),
00081     m_border_list(0),
00082     m_scroll_pos_x(0), m_scroll_pos_y(0),
00083     m_text_scale(1.0f),
00084     m_enable_border(false)
00085 {
00086 }
00087 
00088 //-----------------------------------------------------------------------------
00089 Widget::~Widget()
00090 {
00091     if(glIsList(m_rect_list))
00092     {
00093         glDeleteLists(m_rect_list, 1);
00094     }
00095 
00096     if(glIsList(m_border_list))
00097     {
00098         glDeleteLists(m_border_list, 1);
00099     }
00100 
00101 }
00102 
00103 //-----------------------------------------------------------------------------
00104 void Widget::update( const float DELTA )
00105 {
00106     updateVariables( DELTA );
00107     draw();
00108 }
00109 
00110 //-----------------------------------------------------------------------------
00111 void Widget::resizeToText()
00112 {
00113     if( !m_text.empty() )
00114     {
00115         float left, right, bottom, top;
00116         m_font->getBBoxMultiLine(m_text, m_text_size, false, &left, &right, &bottom, &top);
00117 
00118         const int TEXT_WIDTH = (int)(right - left);
00119         const int TEXT_HEIGHT = (int)(top - bottom);
00120 
00121         if( TEXT_WIDTH > m_width ) m_width = TEXT_WIDTH;
00122         if( TEXT_HEIGHT > m_height ) m_height = TEXT_HEIGHT;
00123     }
00124 }
00125 
00126 //-----------------------------------------------------------------------------
00127 /* Please note that this function only lightens 'non-light' colors */
00128 void Widget::lightenColor()
00129 {
00130     if(m_rect_color == WGT_GRAY)
00131     {
00132         m_rect_color = WGT_LIGHT_GRAY;
00133     }
00134     if(m_rect_color == WGT_BLACK)
00135     {
00136         m_rect_color = WGT_LIGHT_BLACK;
00137     }
00138     else if (m_rect_color == WGT_YELLOW)
00139     {
00140         m_rect_color = WGT_LIGHT_YELLOW;
00141     }
00142     else if (m_rect_color == WGT_RED)
00143     {
00144         m_rect_color = WGT_LIGHT_RED;
00145     }
00146     else if (m_rect_color == WGT_GREEN)
00147     {
00148         m_rect_color = WGT_LIGHT_GREEN;
00149     }
00150     else if (m_rect_color == WGT_BLUE)
00151     {
00152         m_rect_color = WGT_LIGHT_BLUE;
00153     }
00154     else if (m_rect_color == WGT_TRANS_GRAY)
00155     {
00156         m_rect_color = WGT_LIGHT_TRANS_GRAY;
00157     }
00158     else if (m_rect_color == WGT_TRANS_BLACK)
00159     {
00160         m_rect_color = WGT_LIGHT_TRANS_BLACK;
00161     }
00162     else if (m_rect_color == WGT_TRANS_YELLOW)
00163     {
00164         m_rect_color = WGT_LIGHT_TRANS_YELLOW;
00165     }
00166     else if (m_rect_color == WGT_TRANS_RED)
00167     {
00168         m_rect_color = WGT_LIGHT_TRANS_RED;
00169     }
00170     else if (m_rect_color == WGT_TRANS_GREEN)
00171     {
00172         m_rect_color = WGT_LIGHT_TRANS_GREEN;
00173     }
00174     else if (m_rect_color == WGT_TRANS_BLUE)
00175     {
00176         m_rect_color = WGT_LIGHT_TRANS_BLUE;
00177     }
00178 }
00179 
00180 //-----------------------------------------------------------------------------
00181 /* Please note that this function only darkens 'light' colors. */
00182 void Widget::darkenColor()
00183 {
00184     if(m_rect_color == WGT_LIGHT_GRAY)
00185     {
00186         m_rect_color = WGT_GRAY;
00187     }
00188     if(m_rect_color == WGT_LIGHT_BLACK)
00189     {
00190         m_rect_color = WGT_BLACK;
00191     }
00192     else if (m_rect_color == WGT_LIGHT_YELLOW)
00193     {
00194         m_rect_color = WGT_YELLOW;
00195     }
00196     else if (m_rect_color == WGT_LIGHT_RED)
00197     {
00198         m_rect_color = WGT_RED;
00199     }
00200     else if (m_rect_color == WGT_LIGHT_GREEN)
00201     {
00202         m_rect_color = WGT_GREEN;
00203     }
00204     else if (m_rect_color == WGT_LIGHT_BLUE)
00205     {
00206         m_rect_color = WGT_BLUE;
00207     }
00208     else if (m_rect_color == WGT_LIGHT_TRANS_GRAY)
00209     {
00210         m_rect_color = WGT_TRANS_GRAY;
00211     }
00212     else if (m_rect_color == WGT_LIGHT_TRANS_BLACK)
00213     {
00214         m_rect_color = WGT_TRANS_BLACK;
00215     }
00216     else if (m_rect_color == WGT_LIGHT_TRANS_YELLOW)
00217     {
00218         m_rect_color = WGT_TRANS_YELLOW;
00219     }
00220     else if (m_rect_color == WGT_LIGHT_TRANS_RED)
00221     {
00222         m_rect_color = WGT_TRANS_RED;
00223     }
00224     else if (m_rect_color == WGT_LIGHT_TRANS_GREEN)
00225     {
00226         m_rect_color = WGT_TRANS_GREEN;
00227     }
00228     else if (m_rect_color == WGT_LIGHT_TRANS_BLUE)
00229     {
00230         m_rect_color = WGT_TRANS_BLUE;
00231     }
00232 }
00233 
00234 //-----------------------------------------------------------------------------
00235 void Widget::setFont( const WidgetFont FONT )
00236 {
00237     switch( FONT )
00238     {
00239         case WGT_FONT_GUI:
00240             m_font = font_gui;
00241             break;
00242 
00243         case WGT_FONT_RACE:
00244             m_font = font_race;
00245             break;
00246     };
00247 
00248     //TODO: the curr_widget_font variable exists only for a bug around; after
00249     //some restructuration, it should be fine to remove this.
00250     m_curr_widget_font = FONT;
00251 }
00252 
00253 //-----------------------------------------------------------------------------
00254 void Widget::setTexture( const char* FILENAME, bool is_full_path )
00255 {
00256     Material *m = material_manager->getMaterial( FILENAME, is_full_path );
00257     m_texture = m->getState()->getTextureHandle();
00258 }
00259 
00265 bool Widget::createRect()
00266 {
00267 
00268     //TODO: show warning if text > rect
00269     if(m_radius > m_width * 0.5)
00270     {
00271         std::cerr << "Warning: widget's radius > half width.\n";
00272     }
00273     if(m_radius > m_height * 0.5)
00274     {
00275         std::cerr << "Warning: widget's radius > half height.\n";
00276     }
00277     if(m_radius < 1)
00278     {
00279         std::cerr << "Warning: widget's radius < 1, setting to 1.\n";
00280         m_radius = 1;
00281     }
00282 
00283     if(m_width == 0)
00284     {
00285         std::cerr << "Warning: creating widget rect with width 0, " <<
00286             "setting to 1.\n";
00287         m_width = 1;
00288     }
00289     if(m_height == 0)
00290     {
00291         std::cerr << "Warning: creating widget rect with height 0, " <<
00292             "setting to 1.\n";
00293         m_height = 1;
00294     }
00295 
00296     if(!glIsList(m_rect_list))
00297     {
00298         m_rect_list = glGenLists(1);
00299         if(m_rect_list == 0)
00300         {
00301             std::cerr << "Error: could not create a widget's rect list.\n";
00302             return false;
00303         }
00304     }
00305 
00306     //Calculate the number of quads each side should have. The algorithm
00307     //isn't based just on logic, since it went through visual testing to give
00308     //the perception of roundness.
00309     const int MIN_QUADS = 2;
00310     const int NUM_QUADS = MIN_QUADS + m_radius;
00311 
00312     int i;
00313 
00314     const int SMALLER_SIDE_LENGTH = m_height < m_width ? m_height / 2 : m_width / 2;
00315     const float BORDER_LENGTH = SMALLER_SIDE_LENGTH * m_border_percentage;
00316 
00317     typedef std::vector<float> float3;
00318     std::vector<float3> inner_vertex;
00319     std::vector<float3> outer_vertex;
00320 
00321     //NUM_QUADS + 1, because we have to add the union between the sides, and
00322     //multiplied by 2, because there are two sides
00323     inner_vertex.resize((NUM_QUADS + 1) * 2);
00324     outer_vertex.resize((NUM_QUADS + 1) * 2);
00325 
00326     const float HALF_WIDTH = m_width * 0.5f;
00327     const float HALF_HEIGHT = m_height * 0.5f;
00328 
00329     glNewList(m_rect_list, GL_COMPILE);
00330     {
00331         glBegin(GL_QUAD_STRIP);
00332         {
00333             //These are used to center the widget; without centering, the
00334             //widget's 0,0 coordinates are at the lower left corner.
00335             float angle;
00336             float circle_x, circle_y;
00337 
00338             //Draw the left side of the inside
00339             for (i = 0; i <= NUM_QUADS; ++i)
00340             {
00341                 //To find the position in the X and Y axis of each point of
00342                 //the quads, we use the property of the unit circle (a circle
00343                 //with radius = 1) that at any given angle, cos(angle) is the
00344                 //position of the unit circle at that angle in the X axis,
00345                 //and that sin(angle) is the position of the unit circle at
00346                 //that angle in the Y axis. Then the values from cos(angle)
00347                 //and sin(angle) are multiplied by the radius.
00348                 //
00349                 //First we find the angle: since 2 * pi is the number of
00350                 //radians in an entire circle, 0.5 * pi is a quarter of the
00351                 //circle, which is a corner of the rounded rectangle. Based
00352                 //on that, we just split the radians in a corner in NUM_QUADS
00353                 //+ 1 parts, and use the angles at those parts to find the
00354                 //X and Y position of the points.
00355                 angle = 0.5f * M_PI * (float)i / (float)NUM_QUADS;
00356                 circle_x = m_radius * cos(angle);
00357                 circle_y = m_radius * sin(angle);
00358 
00359                 //After we generate the positions in circle for the angles,
00360                 //we have to position each rounded corner properly depending
00361                 //on the position of the rectangle and the radius. The y
00362                 //position for the circle is dependant on rect; if a corner
00363                 //wasn't given, then the y position is computed as if it was
00364                 //for a rectangle without rounder corners.
00365                 //
00366                 //The value in the position 0 of these vectors is X, the
00367                 //second the Y for the top part of the widget and the third
00368                 //the Y for the lower part of the widget.
00369                 inner_vertex[i].resize(3);
00370                 outer_vertex[i].resize(3);
00371                 outer_vertex[i][0] = m_radius - circle_x;
00372                 inner_vertex[i][0] = outer_vertex[i][0] + BORDER_LENGTH;
00373 
00374                 if( m_round_corners & WGT_AREA_NW )
00375                 {
00376                     outer_vertex[i][1] = m_height + circle_y - m_radius;
00377                     inner_vertex[i][1] = outer_vertex[i][1] - BORDER_LENGTH;
00378                 }
00379                 else
00380                 {
00381                     outer_vertex[i][1] =(float) m_height;
00382                     inner_vertex[i][1] = outer_vertex[i][1] - BORDER_LENGTH;
00383                 }
00384 
00385                 if( m_round_corners & WGT_AREA_SW )
00386                 {
00387                     outer_vertex[i][2] = m_radius - circle_y;
00388                     inner_vertex[i][2] = outer_vertex[i][2] + BORDER_LENGTH;
00389                 }
00390                 else
00391                 {
00392                     outer_vertex[i][2] = 0;
00393                     inner_vertex[i][2] = outer_vertex[i][2] + BORDER_LENGTH;
00394                 }
00395 
00396                 glTexCoord2f(inner_vertex[i][0] / (float)m_width, inner_vertex[i][1] / (float)m_height);
00397                 glVertex2f(inner_vertex[i][0] - HALF_WIDTH, inner_vertex[i][1] - HALF_HEIGHT);
00398 
00399                 glTexCoord2f(inner_vertex[i][0] / (float)m_width, inner_vertex[i][2] / (float)m_height);
00400                 glVertex2f(inner_vertex[i][0] - HALF_WIDTH, inner_vertex[i][2] - HALF_HEIGHT);
00401             }
00402 
00403             //Draw the right side of a rectangle
00404             for (i = NUM_QUADS; i < NUM_QUADS * 2 + 1; ++i)
00405             {
00406                 angle = 0.5f * M_PI * (float) (i - NUM_QUADS) / (float) NUM_QUADS;
00407 
00408                 //By inverting the use of sin and cos we get corners that are
00409                 //drawn from left to right instead of right to left
00410                 circle_x = m_radius * sin(angle);
00411                 circle_y = m_radius * cos(angle);
00412 
00413                 inner_vertex[i+1].resize(3);
00414                 outer_vertex[i+1].resize(3);
00415                 outer_vertex[i+1][0] = m_width - m_radius + circle_x;
00416                 inner_vertex[i+1][0] = outer_vertex[i+1][0] - BORDER_LENGTH;
00417 
00418                 if( m_round_corners & WGT_AREA_NE )
00419                 {
00420                     outer_vertex[i+1][1] = m_height - m_radius + circle_y;
00421                     inner_vertex[i+1][1] = outer_vertex[i+1][1] - BORDER_LENGTH;
00422                 }
00423                 else
00424                 {
00425                     outer_vertex[i+1][1] = (float)m_height;
00426                     inner_vertex[i+1][1] = outer_vertex[i+1][1] - BORDER_LENGTH;
00427                 }
00428 
00429                 if( m_round_corners & WGT_AREA_SE )
00430                 {
00431                     outer_vertex[i+1][2] = m_radius - circle_y;
00432                     inner_vertex[i+1][2] = outer_vertex[i+1][2] + BORDER_LENGTH;
00433                 }
00434                 else
00435                 {
00436                     outer_vertex[i+1][2] = 0;
00437                     inner_vertex[i+1][2] = outer_vertex[i+1][2] + BORDER_LENGTH;
00438                 }
00439 
00440                 glTexCoord2f(inner_vertex[i+1][0] / (float)m_width, inner_vertex[i+1][1] / (float)m_height);
00441                 glVertex2f(inner_vertex[i+1][0] - HALF_WIDTH, inner_vertex[i+1][1] - HALF_HEIGHT);
00442 
00443                 glTexCoord2f(inner_vertex[i+1][0] / (float)m_width, inner_vertex[i+1][2] / (float)m_height);
00444                 glVertex2f(inner_vertex[i+1][0] - HALF_WIDTH, inner_vertex[i+1][2] - HALF_HEIGHT);
00445             }
00446         }
00447         glEnd();
00448     }
00449     glEndList();
00450 
00451 
00452     if(!glIsList(m_border_list))
00453     {
00454         m_border_list = glGenLists(1);
00455         if(m_border_list == 0)
00456         {
00457             std::cerr << "Error: could not create a widget's border list.\n";
00458             return false;
00459         }
00460     }
00461 
00462     glNewList(m_border_list, GL_COMPILE);
00463     {
00464         glBegin(GL_QUAD_STRIP);
00465         {
00466             for (i = NUM_QUADS * 2 + 1; i >= 0 ; --i)
00467             {
00468                 glVertex2f(outer_vertex[i][0] - HALF_WIDTH, outer_vertex[i][1] - HALF_HEIGHT);
00469                 glVertex2f(inner_vertex[i][0] - HALF_WIDTH, inner_vertex[i][1] - HALF_HEIGHT);
00470             }
00471 
00472             for (i = 0; i <= NUM_QUADS * 2 + 1; ++i)
00473             {
00474                 glVertex2f(outer_vertex[i][0] - HALF_WIDTH, outer_vertex[i][2] - HALF_HEIGHT);
00475                 glVertex2f(inner_vertex[i][0] - HALF_WIDTH, inner_vertex[i][2] - HALF_HEIGHT);
00476             }
00477 
00478             //Close the border
00479             glVertex2f(outer_vertex[NUM_QUADS * 2 + 1][0] - HALF_WIDTH, outer_vertex[NUM_QUADS * 2 + 1][1] - HALF_HEIGHT);
00480             glVertex2f(inner_vertex[NUM_QUADS * 2 + 1][0] - HALF_WIDTH, inner_vertex[NUM_QUADS * 2 + 1][1] - HALF_HEIGHT);
00481         }
00482         glEnd();
00483 
00484     }
00485     glEndList();
00486 
00487     return true;
00488 }
00489 
00490 //-----------------------------------------------------------------------------
00491 void Widget::updateVariables( const float DELTA )
00492 {
00493     if( m_enable_rotation ) m_rotation_angle += m_rotation_speed * DELTA;
00494 
00495     /*Handle delta time dependent features*/
00496     if(m_text_scale > MIN_TEXT_SCALE)
00497     {
00498         m_text_scale -= MIN_TEXT_SCALE * DELTA;
00499         if(m_text_scale < MIN_TEXT_SCALE) m_text_scale = MIN_TEXT_SCALE;
00500     }
00501 
00502 
00503     //For multilines we have to do a *very* ugly workaround for a plib
00504     //bug which causes multiline strings to move to the left, at least
00505     //while centering, and also gives wrong values for the size of the
00506     //text when there are multiple lines. Hopefully this work around will
00507     //be removed when we move away from plib; the scrolling and the other
00508     //text handling should be cleaned. Also, for some reason, different
00509     //positions are needed if the text is centered, and on top of that,
00510     //it's not 100% exact. Sorry for the mess.
00511     size_t line_end = 0;
00512     int lines = 0;
00513 
00514     do
00515     {
00516         line_end = m_text.find_first_of('\n', line_end + 1);
00517         ++lines;
00518     } while( line_end != std::string::npos );
00519 
00520 
00521     /* Handle preset scrolling positions */
00522     // In the Y-axis, a scroll position of 0 leaves the text centered, and
00523     // positive values lowers the text, and negatives (obviously) raise the
00524     // text, in the X-axis, a position of 0 leaves the text aligned to the
00525     // left; positive values move to the right and negative
00526     // values to the left.
00527 
00528     float left, right;
00529     m_font->getBBoxMultiLine(m_text, m_text_size, false, &left, &right, NULL, NULL);
00530     int text_width = (int)(right - left + 0.99);
00531 
00532 
00533     const int Y_LIMIT = lines * m_text_size + m_height;
00534 
00535     //A work around for yet another bug with multilines: we get the wrong
00536     //width when using multilines.
00537     if( text_width > m_width )
00538     {
00539         text_width = m_width;
00540     }
00541 
00542     //With the preset positions, we do comparations with the equal sign on
00543     //floating point variables; however, no operations are done of the
00544     //variables between the assignment of these integer values and the
00545     //comparation and the values are small enough to fit in a few bytes,
00546     //so no inaccuracies because of floating point rounding should happen.
00547     //X-axis preset positions
00548     if( m_scroll_pos_x == WGT_SCROLL_START_LEFT )
00549     {
00550         m_scroll_pos_x = 0;
00551     }
00552     else if( m_scroll_pos_x == WGT_SCROLL_START_RIGHT )
00553     {
00554         m_scroll_pos_x = (float)m_width;
00555     }
00556     else if( m_scroll_pos_x == WGT_SCROLL_CENTER )
00557     {
00558         m_scroll_pos_x = (float)( (m_width - text_width) / 2 );
00559     }
00560     else if( m_scroll_pos_x == WGT_SCROLL_END_LEFT )
00561     {
00562         m_scroll_pos_x = (float)(-text_width);
00563     }
00564     else if( m_scroll_pos_x == WGT_SCROLL_END_RIGHT )
00565     {
00566         m_scroll_pos_x = (float)(m_width - text_width);
00567     }
00568     else if( m_scroll_pos_x > MAX_SCROLL )
00569     {
00570         std::cerr << "WARNING: text position is too much to the right to " <<
00571             "scroll!.\n";
00572     }
00573     else if( m_scroll_pos_x < -MAX_SCROLL )
00574     {
00575         std::cerr << "WARNING: text position is too much to the left to " <<
00576             "to scroll!.\n";
00577     }
00578 
00579     //Y-axis preset positions
00580     if( m_scroll_pos_y == WGT_SCROLL_START_TOP )
00581     {
00582         m_scroll_pos_y =(float)(Y_LIMIT / 2 - m_height);
00583     }
00584     else if( m_scroll_pos_y == WGT_SCROLL_START_BOTTOM )
00585     {
00586         m_scroll_pos_y = (float)(Y_LIMIT / 2);
00587     }
00588     else if( m_scroll_pos_y == WGT_SCROLL_CENTER )
00589     {
00590         m_scroll_pos_y = 0;
00591     }
00592     else if( m_scroll_pos_y == WGT_SCROLL_END_TOP )
00593     {
00594         m_scroll_pos_y = (float)(-Y_LIMIT / 2);
00595     }
00596     else if( m_scroll_pos_y == WGT_SCROLL_END_BOTTOM )
00597     {
00598         m_scroll_pos_y = (float)(-Y_LIMIT / 2 + m_height);
00599     }
00600     else if( m_scroll_pos_y > MAX_SCROLL )
00601     {
00602         std::cerr << "WARNING: text position too high to scroll!.\n";
00603     }
00604     else if( m_scroll_pos_y < -MAX_SCROLL )
00605     {
00606         std::cerr << "WARNING: text position too low to scroll!.\n";
00607     }
00608 
00609     if(m_enable_scroll)
00610     {
00611         //TODO: constrain speed to sane values
00612         m_scroll_pos_x += m_scroll_speed_x * DELTA;
00613         m_scroll_pos_y += m_scroll_speed_y * DELTA;
00614 
00615         //Y-axis wrapping
00616         if(m_scroll_pos_y * 2 > Y_LIMIT)
00617         {
00618             m_scroll_pos_y = WGT_SCROLL_END_TOP;
00619         }
00620         else if(-m_scroll_pos_y * 2 > Y_LIMIT)
00621         {
00622             m_scroll_pos_y = WGT_SCROLL_START_BOTTOM;
00623         }
00624 
00625         //X-axis wrapping
00626         if(m_scroll_pos_x > m_width )
00627         {
00628             m_scroll_pos_x = WGT_SCROLL_END_LEFT;
00629         }
00630         else if(m_scroll_pos_x < -text_width )
00631         {
00632             m_scroll_pos_x = WGT_SCROLL_START_RIGHT;
00633         }
00634     }
00635 }
00636 
00637 //-----------------------------------------------------------------------------
00638 void Widget::draw()
00639 {
00640     glPushMatrix();
00641 
00642     glClear( GL_STENCIL_BUFFER_BIT );
00643 
00644     applyTransformations();
00645 
00646     /*Start handling on/off features*/
00647     if( m_enable_texture )
00648     {
00649         glEnable( GL_TEXTURE_2D );
00650         if( glIsTexture ( m_texture ))
00651         {
00652             glBindTexture( GL_TEXTURE_2D, m_texture );
00653         }
00654         else
00655         {
00656             std::cerr << "Warning: widget tried to draw null texture.\n";
00657             std::cerr << "(Did you set the texture?)\n";
00658         }
00659     }
00660     else
00661     {
00662         //This ensures that a texture from another module doesn't affects the widget
00663         glDisable ( GL_TEXTURE_2D );
00664     }
00665 
00666     if( glIsList ( m_rect_list ))
00667     {
00668         //m_enable_rect == false doesn't disables this chunk of code because
00669         //we still need to draw the rect into OpenGL's selection buffer even
00670         //if it's not visible
00671 
00672         //FIXME: maybe there is some sort of stacking method to disable/enable
00673         //color masking
00674         if(!m_enable_rect)
00675         {
00676             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
00677         }
00678         else
00679         {
00680             glColor4fv(m_rect_color);
00681         }
00682 
00683         //FIXME: I should probably revert the values to the defaults within the widget manager
00684         //(if glPushAttrib() doesn't), but right now this is the only thing using the
00685         //stencil test anyways.
00686         glStencilFunc(GL_ALWAYS, 0x1, 0x1);
00687         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
00688         glCallList(m_rect_list);
00689 
00690         if(!m_enable_rect)
00691         {
00692             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
00693         }
00694     }
00695     else
00696     {
00697         std::cerr << "Warning: widget tried to draw null rect list.\n";
00698         std::cerr << "(Did you created the rect?)\n";
00699     }
00700 
00701     if(glIsList(m_border_list))
00702     {
00703         if( m_enable_border )
00704         {
00705             glDisable ( GL_TEXTURE_2D );
00706             glColor4fv(m_border_color);
00707 
00708             //FIXME: I should probably revert the values to the defaults within the widget manager
00709             //(if glPushAttrib() doesn't)
00710             glStencilFunc(GL_ALWAYS, 0x1, 0x1);
00711             glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
00712             glCallList(m_border_list);
00713         }
00714     }
00715     else
00716     {
00717         std::cerr << "Warning: widget tried to draw null border list.\n";
00718         std::cerr << "(Did you created the border?)\n";
00719     }
00720 
00721     if( m_enable_track )
00722     {
00723         if( m_track_num > (int)(track_manager->getTrackCount()) - 1)
00724         {
00725             std::cerr << "Warning: widget tried to draw a track with a " <<
00726                 "number bigger than the amount of tracks available.\n";
00727         }
00728 
00729         if( m_track_num != -1 )
00730         {
00731             track_manager->getTrack( m_track_num )->drawScaled2D( 0.0f, 
00732                 0.0f, (float)m_width, (float)m_height);
00733         }
00734         else
00735         {
00736             std::cerr << "Warning: widget tried to draw an unset track.\n";
00737         }
00738     }
00739 
00740     if(m_enable_text)
00741     {
00742         if(m_text.empty())
00743         {
00744             std::cerr << "Warning: widget tried to print an empty string.\n";
00745             std::cerr << "(Did you set the text?)\n";
00746         }
00747 
00748 
00749         //For multilines we have to do a *very* ugly workaround for a plib
00750         //bug which causes multiline strings to move to the left, at least
00751         //while centering, and also gives wrong values for the size of the
00752         //text when there are multiple lines. Hopefully this work around will
00753         //be removed when we move away from plib; the scrolling and the other
00754         //text handling should be cleaned. Also, for some reason, different
00755         //positions are needed if the text is centered, and on top of that,
00756         //it's not 100% exact. Sorry for the mess.
00757         size_t line_end = 0;
00758         int lines = 0;
00759 
00760         do
00761         {
00762             line_end = m_text.find_first_of('\n', line_end + 1);
00763             ++lines;
00764         } while( line_end != std::string::npos );
00765 
00766 
00767         int x_pos = (int)(m_scroll_pos_x - m_width * 0.5f);
00768         int y_pos = - (int)m_scroll_pos_y + (lines - 1 )* m_text_size / 2;
00769 
00770         size_t line_start = 0;
00771         bool draw;
00772 
00773         glStencilFunc(GL_EQUAL,0x1,0x1);
00774         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
00775         do
00776         {
00777             draw = true;
00778             if(y_pos + m_text_size / 2 > m_height / 2 )
00779             {
00780                 if(y_pos - m_text_size / 2 >  m_height / 2) draw = false;
00781             }
00782             else if(y_pos + (m_height - m_text_size) / 2 < 0)
00783             {
00784                 if(y_pos + (m_height + m_text_size) / 2 < 0) draw = false;
00785             }
00786 
00787             line_end = m_text.find_first_of('\n', line_start);
00788 
00789             if( draw )
00790             {
00791                 glScalef(m_text_scale, m_text_scale, 1.0f);
00792                 m_font->PrintBold(m_text.substr(line_start, line_end - line_start).c_str(), 
00793                     m_text_size,
00794                     x_pos, y_pos - m_text_size / 2,
00795                     m_text_color, 1.0f, 1.0f);
00796                 glScalef(1.0f/m_text_scale, 1.0f/m_text_scale, 1.0f);
00797             }
00798 
00799             y_pos -= m_text_size;
00800             line_start = line_end + 1;
00801 
00802         } while( line_end != std::string::npos );
00803     }
00804     glPopMatrix();
00805 }
00806 
00807 //-----------------------------------------------------------------------------
00808 void Widget::applyTransformations()
00809 {
00810     /* OpenGL transformations are affected by the order of the calls; but the
00811      * operations must be called in the inverse order that you want them to
00812      * be applied, since the calls are stacked, and the one at the top is
00813      * done first, till the one at the bottom.
00814      */
00815     glTranslatef ( (GLfloat)(m_x + m_width * 0.5f), (GLfloat)(m_y + m_height * 0.5f), 0);
00816 
00817     if( m_enable_rotation )
00818     {
00819         glRotatef( (GLfloat)m_rotation_angle, 0.0f, 0.0f, (GLfloat)1.0f );
00820     }
00821 }