Back to index

nux  3.0.0
Coverflow.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2012 Canonical Ltd
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 3 as
00007  * published by the Free Software Foundation.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * Authored by: Jason Smith <jason.smith@canonical.com>
00018  *              Jay Taoko <jay.taoko@canonical.com>
00019  */
00020 
00021 #include "Nux.h"
00022 #include "HLayout.h"
00023 #include "StaticText.h"
00024 #include "TextLoader.h"
00025 
00026 #include "NuxGraphics/GraphicsDisplay.h"
00027 #include "NuxGraphics/GLShader.h"
00028 #include "NuxGraphics/GpuDevice.h"
00029 #include "NuxGraphics/GLDeviceObjects.h"
00030 #include "NuxGraphics/GLShader.h"
00031 #include "NuxGraphics/GraphicsEngine.h"
00032 
00033 #include "NuxGraphics/IOpenGLBaseTexture.h"
00034 
00035 
00036 #include <glib.h>
00037 
00038 #include "Coverflow.h"
00039 
00040 
00041 namespace nux
00042 {
00043 namespace
00044 {
00045   float EaseSin(float x)
00046   {
00047     return sin(x * nux::constants::pi / 2.0f);
00048   }
00049 
00050   float RoundFloor(float x)
00051   {
00052     return std::floor(x + 0.5f);
00053   }
00054 
00055   std::string texture_vertex_code = "attribute vec4 iVertex;                                  \n\
00056                                     attribute vec4 iTextureCoord0;                            \n\
00057                                     attribute vec4 iVertexColor;                              \n\
00058                                     uniform mat4 ViewProjectionMatrix;                        \n\
00059                                     varying vec4 varyTexCoord0;                               \n\
00060                                     varying vec4 varyVertexColor;                             \n\
00061                                     void main()                                               \n\
00062                                     {                                                         \n\
00063                                     gl_Position =  ViewProjectionMatrix * (iVertex);          \n\
00064                                     varyTexCoord0 = iTextureCoord0;                           \n\
00065                                     varyVertexColor = iVertexColor;                           \n\
00066                                     }";
00067 
00068   std::string texture_fragment_code = "varying vec4 varyTexCoord0;                            \n\
00069                                       varying vec4 varyVertexColor;                           \n\
00070                                       uniform sampler2D TextureObject0;                       \n\
00071                                       void main()                                             \n\
00072                                       {                                                       \n\
00073                                         vec4 v = texture2D(TextureObject0, varyTexCoord0.xy);    \n\
00074                                         gl_FragColor = v*varyVertexColor;                     \n\
00075                                       }";
00076 
00077   std::string single_color_fragment_code = "varying vec4 varyTexCoord0;                       \n\
00078                                       varying vec4 varyVertexColor;                           \n\
00079                                       uniform sampler2D TextureObject0;                       \n\
00080                                       void main()                                             \n\
00081                                       {                                                       \n\
00082                                         gl_FragColor = varyVertexColor;                       \n\
00083                                       }";
00084   struct Vec4_
00085   {
00086     float x;
00087     float y;
00088     float z;
00089     float rot;
00090   };
00091 
00092   struct VelocityEvent
00093   {
00094     float velocity;
00095     gint64 time;
00096   };
00097 
00098   struct Cover
00099   {
00100     Vec4_ position;
00101     float opacity;
00102     bool selected;
00103     bool mouse_over;
00104     CoverflowItem::Ptr item;
00105   };
00106 
00107   typedef std::vector<Cover> CoverList;
00108 }
00109 }
00110 
00111 namespace nux
00112 {
00113   struct Coverflow::Impl : public sigc::trackable
00114   {
00115     Impl(Coverflow* parent);
00116     ~Impl();
00117 
00118     void HandleKeyDown(unsigned long eventType,
00119                      unsigned long keysym,
00120                      unsigned long state,
00121                      const char* character,
00122                      unsigned short keyCount);
00123     void HandleKeyUp(unsigned int keysym,
00124                    unsigned long x11_key_code,
00125                    unsigned long special_keys_state);
00126 
00127     void HandleMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags);
00128     void HandleMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags);
00129     void HandleMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags);
00130     void HandleMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags);
00131     void HandleMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);
00132     void HandleMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);
00133     void HandleMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags);
00134     void HandleMouseWheel(int x, int y, int wheel_delta, unsigned long button_flags, unsigned long key_flags);
00135     void HandleGeometryChange(Area* area, Geometry geo);
00136 
00137     void DrawCover(nux::GraphicsEngine& graphics_engine, nux::DrawAreaContext &ctx, Cover const& cover);
00138     void RenderCover(nux::GraphicsEngine& graphics_engine, Cover const& cover, int width, int height, nux::Matrix4 combined_matrix);
00139     void LoadVertexBuffer(int VertexLocation, int TextureCoord0Location, int VertexColorLocation, float* VtxBuffer);
00140 
00141     CoverList GetCoverList(float animation_progress, gint64 timestep);
00142 
00143     void OnItemAdded(CoverflowModel* owner, CoverflowItem::Ptr new_item);
00144     void OnItemRemoved(CoverflowModel* owner, CoverflowItem::Ptr old_item);
00145     void OnSelectionChanged(CoverflowModel* owner, CoverflowItem::Ptr selection);
00146 
00147     void SetPosition(float position, bool animate);
00148 
00149     void UpdateModelSelection();
00150 
00151     float GetCurrentVelocity(int ms);
00152 
00153     void MaybeQueueDraw();
00154 
00155     bool CoverAtPoint(int x, int y, Cover& out_cover);
00156     void Get3DBoundingBox(float distance_from_camera, nux::Point2& top_left_corner, nux::Point2& bottom_right_corner);
00157     void GetCoverScreenCoord(Cover const& cover, nux::Vector4& P0, nux::Vector4& P1, nux::Vector4& P2, nux::Vector4& P3);
00158 
00159     bool TestMouseOverCover(int x, int y, Cover const& cover);
00160     bool TestCoverVisible(Cover const& cover);
00161 
00162     static gboolean OnAnimationTimeout(gpointer data);
00163     static gboolean OnVelocityTimeout(gpointer data);
00164 
00165     nux::Vector3 camera_position_;
00166     nux::Vector3 camera_rotation_;
00167 
00168     nux::ObjectPtr<nux::IOpenGLShaderProgram> cover_shader_program_;
00169     nux::ObjectPtr<nux::IOpenGLShaderProgram> highlight_shader_program_;
00170     
00171     nux::Matrix4 perspective_;
00172     nux::Matrix4 modelview_;
00173 
00174     std::vector<VelocityEvent> velocity_events_;
00175 
00176     Coverflow* parent_;
00177     guint animation_handle_;
00178     float camera_drift_factor_;
00179     gint64 last_draw_time_;
00180     float last_position_;
00181     bool mouse_inside_view_;
00182     float position_;
00183     gint64 position_set_time_;
00184     float saved_position_;
00185     float velocity_;
00186     guint velocity_handle_;
00187     nux::Point2 mouse_down_position_;
00188     nux::Point2 mouse_position_;
00189     CoverList last_covers_;
00190     float cover_width_in_3d_space_;
00191     float near_clip_plan_;
00192     float far_clip_plan_;
00193     TextLoader text_loader_;
00194     ObjectPtr<IOpenGLBaseTexture> drop_shadow_texture_;
00195 
00196     sigc::connection model_selection_connection_;
00197   };
00198 
00199   Coverflow::Impl::Impl(Coverflow* parent)
00200    : parent_(parent)
00201    , animation_handle_(0)
00202    , camera_drift_factor_(0)
00203    , last_draw_time_(0)
00204    , last_position_(0)
00205    , mouse_inside_view_(false)
00206    , position_(0)
00207    , position_set_time_(0)
00208    , saved_position_(0)
00209    , velocity_(0)
00210    , velocity_handle_(0)
00211    , cover_width_in_3d_space_(1.0f)
00212    , near_clip_plan_(1.0f)
00213    , far_clip_plan_(200.0f)
00214   {
00215     mouse_position_ = nux::Point2(0, 0);
00216 
00217     parent_->key_down.connect(sigc::mem_fun(this, &Impl::HandleKeyDown));
00218     parent_->key_up.connect(sigc::mem_fun(this, &Impl::HandleKeyUp));
00219     parent_->mouse_move.connect(sigc::mem_fun(this, &Impl::HandleMouseMove));
00220     parent_->mouse_enter.connect(sigc::mem_fun(this, &Impl::HandleMouseEnter));
00221     parent_->mouse_leave.connect(sigc::mem_fun(this, &Impl::HandleMouseLeave));
00222     parent_->mouse_click.connect(sigc::mem_fun(this, &Impl::HandleMouseClick));
00223     parent_->mouse_drag.connect(sigc::mem_fun(this, &Impl::HandleMouseDrag));
00224     parent_->mouse_up.connect(sigc::mem_fun(this, &Impl::HandleMouseUp));
00225     parent_->mouse_down.connect(sigc::mem_fun(this, &Impl::HandleMouseDown));
00226     parent_->mouse_wheel.connect(sigc::mem_fun(this, &Impl::HandleMouseWheel));
00227     parent_->OnGeometryChanged.connect(sigc::mem_fun(this, &Impl::HandleGeometryChange));
00228 
00229 
00230     camera_position_.x = 0.0f;
00231     camera_position_.y = 0.0f;
00232     camera_position_.z = 5.0f;
00233     camera_rotation_.x = 0.0f;
00234     camera_rotation_.y = 0.0f;
00235     camera_rotation_.z = 0.0f;
00236 
00237     // Create a shader to render a textured quad.
00238     {
00239       // Create the GLSL shader objects: Vertex, Fragment and the Program.
00240       // The Program object act as a container for the Vertex and Fragment object.
00241       nux::ObjectPtr<nux::IOpenGLPixelShader>  fragment_shader_prog_ = nux::GetGraphicsDisplay()->GetGpuDevice()->CreatePixelShader();
00242       nux::ObjectPtr<nux::IOpenGLVertexShader> vertex_shader_prog_ = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateVertexShader();
00243       cover_shader_program_ = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateShaderProgram();
00244 
00245       // Set the actual shader code into the Vertex and Fragment objects.
00246       vertex_shader_prog_->SetShaderCode(texture_vertex_code.c_str());
00247       fragment_shader_prog_->SetShaderCode(texture_fragment_code.c_str());
00248 
00249       // Remove all Vertex and Fragment objects from the Program (Not necessary here but do it anyway).
00250       cover_shader_program_->ClearShaderObjects();
00251       // Add the Vertex and Fragment objects to the Program object.
00252       cover_shader_program_->AddShaderObject(vertex_shader_prog_);
00253       cover_shader_program_->AddShaderObject(fragment_shader_prog_);
00254       // Link
00255       cover_shader_program_->Link();
00256 
00257       // The shaders have been loaded into the program. They can be discarded.
00258       vertex_shader_prog_.Release();
00259       fragment_shader_prog_.Release();
00260     }
00261 
00262     // Create a shader to render a the cover highlight.
00263     {
00264       // Create the GLSL shader objects: Vertex, Fragment and the Program.
00265       // The Program object act as a container for the Vertex and Fragment object.
00266       nux::ObjectPtr<nux::IOpenGLPixelShader>  fragment_shader_prog_ = nux::GetGraphicsDisplay()->GetGpuDevice()->CreatePixelShader();
00267       nux::ObjectPtr<nux::IOpenGLVertexShader> vertex_shader_prog_ = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateVertexShader();
00268       highlight_shader_program_ = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateShaderProgram();
00269 
00270       // Set the actual shader code into the Vertex and Fragment objects.
00271       vertex_shader_prog_->SetShaderCode(texture_vertex_code.c_str());
00272       fragment_shader_prog_->SetShaderCode(single_color_fragment_code.c_str());
00273 
00274       // Remove all Vertex and Fragment objects from the Program (Not necessary here but do it anyway).
00275       highlight_shader_program_->ClearShaderObjects();
00276       // Add the Vertex and Fragment objects to the Program object.
00277       highlight_shader_program_->AddShaderObject(vertex_shader_prog_);
00278       highlight_shader_program_->AddShaderObject(fragment_shader_prog_);
00279       // Link
00280       highlight_shader_program_->Link();
00281 
00282       // The shaders have been loaded into the program. They can be discarded.
00283       vertex_shader_prog_.Release();
00284       fragment_shader_prog_.Release();
00285     }
00286 
00287     text_loader_.font_size = 10;
00288 
00289     BaseTexture* texture = LoadTextureFromFile(PKGDATADIR"/UITextures/coverflow.oval-shadow.png");
00290     drop_shadow_texture_ = texture->GetDeviceTexture();
00291     texture->UnReference();
00292 
00293     text_loader_.lines = 2;
00294   }
00295 
00296   Coverflow::Impl::~Impl()
00297   {
00298     if (animation_handle_)
00299     {
00300       g_source_remove(animation_handle_);
00301       animation_handle_ = 0;
00302     }
00303   }
00304 
00305   void Coverflow::Impl::UpdateModelSelection()
00306   {
00307     model_selection_connection_.disconnect();
00308     parent_->model()->SetSelection((size_t)RoundFloor(position_));
00309     model_selection_connection_ = parent_->model()->selection_changed.connect(sigc::mem_fun(this, &Coverflow::Impl::OnSelectionChanged));
00310   }
00311 
00312   void Coverflow::Impl::Get3DBoundingBox(float distance_from_camera, nux::Point2& top_left_corner, nux::Point2& bottom_right_corner)
00313   {
00314     int width = parent_->GetBaseWidth();
00315     int height = parent_->GetBaseHeight();
00316 
00317     top_left_corner.y = std::tan(DEGTORAD(parent_->fov() * 0.5f)) * distance_from_camera;
00318     top_left_corner.x = -top_left_corner.y * (width / (float)height);
00319 
00320     bottom_right_corner.x = -top_left_corner.x;
00321     bottom_right_corner.y = -top_left_corner.y;
00322   }
00323 
00324   void Coverflow::Impl::GetCoverScreenCoord(Cover const& cover, nux::Vector4& out_p0, nux::Vector4& out_p1, nux::Vector4& out_p2, nux::Vector4& out_p3)
00325   {
00326     if (cover.item->GetTexture().IsNull())
00327       return;
00328 
00329     int width = parent_->GetBaseWidth();
00330     int height = parent_->GetBaseHeight();
00331 
00332     ObjectPtr<IOpenGLBaseTexture> texture = cover.item->GetTexture()->GetDeviceTexture();
00333     float ratio = texture->GetWidth()/(float)texture->GetHeight();
00334     
00335     float fx = cover_width_in_3d_space_/2.0f;
00336     float fy_top = cover.position.y + (cover_width_in_3d_space_) * (1.0f/ratio);
00337     float fy_bot = cover.position.y;
00338 
00339     if (parent_->true_perspective)
00340     {
00341       modelview_ = nux::Matrix4::TRANSLATE(-camera_position_.x, -camera_position_.y, -camera_position_.z) *
00342         nux::Matrix4::ROTATEX(DEGTORAD(-camera_rotation_.x)) *
00343         nux::Matrix4::ROTATEY(DEGTORAD(-camera_rotation_.y)) *
00344         nux::Matrix4::ROTATEZ(DEGTORAD(-camera_rotation_.z)) *
00345         nux::Matrix4::TRANSLATE(cover.position.x, 0.0f, cover.position.z);
00346     }
00347     else
00348     {
00349       modelview_ = nux::Matrix4::TRANSLATE(-camera_position_.x, -camera_position_.y, -camera_position_.z) *
00350         nux::Matrix4::ROTATEX(DEGTORAD(-camera_rotation_.x)) *
00351         nux::Matrix4::ROTATEY(DEGTORAD(-camera_rotation_.y)) *
00352         nux::Matrix4::ROTATEZ(DEGTORAD(-camera_rotation_.z)) *
00353         nux::Matrix4::TRANSLATE(0.0f, 0.0f, cover.position.z);
00354     }
00355 
00356     nux::Matrix4 m = nux::Matrix4::ROTATEY(DEGTORAD(cover.position.rot));
00357     nux::Matrix4 combined_matrix = perspective_ * modelview_ * m;
00358 
00359     nux::Vector4 p0(-fx, fy_top, 0.0f, 1.0f);
00360     nux::Vector4 p1(-fx, fy_bot, 0.0f, 1.0f);
00361     nux::Vector4 p2(fx, fy_bot, 0.0f, 1.0f);
00362     nux::Vector4 p3(fx, fy_top, 0.0f, 1.0f);
00363 
00364     nux::Vector4 p0_proj = combined_matrix * p0;
00365     nux::Vector4 p1_proj = combined_matrix * p1;
00366     nux::Vector4 p2_proj = combined_matrix * p2;
00367     nux::Vector4 p3_proj = combined_matrix * p3;
00368 
00369     p0_proj.x = p0_proj.x/p0_proj.w; p0_proj.y = p0_proj.y/p0_proj.w; p0_proj.z = p0_proj.z/p0_proj.w;
00370     p1_proj.x = p1_proj.x/p1_proj.w; p1_proj.y = p1_proj.y/p1_proj.w; p1_proj.z = p1_proj.z/p1_proj.w;
00371     p2_proj.x = p2_proj.x/p2_proj.w; p2_proj.y = p2_proj.y/p2_proj.w; p2_proj.z = p2_proj.z/p2_proj.w;
00372     p3_proj.x = p3_proj.x/p3_proj.w; p3_proj.y = p3_proj.y/p3_proj.w; p3_proj.z = p3_proj.z/p3_proj.w;
00373 
00374     p0_proj.x = width * (p0_proj.x + 1.0f)/2.0f;
00375     p1_proj.x = width * (p1_proj.x + 1.0f)/2.0f;
00376     p2_proj.x = width * (p2_proj.x + 1.0f)/2.0f;
00377     p3_proj.x = width * (p3_proj.x + 1.0f)/2.0f;
00378 
00379     p0_proj.y = -height * (p0_proj.y - 1.0f)/2.0f;
00380     p1_proj.y = -height * (p1_proj.y - 1.0f)/2.0f;
00381     p2_proj.y = -height * (p2_proj.y - 1.0f)/2.0f;
00382     p3_proj.y = -height * (p3_proj.y - 1.0f)/2.0f;
00383 
00384     float scalar = 0.0f;
00385 
00386     if (!parent_->true_perspective)
00387     {
00388       nux::Point2 top_left, bottom_right;
00389       Get3DBoundingBox(camera_position_.z, top_left, bottom_right);
00390       scalar = parent_->GetGeometry().width / (bottom_right.x - top_left.x);
00391     }
00392 
00393     out_p0.x = p0_proj.x + cover.position.x * scalar; out_p0.y = p0_proj.y;
00394     out_p1.x = p1_proj.x + cover.position.x * scalar; out_p1.y = p1_proj.y;
00395     out_p2.x = p2_proj.x + cover.position.x * scalar; out_p2.y = p2_proj.y;
00396     out_p3.x = p3_proj.x + cover.position.x * scalar; out_p3.y = p3_proj.y;
00397   }
00398 
00399   bool Coverflow::Impl::TestMouseOverCover(int x, int y, Cover const& cover)
00400   {
00401     nux::Vector4 P0;
00402     nux::Vector4 P1;
00403     nux::Vector4 P2;
00404     nux::Vector4 P3;
00405 
00406     GetCoverScreenCoord(cover, P0, P1, P2, P3);
00407     // The polygon is convex and P0->P1->P2->P3 follows the right hand rule
00408     bool test01 = ((y - P0.y) * (P1.x - P0.x) - (x - P0.x) * (P1.y - P0.y)) <= 0.0f;
00409     bool test12 = ((y - P1.y) * (P2.x - P1.x) - (x - P1.x) * (P2.y - P1.y)) <= 0.0f;
00410     bool test23 = ((y - P2.y) * (P3.x - P2.x) - (x - P2.x) * (P3.y - P2.y)) <= 0.0f;
00411     bool test30 = ((y - P3.y) * (P0.x - P3.x) - (x - P3.x) * (P0.y - P3.y)) <= 0.0f;
00412 
00413     if (test01 && test12 && test23 && test30)
00414     {
00415       return true;
00416     }
00417 
00418     return false;
00419   }
00420 
00421   bool Coverflow::Impl::TestCoverVisible(Cover const& cover)
00422   {
00423     int width = parent_->GetBaseWidth();
00424     int height = parent_->GetBaseHeight();
00425 
00426     nux::Vector4 P0;
00427     nux::Vector4 P1;
00428     nux::Vector4 P2;
00429     nux::Vector4 P3;
00430 
00431     GetCoverScreenCoord(cover, P0, P1, P2, P3);
00432     bool test0 = ((P0.x >= 0) && (P0.x < width)) && ((P0.y >= 0) && (P0.y < height));
00433     bool test1 = ((P1.x >= 0) && (P1.x < width)) && ((P1.y >= 0) && (P1.y < height));
00434     bool test2 = ((P2.x >= 0) && (P2.x < width)) && ((P2.y >= 0) && (P2.y < height));
00435     bool test3 = ((P3.x >= 0) && (P3.x < width)) && ((P3.y >= 0) && (P3.y < height));
00436 
00437     if (test0 || test1 || test2 || test3)
00438     {
00439       // The quad is convex. If any of its vertices is inside the view, then the quad is visible.
00440       return true;
00441     }
00442 
00443     return false;
00444   }
00445 
00446   bool Coverflow::Impl::CoverAtPoint(int x, int y, Cover& out_cover)
00447   {
00448     Cover best;
00449 
00450     CoverList::iterator it;
00451     for (it = last_covers_.begin(); it != last_covers_.end(); ++it)
00452     {
00453       Cover cover = *it;
00454       if (cover.item->GetTexture().IsNull())
00455         continue;
00456       if (cover.position.rot > 0 && TestMouseOverCover(mouse_position_.x, mouse_position_.y, cover))
00457       {
00458         best = cover;
00459       }
00460     }
00461 
00462     CoverList::reverse_iterator rit;
00463     for (rit = last_covers_.rbegin(); rit != last_covers_.rend(); ++rit)
00464     {
00465       Cover cover = *rit;
00466       if (cover.item->GetTexture().IsNull())
00467         continue;
00468       if (cover.position.rot <= 0 && TestMouseOverCover(mouse_position_.x, mouse_position_.y, cover))
00469       {
00470         best = cover;
00471       }
00472     }
00473 
00474     if (best.item)
00475     {
00476       out_cover = best;
00477       return true;
00478     }
00479 
00480     return false;
00481   }
00482 
00483   void Coverflow::Impl::HandleKeyDown(unsigned long   eventType  , /*event type*/
00484                                   unsigned long   keysym     , /*event keysym*/
00485                                   unsigned long   state      , /*event state*/
00486                                   const char*     character  , /*character*/
00487                                   unsigned short  keyCount     /*key repeat count*/)
00488   {
00489     switch (keysym)
00490     {
00491       case NUX_VK_LEFT:
00492         parent_->model()->SelectPrev();
00493         break;
00494       case NUX_VK_RIGHT:
00495         parent_->model()->SelectNext();
00496         break;
00497 
00498     }
00499   }
00500 
00501   void Coverflow::Impl::HandleMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags)
00502   {
00503     if (std::abs(mouse_down_position_.x - x) > 10 || std::abs(mouse_down_position_.y - y) > 10)
00504       return;
00505 
00506     Cover best;
00507     if (CoverAtPoint(x, y, best))
00508     {
00509       if (abs(best.position.rot) <= .001)
00510         best.item->Activate();
00511       else
00512       {
00513         SetPosition((float)parent_->model()->IndexOf(best.item), true);
00514         UpdateModelSelection();
00515       }
00516     }
00517   }
00518 
00519   void Coverflow::Impl::HandleKeyUp(unsigned int keysym,
00520                                   unsigned long x11_key_code,
00521                                   unsigned long special_keys_state)
00522   {
00523     switch (keysym)
00524     {
00525       case NUX_VK_ENTER:
00526       case NUX_KP_ENTER:
00527       {
00528         Cover best;
00529         if (CoverAtPoint(0, 0, best))
00530           best.item->Activate();
00531         break;
00532       }
00533       default:
00534         break;
00535     }
00536   }
00537 
00538   void Coverflow::Impl::HandleMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags)
00539   {
00540     mouse_position_.x = x;
00541     mouse_position_.y = y;
00542     MaybeQueueDraw();
00543   }
00544 
00545   void Coverflow::Impl::HandleMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags)
00546   {
00547     mouse_position_.x = x;
00548     mouse_position_.y = y;
00549     mouse_inside_view_ = true;
00550     MaybeQueueDraw();
00551   }
00552 
00553   void Coverflow::Impl::HandleMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags)
00554   {
00555     mouse_position_.x = 0xFFFFFFFF;
00556     mouse_position_.y = 0xFFFFFFFF;
00557     mouse_inside_view_ = false;
00558     MaybeQueueDraw();
00559   }
00560 
00561   void Coverflow::Impl::HandleMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags)
00562   {
00563     velocity_ = 0; // stop an current velocity based animations
00564     mouse_down_position_.x = x;
00565     mouse_down_position_.y = y;
00566     MaybeQueueDraw();
00567   }
00568 
00569   bool TooOld (VelocityEvent event)
00570   {
00571     gint64 current_time = g_get_monotonic_time();
00572     int ms = (current_time - event.time) / 1000;
00573     return ms > 1000;
00574   }
00575 
00576   float Coverflow::Impl::GetCurrentVelocity(int cutoff_ms)
00577   {
00578     gint64 current_time = g_get_monotonic_time();
00579     float result = 0.0f;
00580 
00581     std::vector<VelocityEvent>::iterator it;
00582     for (it = velocity_events_.begin(); it != velocity_events_.end(); ++it)
00583     {
00584       VelocityEvent event = *it;
00585       int ms = (current_time - event.time) / 1000;
00586       if (ms > cutoff_ms)
00587         continue;
00588       
00589       result += event.velocity;
00590     }
00591     result = result / (cutoff_ms / 16); // 16ms timestep
00592 
00593     velocity_events_.erase(std::remove_if(velocity_events_.begin(), velocity_events_.end(), &TooOld), velocity_events_.end());
00594 
00595     return result;
00596   }
00597 
00598   void Coverflow::Impl::HandleMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags)
00599   {
00600     MaybeQueueDraw();
00601     velocity_ = GetCurrentVelocity(32);
00602 
00603     if (velocity_ != 0 && !velocity_handle_)
00604     {
00605       velocity_handle_ = g_timeout_add(16, &Coverflow::Impl::OnVelocityTimeout, this);
00606     } 
00607     else if (!velocity_handle_)
00608     {
00609       SetPosition(RoundFloor(position_), true);
00610       UpdateModelSelection();
00611     }
00612   }
00613 
00614   void Coverflow::Impl::HandleMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags)
00615   {
00616     nux::Point2 top_left, bottom_right;
00617     Get3DBoundingBox(camera_position_.z, top_left, bottom_right);
00618     nux::Geometry geo = parent_->GetGeometry();
00619 
00620     float scalar = (((float)bottom_right.x - (float)top_left.x) / (float)geo.width) / parent_->space_between_icons;
00621     SetPosition(position_ - (dx * scalar * parent_->mouse_drag_rate), false);
00622   }
00623 
00624   void Coverflow::Impl::HandleMouseWheel(int x, int y, int wheel_delta, unsigned long button_flags, unsigned long key_flags)
00625   {
00626     // do nothing yet
00627   }
00628 
00629   void Coverflow::Impl::HandleGeometryChange(Area* area, Geometry geo)
00630   {
00631   }
00632 
00633   CoverList Coverflow::Impl::GetCoverList(float animation_progress, gint64 timestep)
00634   {
00635     CoverList results;
00636 
00637     animation_progress = std::min<float>(1.0f, animation_progress);
00638 
00639     float coverflow_position = position_ - ((position_ - saved_position_) * (1.0f - animation_progress));
00640 
00641     size_t i = 0;
00642     float flat_right = parent_->space_between_icons * (parent_->flat_icons) + .1;
00643     float flat_left = -flat_right;
00644 
00645     CoverflowModel::CoverflowItemList::const_iterator it;
00646     for (it = parent_->model()->Items().begin(); it != parent_->model()->Items().end(); ++it)
00647     {
00648       CoverflowItem::Ptr item = *it;
00649       float item_position = (float)i - coverflow_position;
00650 
00651       Cover cover;
00652       cover.opacity = 1.0f;
00653       cover.selected = item == parent_->model()->Selection();
00654       cover.item = item;
00655 
00656       // This position refers to the bottom center of the cover.
00657       cover.position.x    = 0;
00658       cover.position.y    = -0.5f + parent_->y_offset;
00659       cover.position.z    = 0;
00660       cover.position.rot  = 0;
00661 
00662       float x = item_position * parent_->space_between_icons;
00663       cover.position.x = x;
00664 
00665       float neg_pinch = 1.0f - parent_->pinching;
00666 
00667       if (x < flat_left)
00668       {
00669         cover.position.rot = (atan((x - flat_left) * parent_->folding_rate) / (nux::constants::pi / 2)) * -parent_->folding_angle;
00670         float scale_in_factor = neg_pinch + (1.0f - (std::abs(cover.position.rot) / parent_->folding_angle)) * parent_->pinching;
00671         cover.position.x = flat_left - ((flat_left - x) * scale_in_factor);
00672       }
00673       else if (x > flat_right)
00674       {
00675         cover.position.rot = (atan((x - flat_right) * parent_->folding_rate) / (nux::constants::pi / 2)) * -parent_->folding_angle;
00676         float scale_in_factor = neg_pinch+ (1.0f - (std::abs(cover.position.rot) / parent_->folding_angle)) * parent_->pinching;
00677         cover.position.x = flat_right + ((x - flat_right) * scale_in_factor);
00678       }
00679 
00680       if (cover.selected && parent_->pop_out_selected)
00681         cover.position.z = 0.3f;
00682       
00683       float fold_progress = std::abs(cover.position.rot / parent_->folding_angle());
00684       cover.position.z -= parent_->folding_depth() * fold_progress;
00685 
00686       nux::Point2 top_left, bottom_right;
00687       Get3DBoundingBox(camera_position_.z, top_left, bottom_right);
00688 
00689       float window_width = bottom_right.x - top_left.x;
00690       float distance_from_edge = std::max<float>(0.0f, std::abs(bottom_right.x) - std::abs(cover.position.x));
00691       cover.opacity = std::min<float>(1.0f, distance_from_edge / (window_width * parent_->edge_fade));
00692 
00693       results.push_back(cover);
00694       ++i;
00695     }
00696 
00697     last_position_ = coverflow_position;
00698     return results;
00699   }
00700 
00701   void Coverflow::Impl::SetPosition(float position, bool animate)
00702   {
00703     float min = std::min<float>((float)parent_->flat_icons,(float)parent_->model()->Items().size() - 1.0f);
00704     float max = std::max<float>(0.0f, (float)parent_->model()->Items().size() - 1.0f - (float)parent_->flat_icons);
00705     position = std::max<float>(min, std::min<float>(max, position));
00706     if (position == position_)
00707       return;
00708 
00709     if (animate)
00710     {
00711       position_set_time_ = g_get_monotonic_time();
00712       saved_position_ = last_position_;
00713     }
00714     else
00715     {
00716       position_set_time_ = 0;
00717     }
00718 
00719     position_ = position;
00720     
00721     VelocityEvent ve;
00722     ve.velocity = position_ - last_position_;
00723     ve.time = g_get_monotonic_time();
00724     velocity_events_.push_back(ve);
00725   
00726     MaybeQueueDraw();
00727   }
00728 
00729   void Coverflow::Impl::OnItemAdded(CoverflowModel* owner, CoverflowItem::Ptr new_item)
00730   {
00731     SetPosition(position_, true);
00732     MaybeQueueDraw();
00733   }
00734 
00735   void Coverflow::Impl::OnItemRemoved(CoverflowModel* owner, CoverflowItem::Ptr old_item)
00736   {
00737     SetPosition(position_, true);
00738     MaybeQueueDraw();
00739   }
00740 
00741   void Coverflow::Impl::OnSelectionChanged(CoverflowModel* owner, CoverflowItem::Ptr selection)
00742   {
00743     size_t selection_index = parent_->model()->SelectionIndex();
00744 
00745     if (selection_index > (position_ + parent_->flat_icons))
00746       SetPosition(selection_index - parent_->flat_icons, true);
00747     else if (selection_index < (position_ - parent_->flat_icons))
00748       SetPosition(selection_index + parent_->flat_icons, true);
00749     MaybeQueueDraw();
00750   }
00751 
00752   void Coverflow::Impl::MaybeQueueDraw()
00753   {
00754     if (!animation_handle_)
00755       animation_handle_ = g_timeout_add(0, &Coverflow::Impl::OnAnimationTimeout, this);
00756   }
00757 
00758   void Coverflow::Impl::LoadVertexBuffer(int VertexLocation, int TextureCoord0Location, int VertexColorLocation, float* VtxBuffer)
00759   {
00760     CHECKGL(glEnableVertexAttribArrayARB(VertexLocation));
00761     CHECKGL(glVertexAttribPointerARB((GLuint) VertexLocation, 4, GL_FLOAT, GL_FALSE, 48, VtxBuffer));
00762 
00763     if (TextureCoord0Location != -1)
00764     {
00765       CHECKGL(glEnableVertexAttribArrayARB(TextureCoord0Location));
00766       CHECKGL(glVertexAttribPointerARB((GLuint) TextureCoord0Location, 4, GL_FLOAT, GL_FALSE, 48, VtxBuffer + 4));
00767     }
00768 
00769     if (VertexColorLocation != -1)
00770     {
00771       CHECKGL(glEnableVertexAttribArrayARB(VertexColorLocation));
00772       CHECKGL(glVertexAttribPointerARB((GLuint) VertexColorLocation, 4, GL_FLOAT, GL_FALSE, 48, VtxBuffer + 8));
00773     }
00774   }
00775 
00776   void Coverflow::Impl::RenderCover(nux::GraphicsEngine& graphics_engine, Cover const& cover, int width, int height, nux::Matrix4 combined_matrix)
00777   {
00778     ObjectPtr<IOpenGLBaseTexture> texture = cover.item->GetTexture()->GetDeviceTexture();
00779     nux::TexCoordXForm texxform;
00780     nux::Color ref_color = nux::color::White;
00781 
00782     QRP_Compute_Texture_Coord(width, height, texture, texxform);
00783 
00784     float ratio = texture->GetWidth()/(float)texture->GetHeight();
00785     float drop_shadow_ratio = drop_shadow_texture_->GetWidth()/(float)drop_shadow_texture_->GetHeight();
00786 
00787     float fx = cover_width_in_3d_space_/2.0f;
00788 
00789     float fy_top = cover.position.y + (cover_width_in_3d_space_) * (1.0f/ratio);
00790     float fy_bot = cover.position.y;
00791     float fy_bot_reflec = cover.position.y - (cover_width_in_3d_space_) * (1.0f/ratio) * parent_->reflection_size;
00792 
00793     float fx_shadow = 0.5 * (1.1 * cover_width_in_3d_space_);
00794     float fy_top_shadow = fy_bot + 0.5 * (1.4 * cover_width_in_3d_space_) * (1.0f/drop_shadow_ratio);
00795     float fy_bot_shadow = fy_bot - 0.5 * (1.4 * cover_width_in_3d_space_) * (1.0f/drop_shadow_ratio);
00796     
00797 
00798     cover_shader_program_->Begin();
00799 
00800     int TextureObjectLocation = cover_shader_program_->GetUniformLocationARB("TextureObject0");
00801     int VertexLocation = cover_shader_program_->GetAttributeLocation("iVertex");
00802     int TextureCoord0Location = cover_shader_program_->GetAttributeLocation("iTextureCoord0");
00803     int VertexColorLocation = cover_shader_program_->GetAttributeLocation("iVertexColor");
00804     int VPMatrixLocation = cover_shader_program_->GetUniformLocationARB("ViewProjectionMatrix");
00805 
00806     nux::Matrix4 MVPMatrix = perspective_ * combined_matrix;
00807 
00808     cover_shader_program_->SetUniformLocMatrix4fv((GLint) VPMatrixLocation, 1, true, (GLfloat*) &(MVPMatrix.m));
00809 
00810     // *** Compute the opacities ***
00811     float angular_opacity = 1.0f;
00812     if (parent_->folding_angle == 0)
00813     {
00814       angular_opacity = 1.0f;
00815     }
00816     else
00817     {
00818       float rotation_ratio = std::abs(cover.position.rot / parent_->folding_angle);
00819       angular_opacity = 1.0f - rotation_ratio;
00820     }
00821 
00822     float opacity = 1.0f - std::abs(cover.position.x) / (parent_->reflection_fadeout_distance);
00823     if (opacity < 0.0f)
00824     {
00825       opacity = 0.0f;
00826     }
00827 
00828     opacity *= angular_opacity;
00829 
00830     drop_shadow_texture_->SetFiltering (GL_LINEAR, GL_LINEAR);
00831     graphics_engine.SetTexture(GL_TEXTURE0, drop_shadow_texture_);
00832     CHECKGL(glUniform1iARB(TextureObjectLocation, 0));
00833 
00834     // **** Render the cover drop shadow ****
00835     if (parent_->show_drop_shadow)
00836     {
00837       Color color = ref_color * cover.opacity * angular_opacity;
00838       float VtxBuffer[] =
00839       {
00840         -fx_shadow, fy_top_shadow,  0.0f, 1.0f, texxform.u0, texxform.v0, 0, 0, color.red, color.green, color.blue, color.alpha,
00841         -fx_shadow, fy_bot_shadow,  0.0f, 1.0f, texxform.u0, texxform.v1, 0, 0, color.red, color.green, color.blue, color.alpha,
00842         fx_shadow,  fy_bot_shadow,  0.0f, 1.0f, texxform.u1, texxform.v1, 0, 0, color.red, color.green, color.blue, color.alpha,
00843         fx_shadow,  fy_top_shadow,  0.0f, 1.0f, texxform.u1, texxform.v0, 0, 0, color.red, color.green, color.blue, color.alpha,
00844       };
00845 
00846       LoadVertexBuffer(VertexLocation, TextureCoord0Location, VertexColorLocation, VtxBuffer);
00847       CHECKGL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
00848     }
00849     
00850 
00851     texture->SetFiltering (GL_LINEAR, GL_LINEAR);
00852     graphics_engine.SetTexture(GL_TEXTURE0, texture);
00853     CHECKGL(glUniform1iARB(TextureObjectLocation, 0));
00854 
00855     // **** Render the cover ****
00856     {
00857       Color color = ref_color * cover.opacity;
00858       float VtxBuffer[] =
00859       {
00860         -fx, fy_top,  0.0f, 1.0f, texxform.u0, texxform.v0, 0, 0, color.red, color.green, color.blue, color.alpha,
00861         -fx, fy_bot,  0.0f, 1.0f, texxform.u0, texxform.v1, 0, 0, color.red, color.green, color.blue, color.alpha,
00862         fx,  fy_bot,  0.0f, 1.0f, texxform.u1, texxform.v1, 0, 0, color.red, color.green, color.blue, color.alpha,
00863         fx,  fy_top,  0.0f, 1.0f, texxform.u1, texxform.v0, 0, 0, color.red, color.green, color.blue, color.alpha,
00864       };
00865 
00866       LoadVertexBuffer(VertexLocation, TextureCoord0Location, VertexColorLocation, VtxBuffer);
00867       CHECKGL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
00868     }
00869 
00870     // **** Reflection opacity ****
00871     if (parent_->show_reflection)
00872     {
00873       texxform.flip_v_coord = true;
00874       QRP_Compute_Texture_Coord(width, height, texture, texxform);
00875 
00876       // texture offset use to prevent the texture from scaling when the reflection size changes.
00877       float toff = 1.0f - parent_->reflection_size;
00878 
00879       float opacity_start = opacity*parent_->reflection_strength * cover.opacity;
00880       float opacity_end   = 0.0f;
00881       float VtxBuffer[] =
00882       {
00883         -fx, fy_bot,        0.0f, 1.0f, texxform.u0, texxform.v0,         0, 0, opacity_start, opacity_start, opacity_start, opacity_start,
00884         -fx, fy_bot_reflec, 0.0f, 1.0f, texxform.u0, texxform.v1 + toff,  0, 0, opacity_end,   opacity_end,   opacity_end,   opacity_end,
00885         fx,  fy_bot_reflec, 0.0f, 1.0f, texxform.u1, texxform.v1 + toff,  0, 0, opacity_end,   opacity_end,   opacity_end,   opacity_end,
00886         fx,  fy_bot,        0.0f, 1.0f, texxform.u1, texxform.v0,         0, 0, opacity_start, opacity_start, opacity_start, opacity_start,
00887       };
00888 
00889       LoadVertexBuffer(VertexLocation, TextureCoord0Location, VertexColorLocation, VtxBuffer);
00890       CHECKGL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
00891     }
00892 
00893     // **** Render Cover label ****
00894     if (parent_->show_labels())
00895     {
00896       if (!cover.item->text_texture().IsValid())
00897       {
00898         nux::Point2 top_left, bottom_right;
00899         Get3DBoundingBox(camera_position_.z, top_left, bottom_right);
00900         int individual_width = (int) RoundFloor((float)width / (bottom_right.x - top_left.x));
00901 
00902         char *escaped_text = g_markup_escape_text(cover.item->name().c_str(), -1);
00903         text_loader_.text = escaped_text;
00904         text_loader_.width = individual_width;
00905         text_loader_.minimum_width = individual_width;
00906         cover.item->text_texture = text_loader_.CreateTexture();
00907         g_free(escaped_text);
00908       }
00909 
00910       if (cover.item->text_texture().IsValid())
00911       {
00912         ObjectPtr<IOpenGLBaseTexture> label_texture = cover.item->text_texture()->GetDeviceTexture();
00913         QRP_Compute_Texture_Coord(label_texture->GetWidth(), label_texture->GetHeight(), label_texture, texxform);
00914 
00915         ratio = label_texture->GetWidth() / (float)label_texture->GetHeight();
00916 
00917         
00918         float fx = cover_width_in_3d_space_ / 2.0f;
00919         float fy_top_text = fy_bot - 0.05f;    // push it down a bit
00920         float fy_bot_text = fy_top_text - (cover_width_in_3d_space_) * (1.0f/ratio);
00921         
00922         label_texture->SetFiltering(GL_NEAREST, GL_NEAREST);
00923         graphics_engine.SetTexture(GL_TEXTURE0, label_texture);
00924         CHECKGL(glUniform1iARB(TextureObjectLocation, 0));
00925 
00926         texxform.flip_v_coord = true;
00927         QRP_Compute_Texture_Coord(width, height, texture, texxform);
00928 
00929         float opacity_start = angular_opacity;
00930         float opacity_end   = angular_opacity;
00931 
00932         if (cover.position.rot != 0.0f)
00933         {
00934           opacity_start = 0.4f * angular_opacity;
00935           opacity_end   = 0.4f * angular_opacity;            
00936         }
00937 
00938         float VtxBuffer[] =
00939         {
00940           -fx, fy_top_text, 0.0f, 1.0f, texxform.u0, texxform.v1, 0, 0, opacity_start, opacity_start, opacity_start, opacity_start,
00941           -fx, fy_bot_text, 0.0f, 1.0f, texxform.u0, texxform.v0, 0, 0, opacity_end,   opacity_end,   opacity_end,   opacity_end,
00942           fx,  fy_bot_text, 0.0f, 1.0f, texxform.u1, texxform.v0, 0, 0, opacity_end,   opacity_end,   opacity_end,   opacity_end,
00943           fx,  fy_top_text, 0.0f, 1.0f, texxform.u1, texxform.v1, 0, 0, opacity_start, opacity_start, opacity_start, opacity_start,
00944         };
00945 
00946         LoadVertexBuffer(VertexLocation, TextureCoord0Location, VertexColorLocation, VtxBuffer);
00947         CHECKGL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
00948       }
00949     }
00950 
00951     CHECKGL(glDisableVertexAttribArrayARB(VertexLocation));
00952 
00953     if (TextureCoord0Location != -1)
00954       CHECKGL(glDisableVertexAttribArrayARB(TextureCoord0Location));
00955 
00956     if (VertexColorLocation != -1)
00957       CHECKGL(glDisableVertexAttribArrayARB(VertexColorLocation));
00958 
00959     cover_shader_program_->End();
00960   }
00961 
00962   void Coverflow::Impl::DrawCover(nux::GraphicsEngine& graphics_engine, nux::DrawAreaContext &ctx, Cover const& cover)
00963   {
00964     if (cover.item->GetTexture().IsNull() || !TestCoverVisible(cover))
00965       return;
00966 
00967     int width = parent_->GetBaseWidth();
00968     int height = parent_->GetBaseHeight();
00969 
00970     // Note that (cover.position.x, cover.position.y, cover.position.z) is the position of the bottom 
00971     // center of the cover in 3d space.
00972     // We translate the cover to an y coordinate of 0.0 in the matrix below and we take care of rendering 
00973     // the cover at the right location in Y.
00974 
00975     if (parent_->true_perspective)
00976     {
00977       modelview_ = nux::Matrix4::TRANSLATE(-camera_position_.x, -camera_position_.y, -camera_position_.z) *
00978         nux::Matrix4::ROTATEX(DEGTORAD(-camera_rotation_.x)) *
00979         nux::Matrix4::ROTATEY(DEGTORAD(camera_drift_factor_ * parent_->camera_motion_drift_angle + -camera_rotation_.y)) *
00980         nux::Matrix4::ROTATEZ(DEGTORAD(-camera_rotation_.z)) *
00981         nux::Matrix4::TRANSLATE(cover.position.x, 0.0f, cover.position.z);
00982     }
00983     else
00984     {
00985       modelview_ = nux::Matrix4::TRANSLATE(-camera_position_.x, -camera_position_.y, -camera_position_.z) *
00986         nux::Matrix4::ROTATEX(DEGTORAD(-camera_rotation_.x)) *
00987         nux::Matrix4::ROTATEY(DEGTORAD(camera_drift_factor_ * parent_->camera_motion_drift_angle + -camera_rotation_.y)) *
00988         nux::Matrix4::ROTATEZ(DEGTORAD(-camera_rotation_.z)) *
00989         nux::Matrix4::TRANSLATE(0.0f, 0.0f, cover.position.z);
00990 
00991       nux::Point2 top_left, bottom_right;
00992       Get3DBoundingBox(camera_position_.z, top_left, bottom_right);
00993       float scalar = ctx.width / (bottom_right.x - top_left.x);
00994       glViewport((int) (cover.position.x * scalar), 0.0f, ctx.width, ctx.height);
00995     }
00996 
00997     nux::Matrix4 m = nux::Matrix4::ROTATEY(DEGTORAD(cover.position.rot));
00998     nux::Matrix4 combined_matrix = modelview_ * m;
00999 
01000     RenderCover(graphics_engine, cover, width, height, combined_matrix);
01001   }
01002 
01003   gboolean Coverflow::Impl::OnAnimationTimeout(gpointer data)
01004   {
01005     Coverflow::Impl* self = static_cast<Coverflow::Impl*> (data);
01006     self->parent_->QueueDraw();
01007     self->animation_handle_ = 0;
01008     return FALSE;
01009   }
01010 
01011   gboolean Coverflow::Impl::OnVelocityTimeout(gpointer data)
01012   {
01013     Coverflow::Impl* self = static_cast<Coverflow::Impl*>(data);
01014     if (self->velocity_ == 0)
01015     {
01016       self->SetPosition(RoundFloor(self->position_), true);
01017       self->UpdateModelSelection();
01018       self->velocity_handle_ = 0;
01019       return FALSE;
01020     }
01021 
01022     self->SetPosition(self->position_ + self->velocity_, false);
01023     self->velocity_ = (std::max<float>(0.0f, std::abs(self->velocity_) - self->parent_->kinetic_scroll_rate)) * (self->velocity_ / std::abs(self->velocity_));
01024     self->MaybeQueueDraw();
01025     return TRUE;
01026   }
01027 
01028   NUX_IMPLEMENT_OBJECT_TYPE(Coverflow);
01029 
01030   Coverflow::Coverflow()
01031     : animation_length(250)
01032     , camera_motion_drift_angle(10.0f)
01033     , camera_motion_drift_enabled(false)
01034     , edge_fade(0.07)
01035     , flat_icons(2)
01036     , folding_angle(90.0f)
01037     , folding_depth(0.0f)
01038     , folding_rate(2)
01039     , fov(45)
01040     , kinetic_scroll_rate(0.1f)
01041     , mouse_drag_rate(1)
01042     , pinching(.6)
01043     , pop_out_selected(false)
01044     , reflection_fadeout_distance(4.0f)
01045     , reflection_strength(0.45f)
01046     , space_between_icons(1.25f)
01047     , model(CoverflowModel::Ptr(new CoverflowModel()))
01048     , show_labels(true)
01049     , show_drop_shadow(false)
01050     , show_reflection(false)
01051     , true_perspective(true)
01052     , reflection_size(1.0f)
01053     , pimpl(new Impl(this))
01054   {
01055     SetAcceptKeyboardEvent(true);
01056 
01057     model()->item_added.connect(sigc::mem_fun(pimpl, &Coverflow::Impl::OnItemAdded));
01058     model()->item_removed.connect(sigc::mem_fun(pimpl, &Coverflow::Impl::OnItemRemoved));
01059     pimpl->model_selection_connection_ = model()->selection_changed.connect(sigc::mem_fun(pimpl, &Coverflow::Impl::OnSelectionChanged));
01060   }
01061 
01062   Coverflow::~Coverflow()
01063   {
01064     delete pimpl;
01065   }
01066 
01067   void Coverflow::SetCameraDistance(float distance)
01068   {
01069     pimpl->camera_position_.z = distance;
01070     pimpl->MaybeQueueDraw();
01071   }
01072 
01073   bool Coverflow::InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character)
01074   {
01075     return true;
01076   }
01077 
01078   void Coverflow::ClientDraw(nux::GraphicsEngine& graphics_engine, nux::DrawAreaContext &ctx, bool force_draw)
01079   {
01080     gint64 current_time = g_get_monotonic_time();
01081     float animation_progress = std::min<float>(1.0f, (current_time - pimpl->position_set_time_) / static_cast<float>(animation_length() * 1000));
01082     gint64 timestep = (current_time - pimpl->last_draw_time_) / 1000;
01083 
01084     graphics_engine.GetRenderStates().SetBlend(true);
01085     graphics_engine.GetRenderStates().SetPremultipliedBlend(SRC_OVER);
01086     graphics_engine.GetRenderStates().SetColorMask(true, true, true, true);
01087 
01088     nux::GetPainter().PaintBackground(graphics_engine, GetGeometry());
01089     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT  | GL_STENCIL_BUFFER_BIT);
01090 
01091     glViewport(0, 0, ctx.width, ctx.height);
01092 
01093     pimpl->perspective_.Perspective(DEGTORAD(fov()), (float)ctx.width / (float)ctx.height, pimpl->near_clip_plan_, pimpl->far_clip_plan_);
01094 
01095     if (camera_motion_drift_enabled)
01096     {
01097       float current_velocity = pimpl->GetCurrentVelocity(250);
01098       pimpl->camera_drift_factor_ = std::max<float>(-1.0f, std::min<float>(1.0f, current_velocity / 1.5f));
01099     }
01100 
01101     CoverList covers = pimpl->GetCoverList(EaseSin(animation_progress), timestep);
01102     CoverList::iterator it;
01103     for (it = covers.begin(); it != covers.end(); ++it)
01104     {
01105       Cover cover = *it;
01106       if (cover.position.rot > 0)
01107         pimpl->DrawCover(graphics_engine, ctx, cover);
01108       else
01109         break;
01110     }
01111 
01112     CoverList::reverse_iterator rit;
01113     for (rit = covers.rbegin(); rit != covers.rend(); ++rit)
01114     {
01115       Cover cover = *rit;
01116       if (cover.position.rot <= 0)
01117         pimpl->DrawCover(graphics_engine, ctx, cover);
01118     }
01119 
01120     //graphics_engine.GetRenderStates().SetBlend(false);
01121 
01122     if ((pimpl->camera_drift_factor_ != 0 || animation_progress < 1.0f) && !pimpl->animation_handle_)
01123     {
01124       pimpl->animation_handle_ = g_timeout_add(0, &Coverflow::Impl::OnAnimationTimeout, pimpl);
01125     }
01126 
01127     pimpl->last_draw_time_ = current_time;
01128     pimpl->last_covers_ = covers;
01129   }
01130 
01131   bool Coverflow::AcceptKeyNavFocus()
01132   {
01133     return true;
01134   }
01135 
01136   float Coverflow::ViewportWidthAtDepth(float depth)
01137   {
01138     nux::Point2 top_left;
01139     nux::Point2 bottom_right;
01140     pimpl->Get3DBoundingBox(pimpl->camera_position_.z + depth, top_left, bottom_right);
01141     return bottom_right.x - top_left.x;
01142   }
01143 
01144   float Coverflow::ViewportHeightAtDepth(float depth)
01145   {
01146     nux::Point2 top_left;
01147     nux::Point2 bottom_right;
01148     pimpl->Get3DBoundingBox(pimpl->camera_position_.z + depth, top_left, bottom_right);
01149     return top_left.y - bottom_right.y;
01150   }
01151 
01152 } // namespace nux