3 - Virtual Style Basics

This chapter will teach you the basics of "Trickle down Virtual Styles".  Virtual styles as implemented for FLVW have little to do with styles from FLTK 2.x series styles/schemes.  They are style aware, that means whatever I think it means. :)

The Problem

When creating the Virtual Widgets, I needed a way to specify attributes for cells that was fairly memory efficient.  For a 2 billion by 2 billion row table, it just wasn't feasible to store style information for every cell.  I found that in general a couple of style formats occurred over and over.  One, I generally needed a style for the widget, besides that I found that specific rows and columns often would need a different style.  For instance headers and footers often had a different formatting than the basic cells.  Total columns and rows would often have a different style.  Again I faced the problem that for even a single row or column, storing style information on a cell by cell basis was going to be a problem for large data-sets.

The Solution

The solution I settled on was a two part solution.  Part one required the definition of style information and more importantly, a way to tell whether a particular bit of style information had been defined and of course a way to clear it.  Part two required being able to access a set of styles efficiently, both randomly (the way a user might) and sequentially (the way drawing does).  As another design goal, I wanted the use of styles, however they were implemented, to be trivial for the end-user.  Two classes came from this, Flv_Style and Flv_Style_List

Flv_Style maintains a set of "style" information.  In a nutshell, style information is just the way something looks and in some cases behaves.  This includes; the alignment of drawing, the background color, the font, the font size, the foreground color, the row height, if a cell can be edited, if a cell can be resized, and the column width.  Complex styles also include the x and y position of a cell.

Flv_Style_List maintains a set of styles.  It does this by creating a sparse array, using a value to determine where in the array a style is. The list includes a random access method: find, and a sequential access method: skip_to.  The list also contains a special operator, the [] operator, which is a guaranteed access operator.  What that means is that if you access style value x and style x already exists, that's the style that is returned.  If however you access style value x and style x doesn't exist, the list will create it, insert it in the appropriate place, with no style information defined, and return a reference to that newly created style.

The implementation of these includes global_style which is a single style, row_style which is a list of styles, and if appropriate column_style which is also a list of styles.  Individual cell styles may be accessed via cell_style included in the row_style.

How do I use it?

Before using any bit of style information, you should first test to determine if it in fact is defined.  This should be done for lists as well as individual styles.  The exception to this is any time you are setting a style value.  If you are setting a style value, you probably don't care if anything is defined or not, you simply want to set it to a specific value. 

In the following examples the variable x will be a dynamically created widget.

You would set the global_style foreground color like so:

x->global_style.foreground(FL_RED);

and use the global_style foreground color like so:

value = default_foreground_color;
if (x->global_style.foreground_defined())
  value = x->global_style.foreground();

Note: in the example you would be supplying the variable default_foreground_color.  It is not a part of the style implementations.

Setting row_style 5's frame type to a raised box:

x->row_style[5].frame(FL_THIN_UP_BOX);

Accessing row_style[5]'s frame type:

value = default_frame_style;
if (x->row_style.find(5))
  if (x->row_style[5].frame_defined())
    value = x->row_style[5].frame()

Note: in the example, you would be supplying a variable default_frame_style.  It is not a part of the style implementation.

Notice that setting values is much easier than retrieving them.  This is because normally you will find yourself setting values and letting the widget take care of accessing the values when it draws.  The above examples are the complete correct way to access or set values.  You may however be able to "short circuit" the process.  For instance, if you want to add 4 to row -1's height, the following is acceptable:

v = 20;  // default value
if (x->row_style[-1].height_defined())
  v = x->row_style[-1].height();
x->row_style[-1].height(v+4);

Note that we did not check to see if row_style -1 already existed.  The reason is that whether it exists or not, when we get done, it will.  We still need to check if the height is defined. 

Normally there will already be a default value for something.  In the above example, we chose a height of 20 as the default value.  But normally it would already have a height.  To gain access to the "pre-defined" style defaults, you could change the above code to:

Flv_Style s;
v = 20;  // default value
x->get_style(-1,s);
if (s.height_defined())
  v = s.height()
x->row_style[-1].height(v+4);

That doesn't appear to be much of an improvement!  It's not.  But,... if you NEVER allow global_style information to be undefined (You have to do it in code somewhere) you could change it to:

Flv_Style s;
x->get_style(-1,s);
x->row_style[-1].height(s.height()+4);

Get style will get the "trickle-down" value for a style.  In the above example for Flv_List this means, first undefine all style information in s, apply any defined global_style information to s, and then apply any defined row_style information for row -1 to s.

If you are dealing with an Flv_Table with using the following code:

Flv_Style s;
x->get_style(row, col, s);

the "trickle-down" value is determined in the following way:  First undefine all style information in s, next apply any defined global_style information to s, next apply any defined row_style information for row row to s, next apply any col_style information for column col to s, and last apply any defined cell_style information for row row and column col to s. 

If a row, column, or cell style does not exist in the list, all style information is considered to be undefined.

For the most complex example consider the following cell_style operations.  To set the cell_style foreground to FL_RED for cell (5,7):

x->row_style[5].cell_style[7].foreground(FL_RED);

To retrieve the cell_style foreground color for cell (5,7):

value = default_foreground_color;
if (x->row_style.find(5))
  if (x->row_style[5].cell_style.find(7))
    if (x->row_style[5].cell_style.find(7).foreground_defined())
      value = x->row_style[5].cell_style[7].foreground();

Note: you must define the default_foreground_color in your application, it is not part of this library.

If you simply want to know what the foreground color for cell (5,7) was you would:

Flv_Style s;
x->get_style(5,7,s);
value = s.foreground();

Again the above example assumes that you will NEVER undefine any global_style properties.

How do I undefine a style property?

There are a series of clear_ functions defined that allow you to undefine any single property.  There is allso a clear_all function to undefine all properties for a style.