Lumiera  0.pre.03
»edit your freedom«
ui-style.cpp
Go to the documentation of this file.
1 /*
2  UiStyle - manage coherent UI styling
3 
4  Copyright (C)
5  2008, Joel Holdsworth <joel@airwebreathe.org.uk>
6  2017, Hermann Vosseler <Ichthyostega@web.de>
7 
8   **Lumiera** is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by the
10   Free Software Foundation; either version 2 of the License, or (at your
11   option) any later version. See the file COPYING for further details.
12 
13 * *****************************************************************/
14 
15 
27 #include "stage/style-scheme.hpp"
28 #include "stage/config-keys.hpp"
29 #include "lib/searchpath.hpp"
30 #include "lib/util.hpp"
31 
32 #include <gtkmm/stylecontext.h>
33 #include <boost/filesystem.hpp>
34 
35 using Gtk::IconSize;
36 using Gtk::IconFactory;
37 
38 
39 
40 namespace stage {
41 namespace workspace {
42 
43  namespace fsys = boost::filesystem;
44 
45  IconSize UiStyle::GiantIconSize = Gtk::ICON_SIZE_INVALID;
46  IconSize UiStyle::MenuIconSize = Gtk::ICON_SIZE_INVALID;
47 
48 
49 
50 
60  : Gtk::UIManager()
61  , iconSearchPath_{Config::get (KEY_ICON_PATH)}
62  , resourceSerachPath_{Config::get (KEY_UIRES_PATH)}
63  , styleAdviceTrackBody_{"style(trackBody)"}
64  , styleAdviceTrackRuler_{"style(trackRuler)"}
65  {
66  Glib::set_application_name (Config::get (KEY_TITLE));
67 
68  registerAppIconSizes();
70 
71  setTheme (Config::get (KEY_STYLESHEET));
72  }
73 
74 
75 
76  void
77  UiStyle::setTheme (string const& stylesheetName)
78  {
79  auto screen = Gdk::Screen::get_default();
80  auto css_provider = Gtk::CssProvider::create();
81  try
82  {
83  css_provider->load_from_path (lib::resolveModulePath (stylesheetName, resourceSerachPath_));
86  }
87  catch(Glib::Error const& failure)
88  {
89  WARN (stage, "Failure while loading stylesheet '%s': %s", cStr(stylesheetName), cStr(failure.what()));
90  }
91 
92  Gtk::StyleContext::add_provider_for_screen (screen, css_provider,
93  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
94  }
95 
96 
97 
116  void
118  {
119  // the first Timeline triggers initialisation
120  if (styleAdviceTrackBody_.isGiven()) return;
121 
122  Gtk::WidgetPath path = timeline.getBodyWidgetPath();
123  GType scopeNode = Gtk::Box::get_type();
124  int pos = path.path_append_type (scopeNode); // build a "virtual" CSS node to represent the track scope
125 
126  gtk_widget_path_iter_set_object_name (path.gobj(), pos, NODE_fork); // override the generic node name with a custom widget type "fork"
127  path.iter_add_class(pos, CLASS_timeline_fork); // decorate this CSS node with a CSS class ".timeline__fork" (distinguish it from asset bins)
128  // deliberately we *do not* invoke path.iter_set_name(pos, "id") to add an #ID
129  for (int i=0; i<pos; ++i) // reset any state flags accidentally set (resulting in pseudo classes like ":backdrop")
130  gtk_widget_path_iter_set_state(path.gobj(), i, GTK_STATE_FLAG_NORMAL);
131  PStyleContext style = Gtk::StyleContext::create(); // create a new style context and configure it according to the path defined thus far
132  style->set_screen(Gdk::Screen::get_default());
133  style->set_path (path);
134  styleAdviceTrackBody_.setAdvice (style); // publish as Advice "style(trackBody)"
135 
136  pos = path.path_append_type (scopeNode); // append another nested "virtual" CSS node to represent the a ruler track
137  gtk_widget_path_iter_set_object_name (path.gobj(), pos, NODE_frame); // ...but this time we explicitly use the conventional Name "frame" (hard wired default by GTK)
138  // note: the node is deliberately left as 'frame' to pick up existing styling
139  path.iter_add_class(pos, CLASS_timeline_ruler); // decorate with CSS class ".timeline__ruler" to identify position within the system
140  style = Gtk::StyleContext::create(); // create another style context...
141  style->set_path (path); // ...for this nested path. (Note: Gtk takes a copy of the path, see gtk_style_context_set_path(), line 1120)
142  styleAdviceTrackRuler_.setAdvice (style); // publish as Advice "style(trackRuler)"
143  }
144 
145 
146 
147 
148  Cairo::RefPtr<Cairo::SolidPattern>
149  UiStyle::readStyleColourProperty (Gtk::Widget& widget
150  ,const gchar * property_name
151  ,guint16 red, guint16 green, guint16 blue)
152  {
153  REQUIRE (property_name);
154 
155  // TODO: Can we get rid of the GdkColor completely here?
157  GdkColor *color;
158  gtk_widget_style_get(widget.gobj(), property_name, &color, NULL);
159 
160  Cairo::RefPtr<Cairo::SolidPattern> pattern;
161  // Did the colour load successfully?
162  if (color != NULL)
163  {
164  pattern = Cairo::SolidPattern::create_rgb ( (double)color->red / 0xFFFF,
165  (double)color->green / 0xFFFF,
166  (double)color->blue / 0xFFFF);
167  }
168  else
169  {
170  WARN (stage, "%s style value failed to load", property_name);
171 
172  pattern = Cairo::SolidPattern::create_rgb ( red, green, blue );
173  }
174 
175  return pattern;
176  }
177 
178 
179  void
180  UiStyle::registerAppIconSizes()
181  {
182  if(GiantIconSize == Gtk::ICON_SIZE_INVALID)
183  GiantIconSize = IconSize::register_new ("giant", 48, 48);
184  if(MenuIconSize == Gtk::ICON_SIZE_INVALID)
185  MenuIconSize = IconSize::register_new ("menu", 16, 16);
186  }
187 
188 
193  void
195  {
196  Glib::RefPtr<IconFactory> factory = Gtk::IconFactory::create();
197 
198  addStockIconSet(factory, "panel-assets", "panel_assets", _("_Assets"));
199  addStockIconSet(factory, "panel-viewer", "panel_viewer", _("_Viewer"));
200  addStockIconSet(factory, "panel-infobox", "panel_infobox", _("_InfoBox"));
201  addStockIconSet(factory, "panel-timeline", "panel_timeline",_("_Timeline"));
202  addStockIconSet(factory, "panel-timeline", "panel_timeline_obsolete",_("_ZombieTimeline"));
203 
204  addStockIconSet(factory, "window-new" ,"new_window" ,_("New _Window"));
205 
206  addStockIconSet(factory, "placement" ,ICON_placement ,_("_Placement"));
207  addStockIconSet(factory, "arrow-hand" ,ICON_arrow_hand_menu ,_("_Menu"));
208  addStockIconSet(factory, "arrow-hand" ,ICON_arrow_hand_down ,_("_Expand"));
209  addStockIconSet(factory, "arrow-hand" ,ICON_arrow_hand_up ,_("_Collapse"));
210 
211  addStockIconSet(factory, "tool-arrow", "tool_arrow", _("_Arrow"));
212  addStockIconSet(factory, "tool-i-beam", "tool_i_beam", _("_I-Beam"));
213 
214  addStockIconSet(factory, "track-disabled", "track_disabled",_("Track Disabled"));
215  addStockIconSet(factory, "track-enabled", "track_enabled", _("Track Enabled"));
216  addStockIconSet(factory, "track-locked", "track_locked", _("Track Locked"));
217  addStockIconSet(factory, "track-unlocked", "track_unlocked",_("Track Unlocked"));
218 
219  factory->add_default(); //Add factory to list of factories.
220  }
221 
222 
223  bool
224  UiStyle::addStockIconSet (Glib::RefPtr<IconFactory> const& factory
225  ,Literal iconName
226  ,Literal id
227  ,Literal label)
228  {
229  Glib::RefPtr<Gtk::IconSet> icon_set = Gtk::IconSet::create();
230  cuString uIconName{iconName}, uLabel{label}; // even while we're just passing through a C-string to GTK,
231  // unfortunately Glib::ustring wraps a std::string
232 
233  // Load all the sizes, wildcarding the largest icon to be loaded
234  bool found{false};
235  found |= addStockIcon (icon_set, uIconName, GiantIconSize, not found);
236  found |= addStockIcon (icon_set, uIconName, Gtk::ICON_SIZE_BUTTON, not found);
237  found |= addStockIcon (icon_set, uIconName, Gtk::ICON_SIZE_MENU, not found);
238  found |= addStockIcon (icon_set, uIconName, Gtk::ICON_SIZE_LARGE_TOOLBAR, not found);
239  found |= addStockIcon (icon_set, uIconName, MenuIconSize, not found);
240 
241  if (not found)
242  {
243  // No icons were loaded
244  ERROR (stage, "Unable to load icon '%s'", iconName);
245  return false;
246  }
247 
248  // Add the icon set to the icon factory
249  const Gtk::StockID stock_id(id);
250  factory->add(stock_id, icon_set);
251  Gtk::Stock::add(Gtk::StockItem(stock_id, uLabel));
252  return true;
253  }
254 
255 
256  bool
257  UiStyle::addStockIcon (Glib::RefPtr<Gtk::IconSet> const& icon_set
258  ,cuString& icon_name
259  ,Gtk::IconSize size
260  ,bool wildcard)
261  {
262  // Try the icon theme
263  if (addThemeIconSource(icon_set, icon_name, size, wildcard))
264  return true;
265 
266  // Try to resolve the icon via the configured search path
267  lib::SearchPathSplitter iconLocations (iconSearchPath_);
268  while (iconLocations)
269  if (addNonThemeIconSource (icon_set
270  ,iconLocations.next()
271  ,icon_name
272  ,size
273  ,wildcard))
274  return true;
275 
276  return false; // icon not found
277  }
278 
279 
280  bool
281  UiStyle::addThemeIconSource (Glib::RefPtr<Gtk::IconSet> const& icon_set
282  ,cuString& icon_name
283  ,Gtk::IconSize size
284  ,bool wildcard)
285  {
286  // Get the size
287  int width = 0, height = 0;
288  if(!IconSize::lookup(size, width, height))
289  return false;
290  REQUIRE(width > 0);
291 
292  // Try to load the icon
293  Glib::RefPtr<Gtk::IconTheme> theme = Gtk::IconTheme::get_default();
294  REQUIRE(theme);
295 
297  Gtk::IconInfo info = theme->lookup_icon(icon_name, width, (Gtk::IconLookupFlags)0);
298 
299  if (!info) return false; // unable to resolve Icon
300 
301  cuString path(info.get_filename());
302  return addStockIconFromPath(path, icon_set, size, wildcard);
303  }
304 
305 
306  bool
307  UiStyle::addNonThemeIconSource (Glib::RefPtr<Gtk::IconSet> const& icon_set
308  ,cuString& base_dir
309  ,cuString& icon_name
310  ,Gtk::IconSize size
311  ,bool wildcard)
312  {
313  // Get the size
314  int width = 0, height = 0;
315  if(!IconSize::lookup(size, width, height))
316  return false;
317  REQUIRE(width > 0);
318 
319  // Try to load the icon
320  cuString path(Glib::ustring::compose("%1/%2x%3/%4.png",
321  base_dir, width, height, icon_name));
322  return addStockIconFromPath(path, icon_set, size, wildcard);
323  }
324 
325 
326  bool
328  ,Glib::RefPtr<Gtk::IconSet> const& icon_set
329  ,Gtk::IconSize size
330  ,bool wildcard)
331  {
332  if (!fsys::exists (path)) return false;
333 
334  try {
335  Gtk::IconSource source;
336  source.set_pixbuf(Gdk::Pixbuf::create_from_file(path));
337  source.set_size_wildcarded(wildcard);
338  source.set_size(size);
339 
340  icon_set->add_source(source);
341 
342  return true;
343  }
344 
345  catch(Glib::Exception const& ex)
346  {
347  WARN (stage, "Failure when accessing icon '%s'. Problem: %s", cStr(path), cStr(ex.what()));
348  return false;
349  }
350  }
351 
352 
353 
354 }}// namespace stage::workspace
Definition of access keys for global UI configuration.
Service for global theming and style related concerns.
Helpers to handle directory search paths.
bool addStockIcon(Glib::RefPtr< Gtk::IconSet > const &icon_set, cuString &icon_name, Gtk::IconSize size, bool wildcard)
Loads an icon, searching standard icon locations, and adds it to an icon set.
Definition: ui-style.cpp:257
CStr cStr(std::string const &rendered)
convenience shortcut: forced conversion to c-String via string.
Definition: symbol.hpp:59
bool addStockIconFromPath(string path, Glib::RefPtr< Gtk::IconSet > const &icon_set, Gtk::IconSize size, bool wildcard)
Loads an icon from a specific path and adds it to an icon set.
Definition: ui-style.cpp:327
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
UiStyle()
Set up a coherent theming and styling for the application.
Definition: ui-style.cpp:59
void prepareStyleContext(timeline::TimelineWidget const &)
Use the existing TimelineWidget&#39;s GTK-WidgetPath to establish a systematic CSS styling context...
Definition: ui-style.cpp:117
bool addStockIconSet(Glib::RefPtr< Gtk::IconFactory > const &factory, Literal iconName, Literal id, Literal label)
Adds an icon (in different sizes) to the icon factory.
Definition: ui-style.cpp:224
static Gtk::IconSize GiantIconSize
The registered icon size for giant 48x48 px icons.
Definition: ui-style.hpp:80
static string get(lib::Literal key)
bool addNonThemeIconSource(Glib::RefPtr< Gtk::IconSet > const &icon_set, cuString &base_dir, cuString &icon_name, Gtk::IconSize size, bool wildcard)
Loads an icon from a non theme set.
Definition: ui-style.cpp:307
void registerStockItems()
Registers application stock items: icons and labels associated with IDs.
Definition: ui-style.cpp:194
static Gtk::IconSize MenuIconSize
The registered icon size for giant 16x16 px icons.
Definition: ui-style.hpp:86
Core timeline display (custom widget).
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:37
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
bool addThemeIconSource(Glib::RefPtr< Gtk::IconSet > const &icon_set, cuString &icon_name, Gtk::IconSize size, bool wildcard)
Loads an icon from a the icon theme.
Definition: ui-style.cpp:281
void setTheme(string const &stylesheetName)
Sets the theme to use for the Lumiera GUI.
Definition: ui-style.cpp:77
static Cairo::RefPtr< Cairo::SolidPattern > readStyleColourProperty(Gtk::Widget &widget, const gchar *property_name, guint16 red, guint16 green, guint16 blue)
A utility function which reads a colour style from the GTK Style.
Definition: ui-style.cpp:149
Helper: Access a path Specification as a sequence of filesystem Paths.
Definition: searchpath.hpp:62
Definition of access keys for uniform UI styling.
string resolveModulePath(fsys::path moduleName, string searchPath)
helper to establish the location to search for loadable modules, configuration files, icons and further resources.
Definition: searchpath.cpp:79
This file defines the core component of the Lumiera GUI.