diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kspread/DESIGN.html | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kspread/DESIGN.html')
-rw-r--r-- | kspread/DESIGN.html | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/kspread/DESIGN.html b/kspread/DESIGN.html new file mode 100644 index 00000000..a2248654 --- /dev/null +++ b/kspread/DESIGN.html @@ -0,0 +1,745 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> + <title>KSpread Development Notes</title> +</head> + +<body> + +<h1>KSpread Development Notes</h1> + +<p>Maintainer: Ariya Hidayat (<a href="mailto:ariya@kde.org">ariya@kde.org</a>)</p> +<p>Some portions by Tomas Mecir (<a href="mailto: mecirt@gmail.com">mecirt@gmail.com</a>)</p> +<p>Revision: September 2004.</p> + +<h2>Introduction</h2> + +<p>This document contains information about internal structure of KSpread +as well as some notes of upcoming redesign. The sources for this document +are mainly the discussions which take place in koffice-devel mailing-list +and the source code itself.</p> + +<h2>Document/View Architecture</h2> + +<p>Status: IN PROGRESS.</p> + +<p>MVC (Model/View/Controller) means that the application consists of three +big parts, the <i>Model</i> which holds the data structure and objects, +the <i>View</i> which shows the model to the user and the <i>Controller</i> +which handles user inputs and changes the model accordingly. Like other +office applications, KSpread uses the Document/View architecture, a slightly +different variant of MVC where the View and Controller are put together +as one part.</p> + +<p>In order of its complexity scale, KSpread code has to be well separated, +i.e. the <i>Document</i> and the <i>View</i>. We may also call them as +<i>back-end</i> and <i>front-end</i> respectively. Right now part of which +should belong to the Document sometimes has access to the View. For example, +a cell stores information about its metrics in pixels (which is zoom dependent), +knows whether it is visible to the user or not (which is view dependent), etc. +This needs to be changed.</p> + +<p>One easy way to decide whether some stuff or relationship must really really +alienated in the Document is to imagine that somebody wants to create another +View (front-end) to the Document object model (back-end) that is being worked +on. Say, one decent guy would like to copy the look-and-feel of classic Lotus +1-2-3 (for whatever reason we are not really interested in here); so basically +to some extent he can take most part of the KSpread back-end and glue a new +user interface around the code.</p> + +<h2>Dependency Handling</h2> +<p>Status: IN PROGRESS.</p> + +<p>When a cell holds a formula, then it is likely that it depends on other +cell(s) for calculating the result. For example, if cell A11 has the formula +"=SUM(A1:A10)", this means that values in cells A1, A2, A3, until +A10 must be correctly calculated first before the sum can be obtained for +cell A11. This is called <i>dependency</i>.</p> + +<p>As for now, KSpread tries to manage dependency by storing the dependent +cells or ranges in the cell itself. This is not too efficient. If a cell +is very simple, i.e. stored only value, not formula, such scheme will just +waste a couple of bytes of pointers for the dependency data structure. +It is much more wiser to simply create one <i>dependency manager</i> for each +worksheet; it should be responsible for maintaining and handling cell +dependencies for that sheet. Also KSpread always stores ranges which depend +on one particular cell and ranges whose one of its dependent is that cell +(and these are all in the cell structure itself). This is not necessary as +that information is redundant. The dependency manager should be able to handle +both cases.</p> + +<p>Let us have a look at this simple example:</p> + +<table cellspacing="0" cellpadding="3" border="1"> +<tr> + <td align="center"> </td> + <td align="center">A</td> + <td align="center">B</td> + <td align="center">C</td> + <td align="center">D</td> +</tr> +<tr> + <td align="center">1</td> + <td align="right">14</td> + <td align="right">36</td> + <td align="right"> </td> + <td align="right"> </td> +</tr> +<tr> + <td align="center">2</td> + <td align="right">3</td> + <td align="right"> </td> + <td align="right"> </td> + <td align="right"> </td> +</tr> +<tr> + <td align="center">3</td> + <td align="right">77</td> + <td align="right"> </td> + <td align="right"> </td> + <td align="right"> </td> +</tr> +<tr> + <td align="center">4</td> + <td align="right">=SUM(<b>A1:A3</b>)</td> + <td align="right">=A4+SUM(<b>B1:B3</b>)</td> + <td align="right">=100*<b>B4</b></td> + <td align="right"> </td> +</tr> +</table> + +<p>Such sheet should produce dependencies like:</p> + +<table cellspacing="0" cellpadding="3" border="1"> +<tr> + <td><b>Reference</b></td> + <td><b>Dependent(s)</b></td> +</tr> +<tr> + <td>A4</td> + <td>A1:A3</td> +</tr> +<tr> + <td>B4</td> + <td>A4 and B1:B3</td> +</tr> +<tr> + <td>C4</td> + <td>B4</td> +</tr> +</table> + +<p>When we want to recalculate cell B4, from the dependencies shown above we +may know that first we need to know values of cell A4 and range B1:B3. Further on, +cell A4 needs to know values of cells in range A1:A3. Therefore, <i>given one reference +cell</i> (e.g. B4), the dependency manager must be able to <i>return all +dependents, cells and/or ranges</i> (e.g. A4, B1:B3). Do we need to go +recursively when searching for dependencies? That really depends on the +implementation, but it is not a big problem, though.</p> + +<p>In another case, say the user has changed cell A3 so we need to update the +calculation. We should not recalculate the whole sheet because it wastes time. +We just need to recalculate cells that depend on A3, in this case A4, B4 and C4. +So the dependency manager has another responsibility: <i>given a cell</i> +it should <i>find all cells and/or ranges which depend on that particular +cell</i>. It is a matter of iterating over all dependencies and checking +whether the cell is within the dependent(s) and returning the reference cell. +In this example, cell A3 is in the range A1:A3, a dependent range of cell A4. +Hence, we just return A4. Recursive or not, we can either continue finding +dependents of A4 or just stop here.</p> + +<p>Note also that dependency manager should not store cell pointers, but rather +only the location of the cell (i.e. the sheet that owns the cell, row number and +column number). This is because on some cases the dependent cell may not exist +yet. As illustrated in the example, dependents of cell B4 are A4, B1, B2 and B3 +but here cells B2 and B3 are still empty. Of course, when we just want to +know which cells we need to recalculate for one reference cell, the dependency +manager is allowed to return only non-empty cells (e.g. A4 and B1 in our case) +as empty cells have no effect and will not be recalculated anyway.</p> + +<p>By the same manner, dependency manager can also held responsible when +chart comes into play. Any charts placed in the sheet (that are actually KChart +parts) depend on some values of the cells. An action by the user to changing +those cells, directly or indirectly, should trigger the update of the respective +charts.</p> + +<p>Inter-sheet dependencies can be well handled if we store the owner of +each dependent. This is not shown yet in the explanation above to avoid +unnecessary complication. But let have one example now: if Sheet2!A1 is +"=SUM(Sheet1!A1:A10)" then changing Sheet1!A1 (the dependent) +means updating Sheet2!A1 (the reference). Of course during recalculation we +must take care that all sheets in the document must be processed, even though +only one single cell in one sheet has been changed.</p> + +<p>Implementation-wise, there will be one instance of the dependency manager +for each sheet. This class will fully manage all dependencies and trigger cell +recalculation. An important part of this concept is this: The cell itself knows +<em>nothing</em> about dependencies, and it doesn't care about them either. +The cell will just inform about the fact, that its value has been changed, +and the dependency manager will do the rest. In addition, this gives us +recursive dependency calculation at almost no cost.</p> + +<h2>Manipulators</h2> +<p>Status: PLANNED.</p> + +<p>Currently, every operation on a cell or on a range of cells is quite complex. +You need to ensure correct repainting, recalculation, iterate on a range and so on.</p> + +<p>To address this issue, manipulators shall be implemented. A manipulator will +implement one operation (formatting change, sequence fill, ..., ...).</p> + +<p>Basically, usage of a manipulator should look like this:</p> + +<p><pre> +Manipulator *manip = manipulatorManager::self()->getManip ("seqfill"); +manip->setArgument ("type", 1); +... (more setArgument's) +manip->exec (selection); +</pre></p> + +<p>That's all...</p> + +<p>What concerns manipulator implementation, you'll derive from the base +manipulator and reimplement constructor and methods initialize() +(called just before the operation starts), processCell(), and maybe +done(). The constructor or initialize() would set some properties for +the cell-walking algorithm, and then it won't care about it anymore. +The base class will walk the range and call processCell() for each +cell, possibly creating it if it doesn't exist (if the manipulator +wants so).There will also be some methods that can be used to process +the whole range or row/column at once, if the manipulator wants to do so +(useful for, say, formatting manipulators that will be able to set attributes +of a whole range or row/col, in accordance with thoughts about format storage +below.</p> + +<p>In addition, the manipulator can implement the undo/redo functionality +- the base manipulator will provide some common stuff needed to +accomplish this.</p> + +<h2>Selection handling</h2> +<p>Status: PLANNED</p> + +<p>The selection shall be an instance of some RangeList class, +or however we want to call it - this will contain a list of +cells/ranges/rows/whatever - like current selection, but will contain +more entries. This will allow easy implementation of CTRL-selections and so, +because thanks to manipulators, each operation will automatically support these.</p> + +<h2>Repaint Triggering</h2> +<p>Status: PLANNED</p> + +<p>As mentioned above, the interface between the core and the GUI needs to be kept +at minimum. Also, the number of repaints needs to be as low as possible, and repaints +should be groupped whenever possible. To achieve all this, the following approach +can be used:</p> + +<p>When a cell is changed, it calls some method in KSpread::Sheet - valueChanged() +or formattingChanged(). These methods then trigger everything necessary, like +a call to the painting routine or dependency calculation.</p> + +<p>This simple system would work on itself, but it would be slow. If you do +a sequence fill on A1:A1000 and you have a SUM(A1:A1000) somewhere, why would +you want to compute that SUM 1000 times, when you can simply compute it after +the sequence fill has been finished? Hence, the sheet will offer some more +methods - disableUpdates(), enableUpdates(), rangeListChanged() and +rangeListFormattingChanged(). All these will be used (solely?) by manipulators, +preferably by the base manipulator class, so that we don't have to call these +functions in each operation. After a call to disableUpdates(), there will +be no repainting and no dependency calculation. Note that a call to +enableUpdates() won't cause any repaints either, as the sheet cannot remember +all the calls (due to loss of range information). Hence, the base manipulator +class needs to call the correct rangeList*Changed method to trigger an +update in an effective way. The base manipulator needs to be configurable by +the manipulators that derive from it, so that it knows whether it changed +cell's content or formatting.</p> + +<h2>Formula Engine</h2> +<p>Status: FINISHED.</p> + +<p>This formula engine is just an expression evaluator. To offer better +performance, the expression is first compiled into byte codes which will +be executed later by a virtual machine.</p> + +<p>Before compilation, the expression is separated into pieces, called tokens. +This step, which is also known as lexical analysis, takes places at once +and will produce sequence of tokens. They are however not stored and used only +for the purpose of the subsequent step. +Tokens are supplied to the parser, also known as syntax analyzer. In this +design, the parser is also a code generator. It involve the generation +of byte codes which represents the expression. +Evaluating the formula expression is now basically running the virtual +machine to execute compiled byte codes. No more scanning or parsing +are performed during evaluation, this saves time a lot.</p> + +<p>The virtual machine itself (and of course the byte codes) are designed to be +as simple as possible. This is supposed to be stack-based, i.e. the virtual +machine has an execution stack of values which would be manipulated +as each byte code is processed. Beside the stack, there will be a list of +constant (sometimes also called as "constants pool") to hold Boolean, +integer, floating-point or string values. When a certain byte code needs +a constant for the operand, an index is specified which should be used +to look up the constant in the constants pool.</p> + +<p>There are only few byte code, sufficient enough to perform calculation. +Yes, this is really minimalist but yet does the job fairly well. +The following provides brief description for each type of bytecode.</p> + +<blockquote> + +<p><i>Nop</i> means no operation.</p> + +<p><i>Load</i> means loads a constant and push it to the stack. The constant can +be found at constant pools, at position by 'index', it could be a Boolean, +integer, floating-point or string value.</p> + +<p><i>Ref</i> means gets a value from a reference. Member variable 'index' will +refers to a string value in the constant pools, i.e. the name of the reference. +Typically the reference is either a cell (e.g. A1), range of cells (A1:B10) +or possibly function name. Example: expression A2+B2 will be compiled as:<br> +Constants:<br> +#0: "A2"<br> +#1: "B2"<br> +Codes:<br> +Ref #0<br> +Ref #1<br> +Add +</p> + +<p><i>Function</i>. +Example: expression "sin(x)" will be compiled as:<br> +Constants:<br> +#0: "sin"<br> +#1: "x"<br> +Codes:<br> +Ref #0<br> +Ref #1<br> +Function 1 +</p> + +<p><i>Neg</i> is a unary operator, a value is popped from stack and negated and then +pushed back to the stack. If it is not number (Boolean or string), it +will be converted first.</p> + +<p><i>Add, Sub, Mul, Div and Pow</i> are binary operators, two values are popped from +stack and processed (added, subtracted, multiplied, divided, or power) and +the result is pushed to the stack.</p> + +<p><i>Concat</i> is string operation, two values are popped from stack (and converted +to string if they are not string values), concatenated, and the result is +pushed to the stack.</p> + +<p><i>Not</i> is a logical operation, a value is popped from stack and its Boolean +not is pushed into the stack. When it is not Boolean value, there will be +a cast.</p> + +<p><i>Equal, Less, and Greater</i> are comparison operators, two values are +popped from stack and compared appropriately. The result, which is a Boolean +value, is pushed into the stack. To simplify, there no "not equal" +comparison because it can be regarded as "equal" followed by +"not" byte codes. Same goes for "less than or equal to" and +"greater than or equal to".</p> + +</blockquote> + +<p>The expression scanner is based on finite state acceptor. The state denotes +the position of cursor, e.g. inside a cell token, inside an identifier, etc. +State transition is following by emitting the associated token to the +result buffer. Rather than showing the state diagrams here, it is much more +convenience and less complicated to browse the scanner source code and try +to follow its algorithm from there.</p> + +<p>The parser is designed using one of bottom-up parsing technique, namely +based on Polish notation. Instead of ordering the tokens in suffix Polish +form, the parser (which is also the code generator) simply outputs +byte codes. In its operation, the parser requires the knowledge of operator +precedence to correctly translate unparenthesized infix expression and +thus requires the use of a syntax stack.</p> + +<p>The parser algorithm is given as follows:</p> + +<blockquote> +Repeat the following steps:<br> +Step 1: Get next token<br> +Step 2: If it is an identifier<br> +- push it to syntax stack<br> +- generated "Ref"<br> +Step 3: If it is a Boolean, integer, float or string value<br> +- push it to syntax stack<br> +- generated "Load"<br> +Step 4: If it is an operator<br> +- check for reduce rules<br> +<br> +- when no more rules applies, push token to the syntax stack<br> +</blockquote> + +<p>The reduce rules are:</p> + +<p>Rule A: <i>function argument</i>: +if token is semicolon or right parenthesis, +if syntax stack looks as: +<ul type="square"> +<li>non-operator <--- top</li> +<li>operator ;</li> +<li>non-operator</li> +<li>operator (</li> +<li>identifier</li> +</ul> +then reduce to +<ul type="circle"> +<li>non operator</li> +<li>operator (</li> +<li>identifier</li> +<li>increase number of function arguments</li> +</ul> +</p> + +<p>Rule B: last function argument<br> +if syntax stack looks as:<br> +<ul type="square"> +<li>operator )</li> +<li>non-operator</li> +<li>operator (</li> +<li>identifier</li> +</ul> +then reduce to:<br> +<ul type="circle"> +<li>non-operator</li> +<li>generated "Function" + number of function arguments</li> +</ul> +</p> + +<p>Rule C: function without argument<br> +if syntax stack looks as:<br> +<ul type="square"> +<li>operator )</li> +<li>operator (</li> +<li>identifier</li> +</ul> +then reduce to:<br> +<ul type="circle"> +<li>non-operator (dummy)</li> +</ul> +</p> + +<p>Rule D: parenthesis removal<br> +if syntax stack looks as:<br> +<ul type="square"> +<li>operator (</li> +<li>non-operator</li> +<li>operator )</li> +</ul> +then reduce to:<br> +<ul type="circle"> +<li>non-operator</li> +</ul> +</p> + +<p>Rule E: binary operator<br> +if syntax stack looks as:<br> +<ul type="square"> +<li>non-operator</li> +<li>binary operator</li> +<li>non-operator</li> +<li>and if the precedence of the binary operator in the syntax stack +is greater or equals to the precedence of token</li> +</ul> +then reduce to:<br> +<ul type="circle"> +<li>non-operator</li> +<li>and generated appropriate byte code for the binary operator</li> +</ul> +</p> + +<p>Rule F: unary operator<br> +if syntax stack looks as:<br> +<ul type="square"> +<li>non-operator</li> +<li>unary operator</li> +<li>operator</li> +</ul> +then reduce to:<br> +<ul type="circle"> +<li>operator</li> +<li>and generated "Neg" if unary operator is '-'</li> +</ul> +</p> + +<p>Percent operator is a special case and not handled the above mentioned rule. +When the parser finds the percent operator, it checks whether there's a non-operator +token right before the percent. If yes, then the following code is generated: +<tt>load 0.01</tt> followed by <tt>multiply</tt>.</p> + +<h2>Value</h2> + +<p>Status: FINISHED.<br> +</p> + +<p>to be written.</p> + +<h2>Commands Based on KCommand<br> +</h2> + +<p>Status: IN PROGRESS.</p> + +<p>Until lately, to implement undo and redo, KSpread creates corresponding +KSpreadUndo classes for each action and runs them when the user undoes +those actions. KSpreadUndo also has redo function whose job is to redo +again the action after being undone.</p> + +<p>All this needs to be converted to manipulators - these will be KCommand, +hence we should be able to undo/redo every operation (provided that the +corresponding manipulator provides methods to store/recall the undo information).</p> + +<h2>Cell Storage</h2> +<p>Status: PLANNED.</p> + +<p>Cells are grouped together, and then hashed.</p> + +<h2>Format Storage</h2> +<p>Status: PLANNED.</p> + +<p>Formatting specifies how a cell should look like. It involves font +attributes like bold or italics, vertical and horizontal alignment, +rotation angle, shading, background color and so on. Each cell can have +its own format, but bear also in mind that a whole row or column format +should also apply.</p> + +<p>Current way of storing formatting information is rather inefficient: +pack it together inside the cell. The reason is because most of cells +are either very plain (no formatting) and/or only have partial attribute +(e.g. only marked as bold, no font family or color is specified). +Therefore the approximately 20 bytes used to hold formatting information +are quite a waste of memory. Even worse, this requires that the cell +must exist even if it is not in use. As illustration, imagine a worksheet +where within range A1:B20 only 5 cells are not empty. When the user +selects this range and changes the background color to yellow, then +those 5 cells must store this information in their data structure but +how about the other 35 cells? Since the formatting is attached to the cell, +there is no choice but to create them. Doing this, just for the sake of +storing format, is actually not really good.</p> + +<p>A new way to store formatting information is proposed below.</p> + +<p>For each type of format a user can use, we have the corresponding +<i>formatting piece</i>, for example "bold on", "bold off", +"font Arial", "left-border 1 px", etc. Whenever the user +applies formatting to a range (could also be a whole column, row, or worksheet), +we save appropriate respective formatting piece in a stack. Say the user has +marked column B as bold, row 2 as italics, and set range A1:C5 with +yellow background color. Our formatting stack would look like: +</p> + +<table cellspacing="0" cellpadding="3" border="1"> +<tr> + <td><b>Range</b></td> + <td><b>Formatting Piece</b></td> +</tr> +<tr> + <td>Column B</td> + <td>Bold on</td> +</tr> +<tr> + <td>Row 2</td> + <td>Italics on</td> +</tr> +<tr> + <td>A1:C5</td> + <td>Yellow background</td> +</tr> +</table> + +<p>Now let try to figure out the overall format of cell B2. From the first +we know it should be bold, from the second it should be italics, and last +it should have yellow background. This complete format is the one which +we used to render cell B2 on screen. In similar fashion, we can know that +cell A1 on the other only specifies the yellow background, because the first +and second pieces do not apply there.</p> + +<p>Another possible way to see the format storage is by using 3-D perspective. +For each formatting piece, imagine there is a surface which covers the +formatted range (the xy-plane). The formatting information is simply attached +to the surface (say, as surface attribute). Every surface is stacked together, +its depth (the z-axis) denotes the sequence, i.e. the first surface is the +deepest. For the example above, we can view the pieces as one surface specifies +"bold on" which is a vertical of column B, one surface specifies +"italics on" which is a horizontal band of row 2 and one last surface +which specifies "yellow background" stretched in the range A1:C5. +How to find complete format for A2? This is now a matter of surface +determination. Traversing in the direction of z-axis from B2 reveals +that we hit the last and second surfaces only; thereby we can know the complete +format is "italics, yellow background".</p> + +<p>It is clear that a format storage corresponds to one sheet only. For each +sheet, there should be one format storage. Cells can still have accessors to +its formatting information, these simply become wrapper for proper calls to the +format storage. Since each formatting piece holds information about the applied +range, we must take care that the formatting storage is correctly updated +whenever cells, rows or columns are inserted and deleted.</p> + +<p>In order to avoid low performance, we must use a smart way to iterate over +all formatting pieces whenever we want to find out complete format for given +cell(s). When the sheet gets very complex, it is likely that we will have +many many formatting pieces that are not even overlap. This means, when we +need formatting of cell A1, it is no use to check formatting pieces of range +Z1:Z10 or A100:B100 which do not include cell A1 and are even very far from A1. +One possible solution is to arrange the formatting pieces in a quad-tree. +Because one piece can cover a very large area, it is possible that it will +be in more than one leaf in the quad-tree. <i>Details on the possible use of +quad-tree or other methods should be explored further more</i>.</p> + +<h2>Default Toolbars</h2> +<p>Status: IN PROGRESS.</p> + +<p>Relevant mailing-list threads:</p> + +<ul> +<li><a href="http://lists.kde.org/?l=kde-usability&m=110751297906231&w=2"> +http://lists.kde.org/?l=kde-usability&m=110751297906231&w=2</a></li> +<li><a href="http://lists.kde.org/?l=koffice-devel&m=110723903332496&w=2"> +http://lists.kde.org/?l=koffice-devel&m=110723903332496&w=2</a></li> +</ul> + +<p>Toolbars are utilized to place most frequently used actions. It is +important to present the user with default toolbars which make +sense, i.e. they do not contain unnecessary buttons. In-depth usability +analysis and/or further discussions are needed to make decision which +buttons need to be in the toolbar and which don't.</p> + +<p>For reference, here is a list of default shown toolbars in +some spreadsheet applications:</p> + +<p><b>Microsoft Excel 2002</b>:</p> + +<ul> +<li><em>Standard toolbar</em>: New, Open, Save, Search, Print, Print Preview, +Spelling, Cut, Copy, Paste, Format Painter, Undo, Redo, Insert Hyperlink, +Autosum, Sort Ascending, Sort Descending, Chart Wizard, Drawing, Zoom, Help</li> +<li><em>Formating toolbar:</em>: Font, Font Size, Bold, Italic, Align Left, +Align Center, Align Right, Merge and Center, Format Currency, Format Percent, +Comma Style, Increase Precision, Decrease Precision, Increase Indent, +Decrease Indent, Merge Cells, Unmerge Cells, Merge Across, Borders, +Fill Color, Font Color</li> +</ul> + + +<p><b>OpenOffice 2.0</b>:</p> + +<ul> +<li><em>Standard toolbar</em>: New, Open, Save, E-mail, Edit, PDF, Print, +Page Preview, Spellcheck, Cut, Copy, Paste, Format Paintbrush, Undo, Redo, +Hyperlink, Sort Ascending, Sort Descending, Insert Chart, Navigator, Styles, +Gallery, Zoom</li> +<li><em>Format Object toolbar:</em> Font Name, Font Size, Bold, Italic, +Underline, Font Color, Align Left, Align Center, Align Right, Justified, +Merge Cells, Format as Currency, Format as Percent, Format Standard, +Increase Precision, Decrease Precision, Increase Indent, Decrease Indent, +Borders, Line Style, Line Color, Background Color, Align Top, Align Center +Vertically, Align Bottom </li> +</ul> + +<p><b>Gnumeric 1.4</b>:</p> + +<ul> +<li><em>Standard toolbar</em>: New, Open, Save, Print, Print Preview, +Cut, Copy, Paste, Undo, Redo, Hyperlink, Autosum, Function/Formula, +Insert Chart, Zoom</li> +<li><em>Format toolbar:</em> Font Family, Font Size, Bold, Italic, +Underline, Align Left, Align Center, Align Right, Merge and Center, +Merge Cells, Split Cell, Format Currency, Format Percent, Thousand Separator, +Increase Precision, Decrease Precision, Decrease Indent, Increase Indent, +Borders, Background, Foreground</li> +</ul> + +<h2>Test Framework</h2> +<p>Status: IN PROGRESS.</p> + +<p>Relevant mailing-list threads:</p> + + +<ul> +<li><a href="http://lists.kde.org/?l=kde-cvs&m=109511244721480&w=2"> +http://lists.kde.org/?l=kde-cvs&m=109511244721480&w=2</a></li> +<li><a href="http://lists.kde.org/?l=koffice-devel&m=109518196922944&w=2"> +http://lists.kde.org/?l=koffice-devel&m=109518196922944&w=2</a></li> +</ul> + +<p>It is well known that writing clean and easily understandable module will lead +to better maintenance. However testing that particular module everytime there is +a significant change requires considerable amount of time and effort. Since KSpread +and other applications of its scale consist of hundreds of modules, in this case +automatic testing of each module will help a lot, not to mention that it might +catch bug as early as possible.</p> + +<p>KSpread has a simple test framework to facilitate such kind of test. This can +be activated using the shortcut <tt>Ctrl+Shift+T</tt>. This test is however +not accessible via menu, because it is intended to be used only by the developers. +Ideally, there should be tests for all modules contained in KSpread. It is +the responsibility of the developer to create the corresponding <tt>tester</tt> +for the code that he or she is working on. All tests should be kept in +<tt>koffice/kspread/tests/</tt>.</p> + +<p>Making a new tester is not difficult. The easiest way is to copy an already +existing tester and modify it. Basically, it must be a subclass of class Tester +(see <tt>koffice/kspread/tests/tester.h</tt>). Just reimplement the virtual +function <tt>run()</tt> and it is ready. In order to make it possible to run +the new tester, add an instance of the class in TestRunner +(for details, see <tt>koffice/kspread/tests/testrunner.cc</tt>).</p> + +<p>A tester must be self-contained, it should not use any test data from +current document. If necessary, it must create (or hard code) the data by +itself.</p> + +<p>Whenever parts of KSpread features are improved or rewritten, it is always +a good idea to run the related tests to ensure that all the changes do not do +any harm. However, bear in mind that there is no 100% guarantee that the new +code is bug-free.</p> + +<p>Also, if there is a bug which is not caught by the tester (i.e. it does not +fail the tester, but the bug is confirmed), then the relevant tester must be +modified to include one or more test cases similar to the offending bug. +When the bug is finally fixed, from that point the test should always pass all +test cases.</p> + +</p> + +<h2>Coding Style</h2> +<p>Status: IN PROGRESS.</p> + +<p>(to be written in details).</p> + +<p>Write <b>clean code</b>. To be correct is better than to be fast. +KSpread source code is known to grow very fast in its early days and but later +on also more difficult to understand.</p> + +<p>Put comment as documentation for classes and member functions. There is still +lack of documentation as for now, whoever understands something about the +classes and functions should write the documentation.</p> + +<p>In complex source files, list of header includes can be very long. Unless +there is special reason not do it, try to group them together, i.e. standard +C/C++ headers come first, followed by Qt headers, and then KDE headers, +KOffice core/UI headers and application specific headers. For each group, +sort the header files alphabetically. </p> + +<p>Write test cases. This will ease further maintenance. See also the section on Test +Framework above.</p> + +<p>Do not use the term <i>table</i>. It was incorrectly invented quite likely +because of the term <i>Tabelle</i> (German, literally means table). The correct +term is <i>sheet</i> or <i>worksheet</i>. The English version of Microsoft +uses <i>sheet</i> while the German version uses <i>Tabelle</i>.</p> + +<p>Use <a href="http://developer.kde.org/documentation/library/kdeqt/kde3arch/devel-binarycompatibility.htm">d-pointer</a> trick (also known pimpl) whenever possible. Such practice will help when later on +we want to expose the API and need to maintain binary compatibility. But the +most important thing is to separate the interface and the implementation. +Furthermore, build time is reduced since modification on the implementation +would not cause tons of recompile.</p> + +<p>When creating a new class, use namespace KSpread. Do not use KSpread prefix +anymore. Example: use <tt>KSpread::Foo</tt> instead of <tt>KSpreadFoo</tt>. +Also source file name should not contain kspread prefix anymore, i.e. +<tt>foo.h</tt> and <tt>foo.cc</tt> (but not <tt>kspread_foo.h</tt> and +<tt>kspread_foo.cc</tt>) for the above example.</p> + +</body></html> |