Back to index

unity  6.0.0
ResultRendererTile.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright 2011 Canonical Ltd.
00004  *
00005  * This program is free software: you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser 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, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranties of
00011  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00012  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00013  * License for more details.
00014  *
00015  * You should have received a copy of both the GNU Lesser General Public
00016  * License version 3 along with this program.  If not, see
00017  * <http://www.gnu.org/licenses/>
00018  *
00019  * Authored by: Gordon Allott <gord.allott@canonical.com>
00020  *
00021  */
00022 
00023 
00024 #include <sstream>     // for ostringstream
00025 #include "ResultRendererTile.h"
00026 
00027 #include <boost/algorithm/string.hpp>
00028 
00029 #include <pango/pango.h>
00030 #include <pango/pangocairo.h>
00031 #include <gdk/gdk.h>
00032 #include <gtk/gtk.h>
00033 
00034 #include <NuxCore/Logger.h>
00035 #include <UnityCore/GLibWrapper.h>
00036 
00037 #include "unity-shared/CairoTexture.h"
00038 #include "unity-shared/DashStyle.h"
00039 #include "unity-shared/IconLoader.h"
00040 #include "unity-shared/IconTexture.h"
00041 #include "unity-shared/TextureCache.h"
00042 
00043 
00044 namespace
00045 {
00046   bool neko;
00047 }
00048 
00049 namespace unity
00050 {
00051 namespace
00052 {
00053 nux::logging::Logger logger("unity.dash.results");
00054 
00055 const int FONT_SIZE = 10;
00056 }
00057 
00058 namespace dash
00059 {
00060 
00061 NUX_IMPLEMENT_OBJECT_TYPE(ResultRendererTile);
00062 
00063 ResultRendererTile::ResultRendererTile(NUX_FILE_LINE_DECL)
00064   : ResultRenderer(NUX_FILE_LINE_PARAM)
00065   , highlight_padding(6)
00066   , spacing(10)
00067   , padding(6)
00068 {
00069   dash::Style& style = dash::Style::Instance();
00070   width = style.GetTileWidth();
00071   height = style.GetTileHeight();
00072 
00073   gsize tmp;
00074   gchar* tmp1 = (gchar*)g_base64_decode("VU5JVFlfTkVLTw==", &tmp);
00075   neko = (g_getenv(tmp1));
00076   g_free (tmp1);
00077 
00078   // pre-load the highlight texture
00079   // try and get a texture from the texture cache
00080   TextureCache& cache = TextureCache::GetDefault();
00081   int prelight_texture_size = style.GetTileIconSize() + (highlight_padding * 2);
00082   prelight_cache_ = cache.FindTexture("ResultRendererTile.PreLightTexture",
00083                                       prelight_texture_size, prelight_texture_size,
00084                                       sigc::mem_fun(this, &ResultRendererTile::DrawHighlight));
00085 }
00086 
00087 ResultRendererTile::~ResultRendererTile()
00088 {
00089 }
00090 
00091 void ResultRendererTile::Render(nux::GraphicsEngine& GfxContext,
00092                                 Result& row,
00093                                 ResultRendererState state,
00094                                 nux::Geometry& geometry,
00095                                 int x_offset, int y_offset)
00096 {
00097   TextureContainer* container = row.renderer<TextureContainer*>();
00098   if (container == nullptr)
00099     return;
00100 
00101   dash::Style& style = dash::Style::Instance();
00102   int tile_icon_size = style.GetTileIconSize();
00103 
00104   // set up our texture mode
00105   nux::TexCoordXForm texxform;
00106 
00107   int icon_width, icon_height;
00108   if (container->icon == nullptr)
00109   {
00110     icon_width = icon_height = tile_icon_size;
00111   }
00112   else
00113   {
00114     icon_width = container->icon->GetWidth();
00115     icon_height = container->icon->GetHeight();
00116   }
00117 
00118   int icon_left_hand_side = geometry.x + (geometry.width - icon_width) / 2;
00119   int icon_top_side = geometry.y + padding + (tile_icon_size - icon_height) / 2;
00120 
00121   if (container->blurred_icon && state == ResultRendererState::RESULT_RENDERER_NORMAL)
00122   {
00123     GfxContext.QRP_1Tex(icon_left_hand_side - 5 - x_offset,
00124                         icon_top_side - 5 - y_offset,
00125                         container->blurred_icon->GetWidth(),
00126                         container->blurred_icon->GetHeight(),
00127                         container->blurred_icon->GetDeviceTexture(),
00128                         texxform,
00129                         nux::Color(0.15f, 0.15f, 0.15f, 0.15f));
00130   }
00131 
00132   // render highlight if its needed
00133   if (container->prelight && state != ResultRendererState::RESULT_RENDERER_NORMAL)
00134   {
00135     GfxContext.QRP_1Tex(icon_left_hand_side - highlight_padding,
00136                         icon_top_side - highlight_padding,
00137                         container->prelight->GetWidth(),
00138                         container->prelight->GetHeight(),
00139                         container->prelight->GetDeviceTexture(),
00140                         texxform,
00141                         nux::Color(1.0f, 1.0f, 1.0f, 1.0f));
00142   }
00143 
00144   // draw the icon
00145   if (container->icon)
00146   {
00147     GfxContext.QRP_1Tex(icon_left_hand_side,
00148                         icon_top_side,
00149                         container->icon->GetWidth(),
00150                         container->icon->GetHeight(),
00151                         container->icon->GetDeviceTexture(),
00152                         texxform,
00153                         nux::Color(1.0f, 1.0f, 1.0f, 1.0f));
00154   }
00155 
00156   if (container->text)
00157   {
00158     GfxContext.QRP_1Tex(geometry.x + padding,
00159                         geometry.y + tile_icon_size + spacing,
00160                         style.GetTileWidth() - (padding * 2),
00161                         style.GetTileHeight() - tile_icon_size - spacing,
00162                         container->text->GetDeviceTexture(),
00163                         texxform,
00164                         nux::Color(1.0f, 1.0f, 1.0f, 1.0f));
00165   }
00166 }
00167 
00168 nux::BaseTexture* ResultRendererTile::DrawHighlight(std::string const& texid, int width, int height)
00169 {
00170   nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, width, height);
00171   cairo_t* cr = cairo_graphics.GetContext();
00172 
00173   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00174   cairo_paint(cr);
00175 
00176   int PADDING = 4;
00177   int BLUR_SIZE = 5;
00178 
00179   int bg_width = width - PADDING - BLUR_SIZE;
00180   int bg_height = height - PADDING - BLUR_SIZE;
00181   int bg_x = BLUR_SIZE - 1;
00182   int bg_y = BLUR_SIZE - 1;
00183 
00184   // draw the glow
00185   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00186   cairo_set_line_width(cr, 1.0f);
00187   cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 0.75f);
00188   cairo_graphics.DrawRoundedRectangle(cr,
00189                                        1.0f,
00190                                        bg_x,
00191                                        bg_y,
00192                                        5.0,
00193                                        bg_width,
00194                                        bg_height,
00195                                        true);
00196   cairo_fill(cr);
00197 
00198   cairo_graphics.BlurSurface(BLUR_SIZE - 2);
00199 
00200   cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
00201   cairo_graphics.DrawRoundedRectangle(cr,
00202                                        1.0,
00203                                        bg_x,
00204                                        bg_y,
00205                                        5.0,
00206                                        bg_width,
00207                                        bg_height,
00208                                        true);
00209   cairo_clip(cr);
00210   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00211 
00212   cairo_graphics.DrawRoundedRectangle(cr,
00213                                        1.0,
00214                                        bg_x,
00215                                        bg_y,
00216                                        5.0,
00217                                        bg_width,
00218                                        bg_height,
00219                                        true);
00220   cairo_set_source_rgba(cr, 240 / 255.0f, 240 / 255.0f, 240 / 255.0f, 1.0f);
00221   cairo_fill_preserve(cr);
00222 
00223   cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0);
00224   cairo_stroke(cr);
00225 
00226   cairo_destroy(cr);
00227 
00228   return texture_from_cairo_graphics(cairo_graphics);
00229 }
00230 
00231 void ResultRendererTile::Preload(Result& row)
00232 {
00233   if (row.renderer<TextureContainer*>() == nullptr)
00234   {
00235     row.set_renderer(new TextureContainer());
00236     LoadIcon(row);
00237     LoadText(row);
00238   }
00239 }
00240 
00241 void ResultRendererTile::Unload(Result& row)
00242 {
00243   TextureContainer *container = row.renderer<TextureContainer*>();
00244   delete container;
00245   row.set_renderer<TextureContainer*>(nullptr);
00246 }
00247 
00248 void ResultRendererTile::LoadIcon(Result& row)
00249 {
00250   Style& style = Style::Instance();
00251   std::string const& icon_hint = row.icon_hint;
00252 #define DEFAULT_GICON ". GThemedIcon text-x-preview"
00253   std::string icon_name;
00254   if (G_UNLIKELY(neko))
00255   {
00256     int tmp1 = style.GetTileIconSize() + (rand() % 16) - 8;
00257     gsize tmp3;
00258     gchar* tmp2 = (gchar*)g_base64_decode("aHR0cDovL3BsYWNla2l0dGVuLmNvbS8laS8laS8=", &tmp3);
00259     gchar* tmp4 = g_strdup_printf(tmp2, tmp1, tmp1);
00260     icon_name = tmp4;
00261     g_free(tmp4);
00262     g_free(tmp2);
00263   }
00264   else
00265   {
00266     icon_name = !icon_hint.empty() ? icon_hint : DEFAULT_GICON;
00267   }
00268 
00269   GIcon*  icon = g_icon_new_for_string(icon_name.c_str(), NULL);
00270   TextureContainer* container = row.renderer<TextureContainer*>();
00271 
00272   IconLoader::IconLoaderCallback slot = sigc::bind(sigc::mem_fun(this, &ResultRendererTile::IconLoaded), icon_hint, row);
00273 
00274   if (g_strrstr(icon_name.c_str(), "://"))
00275   {
00276     container->slot_handle = IconLoader::GetDefault().LoadFromURI(icon_name, style.GetTileIconSize(), slot);
00277   }
00278   else if (G_IS_ICON(icon))
00279   {
00280     container->slot_handle = IconLoader::GetDefault().LoadFromGIconString(icon_name, style.GetTileIconSize(), slot);
00281   }
00282   else
00283   {
00284     container->slot_handle = IconLoader::GetDefault().LoadFromIconName(icon_name, style.GetTileIconSize(), slot);
00285   }
00286 
00287   if (icon != NULL)
00288     g_object_unref(icon);
00289 }
00290 
00291 nux::BaseTexture* ResultRendererTile::CreateTextureCallback(std::string const& texid,
00292                                                             int width,
00293                                                             int height,
00294                                                             glib::Object<GdkPixbuf> const& pixbuf)
00295 {
00296   int pixbuf_width, pixbuf_height;
00297   pixbuf_width = gdk_pixbuf_get_width(pixbuf);
00298   pixbuf_height = gdk_pixbuf_get_height(pixbuf);
00299   if (G_UNLIKELY(!pixbuf_height || !pixbuf_width))
00300   {
00301     LOG_ERROR(logger) << "Pixbuf: " << texid << " has a zero height/width: "
00302                       << width << "," << height;
00303     pixbuf_width = (pixbuf_width) ? pixbuf_width : 1; // no zeros please
00304     pixbuf_height = (pixbuf_height) ? pixbuf_height: 1; // no zeros please
00305   }
00306 
00307   if (pixbuf_width == pixbuf_height)
00308   {
00309     // quick path for square icons
00310     return nux::CreateTexture2DFromPixbuf(pixbuf, true);
00311   }
00312   else
00313   {
00314     // slow path for non square icons that must be resized to fit in the square
00315     // texture
00316 
00317     Style& style = Style::Instance();
00318     float aspect = static_cast<float>(pixbuf_height) / pixbuf_width; // already sanitized width/height so can not be 0.0
00319     if (aspect < 1.0f)
00320     {
00321       pixbuf_width = style.GetTileWidth() - (padding * 2);
00322       pixbuf_height = pixbuf_width * aspect;
00323 
00324       if (pixbuf_height > height)
00325       {
00326         // scaled too big, scale down
00327         pixbuf_height = height;
00328         pixbuf_width = pixbuf_height / aspect;
00329       }
00330     }
00331     else
00332     {
00333       pixbuf_height = height;
00334       pixbuf_width = pixbuf_height / aspect;
00335     }
00336 
00337     if (gdk_pixbuf_get_height(pixbuf) == pixbuf_height)
00338     {
00339       // we changed our mind, fast path is good
00340       return nux::CreateTexture2DFromPixbuf(pixbuf, true);
00341     }
00342 
00343     nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, pixbuf_width, pixbuf_height);
00344     cairo_t* cr = cairo_graphics.GetInternalContext();
00345 
00346     cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00347     cairo_paint(cr);
00348 
00349     float scale = float(pixbuf_height) / gdk_pixbuf_get_height(pixbuf);
00350 
00351     //cairo_translate(cr,
00352     //                static_cast<int>((width - (pixbuf_width * scale)) * 0.5),
00353     //                static_cast<int>((height - (pixbuf_height * scale)) * 0.5));
00354 
00355     cairo_scale(cr, scale, scale);
00356 
00357     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00358     gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
00359     cairo_paint(cr);
00360 
00361     return texture_from_cairo_graphics(cairo_graphics);
00362   }
00363 
00364 }
00365 
00366 nux::BaseTexture* ResultRendererTile::CreateBlurredTextureCallback(std::string const& texid,
00367                                                                    int width,
00368                                                                    int height,
00369                                                                    glib::Object<GdkPixbuf> const& pixbuf)
00370 {
00371   int pixbuf_width, pixbuf_height;
00372   pixbuf_width = gdk_pixbuf_get_width(pixbuf);
00373   pixbuf_height = gdk_pixbuf_get_height(pixbuf);
00374 
00375   nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, width + 10, height + 10);
00376   cairo_t* cr = cairo_graphics.GetInternalContext();
00377 
00378   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00379   cairo_translate(cr, 5, 5);
00380   cairo_paint(cr);
00381 
00382   float scale;
00383   if (pixbuf_width > pixbuf_height)
00384     scale = pixbuf_height / static_cast<float>(pixbuf_width);
00385   else
00386     scale = pixbuf_width / static_cast<float>(pixbuf_height);
00387 
00388   cairo_translate(cr,
00389                   static_cast<int>((width - (pixbuf_width * scale)) * 0.5),
00390                   static_cast<int>((height - (pixbuf_height * scale)) * 0.5));
00391 
00392   cairo_scale(cr, scale, scale);
00393 
00394   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00395   gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
00396 
00397   cairo_paint(cr);
00398 
00399   cairo_graphics.BlurSurface(4);
00400 
00401   return texture_from_cairo_graphics(cairo_graphics);
00402 }
00403 
00404 
00405 void ResultRendererTile::IconLoaded(std::string const& texid,
00406                                     unsigned size,
00407                                     glib::Object<GdkPixbuf> const& pixbuf,
00408                                     std::string icon_name,
00409                                     Result& row)
00410 {
00411   TextureContainer *container = row.renderer<TextureContainer*>();
00412   Style& style = Style::Instance();
00413 
00414   if (pixbuf && container)
00415   {
00416     TextureCache& cache = TextureCache::GetDefault();
00417     BaseTexturePtr texture(cache.FindTexture(icon_name, style.GetTileIconSize(), style.GetTileIconSize(),
00418                            sigc::bind(sigc::mem_fun(this, &ResultRendererTile::CreateTextureCallback), pixbuf)));
00419 
00420     std::string blur_texid = icon_name + "_blurred";
00421     BaseTexturePtr texture_blurred(cache.FindTexture(blur_texid, style.GetTileIconSize(), style.GetTileIconSize(),
00422                                    sigc::bind(sigc::mem_fun(this, &ResultRendererTile::CreateBlurredTextureCallback), pixbuf)));
00423 
00424     BaseTexturePtr texture_prelight(cache.FindTexture("resultview_prelight", texture->GetWidth() + highlight_padding*2, texture->GetHeight() + highlight_padding*2,  sigc::mem_fun(this, &ResultRendererTile::DrawHighlight)));
00425 
00426     container->icon = texture;
00427     container->blurred_icon = texture_blurred;
00428     container->prelight = texture_prelight;
00429 
00430     NeedsRedraw.emit();
00431 
00432     if (container)
00433       container->slot_handle = 0;
00434   }
00435   else if (container)
00436   {
00437     // we need to load a missing icon
00438     IconLoader::IconLoaderCallback slot = sigc::bind(sigc::mem_fun(this, &ResultRendererTile::IconLoaded), icon_name, row);
00439     container->slot_handle = IconLoader::GetDefault().LoadFromGIconString(". GThemedIcon text-x-preview", style.GetTileIconSize(), slot);
00440   }
00441 
00442 }
00443 
00444 
00445 void ResultRendererTile::LoadText(Result& row)
00446 {
00447   Style& style = Style::Instance();
00448   nux::CairoGraphics _cairoGraphics(CAIRO_FORMAT_ARGB32,
00449                                     style.GetTileWidth() - (padding * 2),
00450                                     style.GetTileHeight() - style.GetTileIconSize() - spacing);
00451 
00452   cairo_t* cr = _cairoGraphics.GetContext();
00453 
00454   PangoLayout*          layout     = NULL;
00455   PangoFontDescription* desc       = NULL;
00456   PangoContext*         pango_context   = NULL;
00457   GdkScreen*            screen     = gdk_screen_get_default();    // not ref'ed
00458   glib::String          font;
00459   int                   dpi = -1;
00460 
00461   g_object_get(gtk_settings_get_default(), "gtk-font-name", &font, NULL);
00462   g_object_get(gtk_settings_get_default(), "gtk-xft-dpi", &dpi, NULL);
00463 
00464   cairo_set_font_options(cr, gdk_screen_get_font_options(screen));
00465   layout = pango_cairo_create_layout(cr);
00466   desc = pango_font_description_from_string(font.Value());
00467   pango_font_description_set_size (desc, FONT_SIZE * PANGO_SCALE);
00468 
00469   pango_layout_set_font_description(layout, desc);
00470   pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
00471 
00472   pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
00473   pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_START);
00474   pango_layout_set_width(layout, (style.GetTileWidth() - (padding * 2))* PANGO_SCALE);
00475   pango_layout_set_height(layout, -2);
00476 
00477   char *escaped_text = g_markup_escape_text(row.name().c_str()  , -1);
00478 
00479   pango_layout_set_markup(layout, escaped_text, -1);
00480 
00481   g_free (escaped_text);
00482 
00483   pango_context = pango_layout_get_context(layout);  // is not ref'ed
00484   pango_cairo_context_set_font_options(pango_context,
00485                                        gdk_screen_get_font_options(screen));
00486   pango_cairo_context_set_resolution(pango_context,
00487                                      dpi == -1 ? 96.0f : dpi/(float) PANGO_SCALE);
00488   pango_layout_context_changed(layout);
00489 
00490   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00491   cairo_paint(cr);
00492 
00493   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00494   cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f);
00495 
00496   cairo_move_to(cr, 0.0f, 0.0f);
00497   pango_cairo_show_layout(cr, layout);
00498 
00499   // clean up
00500   pango_font_description_free(desc);
00501   g_object_unref(layout);
00502   cairo_destroy(cr);
00503 
00504   TextureContainer *container = row.renderer<TextureContainer*>();
00505   if (container)
00506     container->text = texture_ptr_from_cairo_graphics(_cairoGraphics);
00507 }
00508 
00509 
00510 }
00511 }