Back to index

unity  6.0.0
PanelStyle.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Canonical Ltd
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License version 3 as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authored by: Mirco Müller <mirco.mueller@canonical.com>
00017  *              Neil Jagdish Patel <neil.patel@canonical.com>
00018  *              Marco Trevisan <3v1n0@ubuntu.com>
00019  */
00020 
00021 #include "config.h"
00022 
00023 #include <math.h>
00024 #include <gtk/gtk.h>
00025 #include <gconf/gconf-client.h>
00026 #include <boost/algorithm/string/predicate.hpp>
00027 
00028 #include <Nux/Nux.h>
00029 #include <NuxGraphics/GraphicsEngine.h>
00030 #include <NuxGraphics/CairoGraphics.h>
00031 #include <NuxCore/Logger.h>
00032 
00033 #include "CairoTexture.h"
00034 #include "PanelStyle.h"
00035 
00036 #include <UnityCore/GLibWrapper.h>
00037 #include "unity-shared/UnitySettings.h"
00038 
00039 namespace unity
00040 {
00041 namespace panel
00042 {
00043 namespace
00044 {
00045 Style* style_instance = nullptr;
00046 
00047 nux::logging::Logger logger("unity.panel.style");
00048 
00049 const std::string METACITY_SETTINGS_PATH("/apps/metacity/general/");
00050 const std::string PANEL_TITLE_FONT_KEY("/apps/metacity/general/titlebar_font");
00051 const std::string HIGH_CONTRAST_THEME_PREFIX("HighContrast");
00052 
00053 nux::Color ColorFromGdkRGBA(GdkRGBA const& color)
00054 {
00055   return nux::Color(color.red,
00056                     color.green,
00057                     color.blue,
00058                     color.alpha);
00059 }
00060 
00061 }
00062 
00063 Style::Style()
00064   : panel_height(24)
00065   , _style_context(gtk_style_context_new())
00066 {
00067   if (style_instance)
00068   {
00069     LOG_ERROR(logger) << "More than one panel::Style created.";
00070   }
00071   else
00072   {
00073     style_instance = this;
00074   }
00075 
00076   if (Settings::Instance().GetFormFactor() == FormFactor::TV)
00077     panel_height = 0;
00078   
00079   Settings::Instance().changed.connect([this]() 
00080   {
00081     if (Settings::Instance().GetFormFactor() == FormFactor::TV)
00082       panel_height = 0;
00083   });
00084 
00085   GtkWidgetPath* widget_path = gtk_widget_path_new();
00086   gint pos = gtk_widget_path_append_type(widget_path, GTK_TYPE_WINDOW);
00087   gtk_widget_path_iter_set_name(widget_path, pos, "UnityPanelWidget");
00088 
00089   gtk_style_context_set_path(_style_context, widget_path);
00090   gtk_style_context_add_class(_style_context, "gnome-panel-menu-bar");
00091   gtk_style_context_add_class(_style_context, "unity-panel");
00092 
00093   gtk_widget_path_free(widget_path);
00094 
00095   GtkSettings* settings = gtk_settings_get_default();
00096 
00097   _style_changed_signal.Connect(settings, "notify::gtk-theme-name",
00098   [&] (GtkSettings*, GParamSpec*) {
00099     Refresh();
00100   });
00101 
00102   _font_changed_signal.Connect(settings, "notify::gtk-font-name",
00103   [&] (GtkSettings*, GParamSpec*) {
00104     changed.emit();
00105   });
00106 
00107   _dpi_changed_signal.Connect(settings, "notify::gtk-xft-dpi",
00108   [&] (GtkSettings*, GParamSpec*) {
00109     changed.emit();
00110   });
00111 
00112   GConfClient* client = gconf_client_get_default();
00113   gconf_client_add_dir(client, METACITY_SETTINGS_PATH.c_str(), GCONF_CLIENT_PRELOAD_NONE, nullptr);
00114   _gconf_notify_id = gconf_client_notify_add(client, PANEL_TITLE_FONT_KEY.c_str(),
00115     [] (GConfClient*,guint,GConfEntry*, gpointer data)
00116     {
00117       auto self = static_cast<Style*>(data);
00118       self->changed.emit();
00119     }, this, nullptr, nullptr);
00120 
00121   Refresh();
00122 }
00123 
00124 Style::~Style()
00125 {
00126   if (style_instance == this)
00127     style_instance = nullptr;
00128 
00129   if (_gconf_notify_id)
00130     gconf_client_notify_remove(gconf_client_get_default(), _gconf_notify_id);
00131 }
00132 
00133 Style& Style::Instance()
00134 {
00135   if (!style_instance)
00136   {
00137     LOG_ERROR(logger) << "No panel::Style created yet.";
00138   }
00139 
00140   return *style_instance;
00141 }
00142 
00143 void Style::Refresh()
00144 {
00145   GdkRGBA rgba_text_color;
00146   glib::String theme_name;
00147   bool updated = false;
00148   
00149   GtkSettings* settings = gtk_settings_get_default();
00150   g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
00151 
00152   if (_theme_name != theme_name.Str())
00153   {
00154     _theme_name = theme_name.Str();
00155     updated = true;
00156   }
00157 
00158   gtk_style_context_invalidate(_style_context);
00159   gtk_style_context_get_color(_style_context, GTK_STATE_FLAG_NORMAL, &rgba_text_color);
00160   nux::Color const& new_text_color = ColorFromGdkRGBA(rgba_text_color);
00161 
00162   if (_text_color != new_text_color)
00163   {
00164     _text_color = new_text_color;
00165     updated = true;
00166   }
00167 
00168   if (updated)
00169     changed.emit();
00170 }
00171 
00172 GtkStyleContext* Style::GetStyleContext()
00173 {
00174   return _style_context;
00175 }
00176 
00177 nux::NBitmapData* Style::GetBackground(int width, int height, float opacity)
00178 {
00179   nux::CairoGraphics context(CAIRO_FORMAT_ARGB32, width, height);
00180 
00181   // Use the internal context as we know it is good and shiny new.
00182   cairo_t* cr = context.GetInternalContext();
00183   cairo_push_group(cr);
00184   gtk_render_background(_style_context, cr, 0, 0, width, height);
00185   gtk_render_frame(_style_context, cr, 0, 0, width, height);
00186   cairo_pop_group_to_source(cr);
00187   cairo_paint_with_alpha(cr, opacity);
00188 
00189   return context.GetBitmap();
00190 }
00191 
00192 nux::BaseTexture* Style::GetWindowButton(WindowButtonType type, WindowState state)
00193 {
00194   nux::BaseTexture* texture = NULL;
00195   std::string names[] = { "close", "minimize", "unmaximize", "maximize" };
00196   std::string states[] = { "", "_focused_prelight", "_focused_pressed", "_unfocused",
00197                            "_unfocused", "_unfocused_prelight", "_unfocused_pressed"};
00198 
00199   std::ostringstream subpath;
00200   subpath << "unity/" << names[static_cast<int>(type)]
00201           << states[static_cast<int>(state)] << ".png";
00202 
00203   // Look in home directory
00204   const char* home_dir = g_get_home_dir();
00205   if (home_dir)
00206   {
00207     glib::String filename(g_build_filename(home_dir, ".themes", _theme_name.c_str(), subpath.str().c_str(), NULL));
00208 
00209     if (g_file_test(filename.Value(), G_FILE_TEST_EXISTS))
00210     {
00211       glib::Error error;
00212 
00213       // Found a file, try loading the pixbuf
00214       glib::Object<GdkPixbuf> pixbuf(gdk_pixbuf_new_from_file(filename.Value(), &error));
00215       if (error)
00216         LOG_WARNING(logger) << "Unable to load window button " << filename.Value() << ": " << error.Message();
00217       else
00218         texture = nux::CreateTexture2DFromPixbuf(pixbuf, true);
00219     }
00220   }
00221 
00222   // texture is NULL if the pixbuf is not loaded
00223   if (!texture)
00224   {
00225     const char* var = g_getenv("GTK_DATA_PREFIX");
00226     if (!var)
00227       var = "/usr";
00228 
00229     glib::String filename(g_build_filename(var, "share", "themes", _theme_name.c_str(), subpath.str().c_str(), NULL));
00230 
00231     if (g_file_test(filename.Value(), G_FILE_TEST_EXISTS))
00232     {
00233       glib::Error error;
00234 
00235       // Found a file, try loading the pixbuf
00236       glib::Object<GdkPixbuf> pixbuf(gdk_pixbuf_new_from_file(filename.Value(), &error));
00237       if (error)
00238         LOG_WARNING(logger) << "Unable to load window button " << filename.Value() << ": " << error.Message();
00239       else
00240         texture = nux::CreateTexture2DFromPixbuf(pixbuf, true);
00241     }
00242   }
00243 
00244   if (!texture)
00245     texture = GetFallbackWindowButton(type, state);
00246 
00247   return texture;
00248 }
00249 
00250 nux::BaseTexture* Style::GetFallbackWindowButton(WindowButtonType type,
00251                                                  WindowState state)
00252 {
00253   int width = 17, height = 17;
00254   int canvas_w = 19, canvas_h = 19;
00255 
00256   if (boost::starts_with(_theme_name, HIGH_CONTRAST_THEME_PREFIX))
00257   {
00258     width = 20, height = 20;
00259     canvas_w = 22, canvas_h = 22;
00260   }
00261 
00262   float w = width / 3.0f;
00263   float h = height / 3.0f;
00264   nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, canvas_w, canvas_h);
00265   nux::Color main = (state != WindowState::UNFOCUSED) ? _text_color : nux::color::Gray;
00266   cairo_t* cr = cairo_graphics.GetContext();
00267 
00268   if (type == WindowButtonType::CLOSE)
00269   {
00270     double alpha = (state != WindowState::UNFOCUSED) ? 0.8f : 0.5;
00271     main = nux::Color(1.0f, 0.3f, 0.3f, alpha);
00272   }
00273 
00274   switch (state)
00275   {
00276     case WindowState::PRELIGHT:
00277       main = main * 1.2f;
00278       break;
00279     case WindowState::UNFOCUSED_PRELIGHT:
00280       main = main * 0.9f;
00281       break;
00282     case WindowState::PRESSED:
00283       main = main * 0.8f;
00284       break;
00285     case WindowState::UNFOCUSED_PRESSED:
00286       main = main * 0.7f;
00287       break;
00288     case WindowState::DISABLED:
00289       main = main * 0.5f;
00290       break;
00291     default:
00292       break;
00293   }
00294 
00295   cairo_translate(cr, 0.5, 0.5);
00296   cairo_set_line_width(cr, 1.5f);
00297 
00298   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00299   cairo_paint(cr);
00300 
00301   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00302 
00303   cairo_set_source_rgba(cr, main.red, main.green, main.blue, main.alpha);
00304 
00305   cairo_arc(cr, width / 2.0f, height / 2.0f, (width - 2) / 2.0f, 0.0f, 360 * (M_PI / 180));
00306   cairo_stroke(cr);
00307 
00308   if (type == WindowButtonType::CLOSE)
00309   {
00310     cairo_move_to(cr, w, h);
00311     cairo_line_to(cr, width - w, height - h);
00312     cairo_move_to(cr, width - w, h);
00313     cairo_line_to(cr, w, height - h);
00314   }
00315   else if (type == WindowButtonType::MINIMIZE)
00316   {
00317     cairo_move_to(cr, w, height / 2.0f);
00318     cairo_line_to(cr, width - w, height / 2.0f);
00319   }
00320   else if (type == WindowButtonType::UNMAXIMIZE)
00321   {
00322     cairo_move_to(cr, w, h + h/5.0f);
00323     cairo_line_to(cr, width - w, h + h/5.0f);
00324     cairo_line_to(cr, width - w, height - h - h/5.0f);
00325     cairo_line_to(cr, w, height - h - h/5.0f);
00326     cairo_close_path(cr);
00327   }
00328   else // if (type == WindowButtonType::MAXIMIZE)
00329   {
00330     cairo_move_to(cr, w, h);
00331     cairo_line_to(cr, width - w, h);
00332     cairo_line_to(cr, width - w, height - h);
00333     cairo_line_to(cr, w, height - h);
00334     cairo_close_path(cr);
00335   }
00336 
00337   cairo_stroke(cr);
00338   cairo_destroy(cr);
00339 
00340   return texture_from_cairo_graphics(cairo_graphics);
00341 }
00342 
00343 glib::Object<GdkPixbuf> Style::GetHomeButton()
00344 {
00345   glib::Object<GdkPixbuf> pixbuf;
00346 
00347   pixbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
00348                                     "start-here",
00349                                     panel_height,
00350                                     (GtkIconLookupFlags)0,
00351                                     NULL);
00352   if (!pixbuf)
00353     pixbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
00354                                       "distributor-logo",
00355                                       panel_height,
00356                                       (GtkIconLookupFlags)0,
00357                                       NULL);
00358   return pixbuf;
00359 }
00360 
00361 std::string Style::GetFontDescription(PanelItem item)
00362 {
00363   switch (item)
00364   {
00365     case PanelItem::INDICATOR:
00366     case PanelItem::MENU:
00367     {
00368       glib::String font_name;
00369       g_object_get(gtk_settings_get_default(), "gtk-font-name", &font_name, nullptr);
00370       return font_name.Str();
00371     }
00372     case PanelItem::TITLE:
00373     {
00374       GConfClient* client = gconf_client_get_default();
00375       glib::String font_name(gconf_client_get_string(client, PANEL_TITLE_FONT_KEY.c_str(), nullptr));
00376       return font_name.Str();
00377     }
00378   }
00379 
00380   return "";
00381 }
00382 
00383 int Style::GetTextDPI()
00384 {
00385   int dpi = 0;
00386   g_object_get(gtk_settings_get_default(), "gtk-xft-dpi", &dpi, nullptr);
00387 
00388   return dpi;
00389 }
00390 
00391 } // namespace panel
00392 } // namespace unity