RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Nano-Sheets: A Small But Mighty Spreadsheet Engine in REBOL  : Page 3

Even in today's big-computing environment, not everything useful has to be big. This article shows you how to build a tiny and basic—but powerful—GUI spreadsheet engine in REBOL, using fewer than 1500 bytes of code.

Analyzing the Code
The Nano-Sheets code begins with a few definitions that control the size of the cells and the overall sheet itself. The cell size is in pixels; the sheet size in cells. They are both REBOL pair! values.

   csize: 100x20
   size: 8x16
There are a few helper functions to make things easier and clearer in the main generation loop. The meaning of col-lbl and cell-name should be pretty obvious, but mk-var might not be; it creates a variable name dynamically so you don't have to declare variables statically for every cell.

   col-lbl: func [col] [form to char! 64 + col]
   cell-name: func [x y] [join col-lbl x y]
   mk-var: func [x y] [to lit-word! cell-name x y]
The sheet definition starts out with some static elements that set the spacing and layout orientation, and then define a couple of styles used in the UI, much as you might use CSS to define styles for use in an HTML document.

   sheet: copy [
       space 1x1 across
       style cell field csize edge none with [formula: 
           [enter face  compute  face/para/scroll: 0x0]
       style label text csize white black bold center
Next is the real magic. The two nested loops add elements to the sheet layout spec. If you've ever written a CGI app to dynamically generate a table in HTML, you'll recognize that this is the same concept. The big difference is that these items will become active cells when they are rendered, and will be bound to a variable that represents them for use in formulas.

   repeat y 1 + size/y [
       repend sheet ['label (csize / 2x1) either 1 = y 
          [""] [form y - 1]]
       repeat x size/x [
           append sheet compose/deep either 1 = y [[label 
             (col-lbl x)]] [
               [cell with [var: (mk-var x y - 1)]]
       append sheet 'return
Setting Cell Values and Triggering Recalculations
The spreadsheet calls the enter function when a cell loses focus. Its job is to evaluate what a user entered, decide if it's a formula, and set the value for the variable bound to the cell. Formulas, as in other spreadsheets, are identified by a leading equal sign (=). The spreadsheet coerces text entered in the cell to the most appropriate internal data type, so calculations operate in an intuitive way (e.g. $100 * 3 = $300).

   enter: func [face /local data] [
       if empty? face/text [exit]
       set face/var face/text
       data: either #"=" = face/text/1 [next 
       if error? try [data: load data] [exit]
       if find [integer! decimal! money! time! date! 
          tuple! pair!] type?/word :data [
           set face/var data exit
       if face/text/1 = #"=" [face/formula: :data]

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date