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