Initial import of Dillo

This commit is contained in:
2025-02-28 13:34:30 -05:00
parent bd4e3eebd8
commit 20fea64cb5
496 changed files with 156174 additions and 0 deletions

153
devdoc/CCCwork.txt Normal file
View File

@ -0,0 +1,153 @@
Last review: August 04, 2009 --jcid
----------------------------
Internal working for the CCC
----------------------------
HTTP protocol
-------------
Query: |
.
1B --> 1B 1B --> 1B --> | -------------.
.----. .----. .----. . |
I |Capi| |http| | IO | | |
'----' '----' '----' . |
1F <-- 1F 1F <-- 1F | V
.
| [Server]
Answer: .
2B --> 2B 2B --> 2B | |
.----. .----. .----. . |
II |Capi| |Dpi | | IO | | |
'----' '----' '----' . |
2F <-- 2F 2F <-- 2F <-- | <------------'
.
|
* a_Capi_open_url() builds both the Answer and Query chains at
once (Answer first then Query), to ensure a uniform structure
that avoids complexity (e.g. race conditions).
* Http_get() sets a callback for the DNS hostname resolve.
Normally it comes later, but may also by issued immediately if
the hostname is cached.
* The socket FD is passed by means of OpSend by the http module
once the remote IP is known and the socket is connected.
Function calls for HTTP CCC
---------------------------
a_Capi_open_url
if (reload)
Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
Http_get
a_Cache_open_url
if URL_E2EReload -> prepare reload
if cached
client enqueue
delayed process queue
else
Cache_entry_add
client enqueue
-//->
a_Http_dns_cb
Http_connect_socket
OpSend FD, BCK
OpSend FD, FWD
Http_send_query
a_Http_make_query_str
OpSend, BCK
IO_submit
a_IOwatch_add_fd (DIO_WRITE, ...)
Note about 'web' structures. They're created using a_Web_new().
The web.c module keeps a list of valid webs, so anytime you're
unsure of a weak reference to 'web', it can be checked with
a_Web_valid(web).
------------
Dpi protocol
------------
Query: |
.
1B --> 1B 1B --> 1B --> | -------------.
.----. .----. .----. . |
I |Capi| |Dpi | | IO | | |
'----' '----' '----' . |
1F <-- 1F 1F <-- 1F | V
.
| [Server]
.
Answer (same as HTTP): | |
. |
2B --> 2B 2B --> 2B | |
.----. .----. .----. . |
II |Capi| |Dpi | | IO | | |
'----' '----' '----' . |
2F <-- 2F 2F <-- 2F <-- | <------------'
.
|
CCC Construction:
a_Capi_open_url() calls a_Capi_dpi_send_cmd() when the URL
belongs to a dpi and it is not cached.
a_Capi_dpi_send_cmd() builds both the Answer and Query chains
at once (Answer first then Query), in the same way as HTTP does.
Note that the answer chain is the same for both, and the query
chain only differs in the module in the middle ([http] or [dpi]).
Function calls for DPI CCC
--------------------------
a_Capi_open_url
a_Capi_dpi_send_cmd
Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
a_Cache_open_url
[...]
Normal termination:
When the dpi server is done, it closes the FD, and OpEnd flows
from IO to Capi (answer branch). When in Capi, capi propagates
OpEnd to the query branch.
Abnormal termination:
The transfer may be aborted by a_Capi_conn_abort_by_url(). The
OpAbort is not yet standardized and has an ad-hoc implementation.
One idea is to have OpAbort always propagate BCK and then FWD and
to jump into the other chain when it gets to [Capi].
Debugging CCC
-------------
A simple way to "look" inside it, is to "#define VERBOSE 1" in
chain.c, and then to follow its work with a printed copy of the
diagrams in this document.
Each new data request generates a CCC, so if you want to debug,
it's good to refine the testcase to the minimum possible number
of connections.

166
devdoc/Cache.txt Normal file
View File

@ -0,0 +1,166 @@
June 2000, --Jcid
Last update: Jul 09
-------
CACHE
-------
The cache module is the main abstraction layer between
rendering and networking.
The capi module acts as a discriminating wrapper which either
calls the cache or the dpi routines depending on the type of
request.
Every URL must be requested using a_Capi_open_url, which
sends the request to the cache if the data is cached, to dillo's
http module for http: URLs, and through dillo's DPI system for
other URLs.
Here we'll document non dpi requests.
----------------
CACHE PHILOSOPHY
----------------
Dillo's cache is very simple; every single resource that's
retrieved (URL) is kept in memory. NOTHING is saved to disk.
This is mainly for three reasons:
- Dillo encourages personal privacy and it assures there'll be
no recorded tracks of the sites you visited.
- The Network is full of intermediate transparent proxys that
serve as caches.
- If you still want to have cached stuff, you can install an
external cache server (such as WWWOFFLE), and benefit from it.
---------------
CACHE STRUCTURE
---------------
Currently, dillo's cache code is spread in different sources:
mainly in cache.[ch], dicache.[ch] and it uses some other
functions from mime.c and web.cc.
Cache.c is the principal source, and it also is the one
responsible for processing cache-clients (held in a queue).
Dicache.c is the interface to the decompressed RGB representations
of currently-displayed images held in DW's imgbuf.
mime.c and web.cc are used for secondary tasks such as
assigning the right "viewer" or "decoder" for a given URL.
----------------
A bit of history
----------------
Some time ago, the cache functions, URL retrieval and
external protocols were a whole mess of mixed code, and it was
getting REALLY hard to fix, improve or extend the functionality.
The main idea of this "layering" is to make code-portions as
independent as possible so they can be understood, fixed,
improved or replaced without affecting the rest of the browser.
An interesting part of the process is that, as resources are
retrieved, the client (dillo in this case) doesn't know the
Content-Type of the resource at request-time. It only becomes known
when the resource header is retrieved (think of http). This
happens when the cache has control, so the cache sets the
proper viewer for it (unless the Callback function was already
specified with the URL request).
You'll find a good example in http.c.
Note: All resources received by the cache have HTTP-style headers.
The file/data/ftp DPIs generate these headers when sending their
non-HTTP resources. Most importantly, a Content-Type header is
generated based on file extension or file contents.
-------------
Cache clients
-------------
Cache clients MUST use a_Capi_open_url to request an URL. The
client structure and the callback-function prototype are defined,
in cache.h, as follows:
struct _CacheClient {
int Key; /* Primary Key for this client */
const DilloUrl *Url; /* Pointer to a cache entry Url */
int Version; /* Dicache version of this Url (0 if not used) */
void *Buf; /* Pointer to cache-data */
uint_t BufSize; /* Valid size of cache-data */
CA_Callback_t Callback; /* Client function */
void *CbData; /* Client function data */
void *Web; /* Pointer to the Web structure of our client */
};
typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client);
Notes:
* Op is the operation that the callback is asked to perform
by the cache. { CA_Send | CA_Close | CA_Abort }.
* Client: The Client structure that originated the request.
--------------------------
Key-functions descriptions
--------------------------
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData)
if Web->url is not cached
Create a cache-entry for that URL
Send client to cache queue
else
Feed our client with cached data
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
----------------------
Redirections mechanism
(HTTP 30x answers)
----------------------
This is by no means complete. It's a work in progress.
Whenever an URL is served under an HTTP 30x header, its cache
entry is flagged with 'CA_Redirect'. If it's a 301 answer, the
additional 'CA_ForceRedirect' flag is also set, if it's a 302
answer, 'CA_TempRedirect' is also set (this happens inside the
Cache_parse_header() function).
Later on, in Cache_process_queue(), when the entry is flagged
with 'CA_Redirect' Cache_redirect() is called.
-----------
Notes
-----------
The whole process is asynchronous and very complex. I'll try
to document it in more detail later (source is commented).
Currently I have a drawing to understand it; hope the ASCII
translation serves the same as the original.
If you're planning to understand the cache process thoroughly,
write me a note and I will assign higher priority to further
improvement of this doc.
Hope this helps!

96
devdoc/Dillo.txt Normal file
View File

@ -0,0 +1,96 @@
"Eliminate the guesswork and quality goes up."
-------
DILLO
-------
These notes are written with a view to make it less hard, not
easier yet ;), to get into Dillo development.
When I first got into it, I was totally unaware of the browser
internals. Now that I've made my way deep into the core of it,
(we rewrote it 90% and modified the rest), is time to write some
documentation, just to make a less steep learning curve for new
developers.
--Jcid
--------
OVERVIEW
--------
Dillo can be viewed as the sum of five main parts:
1.- Dillo Widget: A custom widget, FLTK-based, that holds the
necessary data structures and mechanisms for graphical rendering.
(Described in Dw*.txt, dw*.c files among the sources.)
2.- Dillo Cache: Integrated with a signal driven Input/Output
engine that handles file descriptor activity, the cache acts as
the main abstraction layer between rendering and networking.
Every URL, whether cached or not, must be retrieved using
a_Capi_open_url (Described briefly in Cache.txt, source
contained in capi.c).
IO is described in IO.txt (recommended), source in src/IO/.
3.- The HTML parser: A streamed parser that joins the Dillo
Widget and the Cache functionality to make browsing possible
(Described in HtmlParser.txt, source mainly inside html.cc).
4.- Image processing code: The part that handles image
retrieval, decoding, caching and displaying. (Described in
Images.txt. Sources: image.c, dw/image.cc, dicache.c, gif.c,
jpeg.c and png.c)
5.- The dpi framework: a gateway to interface the browser with
external programs (Example: the bookmarks server plugin).
Dpi spec: https://dillo-browser.github.io/old/dpi1.html
-------------------------
HOW IS THE PAGE RENDERED?
-------------------------
(A short description of the internal function calling process)
When the user requests a new URL, a_UIcmd_open_url
is queried to do the job; it calls a_Nav_push (The highest level
URL dispatcher); a_Nav_push updates current browsing history and
calls Nav_open_url. Nav_open_url closes all open connections by
calling a_Bw_stop_clients, and then calls
a_Capi_open_url which calls a_Cache_open_url (or the dpi module if
this gateway is used).
If Cache_entry_search hits (due to a cached url :), the client is
fed with cached data, but if the URL isn't cached yet, a new CCC
(Concomitant Control Chain) is created and committed to fetch the
URL.
The next CCC link is dynamically assigned by examining the
URL's protocol. It can be a_Http_ccc or a_Dpi_ccc.
If we have an HTTP URL, a_Http_ccc will succeed, and the http
module will be linked; it will create the proper HTTP query and
link the IO module to submit and deliver the answer.
Note that as the Content-Type of the URL is not always known
in advance, the answering branch decides where to dispatch it to
upon HTTP header arrival.
What happens then?
Well, the html parser gets fed, and proper functions are
called for each tag (to parse and call the appropriate methods)
and the whole page is constructed in a streamed way.
Somewhere in the middle of it, resize and repaint functions
are activated and idle functions draw to screen what has been
processed.
(The process for images is described in Images.txt)

331
devdoc/Dpid.txt Normal file
View File

@ -0,0 +1,331 @@
Aug 2003, Jorge Arellano Cid,
Ferdi Franceschini --
Last update: Nov 2009
------
dpid
------
-------------
Nomenclature:
-------------
dpi:
generic term referring to dillo's plugin system (version1).
dpi1:
specific term for dillo's plugin spec version 1.
at: https://dillo-browser.github.io/old/dpi1.html
dpi program:
any plugin program itself.
dpi framework:
the code base inside and outside dillo that makes dpi1
working possible (it doesn't include dpi programs).
dpip:
dillo plugin protocol. The HTML/XML like set of command tags
and information that goes inside the communication sockets.
Note: not yet fully defined, but functional.
Note2: it was designed to be extensible.
dpid:
dillo plugin daemon.
server plugin:
A plugin that is capable of accepting connections on a socket. Dpid will
never run more than one instance of a server plugin at a time.
filter plugin:
A dpi program that reads from stdin and writes to stdout, and that
exits after its task is done (they don't remain as server plugins).
Warning, dpid will run multiple instances of filter plugins if requested.
-----------
About dpid:
-----------
* dpid is a program which manages dpi connections.
* dpid is a daemon that serves dillo using IDS sockets.
* dpid launches dpi programs and arranges socket communication
between the dpi program and dillo.
The concept and motivation is similar to that of inetd. The
plugin manager (dpid) listens for a service request on a socket
and returns the socket/port pair of a plugin that handles the
service. It also watches sockets of inactive plugins and starts
them when a connection is requested.
-----------------------------------------------------------
What's the problem with managing dpi programs inside dillo?
-----------------------------------------------------------
That's the other way to handle it, but it started to show some
problems (briefly outlined here):
* When having two or more running instances of Dillo, one
should prevail, and take control of dpi managing (but all
dillos carry the managing code).
* If the managing-dillo exits, it must pass control to another
instance, or leave it void if there's no other dillo running!
* The need to synchronize all the running instances of
dillo arises.
* If the controlling instance finishes and quits, all the
dpi-program PIDs are lost.
* Terminating hanged dpis is hard if it's not done with signals
(PIDs)
* Forks can be expensive (Dillo had to fork its dpis).
* When a managing dillo exits, the new one is no longer the
parent of the forked dpis.
* If Unix domain sockets for the dpis were to be named
randomly, it gets very hard to recover their names if the
controlling instance of dillo exits and another must "take
over" the managing.
* It increments dillo's core size.
* If dillo hangs/crashes, dpi activity is lost (e.g. downloads)
* ...
That's why the managing daemon scheme was chosen.
----------------------
What does dpid handle?
----------------------
It solves all the above mentioned shortcomings and also can do:
* Multiple dillos:
dpid can communicate and serve more than one instance
of dillo.
* Multiple dillo windows:
two or more windows of the same dillo instance accessing dpis
at the same time.
* Different implementations of the same service
dpi programs ("dpis") are just an implementation of a
service. There's no problem in implementing a different one
for the same service (e.g. downloads).
* Upgrading a service:
to a new version or implementation without requiring
patching dillo's core or even bringing down the dpid.
And finally, being aware that this design can support the
following functions is very helpful:
SCHEME Example
------------------------------------------------------------
* "one demand/one response" man, preferences, ...
* "resident while working" downloads, mp3, ...
* "resident until exit request" bookmarks, ...
* "one client only" cd burner, ...
* "one client per instance" man, ...
* "multiple clients/one instance" downloads, cookies ...
--------
Features
--------
* Dpi programs go in: "EPREFIX/dillo/dpi" or "~/.dillo/dpi". The binaries
are named <name>.dpi as "bookmarks.dpi" and <name>.filter.dpi as in
"hello.filter.dpi". The ".filter" plugins simply read from stdin
and write to stdout.
* Register/update/remove dpis from list of available dpis when a
'register_all' command is received.
* dpid terminates when it receives a 'DpiBye' command.
* dpis can be terminated with a 'DpiBye' command.
* dpidc control program for dpid, currently allows register and stop.
-----
todo:
-----
These features are already designed, waiting for implementation:
* dpidc remove // May be not necessary after all...
-----------------
How does it work?
-----------------
o on startup dpid reads dpidrc for the path to the dpi directory
(usually EPREFIX/lib/dillo/dpi). ~/.dillo/dpi is scanned first.
o both directories are scanned for the list of available plugins.
~/.dillo/dpi overrides system-wide dpis.
o next it creates internet domain sockets for the available plugins and
then listens for service requests on its own socket,
and for connections to the sockets of inactive plugins.
o dpid returns the port of a plugin's socket when a client (dillo)
requests a service.
o if the requested plugin is a 'server' then
1) dpid stops watching the socket for activity
2) forks and starts the plugin
3) resumes watching the socket when the plugin exits
o if the requested plugin is a 'filter' then
1) dpid accepts the connection
2) maps the socket fd to stdin/stdout (with dup2)
3) forks and starts the plugin
4) continues to watch the socket for new connections
---------------------------
dpi service process diagram
---------------------------
These drawings should be worth a thousand words! :)
(I)
.--- s1 s2 s3 ... sn
|
[dpid] [dillo]
|
'--- srs
The dpid is running listening on several sockets.
(II)
.--- s1 s2 s3 ... sn
|
[dpid] [dillo]
| |
'--- srs ------------------'
dillo needs a service so it connects to the service request
socket of the dpid (srs) and asks for the socket name of the
required plugin (using dpip).
(III)
.--- s1 s2 s3 ... sn
| |
[dpid] | [dillo]
| | |
'--- srs '---------------'
then it connects to that socket (s3, still serviced by dpid!)
(IV)
.--- s1 s2 s3 ... sn
| |
.[dpid] | [dillo]
. | | |
. '--- srs '---------------'
.
.............[dpi program]
when s3 has activity (incoming data), dpid forks the dpi
program for it...
(V)
.--- s1 s2 (s3) ... sn
|
[dpid] [dillo]
| |
'--- srs .---------------'
|
[dpi program]
... and lets it "to take over" the socket.
Once there's a socket channel for dpi and dillo, the whole
communication process takes place until the task is done. When
the dpi program exits, dpid resumes listening on the socket (s3).
--------------------------------
So, how do I make my own plugin?
--------------------------------
Maybe the simplest way to get started is to understand a few
concepts and then to use the hands-on method by using/modifying
the hello dpi. It's designed as an example to get developers
started.
---------
Concepts:
---------
* Dillo plugins work by communicating two processes: dillo
and the dpi.
* The underlying protocol (DPIP) has a uniform API which is
powerful enough for both blocking and nonblocking IO, and
filter or server dpis.
* The simplest example is one-request one-answer (for example
dillo asks for a URL and the dpi sends it). You'll find
this and more complex examples in hello.c
First, you should get familiar with the hello dpi as a user:
$dillo dpi:/hello/
Once you've played enough with it, start reading the well
commented code in hello.c and start making changes!
---------------
Debugging a dpi
---------------
The simplest way is to add printf-like feedback using the MSG*
macros. You can start the dpid by hand on a terminal to force
messages to go there. Filter dpis use sdterr and server dpis
stdout.
Sometimes more complex dpis need more than MSG*. In this case
you can use gdb like this.
1.- Add an sleep(20) statement just after the dpi starts.
2.- Start dillo and issue a request for your dpi. This will
get your dpi started.
3.- Standing in the dpi source directory:
ps aux|grep dpi
4.- Take note of the dpi's PID and start gdb, then:
(gdb) attach <PID>
5.- Continue from there...
------------
Final Notes:
------------
1.- If you already understand the hello dpi and want to try
something more advanced:
* bookmarks.c is a good example of a blocking server
* file.c is an advanced example of a server handling multiple
non-blocking connections with select().
2.- Multiple instances of a filter plugin may be run
concurrently, this could be a problem if your plugin records data
in a file, however it is safe if you simply write to stdout.
Alternatively you could write a 'server' plugin instead as they
are guaranteed not to run concurrently.
3.- The hardest part is to try to modify the dpi framework code
inside dillo; you have been warned! It already supports a lot of
functionality, but if you need to do some very custom stuff, try
extending the "chat" command, or asking in dillo-dev.
>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<

124
devdoc/HtmlParser.txt Normal file
View File

@ -0,0 +1,124 @@
October 2001, --Jcid
Last update: Jul 2009
---------------
THE HTML PARSER
---------------
Dillo's parser is more than just a HTML parser, it does XHTML
and plain text also. It has parsing 'modes' that define its
behaviour while working:
typedef enum {
DILLO_HTML_PARSE_MODE_INIT = 0,
DILLO_HTML_PARSE_MODE_STASH,
DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
DILLO_HTML_PARSE_MODE_BODY,
DILLO_HTML_PARSE_MODE_VERBATIM,
DILLO_HTML_PARSE_MODE_PRE
} DilloHtmlParseMode;
The parser works upon a token-grained basis, i.e., the data
stream is parsed into tokens and the parser is fed with them. The
process is simple: whenever the cache has new data, it is
passed to Html_write, which groups data into tokens and calls the
appropriate functions for the token type (tag, space, or word).
Note: when in DILLO_HTML_PARSE_MODE_VERBATIM, the parser
doesn't try to split the data stream into tokens anymore; it
simply collects until the closing tag.
------
TOKENS
------
* A chunk of WHITE SPACE --> Html_process_space
* TAG --> Html_process_tag
The tag-start is defined by two adjacent characters:
first : '<'
second: ALPHA | '/' | '!' | '?'
Note: comments are discarded ( <!-- ... --> )
The tag's end is not as easy to find, nor to deal with!:
1) The HTML 4.01 sec. 3.2.2 states that "Attribute/value
pairs appear before the final '>' of an element's start tag",
but it doesn't define how to discriminate the "final" '>'.
2) '<' and '>' should be escaped as '&lt;' and '&gt;' inside
attribute values.
3) The XML SPEC for XHTML states:
AttrValue ::== '"' ([^<&"] | Reference)* '"' |
"'" ([^<&'] | Reference)* "'"
Current parser honors the XML SPEC.
As it's a common mistake for human authors to mistype or
forget one of the quote marks of an attribute value; the
parser solves the problem with a look-ahead technique
(otherwise the parser could skip significant amounts of
properly-written HTML).
* WORD --> Html_process_word
A word is anything that doesn't start with SPACE, that's
outside of a tag, up to the first SPACE or tag start.
SPACE = ' ' | \n | \r | \t | \f | \v
-----------------
THE PARSING STACK
-----------------
The parsing state of the document is kept in a stack:
class DilloHtml {
[...]
lout::misc::SimpleVector<DilloHtmlState> *stack;
[...]
};
struct _DilloHtmlState {
CssPropertyList *table_cell_props;
DilloHtmlParseMode parse_mode;
DilloHtmlTableMode table_mode;
bool cell_text_align_set;
DilloHtmlListMode list_type;
int list_number;
/* TagInfo index for the tag that's being processed */
int tag_idx;
dw::core::Widget *textblock, *table;
/* This is used to align list items (especially in enumerated lists) */
dw::core::Widget *ref_list_item;
/* This is used for list items etc; if it is set to TRUE, breaks
have to be "handed over" (see Html_add_indented and
Html_eventually_pop_dw). */
bool hand_over_break;
};
Basically, when a TAG is processed, a new state is pushed into
the 'stack' and its 'style' is set to reflect the desired
appearance (details in DwStyle.txt).
That way, when a word is processed later (added to the Dw), all
the information is within the top state.
Closing TAGs just pop the stack.

468
devdoc/IO.txt Normal file
View File

@ -0,0 +1,468 @@
This is the updated base of a paper I wrote with Horst.
It provides a good introduction to Dillo's internals.
(Highly recommended if you plan to patch or develop in Dillo)
It may not be exactly accurate (it's quite old), but it explains
the theory behind in some detail, so it's more than recommended
reading material.
--Jcid
-----------------------------------------------------
Parallel network programming of the Dillo web browser
-----------------------------------------------------
Jorge Arellano-Cid <jcid@dillo.org>
Horst H. von Brand <vonbrand@inf.utfsm.cl>
--------
Abstract
--------
Network programs face several delay sources when sending or
retrieving data. This is particularly problematic in programs
which interact directly with the user, most notably web browsers.
We present a hybrid approach using threads communicated through
pipes and signal driven I/O, which allows a non-blocking main
thread and overlapping waiting times.
------------
Introduction
------------
The Dillo project didn't start from scratch but mainly working
on the code base of gzilla (a light web browser written by Raph
Levien). As the project went by, the code of the whole source was
standardized, and the networking engine was replaced with a new,
faster design. Later, the parser was changed, the streamed
handling of data and its error control was put under the control
of the CCC (Concomitant Control Chain), and the old widget system
was replaced with a new one (Dw). The source code is currently
regarded as "very stable beta", and is available at
<https://github.com/dillo-browser/dillo>. Dillo is a project
licensed under the GNU General Public License.
This paper covers basic design aspects of the hybrid approach
that the Dillo web browser uses to solve several latency
problems. After introducing the main delay-sources, the main
points of the hybrid design will be addressed.
-------------
Delay sources
-------------
Network programs face several delay-sources while sending or
retrieving data. In the particular case of a web browser, they
are found in:
DNS querying:
The time required to solve a name.
Initiating the TCP connection:
The three way handshake of the TCP protocol.
Sending the query:
The time spent uploading queries to the remote server.
Retrieving data:
The time spent expecting and receiving the query answer.
Closing the TCP connection:
The four packet-sending closing sequence of the TCP protocol.
In a WAN context, every single item of this list has an
associated delay that is non deterministic and often measured in
seconds. If we add several connections per browsed page (each one
requiring at least the 4 last steps), the total latency can be
considerable.
-----------------------------------
The traditional (blocking) approach
-----------------------------------
The main problems with the blocking approach are:
When issuing an operation that can't be completed
immediately, the process is put to sleep waiting for
completion, and the program doesn't do any other
processing in the meantime.
When waiting for a specific socket operation to complete,
packets that belong to other connections may be arriving,
and have to wait for service.
Web browsers handle many small transactions,
if waiting times are not overlapped
the latency perceived by the user can be very annoying.
If the user interface is just put to sleep during network
operations, the program becomes unresponsive, confusing
and perhaps alarming the user.
Not overlapping waiting times and processing makes
graphical rendering (which is arguably the central function
of a browser) unnecessarily slow.
---------------------
Dillo's hybrid design
---------------------
Dillo uses threads and signal driven I/O extensively to
overlap waiting times and computation. Handling the user
interface in a thread that never blocks gives a good interactive
``feel.'' The use of GTK+, a sophisticated widget framework for
graphical user interfaces, helped very much to accomplish this
goal. All the interface, rendering and I/O engine was built upon
its facilities.
The design is said to be ``hybrid'' because it uses threads
for DNS querying and reading local files, and signal driven I/O
for TCP connections. The threaded DNS scheme is potentially
concurrent (this depends on underlying hardware), while the I/O
handling (both local files and remote connections) is
definitively parallel.
To simplify the structure of the browser, local files are
encapsulated into HTTP streams and presented to the rest of the
browser as such, in exactly the same way a remote connection is
handled. To create this illusion, a thread is launched. This
thread opens a pipe to the browser, it then synthesizes an
appropriate HTTP header, sends it together with the file to the
browser proper. In this way, all the browser sees is a handle,
the data on it can come from a remote connection or from a local
file.
To handle a remote connection is more complex. In this case,
the browser asks the cache manager for the URL. The name in the
URL has to be resolved through the DNS engine, a socket TCP
connection must be established, the HTTP request has to be sent,
and finally the result retrieved. Each of the steps mentioned
could give rise to errors, which have to be handled and somehow
communicated to the rest of the program. For performance reasons,
it is critical that responses are cached locally, so the remote
connection doesn't directly hand over the data to the browser;
the response is passed to the cache manager which then relays it
to the rest of the browser. The DNS engine caches DNS responses,
and either answers them from the cache or by querying the DNS.
Querying is done in a separate thread, so that the rest of the
browser isn't blocked by long waits here.
The activities mentioned do not happen strictly in the order
stated above. It is even possible that several URLs are being
handled at the same time, in order to overlap waiting and
downloading. The functions called directly from the user
interface have to return quickly to maintain interactive
response. Sometimes they return connection handlers that haven't
been completely set up yet. As stated, I/O is signal-driven, when
one of the descriptors is ready for data transfer (reading or
writing), it wakes up the I/O engine.
Data transfer between threads inside the browser is handled by
pipes, shared memory is little used. This almost obviates the
need for explicit synchronization, which is one of the main areas
of complexity and bugs in concurrent programs. Dillo handles its
threads in a way that its developers can think of it as running
on a single thread of control. This is accomplished by making the
DNS engine call-backs happen within the main thread, and by
isolating file loading with pipes.
Using threads in this way has three big advantages:
The browser doesn't block when one of its child threads
blocks. In particular, the user interface is responsive
even while resolving a name or downloading a file.
Developers don't need to deal with complex concurrent
concerns. Concurrency is hard to handle, and few developers
are adept at this. This gives access a much larger pool of
potential developers, something which can be critical
in an open-source development project.
By making the code mostly sequential, debugging the code
with traditional tools like gdb is possible. Debugging
parallel programs is very hard, and appropriate tools are
hard to come by.
Because of simplicity and portability concerns, DNS querying
is done in a separate thread. The standard C library doesn't
provide a function for making DNS queries that don't block. The
alternative is to implement a new, custom DNS querying function
that doesn't block. This is certainly a complex task, integrating
this mechanism into the thread structure of the program is much
simpler.
Using a thread and a pipe to read a local file adds a
buffering step to the process (and a certain latency), but it has
a couple of significative advantages:
By handling local files in the same way as remote
connections, a significant amount of code is reused.
A preprocessing step of the file data can be added easily,
if needed. In fact, the file is encapsulated into an HTTP
data stream.
-----------
DNS queries
-----------
Dillo handles DNS queries with threads, letting a child thread
wait until the DNS server answers the request. When the answer
arrives, a call-back function is called, and the program resumes
what it was doing at DNS-request time. The interesting thing is
that the call-back happens in the main thread, while the child
thread simply exits when done. This is implemented through a
server-channel design.
The server channel
------------------
There is one thread for each channel, and each channel can
have multiple clients. When the program requests an IP address,
the server first looks for a cached match; if it hits, the client
call-back is invoked immediately, but if not, the client is put
into a queue, a thread is spawned to query the DNS, and a GTK+
idle client is set to poll the channel 5~times per second for
completion, and when it finally succeeds, every client of that
channel is serviced.
This scheme allows all the further processing to continue on
the same thread it began: the main thread.
------------------------
Handling TCP connections
------------------------
Establishing a TCP connection requires the well known
three-way handshake packet-sending sequence. Depending on network
traffic and several other issues, significant delay can occur at
this phase.
Dillo handles the connection by a non blocking socket scheme.
Basically, a socket file descriptor of AF_INET type is requested
and set to non-blocking I/O. When the DNS server has resolved the
name, the socket connection process begins by calling connect(2);
{We use the Unix convention of identifying the manual section
where the concept is described, in this case
section 2 (system calls).}
which returns immediately with an EINPROGRESS error.
After the connection reaches the EINPROGRESS ``state,'' the
socket waits in background until connection succeeds (or fails),
when that happens, a callback function is awaked to perform the
following steps: set the I/O engine to send the query and expect
its answer (both in background).
The advantage of this scheme is that every required step is
quickly done without blocking the browser. Finally, the socket
will generate a signal whenever I/O is possible.
----------------
Handling queries
----------------
In the case of a HTTP URL, queries typically translate into a
short transmission (the HTTP query) and a lengthy retrieval
process. Queries are not always short though, specially when
requesting forms (all the form data is attached within the
query), and also when requesting CGI programs.
Regardless of query length, query sending is handled in
background. The thread that was initiated at TCP connecting time
has all the transmission framework already set up; at this point,
packet sending is just a matter of waiting for the
write signal (G_IO_OUT) to come and then sending the data. When
the socket gets ready for transmission, the data is sent using
IO_write.
--------------
Receiving data
--------------
Although conceptually similar to sending queries, retrieving
data is very different as the data received can easily exceed the
size of the query by many orders of magnitude (for example when
downloading images or files). This is one of the main sources of
latency, the retrieval can take several seconds or even minutes
when downloading large files.
The data retrieving process for a single file, that began by
setting up the expecting framework at TCP connecting time, simply
waits for the read signal (G_IO_IN). When it happens, the
low-level I/O engine gets called, the data is read into
pre-allocated buffers and the appropriate call-backs are
performed. Technically, whenever a G_IO_IN event is generated,
data is received from the socket file descriptor, by using the
IO_read function. This iterative process finishes upon EOF (or on
an error condition).
----------------------
Closing the connection
----------------------
Closing a TCP connection requires four data segments, not an
impressive amount but twice the round trip time, which can be
substantial. When data retrieval finishes, socket closing is
triggered. There's nothing but a IO_close_fd call on the socket's
file descriptor. This process was originally designed to split
the four segment close into two partial closes, one when query
sending is done and the other when all data is in. This scheme is
not currently used because the write close also stops the reading
part.
The low-level I/O engine
------------------------
Dillo I/O is carried out in the background. This is achieved
by using low level file descriptors and signals. Anytime a file
descriptor shows activity, a signal is raised and the signal
handler takes care of the I/O.
The low-level I/O engine ("I/O engine" from here on) was
designed as an internal abstraction layer for background file
descriptor activity. It is intended to be used by the cache
module only; higher level routines should ask the cache for its
URLs. Every operation that is meant to be carried out in
background should be handled by the I/O engine. In the case of
TCP sockets, they are created and submitted to the I/O engine for
any further processing.
The submitting process (client) must fill a request structure
and let the I/O engine handle the file descriptor activity, until
it receives a call-back for finally processing the data. This is
better understood by examining the request structure:
typedef struct {
gint Key; /* Primary Key (for klist) */
gint Op; /* IORead | IOWrite | IOWrites */
gint FD; /* Current File Descriptor */
gint Flags; /* Flag array */
glong Status; /* Number of bytes read, or -errno code */
void *Buf; /* Buffer place */
size_t BufSize; /* Buffer length */
void *BufStart; /* PRIVATE: only used inside IO.c! */
void *ExtData; /* External data reference (not used by IO.c) */
void *Info; /* CCC Info structure for this IO */
GIOChannel *GioCh; /* IO channel */
guint watch_id; /* glib's event source id */
} IOData_t;
To request an I/O operation, this structure must be filled and
passed to the I/O engine.
'Op' and 'Buf' and 'BufSize' MUST be provided.
'ExtData' MAY be provided.
'Status', 'FD' and 'GioCh' are set by I/O engine internal
routines.
When there is new data in the file descriptor, 'IO_callback'
gets called (by glib). Only after the I/O engine finishes
processing the data are the upper layers notified.
The I/O engine transfer buffer
------------------------------
The 'Buf' and 'BufSize' fields of the request structure
provide the transfer buffer for each operation. This buffer must
be set by the client (to increase performance by avoiding copying
data).
On reads, the client specifies the amount and where to place
the retrieved data; on writes, it specifies the amount and source
of the data segment that is to be sent. Although this scheme
increases complexity, it has proven very fast and powerful. For
instance, when the size of a document is known in advance, a
buffer for all the data can be allocated at once, eliminating the
need for multiple memory reallocations. Even more, if the size is
known and the data transfer is taking the form of multiple small
chunks of data, the client only needs to update 'Buf' and
BufSize' to point to the next byte in its large preallocated
reception buffer (by adding the chunk size to 'Buf'). On the
other hand, if the size of the transfer isn't known in advance,
the reception buffer can remain untouched until the connection
closes, but the client must then accomplish the usual buffer
copying and reallocation.
The I/O engine also lets the client specify a full length
transfer buffer when sending data. It doesn't matter (from the
client's point of view) if the data fits in a single packet or
not, it's the I/O engine's job to divide it into smaller chunks
if needed and to perform the operation accordingly.
------------------------------------------
Handling multiple simultaneous connections
------------------------------------------
The previous sections describe the internal work for a single
connection, the I/O engine handles several of them in parallel.
This is the normal downloading behavior of a web page. Normally,
after retrieving the main document (HTML code), several
references to other files (typically images) and sometimes even
to other sites (mostly advertising today) are found inside the
page. In order to parse and complete the page rendering, those
other documents must be fetched and displayed, so it is not
uncommon to have multiple downloading connections (every one
requiring the whole fetching process) happening at the same time.
Even though socket activity can reach a hectic pace, the
browser never blocks. Note also that the I/O engine is the one
that directs the execution flow of the program by triggering a
call-back chain whenever a file descriptor operation succeeds or
fails.
A key point for this multiple call-back chained I/O engine is
that every single function in the chain must be guaranteed to
return quickly. Otherwise, the whole system blocks until it
returns.
-----------
Conclusions
-----------
Dillo is currently in very stable beta state. It already shows
impressive performance, and its interactive ``feel'' is much
better than that of other web browsers.
The modular structure of Dillo, and its reliance on GTK1 allow
it to be very small. Not every feature of HTML-4.01 has been
implemented yet, but no significant problems are foreseen in
doing this.
The fact that Dillo's central I/O engine is written using
advanced features of POSIX and TCP/IP networking makes its
performance possible, but on the other hand this also means that
only a fraction of the interested hackers are able to work on it.
A simple code base is critical when trying to attract hackers
to work on a project like this one. Using the GTK+ framework
helped both in creating the graphical user interface and in
handling the concurrency inside the browser. By having threads
communicate through pipes the need for explicit synchronization
is almost completely eliminated, and with it most of the
complexity of concurrent programming disappears.
A clean, strictly applied layering approach based on clear
abstractions is vital in each programming project. A good,
supportive framework is of much help here.

129
devdoc/Images.txt Normal file
View File

@ -0,0 +1,129 @@
January 2009, --Jcid
Update June 2015: See also doc/dw-images-and-backgrounds.doc, or
../html/dw-images-and-backgrounds.html (generated by doxygen).
------
IMAGES
------
* When a image tag is found within a HTML page, Html_tag_open_img
handles it by:
- Parsing & getting attribute values.
- Creating a new image structure (DilloImage) and its
associated widget (DwImage).
i.e. If 'Image' is the var for the structure, then
'Image->dw' is the widget.
- Requesting the image to be fed by the cache.
- Sending some info to the browser interface.
* The cache can either request the image data from the net, or
feed it directly from the dicache (decompressed image cache).
* Both processes are somewhat different because the first one
requires to decode the image data into RGB format, and the second
one has the whole data already decoded.
* Regardless of the RGB-data feeding method, the decoded data is
passed to the widget (DwImage) and drawn in a streamed way.
Note that INDEXED images are also decoded into RGB format.
Html_tag_open_img // IMG element processing
Html_add_new_image // Read attributes, create image, add to HTML page
a_Image_new // Create a 'DilloImage' data structure, to coordinate
// decoded image-data transfer to an 'Imgbuf'.
Html_add_widget // Adds the dw::Image to the page
Html_load_image // Tells cache to retrieve image
---------------------
Fetching from the net
---------------------
* a_Capi_open_url initiates the resource request, and when
finally the answer arrives, the HTTP header is examined for MIME
type and either the GIF or PNG or JPEG decoder is set to handle
the incoming data stream.
Decoding functions:
a_Gif_callback, a_Jpeg_callback and a_Png_callback.
* The decoding function calls the following dicache methods as
the data is processed (listed in order):
a_Dicache_set_parms
a_Dicache_set_cmap (only for indexed-GIF images)
a_Dicache_write
a_Dicache_new_scan (MAY be called here or after set_cmap)
a_Dicache_close
* The dicache methods call the necessary functions to connect
with the widget code. This is done by calling image.c functions:
a_Image_set_parms
a_Image_set_cmap
a_Image_write
a_Image_new_scan
a_Image_close
* The functions in image.c make the required Dw calls.
-------------------------
Fetching from the dicache
-------------------------
* a_Capi_open_url() tests the cache for the image, and the cache,
via a_Cache_open_url(), enqueues a client for it, without asking
the network for the data. When the client queue is processed (a
bit later), Dicache_image() is set as the callback.
* When Dicache_image() is called, it sets the proper image data
decoder (RGB) and its data structure based on the entry's Type.
Then it substitutes itself with a_Dicache_callback() as the
handling function, and gets out of the way.
* Thenceforth the rest of the functions calls is driven by
a_Dicache_callback().
-----------
Misc. notes
-----------
* Repeated images generate new cache clients, but they may share
the imgbuf.
Note: Currently there's no proper support for transparent
images (i.e. decode to RGBA), but most of the time they render
the background color OK. This is: when first loaded, repeated
images share a background color, but when cached they render
correctly ;-). There's no point in trying to fix this because the
correct solution is to decode to RGBA and let the toolkit (FLTK)
handle the transparency.
* The first cache-client callback (Dicache_image()) is set when
the Content-type of the image is got.
* Later on, when there's a shared imgbuf, the dicache's logic
avoids decoding it multiple times and reuses what's already done.
* The dicache-entry and the Image structure hold bit arrays that
represent which rows have been decoded.
* The image processing can be found in the following sources:
- image.{cc,hh}
- dicache.[ch]
- gif.[ch], png.[ch], jpeg.[ch]
- dw/image.{cc,hh}
* Bear in mind that there are four data structures for image
code:
- DilloImage (image.hh)
- DICacheEntry (dicache.h)
- dw::Image (class Image in dw/image.hh)
- core::Imgbuf (imgbuf.hh)

127
devdoc/NC_design.txt Normal file
View File

@ -0,0 +1,127 @@
_________________________________________________________________
Naming&Coding design
_________________________________________________________________
Dillo's code is divided into modules. For instance: bookmark, cache,
dicache, gif.
Let's think of a module named "menu", then:
* Every internal routine of the module, should start with "Menu_"
prefix.
* "Menu_" prefixed functions are not meant to be called from outside
the module.
* If the function is to be exported to other modules (i.e. it will
be called from the outside), it should be wrapped with an "a_"
prefix.
For instance: if the function name is "Menu_create", then it's an
internal function, but if we need to call it from the outside, then it
should be renamed to "a_Menu_create".
Why the "a_" prefix?
Because of historical reasons.
And "a_Menu_create" reads better than "d_Menu_create" because the
first one suggests "a Menu create" function!
Another way of understanding this is thinking of "a_" prefixed
functions as Dillo's internal library, and the rest ("Menu_" prefixed
in our example) as a private module-library.
Indentation:
Source code must be indented with 3 blank spaces, no Tabs.
Why?
Because different editors expand or treat tabs in several ways; 8
spaces being the most common, but that makes code really wide and
we'll try to keep it within the 80 columns bounds (printer friendly).
You can use: indent -kr -sc -i3 -bad -nbbo -nut -l79 myfile.c
Function commenting:
Every single function of the module should start with a short comment
that explains its purpose; three lines must be enough, but if you
think it requires more, enlarge it.
/*
* Try finding the url in the cache. If it hits, send the contents
* to the caller. If it misses, set up a new connection.
*/
int a_Cache_open_url(const char *url, void *Data)
{
...
...
...
}
We also have the BUG:, TODO:, and WORKAROUND: tags.
Use them within source code comments to spot hidden issues. For
instance:
/* BUG: this counter is not accurate */
++i;
/* TODO: get color from the right place */
a = color;
/* WORKAROUND: the canonical way of doing it doesn't work yet. */
++a; ++a; ++a;
Function length:
Let's try to keep functions within the 45 lines boundary. This eases
code reading, following, understanding and maintenance.
Functions with a single exit:
It's much easier to follow and maintain functions with a single exit
point at the bottom (instead of multiple returns). The exception to
the rule are calls like dReturn_if_fail() at its head.
dlib functions:
* Dillo uses dlib extensively in its C sources. Before starting
to code something new, a good starting point is to check what
this library has to offer (check dlib/dlib.h).
* Memory management must be done using dNew, dNew0, dMalloc, dFree
and their relatives.
* For debugging purposes and error catching (not for normal flow):
dReturn_if_fail, dReturn_val_if_fail etc. are encouraged.
* The MSG macro is extensively used to output additional information
to the calling terminal.
_________________________________________________________________
C++
Source code in C++ should follow the same rules with these exceptions:
* Class method names are camel-cased and start with lowercase
e.g. appendInputMultipart
* Classes and types start uppercased
e.g. class DilloHtmlReceiver
* Class methods don't need to prefix its module name
e.g. links->get()
We also try to keep the C++ relatively simple. Dillo does use
inheritance and templates, but that's about all.
_________________________________________________________________
What do we get with this?
* A clear module API for Dillo; every function prefixed "a_" is to
be used outside the module.
* A way to identify where the function came from (the
capitalized word is the module name).
* An inner ADT (Abstract data type) for the module that can be
isolated, tested and replaced independently.
* A two stage instance for bug-fixing. You can change the exported
function algorithms while someone else fixes the internal
module-ADT!
* A coding standard ;)
_________________________________________________________________
Naming&Coding design by Jorge Arellano Cid

51
devdoc/README Normal file
View File

@ -0,0 +1,51 @@
README: Last update Jul 2009
These documents cover dillo's internals.
For user help, see https://dillo-browser.github.io/old/dillo3-help.html
--------------------------------------------------------------------------
These documents need a review.
*.txt were current with Dillo1, but many have since become more or
less out-of-date.
*.doc are doxygen source for the Dillo Widget (dw) component, and
were written for Dillo2.
They will give you an overview of what's going on, but take them
with a pinch of salt.
Of course I'd like to have *.txt as doxygen files too!
If somebody wants to make this conversion, please let me know
to assign higher priority to updating these docs.
--
Jorge.-
--------------------------------------------------------------------------
FILE DESCRIPTION STATE
--------------------------------------------------------------------------
NC_design.txt Naming&Coding design (Be sure to Current
read it before any other doc)
Dillo.txt General overview of the program Current
IO.txt Extensive introduction Current
Cache.txt Informative description Current
Images.txt Image handling and processing Current
HtmlParser.txt A versatile parser Current
Dw.txt The New Dillo Widget (Overview) Current
Imgbuf.txt Image buffers Pending
Selection.txt Selections, and link activation Current (?)
Cookies.txt Explains how to enable cookies Current
Dpid.txt Dillo plugin daemon Current
--------------------------------------------------------------------------
* BTW, there's a small program (srch) within the src/ dir. It searches
tokens within the whole code (*.[ch]). It has proven very useful.
Ex: ./srch a_Image_write
./srch todo:
* Please submit your patches with 'hg diff'.
Happy coding!
--Jcid

2669
devdoc/doxygen-awesome.css Normal file

File diff suppressed because it is too large Load Diff

105
devdoc/dw-changes.doc Normal file
View File

@ -0,0 +1,105 @@
/** \page dw-changes Changes to the GTK+-based Release Version
<h2>Changes in Dw</h2>
Related to the FLTK port, there have been many changes, this is a
(hopefully complete) list:
<ul>
<li> Rendering abstraction, read \ref dw-overview and \ref dw-layout-views
for details. Some important changes:
<ul>
<li> The underlying platform (e.g. the UI toolkit) is fully abstract,
there are several platform independent structures replacing
GTK+ structures, e.g. dw::core::Event.
<li> The central class managing the widget tree is not anymore
GtkDwViewport, but dw::core::Layout.
<li> Drawing is done via dw::core::View, a pointer is passed to
dw::core::Widget::draw.
<li> The distinction between viewport coordinates and canvas
coordinates (formerly world coordinates) has been mostly
removed. (Only for views, it sometimes plays a role, see
\ref dw-layout-views).
</ul>
<li> Cursors have been moved to dw::core::style, see
dw::core::style::Style::cursor. dw::core::Widget::setCursor is now
protected (and so only called by widget implementations).
<li> World coordinates are now called canvas coordinates.
<li> There is now a distinction between dw::core::style::StyleAttrs and
dw::core::style::Style.
<li> There is no base class for container widgets anymore. The former
DwContainer::for_all has been removed, instead this functionality
is now done via iterators (dw::core::Widget::iterator,
dw::core::Iterator).
<li> DwPage is now called dw::Textblock, and DwAlignedPage
dw::AlignedTextblock.
<li> dw::Textblock, all sub classes of it, and dw::Table do not read
"limit_text_width" from the preferences, but get it as an argument.
(May change again.)
<li> dw::Table has been rewritten.
<li> Instead of border_spacing in the old DwStyle, there are two attributes,
dw::core::style::Style::hBorderSpacing and
dw::core::style::Style::vBorderSpacing, since CSS allows to specify
two values. Without CSS, both attributes should have the same value.
<li> Images are handled differently, see \ref dw-images-and-backgrounds.
<li> Embedded UI widgets (formerly GtkWidget's) are handled differently,
see dw::core::ui.
<li> DwButton has been removed, instead, embedded UI widgets are used. See
dw::core::ui and dw::core::ui::ComplexButtonResource.
</ul>
Dw is now written C++, the transition should be obvious. All "Dw"
prefixes have been removed, instead, namespaces are used now:
<ul>
<li>dw::core contains the core,
<li>dw::core::style styles,
<li>dw::core::ui embedded UI resources,
<li>dw::fltk classes related to FLTK, and
<li>::dw the widgets.
</ul>
<h2>Documentation</h2>
The old documentation has been moved to:
<table>
<tr><th colspan="2">Old <th>New
<tr><td rowspan="2">Dw.txt
<td>general part <td>\ref dw-overview, \ref dw-usage,
\ref dw-layout-widgets,
\ref dw-widget-sizes
<tr><td>remarks on specific widgets <td>respective source files: dw::Bullet,
dw::core::ui::Embed
<tr><td rowspan="2">DwImage.txt
<td>signals <td>dw::core::Layout::LinkReceiver
<tr><td>rest <td>dw::Image,
\ref dw-images-and-backgrounds
<tr><td colspan="2">Imgbuf.txt <td>dw::core::Imgbuf,
\ref dw-images-and-backgrounds
<tr><td colspan="2">DwPage.txt <td>dw::Textblock
<tr><td colspan="2">DwRender.txt <td>\ref dw-overview, \ref dw-layout-views,
dw::core::ui
<tr><td colspan="2">DwStyle.txt <td>dw::core::style
<tr><td colspan="2">DwTable.txt <td>dw::Table
<tr><td colspan="2">DwWidget.txt <td>dw::core::Widget, \ref dw-layout-widgets,
\ref dw-widget-sizes
<tr><td colspan="2">Selection.txt <td>dw::core::SelectionState
</table>
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,63 @@
/** \page dw-fixed-positions Fixed positions
In some cases, widgets or widget content must be positioned relative
to the viewport. As in the CSS specification, these positions will be
called "fixed positions". This must not be confused with "fixedly
positioned elements" (see \ref dw-out-of-flow), which are a special
case of fixed positions.
Applications
============
As defined by CSS
-----------------
- "position: fixed"; see \ref dw-out-of-flow.
- "background-attachment: fixed"; see \ref dw-images-and-backgrounds.
Idea for tables
---------------
Often, tables have a header, which contains information necessary to
interpret the columns in the table body. For this, HTML defines the elements
&lt;thead&gt; and &lt;tbody&gt;
<sup><a href="#note-table-footer" id="ref-table-footer">[1]</a></sup>.
For large tables, the problem occurs that the table header gets out of
the reader's view. In paged media, where a large table covers multiple
pages, this is often solved by *repeating* the table header on each
page occupied by the table. When using a viewport, a table larger than
the viewport could be displayed like this:
1. If the top of the table is within the viewport, show the table
header at the usual position.
2. As soon as top of the table gets above the top border of the
viewport, keep the table header at the viewport top, so that it is
still visible (this means, it moves down, relative to the
*canvas*). This way, the header is still visible, so our objective
is achieved.
3. When scrolling further down, at some point the table body gets out
of the viewport again, and so should the table header.
(Some images would be nice.)
These ideas should be considered when developing a design for fixed
positions.
Design sketch
==============
[...]
----------------------------------------------------------------------
<sup><a href="#ref-table-footer" id="note-table-footer">[1]</a></sup>
... and also &lt;tfoot&gt;, which is not discussed here, for reasons
of simplicity. However, it is obvious that &lt;tfoot&gt; should be
dealt with in an analogue way as &lt;thead&gt;.
*/

BIN
devdoc/dw-floats-01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

209
devdoc/dw-grows.doc Normal file
View File

@ -0,0 +1,209 @@
/** \page dw-grows GROWS - Grand Redesign Of Widget Sizes
<div style="border: 2px solid #ffff00; margin: 1em 0;
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
sizes" is currently divided into three documents: \ref
dw-widget-sizes, **Grand Redesign Of Widget Sizes** (this document),
and \ref dw-size-request-pos. Furthermore, there are some notes in
\ref dw-miscellaneous.</div>
This paper describes (will describe) some design changes to
calculating widget sizes. Goals are:
- Simplification of widget size calculation by the parent widget;
dw::Textblock::calcWidgetSize, dw::OutOfFlowMgr::ensureFloatSize
etc. should become simpler or perhaps even obsolete.
- Making the implementation of some features possible:
- *max-width*, *max-height*, *min-width*, *min-height*;
- correct aspect ratio for images with only one percentage size defined;
- *display: inline-block*;
- &lt;button&gt;.
A short sketch
==============
**dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes will
return final results.** The caller does not have to correct the size,
e.&nbsp;g. when percentages are defined. As an example,
dw::Textblock::calcWidgetSize has already become much simpler.
**A new hierarchy, *container*:** Aside from dw::core::Widget::parent
and dw::core::Widget::generator, there is a third hierarchy
dw::core::Widget::container, which is (unlike *generator*) always a
direct ancestor, and represents what in CSS is called *containing
block*. Containers are important to define the "context size", which
is (not solely) used for percentage sizes.
(There is another "containing block", dw::Textblock::containingBlock;
these may be consolidated some day.)
**The process of size calculation is split between the widget itself
and its container:**
- The container provides some abstract methods:
dw::core::Widget::getAvailWidthOfChild,
dw::core::Widget::getAvailHeightOfChild,
dw::core::Widget::correctRequisitionOfChild, and
dw::core::Widget::correctExtremesOfChild, which can be used in the
actual implementation of dw::core::Widget::sizeRequestImpl;
different containers with different ways how to arrange their
children will implement these methods in a different way. (Simple
example: the *available width* for children within a textblock is
the *available width* for the textblock itself, minus
margin/border/padding; on the other hand, it is completely different
for children of tables, for which a complex column width calculation
is used.)
- The actual size calculation is, however, controlled by the widget
itself, which only *uses* these methods above.
<div style="border: 2px solid #ffff00; margin-top: 0.5em;
margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0">
<b>Update:</b> This is not fully correct; the parents are also involved
for calculating available widths and heights, at least when CSS 'width'
and 'height' are not set.</div>
**Size hints are removed.** Instead, the container methods in the
previous paragraph are used. Changes of container sizes (especially
viewport the size) are handled in a different way.
**Extremes are extended by intrinsic values.** In some cases (see
dw::Table::forceCalcCellSizes, case *minWidth* > *totalWidth*, for an
example) it is useful to know about minimal and maximal width of a
widget independent of CSS attributes. For this, dw::core::Extremes is
extended by:
- dw::core::Extremes::minWidthIntrinsic and
- dw::core::Extremes::maxWidthIntrinsic.
The rules for the calculation:
1. If a widget has no children, it calculates *minWidthIntrinsic* and
*maxWidthIntrinsic* as those values not affected by CSS hints.
(dw::core::Widget::correctExtremes will not change these values.)
2. A widget must calculate *minWidthIntrinsic* and *maxWidthIntrinsic*
from *minWidthIntrinsic* and *maxWidthIntrinsic* of its children,
and *minWidth* and *maxWidth* from *minWidth* and *maxWidth* of its
children.
3. At the end, *minWidth* and *maxWidth* of a widget are corrected by
CSS attributes. (dw::core::Widget::correctExtremes will do this.)
<div style="border: 2px solid #ffff00; margin-top: 0.5em;
margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0">
<b>Notice:</b> Currently, dw::core::Widget::getExtremesImpl must
set all four members in dw::core::Extremes; this may change.</div>
Another **extension of extremes: *adjustmentWidth*.** This is used as
minimum for the width, when "adjust_min_width" (or,
"adjust_table_min_width", respectively) is set.
The rules for the calculation:
1. If a widget has no children, it can choose a suitable value,
typically based on dw::core::Extremes::minWidth and
dw::core::Extremes::minWidthIntrinsic.
2. A widget must calculate *adjustmentWidth* from *adjustmentWidth* of
its children.
*Note:* An implementation of dw::core::Widget::getExtremesImpl may set
this value *after* calling dw::core::Widget::correctExtremesOfChild,
so that it cannot be used for the correction of extremes. In this case
*useAdjustmentWidth = false* should be passed to
dw::core::Widget::correctExtremesOfChild. On the other hand, if known
before, *useAdjustmentWidth* should be set to *true*.
Rules for *new* methods related to resizing
===========================================
- Of course, *sizeRequestImpl* may (should) call *correctRequisition*,
and *getExtremesImpl* may (should) call *correctExtremes*.
- *sizeRequestImpl* (and *correctRequisition*) is allowed to call
*getAvailWidth* and *getAvailHeight* with *forceValue* set, but
*getExtremesImpl* (and *correctExtremes*) is allowed to call these
only with *forceValue* unset.
- For this reason, *sizeRequestImpl* is indeed allowed to call
*getExtremes* (dw::Table does so), but the opposite
(*getExtremesImpl* calling *sizeRequest*) is not allowed
anymore. (Before GROWS, the standard implementation
dw::core::Widget::getExtremesImpl did so.)
- Finally, *getAvailWidth* and *getAvailHeight* may call
*getExtremes*, if and only if *forceValue* is set.
Here is a diagram showing all permitted dependencies:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"];
edge [arrowhead="open", arrowtail="none", color="#404040"];
"sizeRequest[Impl]" -> "getExtremes[Impl]";
"sizeRequest[Impl]" -> correctRequisition;
"getExtremes[Impl]" -> correctExtremes;
"sizeRequest[Impl]" -> "getAvail[Width|Height] (true)";
"getExtremes[Impl]" -> "getAvail[Width|Height] (false)";
correctRequisition -> "getAvail[Width|Height] (true)";
correctExtremes -> "getAvail[Width|Height] (false)";
"getAvail[Width|Height] (true)" -> "getExtremes[Impl]";
}
\enddot
Open issues
===========
**Do CSS size dimensions override intrinsic sizes in all cases?** If a
textblock needs at least, say, 100 pixels width so that the text can
be read, but has a specification "width: 50px", should half of the
text be invisible? Or should the width be corrected again to 100
pixels?
Currently, in the CSS size specification is honoured in all cases,
with one exception: see dw::Textblock::sizeRequestImpl and see
dw::Textblock::getExtremesImpl (the time when
dw::core::Widget::correctRequisition and
dw::core::Widget::correctExtremes, respectively, is called).
*Not* honouring the CSS size specification in all cases could improve
readability in some cases, so this could depend on a user preference.
**Update:** There is now a dillorc option <tt>adjust_min_width</tt>,
which is implemented for widths, but not heights (since it is based on
width extremes, but there are currently no height extremes).
Another problem is that in most cases, there is no clippping, so that
contents may exceed the allocation of the widget, but redrawing is not
necessarily triggered.
**Percentage values for margins and paddings, as well as negative
margins** are interesting applications, but have not been considered
yet. For negative margins, a new attribute
dw::core::Widget::extraSpace could solve the problem of widgets
sticking out of the allocation of parent.
**Clarify percentage heights.** Search in widget.cc, and compare
section 10.5 ('height') of the CSS 2.1 specification to section 10.2
('width').
**Fast queue resize does not work fully.** Example: run
*test/dw-simple-container-test* (dw_simple_container_test.cc), resize
(best maximize) the window and follow (e.&nbsp;g. by using RTFL) what
happens in consequence of dw::core::Layout::viewportSizeChanged. The
dw::SimpleContainer in the middle is not affected, so only the two
dw::Textblock's (at the top and at the bottom) call queueResize with
*fast = true*, and so get *NEEDS_RESIZE* set; but since it is not set
for the dw::SimpleContainer, *sizeRequest* is never called for the
bottom dw::Textblock.
There does not seem to be a real case for this problem in dillo, since
all widgets which may contain other widgets (except
dw::SimpleContainer, which is not used outside tests) use the
available width and height (dw::core::Widget::usesAvailWidth and
dw::core::Widget::usesAvailHeight), and so are always affected by
viewport size changes.
*/

View File

@ -0,0 +1,235 @@
/** \page dw-images-and-backgrounds Images and Backgrounds in Dw
Image Buffers
=============
Representation of the image data is done by dw::core::Imgbuf, see
there for details. Drawing is done by dw::core::View
(dw::core::View::drawImage).
Since dw::core::Imgbuf provides memory management based on reference
counting, there may be an 1-to-n relation from image renderers (image
widgets or backgrounds, see below) and dw::core::Imgbuf. Since
dw::core::Imgbuf does not know about renderers, but just provides
rendering functionality, the caller must (typically after calling
dw::core::Imgbuf::copyRow) notify all renderers connected to the
buffer.
Image Renderer
==============
Generally, there are no restrictions on how to manage
dw::core::Imgbuf; but to handle image data from web resources, the
interface dw::core::ImgRenderer should be implemented. It is again
wrapped by DilloImage (to make access from the C part possible, since
dw::core::ImgRenderer is written in C++), which is referenced by
DilloWeb. There are two positions where retrieving image data is
initiated:
- Html_load_image: for embedded images (implemented by dw::Image,
which implements dw::core::ImgRenderer);
- StyleEngine::apply (search for "case
CSS_PROPERTY_BACKGROUND_IMAGE"): for background images; there are
some implementations of dw::core::ImgRenderer within the context of
dw::core::style::StyleImage.
Both are described in detail below. Notice that the code is quite
similar; only the implementation of dw::core::ImgRenderer differs.
At this time, dw::core::ImgRenderer has got two methods (see more
documentation there):
- dw::core::ImgRenderer::setBuffer,
- dw::core::ImgRenderer::drawRow,
- dw::core::ImgRenderer::finish, and
- dw::core::ImgRenderer::fatal.
Images
======
This is the simplest renderer, displaying an image. For each row to be
drawn,
- first dw::core::Imgbuf::copyRow, and then
- for each dw::Image, dw::Image::drawRow must be called, with the same
argument (no scaling is necessary).
dw::Image automatically scales the dw::core::Imgbuf, the root buffer
should be passed to dw::Image::setBuffer.
\see dw::Image for more details.
Background Images
=================
Since background images are style resources, they are associated with
dw::core::style::Style, as dw::core::style::StyleImage, which is
handled in a similar way (reference counting etc.) as
dw::core::style::Color and dw::core::style::Font, although it is
concrete and not platform-dependant.
The actual renderer (passed to Web) is an instance of
dw::core::ImgRendererDist (distributes all calls to a set of other
instances of dw::core::ImgRenderer), which contains two kinds of
renderers:
- one instance of dw::core::style::StyleImage::StyleImgRenderer, which
does everything needed for dw::core::style::StyleImage, and
- other renderers, used externally (widgets etc.), which are added by
dw::core::style::StyleImage::putExternalImgRenderer (and removed by
dw::core::style::StyleImage::removeExternalImgRenderer).
This diagram gives an comprehensive overview:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="open", dir="both", arrowtail="none",
labelfontname=Helvetica, labelfontsize=10, color="#404040",
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
subgraph cluster_dw_style {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
Style [URL="\ref dw::core::style::Style"];
StyleImage [URL="\ref dw::core::style::StyleImage"];
Imgbuf [URL="\ref dw::core::Imgbuf", color="#a0a0a0"];
StyleImgRenderer
[URL="\ref dw::core::style::StyleImage::StyleImgRenderer"];
ImgRenderer [URL="\ref dw::core::ImgRenderer", color="#ff8080"];
ImgRendererDist [URL="\ref dw::core::ImgRendererDist"];
ExternalImgRenderer
[URL="\ref dw::core::style::StyleImage::ExternalImgRenderer",
color="#a0a0a0"];
ExternalWidgetImgRenderer
[URL="\ref dw::core::style::StyleImage::ExternalWidgetImgRenderer",
color="#a0a0a0"];
}
subgraph cluster_dw_layout {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
Layout [URL="\ref dw::core::Layout"];
LayoutImgRenderer [URL="\ref dw::core::Layout::LayoutImgRenderer"];
}
subgraph cluster_dw_widget {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
WidgetImgRenderer [URL="\ref dw::core::Widget::WidgetImgRenderer"];
}
subgraph cluster_dw_textblock {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
Textblock [URL="\ref dw::Textblock"];
Word [URL="\ref dw::Textblock::Word"];
WordImgRenderer [URL="\ref dw::Textblock::WordImgRenderer"];
SpaceImgRenderer [URL="\ref dw::Textblock::SpaceImgRenderer"];
}
Style -> StyleImage [headlabel="*", taillabel="1"];
StyleImage -> Imgbuf [headlabel="*", taillabel="1"];
StyleImage -> StyleImgRenderer [headlabel="1", taillabel="1"];
StyleImage -> ImgRendererDist [headlabel="1", taillabel="1"];
ImgRendererDist -> StyleImgRenderer [headlabel="1", taillabel="1"];
ImgRendererDist -> ImgRenderer [headlabel="1", taillabel="*"];
ImgRenderer -> ImgRendererDist [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
ImgRenderer -> StyleImgRenderer [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
ImgRenderer -> ExternalImgRenderer [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
ExternalImgRenderer -> ExternalWidgetImgRenderer [arrowhead="none",
arrowtail="empty", dir="both", style="dashed"];
Layout -> LayoutImgRenderer [headlabel="1", taillabel="0..1"];
ExternalImgRenderer -> LayoutImgRenderer [arrowhead="none",
arrowtail="empty", dir="both", style="dashed"];
Widget -> WidgetImgRenderer [headlabel="1", taillabel="0..1"];
ExternalWidgetImgRenderer -> WidgetImgRenderer [arrowhead="none",
arrowtail="empty", dir="both", style="dashed"];
Textblock -> Word [headlabel="1", taillabel="*"];
Word -> WordImgRenderer [headlabel="1", taillabel="0..1"];
Word -> SpaceImgRenderer [headlabel="1", taillabel="0..1"];
ExternalWidgetImgRenderer -> WordImgRenderer [arrowhead="none",
arrowtail="empty", dir="both", style="dashed"];
WordImgRenderer -> SpaceImgRenderer [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
}
\enddot
<center>[\ref uml-legend "legend"]</center>
Memory management
-----------------
dw::core::style::StyleImage extends lout::signal::ObservedObject, so
that deleting this instance can be connected to code dealing with
cache clients etc. See StyleImageDeletionReceiver and how it is
attached in StyleEngine::apply ("case CSS_PROPERTY_BACKGROUND_IMAGE").
Bugs and Things Needing Improvement
===================================
(Mostly related to image backgrounds, when not otherwise mentioned.)
High Priority
-------------
**Configurability, security/privacy aspects, etc.,** which are
currently available for image widgets, should be adopted. Perhaps some
more configuration options specially for background images.
Medium Priority
---------------
**Background-attachment** is not yet implemented, and will be postponed.
**Calls to dw::core::ImgRenderer::fatal** are incomplete. As an
example, it is not called, when connecting to a server fails. (And so,
as far as I see, no cache client is started.)
Low Priority
------------
**Alpha support:** (not related to image backgrounds) currently alpha
support (and also colormap management) is done in dicache, while
dw::Image is only created with type RGB. This leads to several problems:
- One dicache entry (representing an image related to the same URL),
which has only one background color, may refer to different images
with different background colors.
- The dicache only handles background colors, not background images.
The solution is basically simple: keep alpha support out of dicache;
instead implement RGBA in dw::Image. As it seems, the main problem is
alpha support in FLTK/X11.
Solved (Must Be Documented)
---------------------------
*Drawing background images row by row may become slow. As an
alternative, dw::core::ImgRenderer::finish could be used. However,
drawing row by row could become an option.* There is now
dw::core::style::drawBackgroundLineByLine, which can be changed in the
code, and is set to *false*. The old code still exists, so changing
this to *true* activates again drawing line by line.
(For image widgets, this could also become an option: in contexts,
when image data is retrieved in a very fast way.)
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -0,0 +1,706 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="dw-interrupted-drawing-2.svg"
inkscape:export-filename="/home/sg/dev/dillo/dillo-grows-work-intdraw/doc/dw-interrupted-drawing-2.png"
inkscape:export-xdpi="69.620003"
inkscape:export-ydpi="69.620003">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path4042"
style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-2"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-4"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-8"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-0"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-24"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-2"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-29"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-5"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-41"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-23"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-19"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-7"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-9"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0-4"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97-1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0-7"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97-5"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0-8"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97-8"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0-3"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97-2"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-1"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-7"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker5604"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path5606"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35355339"
inkscape:cx="437.30872"
inkscape:cy="337.60168"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1600"
inkscape:window-height="900"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid2985" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g3977"
transform="translate(2.992126e-6,177.16535)">
<rect
y="24.803127"
x="17.716536"
height="70.866142"
width="141.73228"
id="rect2997"
style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<flowRoot
transform="translate(20.094709,-2.1798012)"
style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
id="flowRoot3786"
xml:space="preserve"><flowRegion
id="flowRegion3788"><rect
y="48.270561"
x="46.467018"
height="28.284271"
width="75.256363"
id="rect3790" /></flowRegion><flowPara
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
id="flowPara3792">body</flowPara></flowRoot> </g>
<g
id="g3984"
transform="translate(2.992126e-6,177.16535)">
<rect
y="24.803127"
x="194.8819"
height="70.866142"
width="141.73228"
id="rect3767"
style="fill:#ffe0e0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<text
sodipodi:linespacing="125%"
id="text3832"
y="67.096199"
x="243.2318"
style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="67.096199"
x="243.2318"
id="tspan3834"
sodipodi:role="line">#sc-1</tspan></text>
</g>
<g
id="g3989"
transform="translate(176.96508,177.13197)">
<rect
y="24.803127"
x="372.04724"
height="70.866142"
width="141.73228"
id="rect3767-6"
style="fill:#b0ffb0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<text
sodipodi:linespacing="125%"
id="text3836"
y="67.356201"
x="425.39713"
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="67.356201"
x="425.39713"
id="tspan3838"
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">#fl-1</tspan></text>
</g>
<g
id="g3994"
transform="translate(-177.81225,177.39197)">
<rect
y="24.803127"
x="549.21259"
height="70.866142"
width="141.73228"
id="rect3767-2"
style="fill:#f0f0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<text
sodipodi:linespacing="125%"
id="text3840"
y="67.096199"
x="595.92249"
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="67.096199"
x="595.92249"
id="tspan3842"
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">#sc-2</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 88.582679,272.83462 -2e-6,531.49607"
id="path3890"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 265.74803,272.83462 0,531.49607"
id="path3890-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 442.91339,272.83462 0,531.49607"
id="path3890-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 620.07874,272.83462 0,531.49607"
id="path3890-29"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3931"
width="35.433071"
height="460.62991"
x="70.866142"
y="308.2677" />
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3933"
width="35.433071"
height="354.33069"
x="248.03149"
y="343.70078" />
<flowRoot
xml:space="preserve"
id="flowRoot3935"
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
transform="translate(2.992126e-6,177.16535)"><flowRegion
id="flowRegion3937"><rect
id="rect3939"
width="835.71429"
height="147.14285"
x="-1.4285715"
y="-40.494953" /></flowRegion><flowPara
id="flowPara3941"></flowPara></flowRoot> <path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 106.29921,343.70076 141.73229,0"
id="path3890-29-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:3.00000191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4643"
width="35.433094"
height="70.866158"
x="602.36218"
y="414.56689" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 283.46457,414.56691 318.89763,0"
id="path3890-29-5-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
d="m 602.36221,485.43305 -318.89764,0"
id="path3890-29-5-2-0-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
d="m 248.0315,698.03147 -141.73229,0"
id="path3890-29-5-2-0-0-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect4767"
width="35.433071"
height="35.433071"
x="602.36218"
y="733.46454" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 106.29921,733.46454 496.06299,10e-6"
id="path3890-29-5-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 17.716536,308.26769 53.149608,0"
id="path3890-29-5-27"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
d="m 70.866142,768.89762 -53.149613,0"
id="path3890-29-5-2-0-0-8-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ff0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 354.33071,715.74801 318.89763,53.14961 0,0 0,0 0,0"
id="path4865"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:none;stroke:#ff0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 354.3307,768.89762 318.89764,-53.14961 0,0 0,0"
id="path4865-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
d="m 88.582677,201.96848 c 17.716533,-88.58268 141.732283,-88.58268 159.448823,0"
id="path4914"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
d="m 283.46456,201.96848 c 17.71654,-88.58268 141.73229,-88.58268 159.44883,0"
id="path4914-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
d="m 88.582677,201.96848 c 35.433073,-212.598421 513.779523,-212.598423 549.212603,0"
id="path4914-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0;marker-end:url(#Arrow2Lend-0)"
d="m 265.74802,201.96848 c 35.43308,-159.448817 301.18111,-159.448817 336.61418,0"
id="path4914-8-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2,4;stroke-dashoffset:0"
d="m 318.89764,361.4173 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
id="path5443"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="414.18863"
y="362.88965"
id="text5445"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5447"
x="414.18863"
y="362.88965" /></text>
<g
id="g5586">
<rect
rx="0"
ry="17.716547"
y="325.98422"
x="318.89764"
height="70.866142"
width="194.8819"
id="rect5453"
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<text
sodipodi:linespacing="125%"
id="text5455"
y="354.7933"
x="418.09937"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
xml:space="preserve"><tspan
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="354.7933"
x="418.09937"
id="tspan5457"
sodipodi:role="line">#sc-1 detects that #fl-1</tspan><tspan
id="tspan5459"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="377.2933"
x="418.09937"
sodipodi:role="line">interrupts the drawing</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 673.22836,450.00001 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
id="path5443-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<g
id="g5531"
transform="translate(-88.582663,106.29923)">
<rect
rx="0"
ry="17.716547"
y="414.56689"
x="673.22833"
height="70.866142"
width="194.8819"
id="rect5453-7"
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<text
sodipodi:linespacing="125%"
id="text5455-9"
y="443.37598"
x="770.91229"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
xml:space="preserve"><tspan
id="tspan5517"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="443.37598"
x="770.91229"
sodipodi:role="line">#fl-1 is drawn as</tspan><tspan
id="tspan5521"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="465.87598"
x="770.91229"
sodipodi:role="line">an interruption</tspan></text>
</g>
<flowRoot
xml:space="preserve"
id="flowRoot5523"
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Nimbus Mono L;font-style:normal;font-weight:normal;font-size:20px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Nimbus Mono L;font-stretch:normal;font-variant:normal"><flowRegion
id="flowRegion5525"><rect
id="rect5527"
width="230.37166"
height="100.60158"
x="662.54175"
y="400.53543" /></flowRegion><flowPara
id="flowPara5529"></flowPara></flowRoot> <g
id="g5699">
<rect
rx="0"
ry="17.716547"
y="502.68695"
x="318.90851"
height="70.866142"
width="194.8819"
id="rect5453-70"
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<text
sodipodi:linespacing="125%"
id="text5455-7"
y="531.49603"
x="418.11023"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
xml:space="preserve"><tspan
id="tspan5459-7"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="531.49603"
x="418.11023"
sodipodi:role="line">drawing of #sc-1</tspan><tspan
id="tspan5584"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="553.99603"
x="418.11023"
sodipodi:role="line">is continued</tspan></text>
</g>
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:3.00000191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4643-9"
width="35.433094"
height="70.866158"
x="425.19681"
y="591.73224" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 283.46457,591.73226 141.73227,10e-6"
id="path3890-29-5-2-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
d="m 425.19685,662.59841 -141.73228,-10e-6"
id="path3890-29-5-2-0-0-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 673.22835,751.18108 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
id="path5443-8-2"
inkscape:connector-curvature="0" />
<g
id="g5713"
transform="translate(-88.571786,105.83661)">
<path
inkscape:connector-curvature="0"
id="path5443-8"
d="m 726.37796,786.61415 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" />
<rect
rx="0"
ry="17.716547"
y="715.74799"
x="673.22833"
height="70.866142"
width="194.8819"
id="rect5453-70-2"
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<text
sodipodi:linespacing="125%"
id="text5455-7-4"
y="744.55707"
x="772.43005"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
xml:space="preserve"><tspan
id="tspan5707"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="744.55707"
x="772.43005"
sodipodi:role="line">no need anymore</tspan><tspan
id="tspan5711"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="767.05707"
x="772.43005"
sodipodi:role="line">to draw #fl-1</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 318.89764,538.58266 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
id="path5443-9-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 673.22835,449.99998 0,0"
id="path5443-9-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2,4;stroke-dashoffset:0"
d="m 673.22835,520.86612 c 0,-70.86614 0,-70.86614 0,-70.86614"
id="path5738"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 673.22835,822.04722 c 0,-70.86614 0,-70.86614 0,-70.86614"
id="path5738-7"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,121 @@
/** \page dw-interrupted-drawing Interrupted drawing
Describing the problem
======================
Without interrupting drawing (which is described below), a widget can
define the order in which its parts (background, non-widget content,
child widgets, etc.) are drawn, but it must be drawn as a whole. There
are situations when this is not possible.
Consider the following simple HTML document:
<head>
<style>
#sc-1 { position: relative; z-index: 1; background: #ffe0e0; }
#fl-1 { float: right; background: #b0ffb0; }
#sc-2 { position: relative; z-index: 1; background: #f0f0ff; }
</style>
</head>
<body>
<div id="sc-1">
<div id="fl-1">
Float, line 1/3<br/>
Float, line 2/3<br/>
Float, line 3/3
</div>
Stacking Context 1
<div id="sc-2">Stacking Context 2</div>
</div>
</body>
The rendering will look like this:
\image html dw-interrupted-drawing-1.png
Note the missing "Float, line 2/3" of element #fl-1, which is covered
by element #sc-2.
As described in \ref dw-out-of-flow, it has to be distinguished
between the *container* hierarchy (equivalent to the hierarchy of
dw::core::Widget.) and the the *generator* hierarchy. In the following
diagram, the former is represented by solid lines, the latter by
dotted lines:
\dot
digraph G {
node [shape=rect, fontname=Helvetica, fontsize=10];
edge [arrowhead="vee"];
"#sc-1" [fillcolor="#ffe0e0", style="filled"];
"#fl-1" [fillcolor="#b0ffb0", style="filled"];
"#sc-2" [fillcolor="#f0f0ff", style="filled"];
"body" -> "#sc-1";
"body" -> "#fl-1";
{ rank=same; "#sc-1" -> "#fl-1" [style=dotted]; }
"#sc-1" -> "#sc-2";
}
\enddot
The drawing order of the four elements (represented by widgets) is:
- body,
- #sc-1,
- #fl-1,
- #sc-2.
Since
1. #sc-2 is a child of #sc-1, but
2. #fl-1 is a child of the body, and
3. a widget can only draw its descendants (not necessary children,
but drawing siblings is not allowed),
#sc-1 cannot be drawn as a whole; instead drawing is **interrupted**
by #fl-1. This means:
1. the background and text of #sc-1 is drawn;
2. drawing of #sc-1 is **interrupted** by #fl-1 (see below for details),
3. drawing of #sc-1 is **continued**, by drawing #sc-2.
The exact control flow is described in this sequence diagram:
\image html dw-interrupted-drawing-2.png
When is drawing interrupted?
============================
A widget out of flow is regarded as part of the stacking context (see
\ref dw-stacking-context) of its *generator* (in the example above:
#fl-1 is part of the stacking context stablished by #sc-1, not the one
established by body). For this reason, a widget out of flow must, in
some cases, drawn while the *gerator* is drawn, as an
interruption. The exact rule:
A widget out of flow must be drawn as an interruption (while the
*generator* is drawn) if the stacking context of the generator (to
which this widget belongs) is in front of the stacking context of the
container (the parent widget).
See dw::oof::OOFAwareWidget::doesWidgetOOFInterruptDrawing.
How does interruption of drawing work?
======================================
When a widget detects that an other widget should be drawn as
interruption (see above), it calls dw::core::Widget::drawInterruption,
which
1. draws the widget within another "context" (area and reference
widget); for this the original drawing area
(dw::core::DrawingContext::getToplevelArea) is used.
2. Using dw::core::DrawingContext::addWidgetDrawnAsInterruption, and
checking later with
dw::core::DrawingContext::hasWidgetBeenDrawnAsInterruption prevents
these widgets from being drawn twice.
*/

256
devdoc/dw-layout-views.doc Normal file
View File

@ -0,0 +1,256 @@
/** \page dw-layout-views Layout and Views
Rendering of Dw is done in a way resembling the model-view pattern, at
least formally. Actually, the counterpart of the model, the layout
(dw::core::Layout), does a bit more than a typical model, namely the
layouting (delegated to the widget tree, see \ref dw-layout-widgets),
and the view does a bit less than a typical view, i.e. only the actual
drawing.
Additionally, there is a structure representing common properties of
the platform. A platform is typically related to the underlying UI
toolkit, but other uses may be thought of.
This design helps to achieve two important goals:
<ul>
<li> Abstraction of the actual drawing, by different implementations
of dw::core::View.
<li> It makes portability simple.
</ul>
<h2>Viewports</h2>
Although the design implies that the usage of viewports should be
fully transparent to the layout module, this cannot be fully achieved,
for the following reasons:
<ul>
<li> Some features, which are used on the level of dw::core::Widget,
e.g. anchors, refer to scrolling positions.
<li> Size hints (see \ref dw-layout-widgets) depend on the viewport
sizes, e.g. when the user changes the window size, and so also
the size of a viewport, the text within should be rewrapped.
</ul>
Therefore, dw::core::Layout keeps track of the viewport size, the
viewport position, and even the thickness of the scrollbars, they are
relevant, see below for more details.
If a viewport is not used, however, the size is not defined.
Whether a given dw::core::View implementation is a viewport or not, is
defined by the return value of dw::core::View::usesViewport. If this
method returns false, the following methods need not to be implemented
at all:
<ul>
<li> dw::core::View::getHScrollbarThickness,
<li> dw::core::View::getVScrollbarThickness,
<li> dw::core::View::scrollTo, and
<li> dw::core::View::setViewportSize.
</ul>
<h3>Scrolling Positions</h3>
The scrolling position is the canvas position at the upper left corner
of the viewport. Views using viewports must
<ol>
<li> change this value on request (dw::core::View::scrollTo), and
<li> tell other changes to the layout, e.g. caused by user events
(dw::core::Layout::scrollPosChanged).
</ol>
Applications of scrolling positions (anchors, test search etc.) are
handled by the layout, in a way fully transparent to the view.
<h3>Scrollbars</h3>
A feature of the viewport size model are scrollbars. There may be a
vertical scrollbar and a horizontal scrollbar, displaying the
relationship between canvas and viewport height or width,
respectively. If they are not needed, they are hidden, to save screen
space.
Since scrollbars decrease the usable space of a view, dw::core::Layout
must know how much space they take. The view returns, via
dw::core::View::getHScrollbarThickness and
dw::core::View::getVScrollbarThickness, how thick they will be, when
visible.
Viewport sizes, which denote the size of the viewport widgets, include
scrollbar thicknesses. When referring to the viewport \em excluding
the scrollbars space, we will call it "usable viewport size", this is
the area, which is used to display the canvas.
<h2>Drawing</h2>
A view must implement several drawing methods, which work on the whole
canvas. If it is necessary to convert them (e.g. into
dw::fltk::FltkViewport), this is done in a way fully transparent to
dw::core::Widget and dw::core::Layout, instead, this is done by the
view implementation.
There exist following situations:
<ul>
<li> A view gets an expose event: It will delegate this to the
layout (dw::core::Layout::draw), which will then pass it to the
widgets (dw::core::Widget::draw), with the view as a parameter.
Eventually, the widgets will call drawing methods of the view.
<li> A widget requests a redraw: In this case, the widget will
delegate this to the layout (dw::core::Layout::queueDraw), which
delegates it to the view (dw::core::View::queueDraw).
Typically, the view will queue these requests for efficiency.
<li> A widget requests a resize: This case is described below, in short,
dw::core::View::queueDrawTotal is called for the view.
</ul>
If the draw method of a widget is implemented in a way that it may
draw outside of the widget's allocation, it should draw into a
<i>clipping view.</i> A clipping view is a view related to the actual
view, which guarantees that the parts drawn outside are discarded. At
the end, the clipping view is merged into the actual view. Sample
code:
\code
void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
{
// 1. Create a clipping view.
dw::core::View clipView =
view->getClippingView (allocation.x, allocation.y,
allocation.width, getHeight ());
// 2. Draw into clip_view
clipView->doSomeDrawing (...);
// 3. Draw the children, they receive the clipping view as argument.
dw::core::Rectangle *childArea
for (<all relevant children>) {
if (child->intersects (area, &childArea))
child->draw (clipView, childArea);
}
// 4. Merge
view->mergeClippingView (clipView);
}
\endcode
A drawing process is always embedded into calls of
dw::core::View::startDrawing and dw::core::View::finishDrawing. An
implementation of this may e.g. use backing pixmaps, to prevent
flickering.
<h2>Sizes</h2>
In the simplest case, the view does not have any influence on
the canvas size, so it is told about changes of the
canvas size by a call to dw::core::View::setCanvasSize. This happens
in the following situations:
<ul>
<li> dw::core::Layout::addWidget,
<li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
and
<li> dw::core::Layout::queueResize (called by
dw::core::Widget::queueResize, when a widget itself requests a size
change).
</ul>
<h3>Viewports</h3>
There are two cases where the viewport size changes:
<ul>
<li> As an reaction on a user event, e.g. when the user changes the
window size. In this case, the view delegates this
change to the layout, by calling
dw::core::Layout::viewportSizeChanged.
<li> The viewport size may also depend on the visibility of UI
widgets, which depend on the world size, e.g scrollbars,
generally called "viewport markers". This is described in a separate
section.
</ul>
After the creation of the layout, the viewport size is undefined. When
a view is attached to a layout, and this view can already specify
its viewport size, it may call
dw::core::Layout::viewportSizeChanged within the implementation of
dw::core::Layout::setLayout. If not, it may do this as soon as the
viewport size is known.
Generally, the scrollbars have to be considered. If e.g. an HTML page
is rather small, it looks like this:
\image html dw-viewport-without-scrollbar.png
If some more data is retrieved, so that the height exceeds the
viewport size, the text has to be rewrapped, since the available width
gets smaller, due to the vertical scrollbar:
\image html dw-viewport-with-scrollbar.png
Notice the different line breaks.
This means circular dependencies between these different sizes:
<ol>
<li> Whether the scrollbars are visible or not, determines the
usable space of the viewport.
<li> From the usable space of the viewport, the size hints for the
toplevel are calculated.
<li> The size hints for the toplevel widgets may have an effect on its
size, which is actually the canvas size.
<li> The canvas size determines the visibility of the scrollbarss.
</ol>
To make an implementation simpler, we simplify the model:
<ol>
<li> For the calls to dw::core::Widget::setAscent and
dw::core::Widget::setDescent, we will always exclude the
horizontal scrollbar thickness (i.e. assume the horizontal
scrollbar is used, although the visibility is determined correctly).
<li> For the calls to dw::core::Widget::setWidth, we will calculate
the usable viewport width, but with the general assumption, that
the widget generally gets higher.
</ol>
This results in the following rules:
<ol>
<li> Send always (when it changes) dw::core::Layout::viewportHeight
minus the maximal value of dw::core::View::getHScrollbarThickness as
argument to dw::core::Widget::setAscent, and 0 as argument to
dw::core::Widget::setDescent.
<li> There is a flag, dw::core::Layout::canvasHeightGreater, which is set
to false in the following cases:
<ul>
<li> dw::core::Layout::addWidget,
<li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
and
<li> dw::core::Layout::viewportSizeChanged.
</ul>
Whenever the canvas size is calculated (dw::core::Layout::resizeIdle),
and dw::core::Layout::canvasHeightGreater is false, a test is made,
whether the widget has in the meantime grown that high, that the second
argument should be set to true (i.e. the vertical scrollbar gets visible).
As soon as and dw::core::Layout::canvasHeightGreater is true, no such test
is done anymore.
</ol>
*/

View File

@ -0,0 +1,267 @@
/** \page dw-layout-widgets Layout and Widgets
Both, the layouting and the drawing is delegated to a tree of
widgets. A widget represents a given part of the document, e.g. a text
block, a table, or an image. Widgets may be nested, so layouting and
drawing may be delegated by one widget to its child widgets.
Where to define the borders of a widget, whether to combine different
widgets to one, or to split one widget into multiple ones, should be
considered based on different concerns:
<ul>
<li> First, there are some restrictions of Dw:
<ul>
<li> The allocation (this is the space a widget allocates at
a time) of a dillo widget is always rectangular, and
<li> the allocation of a child widget must be a within the allocation
of the parent widget.
</ul>
<li> Since some widgets are already rather complex, an important goal
is to keep the implementation of the widget simple.
<li> Furthermore, the granularity should not be too fine, because of the
overhead each single widget adds.
</ul>
For CSS, there will be a document tree on top of Dw, this will be
flexible enough when mapping the document structure on the widget
structure, so you should not have the document structure in mind.
<h2>Sizes</h2>
\ref dw-widget-sizes
<h2>Styles</h2>
Each widget is assigned a style, see dw::core::style for more
information.
<h2>Iterators</h2>
Widgets must implement dw::core::Widget::iterator. There are several
common iterators:
<ul>
<li>dw::core::EmptyIterator, and
<li>dw::core::TextIterator.
</ul>
Both hide the constructor, use the \em create method.
These simple iterators only iterate through one widget, it does not
have to iterate recursively through child widgets. Instead, the type
dw::core::Content::WIDGET is returned, and the next call of
dw::core::Iterator::next will return the piece of contents \em after
(not within) this child widget.
This makes implementation much simpler, for recursive iteration, there
is dw::core::DeepIterator.
<h2>Anchors and Scrolling</h2>
\todo This section is not implemented yet, after the implementation,
the documentation should be reviewed.
Here is a description, what is to be done for a widget
implementation. How to jump to anchors, set scrolling positions
etc. is described in \ref dw-usage.
<h3>Anchors</h3>
Anchors are position markers, which are identified by a name, which is
unique in the widget tree. The widget must care about anchors in three
different situations:
<ol>
<li> Adding an anchor is inititiated by a specific widget method, e.g.
dw::Textblock::addAnchor. Here, dw::core::Widget::addAnchor must be
called,
<li> Whenever the position of an anchor is changed,
dw::core::Widget::changeAnchor is called (typically, this is done
in the implementation of dw::core::Widget::sizeAllocateImpl).
<li> When a widget is destroyed, the anchor must be removed, by calling
dw::core::Widget::removeAnchor.
</ol>
All these methods are delegated to dw::core::Layout, which manages
anchors centrally. If the anchor in question has been set to jump to,
the viewport position is automatically adjusted, see \ref
dw-usage.
<h2>Drawing</h2>
In two cases, a widget has to be drawn:
<ol>
<li> as a reaction on an expose event,
<li> if the widget content has changed and it needs to be redrawn.
</ol>
In both cases, drawing is done by the implementation of
dw::core::Widget::draw, which draws into the view.
Each view provides some primitive methods for drawing, most should be
obvious. Note that the views do not know anything about dillo
widgets, and so coordinates have to be passed as canvas coordinates.
A widget may only draw in its own allocation. If this cannot be
achieved, a <i>clipping view</i> can be used, this is described in
\ref dw-layout-views. Generally, drawing should then look like:
\code
void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
{
// 1. Create a clipping view.
dw::core::View clipView =
view->getClippingView (allocation.x, allocation.y,
allocation.width, getHeight ());
// 2. Draw into clip_view
clipView->doSomeDrawing (...);
// 3. Draw the children, they receive the clipping view as argument.
dw::core::Rectangle *childArea
for (<all relevant children>) {
if (child->intersects (area, &childArea))
child->draw (clipView, childArea);
}
// 4. Merge
view->mergeClippingView (clipView);
}
\endcode
Clipping views are expensive, so they should be avoided when possible.
The second argument to dw::core::Widget::draw is the region, which has
to be drawn. This may (but needs not) be used for optimization.
If a widget contains child widgets, it must explicitly draw these
children (see also code example above). For this, there is the useful
method dw::core::Widget::intersects, which returns, which area of the
child must be drawn.
<h3>Explicit Redrawing</h3>
If a widget changes its contents, so that it must be redrawn, it must
call dw::core::Widget::queueDrawArea or
dw::core::Widget::queueDraw. The first variant expects a region within
the widget, the second will cause the whole widget to be redrawn. This
will cause an asynchronous call of dw::core::Widget::draw.
If only the size changes, a call to dw::core::Widget::queueResize is
sufficient, this will also queue a complete redraw (see \ref
dw-widget-sizes.)
<h2>Mouse Events</h2>
A widget may process mouse events. The view (\ref dw-layout-views)
passes mouse events to the layout, which then passes them to the
widgets. There are two kinds of mouse events:
<ul>
<li>events returning bool, and
<li>events returning nothing (void).
</ul>
The first group consists of:
<ul>
<li>dw::core::Widget::buttonPressImpl,
<li>dw::core::Widget::buttonReleaseImpl, and
<li>dw::core::Widget::motionNotifyImpl.
</ul>
For these events, a widget returns a boolean value, which denotes,
whether the widget has processed this event (true) or not (false). In
the latter case, the event is delegated according to the following
rules:
<ol>
<li> First, this event is passed to the bottom-most widget, in which
allocation the mouse position is in.
<li> If the widget does not process this event (returning false), it is
passed to the parent, and so on.
<li> The final result (whether \em any widget has processed this event) is
returned to the view.
</ol>
The view may return this to the UI toolkit, which then interprets this
in a similar way (whether the viewport, a UI widget, has processed
this event).
These events return nothing:
<ul>
<li>dw::core::Widget::enterNotifyImpl and
<li>dw::core::Widget::leaveNotifyImpl.
</ul>
since they are bound to a widget.
When processing mouse events, the layout always deals with two
widgets: the widget, the mouse pointer was in, when the previous mouse
event was processed, (below called the "old widget") and the widget,
in which the mouse pointer is now ("new widget").
The following paths are calculated:
<ol>
<li> the path from the old widget to the nearest common ancestor of the old
and the new widget, and
<li> the path from this ancestor to the new widget.
</ol>
For the widgets along these paths, dw::core::Widget::enterNotifyImpl
and dw::core::Widget::leaveNotifyImpl are called.
<h3>Signals</h3>
If a caller outside of the widget is interested in these events, he
can connect a dw::core::Layout::LinkReceiver. For those events with a
boolean return value, the results of the signal emission is regarded,
i.e. the delegation of an event to the parent of the widget can be
stopped by a signal receiver returning true, even if the widget method
returns false.
First, the widget method is called, then (in any case) the signal is
emitted.
<h3>Selection</h3>
If your widget has selectable contents, it should delegate the events
to dw::core::SelectionState (dw::core::Layout::selectionState).
<h2>Miscellaneous</h2>
<h3>Cursors</h3>
Each widget has a cursor, which is set by
dw::core::Widget::setCursor. If a cursor is assigned to a part of a
widget, this widget must process mouse events, and call
dw::core::Widget::setCursor explicitly.
(This will change, cursors should become part of
dw::core::style::Style.)
<h3>Background</h3>
Backgrounds are part of styles
(dw::core::style::Style::backgroundColor). If a widget assigns
background colors to parts of a widget (as dw::Table does for rows),
it must call dw::core::Widget::setBgColor for the children inside this
part.
*/

470
devdoc/dw-line-breaking.doc Normal file
View File

@ -0,0 +1,470 @@
/** \page dw-line-breaking Changes in Line-Breaking and Hyphenation
<div style="border: 2px solid #ffff00; margin-bottom: 0.5em;
padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b>
Should be incorporated into dw::Textblock.</div>
Introduction
============
For the implementation of hyphenation in dillo, not only a
hyphenation algorithm was implemented, but also, the line breaking was
changed to a simple optimization per line. Aside from the improvement
by this change per se, an important aspect is the introduction of
"penalties". Before this change, dillo put all words into a line which
fitted into it; now, a "badness" is calculated for a possible
breakpoint, and the best breakpoint, i.&nbsp;e. the breakpoint with the
smallest value for "badness", is chosen. This can be simply refined
to define "good" and "bad" breakpoints by assigning a "penalty"; the
best breakpoint is then the one with the smallest value of "badness +
penalty". Details can be found below.
Example: Normal spaces have a penalty of 0, while hyphenation points
get a penalty of, say, 1, since hyphenation is generally considered as
a bit "ugly" and should rather be avoided. Consider a situation where
the word "dillo" could be hyphenated, with the following badnesses:
- before "dillo": 0.6;
- between "dil-" and "lo": 0.2;
- after "dillo": 0.5.
Since the penalty is added, the last value is the best one, so "dillo"
is put at the end of the line, without hyphenation.
Under other circumstances (e.&nbsp;g. narrower lines), the values
might be different:
- before "dillo": infinite;
- between "dil-" and "lo": 0.3;
- after "dillo": 1.5.
In this case, even the addition of the penalty makes hyphenation the
best choice.
Literature
==========
Breaking Paragraphs Into Lines
------------------------------
Although dillo does not (yet?) implement the algorithm T<sub>E</sub>X
uses for line breaking, this document shares much of the notation used
by the article *Breaking Paragraphs Into Lines* by Donald E. Knuth and
Michael F. Plass; originally published in: Software -- Practice and
Experience **11** (1981), 1119-1184; reprinted in: *Digital
Typography* by Donalt E. Knuth, CSLI Publications 1999. Anyway an
interesting reading.
Hyphenation
-----------
Dillo uses the algorithm by Frank Liang, which is described in his
doctoral dissertation found at http://www.tug.org/docs/liang/. There
is also a description in chapter H ("Hyphenation") of *The
T<sub>E</sub>Xbook* by Donald E. Knuth, Addison-Wesley 1984.
Pattern files can be found at
http://www.ctan.org/tex-archive/language/hyphenation.
Overview of Changes
===================
Starting with this change, dw/textblock.cc has been split up; anything
related to line breaking has been moved into
dw/textblock_linebreaking.cc. This will also be done for other aspects
like floats. (Better, however, would be a clean logical split.)
An important change relates to the way that lines are added: before,
dillo would add a line as soon as a new word for this line was
added. Now, a line is added not before the *last* word of this line is
known. This has two important implications:
- Some values in dw::Textblock::Line, which represented values
accumulated within the line, could be removed, since now, these
values can be calculated simply in a loop.
- On the other hand, this means that some words may not belong to any
line. For this reason, in some cases (e.&nbsp;g. in
dw::Textblock::sizeRequestImpl) dw::Textblock::showMissingLines is
called, which creates temporary lines, which must, under other
circumstances, be removed again by
dw::Textblock::removeTemporaryLines, since they have been created
based on limited information, and so possibly in a wrong way. (See
below for details.)
When a word can be hyphenated, an instance of dw::Textblock::Word is
used for each part. Notice that soft hyphens are evaluated
immediately, but automatic hyphenation is done in a lazy way (details
below), so the number of instances may change. There are some new
attributes: only when dw::Textblock::Word::canBeHyphenated is set to
*true*, automatic hyphenation is allowed; it is set to false when soft
hyphens are used for a word, and (of course) by the automatic
hyphenation itself. Furthermore, dw::Textblock::Word::hyphenWidth
(more details in the comment there) has to be included when
calculating line widths.
Some values should be configurable: dw::Textblock::HYPHEN_BREAK, the
penalty for hyphens. Also dw::Textblock::Word::stretchability,
dw::Textblock::Word::shrinkability, which are both set in
dw::Textblock::addSpace.
Criteria for Line-Breaking
==========================
Before these changes to line breaking, a word (represented by
dw::Textblock::Word) had the following attributes related to
line-breaking:
- the width of the word itself, represented by
dw::Textblock::Word::size;
- the width of the space following the word, represented by
dw::Textblock::Word::origSpace.
In a more mathematical notation, the \f$i\f$th word has a width
\f$w_i\f$ and a space \f$s_i\f$.
A break was possible, when there was a space between the two words,
and the first possible break was chosen.
With hyphenation, the criteria are refined. Hyphenation should only be
used when otherwise line breaking results in very large spaces. We
define:
- the badness \f$\beta\f$ of a line, which is greater the more the
spaces between the words differ from the ideal space;
- a penalty \f$p\f$ for any possible break point.
The goal is to find those break points, where \f$\beta + p\f$ is
minimal.
Examples for the penalty \f$p\f$:
- 0 for normal line breaks (between words);
- \f$\infty\f$ to prevent a line break at all costs;
- \f$-\infty\f$ to force a line
- a positive, but finite, value for hyphenation points.
So we need the following values:
- \f$w_i\f$ (the width of the word \f$i\f$ itself);
- \f$s_i\f$ (the width of the space following the word \f$i\f$);
- the stretchability \f$y_i\f$, a value denoting how much the space
after word\f$i\f$ can be stretched (typically \f${1\over 2} s_i\f$
for justified text; otherwise 0, since the spaces are not
stretched);
- the shrinkability \f$y_i\f$, a value denoting how much the space
after word\f$i\f$ can be shrunken (typically \f${1\over 3} s_i\f$
for justified text; otherwise 0, since the spaces are not shrunk);
- the penalty \f$p_i\f$, if the line is broken after word \f$i\f$;
- a width \f$h_i\f$, which is added, when the line is broken after
word \f$i\f$.
\f$h_i\f$ is the width of the hyphen, if the word \f$i\f$ is a part of
the hyphenated word (except the last part); otherwise 0.
Let \f$l\f$ be the (ideal) width (length) of the line, which is
e.&nbsp;at the top given by the browser window width. Furthermore, all words
from \f$a\f$ to \f$b\f$ are added to the line. \f$a\f$ is fixed: we do
not modify the previous lines anymore; but our task is to find a
suitable \f$b\f$.
We define:
\f[W_a^b = \sum_{i=a}^{b} w_i + \sum_{i=a}^{b-1} s_i + h_b\f]
\f[Y_a^b = {Y_0}_a^b + \sum_{i=a}^{b-1} y_i\f]
\f[Z_a^b = {Z_0}_a^b + \sum_{i=a}^{b-1} z_i\f]
\f$W_a^b\f$ is the total width, \f$Y_a^b\f$ the total stretchability,
and \f$Z_a^b\f$ the total shrinkability. \f${Y_0}_a^b\f$ and
\f${Z_0}_a^b\f$ are the stretchability and shrinkability defined per
line, and applied at the borders; they are 0 for justified text, but
\f${Y_0}_a^b\f$ has a positive value otherwise, see below for details.
Furthermore the *adjustment ratio* \f$r_a^b\f$:
- in the ideal case that \f$W_a^b = l\f$: \f$r_a^b = 0\f$;
- if \f$W_a^b < l\f$: \f$r_a^b = (l - W_a^b) / Y_a^b\f$
(\f$r_a^b < 0\f$ in this case);
- if \f$W_a^b > l\f$: \f$r_a^b = (l - W_a^b) / Z_a^b\f$
(\f$r_a^b < 0\f$ in this case).
The badness \f$\beta_a^b\f$ is defined as follows:
- if \f$r_a^b\f$ is undefined or \f$r_a^b < -1\f$: \f$\beta_a^b = \infty\f$;
- otherwise: \f$\beta_a^b = |r_a^b|^3\f$
The goal is to find the value of \f$b\f$ where \f$\beta_a^b + p_b\f$
is minimal. (\f$a\f$ is given, since we do not modify the previous
lines.)
After a couple of words, it is not predictable whether this minimum
has already been reached. There are two cases where this is possible
for a given \f$b'\f$:
- \f$\beta_{b'}^a = \infty\f$ (line gets too tight):
\f$a \le b < b'\f$, the minimum has to be searched between these two
values;
- \f$p_{b'} = -\infty\f$ (forced line break):
\f$a \le b \le b'\f$ (there may be another minimum of
\f$\beta_a^b\f$ before; note the \f$\le\f$ instead of \f$<\f$).
This leads to a problem that the last words of a text block are not
displayed this way, since they do not fulfill these rules for being
added to a line. For this reason, there are "temporary" lines already
described above.
(Note that the actual calculation differs from this description, since
integer arithmetic is used for performance, which make the actual
code more complicated. See dw::Textblock::BadnessAndPenalty for
details.)
Ragged Borders
--------------
For other than justified text (left-, right-aligned and centered), the
spaces between the words are not shrunk or stretched (so \f$y_i\f$
and \f$z_i\f$ are 0), but additional space is added to the left or
right border or to both. For this reason, an additional stretchability
\f${Y_0}_a^b\f$ is added (see definition above). Since this space at
the border is 0 in an ideal case (\f$W_a^b = l\f$), it cannot be
shrunken, so \f${Z_0}_a^b\f$ is 0.
This is not equivalent to the calculation of the total stretchability
as done for justified text, since in this case, the stretchability
depends on the number of words: consider the typical case that all
spaces and stretchabilities are equal (\f$y_a = y_{a + 1} = \ldots =
y_b\f$). With \f$n\f$ words, the total strechability would be \f$n
\cdot y_a\f$, so increase with an increasing number of words
(\f$y_a\f$ is constant). This is correct for justified text, but for
other alignments, where only one space (or two, for centered text) is
changed, this would mean that a line with many narrow words is more
stretchable than a line with few wide words.
It is obvious that left-aligned text can be handled in the same way as
right-aligned text. [... Centered text? ...]
The default value for the stretchability is the line height without
the space between the lines (more precisely: the maximum of all word
heights). The exact value not so important when comparing different
possible values for the badness \f$\beta_a^b\f$, when \f${Y_0}_a^b\f$
is nearly constant for different \f$b\f$ (which is the case for the
actual value), but it is important for the comparison with penalties,
which are constant. To be considered is also that for non-justified
text, hyphenation is differently (less) desirable; this effect can be
achieved by enlarging the stretchability, which will lead to a smaller
badness, and so make hyphenation less likely. The user can configure
the stretchability by changing the preference value
*stretchability_factor* (default: 1.0).
(Comparison to T<sub>E</sub>X: Knuth and Plass describe a method for
ragged borders, which is effectively the same as described here (Knuth
1999, pp.&nbsp;93--94). The value for the stretchability of the line
is slightly less, 1&nbsp;em (ibid., see also p.&nbsp;72 for the
definition of the units). However, this article suggests a value for
the hyphenation penalty, which is ten times larger than the value for
justified text; this would suggest a larger value for
*stretchability_factor*.)
Hyphens
=======
Words (instances of dw::Textblock::Word), which are actually part of a
hyphenated word, are always drawn as a whole, not separately. This
way, the underlying platform is able to apply kerning, ligatures, etc.
Calculating the width of such words causes some problems, since it is
not required that the width of text "AB" is identical to the width of
"A" plus the width of "B", just for the reasons mentioned above. It
gets even a bit more complicated, since it is required that a word
part (instance of dw::Textblock::Word) has always the same length,
independent of whether hyphenation is applied or not. Furthermore, the
hyphen length is fixed for a word; for practical reasons, it is always
the width of a hyphen, in the given font.
For calculating the widths, consider a word of four syllables:
A-B-C-D. There are 3 hyphenation points, and so 2<sup>3</sup> = 8
possible ways of hyphenation: ABCD, ABC-D, AB-CD, AB-C-D, A-BCD,
A-BC-D, A-B-CD, A-B-C-D. (Some of them, like the last one, are only
probable for very narrow lines.)
Let w(A), w(B), w(C), w(D) be the word widths (part of
dw::Textblock::Word::size), which have to be calculated, and l be a
shorthand for dw::core::Platform::textWidth. Without considering
this problem, the calculation would be simple: w(A) = l(A)
etc. However, it gets a bit more complicated. Since all
non-hyphenations are drawn as a whole, the following conditions can be
concluded:
- from drawing "ABCD" (not hyphenated at all): w(A) + w(B) + w(C) +
w(D) = l(ABCD);
- from drawing "BCD", when hyphenated as "A-BCD" ("A-" is not
considered here): w(B) + w(C) + w(D) = l(BCD);
- likewise, from drawing "CD" (cases "AB-CD" and "A-B-CD"): w(C) +
w(D) = l(CD);
- finally, for the cases "ABC-D", "AB-C-D", "A-BC-D", and "A-B-C-D":
w(D) = l(D).
So, the calculation is simple:
- w(D) = l(D)
- w(C) = l(CD) - w(D)
- w(B) = l(BCD) - (w(C) + w(D))
- w(A) = l(ABCD) - (w(B) + w(C) + w(D))
For calculation the hyphen widths, the exact conditions would be
over-determined, even when the possibility for individual hyphen
widths (instead of simply the text width of a hyphen character) would
be used. However, a simple approach of fixed hyphen widths will have
near-perfect results, so this is kept simple.
Automatic Hyphenation
=====================
When soft hyphens are used, words are immediately divided into
different parts, and so different instances of
dw::Textblock::Word. Automatic hyphenation (using Liang's algorithm)
is, however, not applied always, but only when possibly needed, after
calculating a line without hyphenation:
- When the line is tight, the last word of the line is hyphenated;
possibly this will result in a line with less parts of this word,
and so a less tight line.
- When the line is loose, and there is another word (for the next
line) available, this word is hyphenated; possibly, some parts of
this word are taken into this line, making it less loose.
After this, the line is re-calculated.
A problem arises when the textblock is rewrapped, e.&nbsp;g. when the
user changes the window width. In this case, some new instances of
dw::Textblock::Word must be inserted into the word list,
dw::Textblock::words. This word list is implemented as an array, which
is dynamically increased; a simple approach would involve moving all
of the <i>n</i> elements after position <i>i</i>, so
<i>n</i>&nbsp;-&nbsp;<i>i</i> steps are necessary. This would not be a
problem, since O(n) steps are necessary; however, this will be
necessary again for the next hyphenated word (at the end of a
following line), and so on, so that
(<i>n</i>&nbsp;-&nbsp;<i>i</i><sub>1</sub>) +
(<i>n</i>&nbsp;-&nbsp;<i>i</i><sub>2</sub>) + ..., with
<i>i</i><sub>1</sub>&nbsp;&lt;&nbsp;<i>i</i><sub>2</sub>&nbsp;&lt;&nbsp;...,
which results in O(n<sup>2</sup>) steps. For this reason, the word
list is managed by the class lout::misc::NotSoSimpleVector, which uses
a trick (a second array) to deal with exactly this problem. See there
for more details.
Tests
=====
There are test HTML files in the <i>test</i> directory. Also, there is
a program testing automatic hyphenation, <i>test/liang</i>, which can
be easily extended.
Bugs and Things Needing Improvement
===================================
High Priority
-------------
None.
Medium Priority
---------------
None.
Low Priority
------------
**Mark the end of a paragraph:** Should dw::core::Content::BREAK still
be used? Currently, this is redundant to
dw::Textblock::BadnessAndPenalty.
Solved (Must Be Documented)
---------------------------
These have been solved recently and should be documented above.
*Bugs in hyphenation:* There seem to be problems when breaking words
containing hyphens already. Example: "Abtei-Stadt", which is divided
into "Abtei-" and "Stadt", resulting possibly in
&quot;Abtei-<span></span>-[new line]Stadt&quot;. See also below under
"Medium Priority", on how to deal with hyphens and dashes.
**Solution:** See next.
*Break hyphens and dashes:* The following rules seem to be relevant:
- In English, an em-dash is used with no spaces around. Breaking
before and after the dash should be possible, perhaps with a
penalty > 0. (In German, an en-dash (Halbgeviert) with spaces around
is used instead.)
- After a hyphen, which is part of a compound word, a break should be
possible. As described above ("Abtei-Stadt"), this collides with
hyphenation.
Where to implement? In the same dynamic, lazy way like hyphenation? As
part of hyphenation?
Notice that Liang's algorithm may behave different regarding hyphens:
"Abtei-Stadt" is (using the patterns from CTAN) divided into "Abtei-"
and "Stadt", but "Nordrhein-Westfalen" is divided into "Nord",
"rhein-West", "fa", "len": the part containing the hyphen
("rhein-West") is untouched. (Sorry for the German words; if you have
got English examples, send them me.)
**Solution for both:** This has been implemented in
dw::Textblock::addText, in a similar way to soft hyphens. Liang's
algorithm now only operates on the parts: "Abtei" and "Stadt";
"Nordrhein" and "Westfalen".
*Hyphens in adjacent lines:* It should be simple to assign a larger
penalty for hyphens, when the line before is already hyphenated. This
way, hyphens in adjacent lines are penalized further.
**Solved:** There are always two penalties. Must be documented in
detail.
*Incorrect calculation of extremes:* The minimal width of a text block
(as part of the width extremes, which are mainly used for tables) is
defined by everything between two possible breaks. A possible break
may also be a hyphenation point; however, hyphenation points are
calculated in a lazy way, when the lines are broken, and not when
extremes are calculated. So, it is a matter of chance whether the
calculation of the minimal width will take the two parts "dil-" and
"lo" into account (when "dillo" has already been hyphenated), or only
one part, "dillo" (when "dillo" has not yet been hyphenated),
resulting possibly in a different value for the minimal width.
Possible strategies to deal with this problem:
- Ignore. The implications should be minimal.
- Any solution will make it necessary to hyphenate at least some
words when calculating extremes. Since the minimal widths of all
words are used to calculate the minimal width of the text block, the
simplest approach will hyphenate all words. This would, of course,
eliminate the performance gains of the current lazy approach.
- The latter approach could be optimized in some ways. Examples: (i)
If a word is already narrower than the current accumulated value for
the minimal width, it makes no sense to hyphenate it. (ii) In other
cases, heuristics may be used to estimate the number of syllables,
the width of the widest of them etc.
**Solved:** Hyphenated parts of a word are not considered anymore for
width extremes, but only whole words. This is also one reason for the
introduction of the paragraphs list.
**Also:**
- Configuration of penalties.
*/

59
devdoc/dw-map.doc Normal file
View File

@ -0,0 +1,59 @@
/** \page dw-map Dillo Widget Documentation Map
This maps includes special documentations as well as longer comments
in the sources. Arrows denote references between the documents.
\dot
digraph G {
rankdir=LR;
node [shape=record, fontname=Helvetica, fontsize=8];
fontname=Helvetica; fontsize=8;
dw_overview [label="Dillo Widget Overview", URL="\ref dw-overview"];
dw_usage [label="Dillo Widget Usage", URL="\ref dw-usage"];
dw_layout_views [label="Layout and Views", URL="\ref dw-layout-views"];
dw_layout_widgets [label="Layout and Widgets",
URL="\ref dw-layout-widgets"];
dw_widget_sizes [label="Sizes of Dillo Widgets",
URL="\ref dw-widget-sizes"];
dw_changes [label="Changes to the GTK+-based Release Version",
URL="\ref dw-changes"];
dw_images_and_backgrounds [label="Images and Backgrounds in Dw",
URL="\ref dw-images-and-backgrounds"];
dw_Image [label="dw::Image", URL="\ref dw::Image"];
dw_core_Imgbuf [label="dw::core::Imgbuf", URL="\ref dw::core::Imgbuf"];
dw_core_SelectionState [label="dw::core::SelectionState",
URL="\ref dw::core::SelectionState"];
dw_core_style [label="dw::core::style", URL="\ref dw::core::style"];
dw_Table [label="dw::Table", URL="\ref dw::Table"];
dw_Textblock [label="dw::Textblock", URL="\ref dw::Textblock"];
dw_core_ui [label="dw::core::ui", URL="\ref dw::core::ui"];
dw_overview -> dw_changes;
dw_overview -> dw_usage;
dw_overview -> dw_core_style;
dw_overview -> dw_core_ui;
dw_overview -> dw_images_and_backgrounds;
dw_overview -> dw_layout_widgets;
dw_overview -> dw_widget_sizes;
dw_overview -> dw_layout_views;
dw_usage -> dw_Table;
dw_usage -> dw_Textblock;
dw_usage -> dw_core_style;
dw_usage -> dw_core_ui;
dw_usage -> dw_images_and_backgrounds;
dw_layout_widgets -> dw_widget_sizes;
dw_layout_widgets -> dw_core_SelectionState;
dw_widget_sizes -> dw_Table;
dw_widget_sizes -> dw_Textblock;
dw_images_and_backgrounds -> dw_core_Imgbuf;
dw_images_and_backgrounds -> dw_Image;
dw_core_style -> dw_Textblock;
}
\enddot
*/

View File

@ -0,0 +1,97 @@
/** \page dw-miscellaneous Miscellaneous Notes on Dw
This is a barely sorted list of issues which I consider noteworthy,
but have yet to be moved to other parts of the documentation (which is
partly to be created).
General
=======
Widget allocation outside of parent allocation
----------------------------------------------
A widget allocation outside of the allocation of the parent is
allowed, but the part outside is not visible.
Which widgets may be drawn?
---------------------------
All drawing starts with the toplevel widget
(cf. dw::core::Widget::queueDrawArea, dw::core::Layout::queueDraw, and
dw::core::Layout::expose), and a widget has to draw its children, in a
way consistent with their stacking order.
There are two exceptions:
1. Direct descendants, which are not children, may be drawn, if the
parent can distinguish them and so omit drawing them a second
time. See dw::core::StackingContextMgr and \ref dw-stacking-context.
Parents should not draw children in flow for which
dw::core::StackingContextMgr::handledByStackingContextMgr returns
true.
2. Interrupted drawing: via dw::core::Widget::drawInterruption; see
\ref dw-interrupted-drawing.
Similar rules apply to handling mouse events
(dw::core::Widget::getWidgetAtPoint).
Interrupted drawing
-------------------
→ \ref dw-interrupted-drawing.
Similar rules apply to handling mouse events
(dw::core::Widget::getWidgetAtPoint).
Extra space
-----------
Should dw::core::Widget::calcExtraSpace be called from
dw::core::Widget::getExtremes?
Widgets out of flow
===================
dw::Textblock::getGeneratorWidth
--------------------------------
Re-evaluate dw::Textblock::getGeneratorWidth (especially the limitation on
instances of dw::Textblock) for positioned elements. Is this method really only
called for floats?
Widget sizes
============
Relation between dw::core::Widget::markSizeChange and dw::core::Widget::queueResize
------------------------------------------------------------------------------------
The following comment should be re-evaluated. Implementing incremental resizing
for dw::oof::OOFFloatsMgr seems to fix the performance problems, but this should
be examined further.
<div style="text-decoration: line-through; color: #606060">
dw::oof::OOFFloatsMgr::markSizeChange (called from
dw::Textblock::markSizeChange) calls dw::oof::OOFAwareWidget::updateReference,
whose implementation dw::Textblock::updateReference calls
dw::core::Widget::queueResize. This may result in a recursion,
- for which it is not clear whether it ends in all cases (although endless cases
are not known yet), and
- which nevertheless may take much time in cases where the number of calls
increases exponentially with the depth of the widget tree.
The recent change in dw::Textblock::updateReference (`if (lines->size () > 0)`)
seems to fix the performance problem, but the issue should be examined further,
albeit with lower priority. Especially, it has to be determined, under which
conditions it is allowed to (directly or indirectly) call
dw::core::Widget::queueResize within an implementation of
dw::core::Widget::markSizeChange.
Here is the original test case (slow, when `if (lines->size () > 0)` is removed
again):
(for i in $(seq 1 20); do echo '<div style="float:left"><div></div>'; done) > tmp.html; src/dillo tmp.html
You may change the number (20), or examine smaller cases with
<a href="http://home.gna.org/rtfl/">RTFL</a>:
(for i in $(seq 1 3); do echo '<div style="float:left"><div></div>'; done) > tmp.html; src/dillo tmp.html | rtfl-objview -OM -A "*" -a resize -a resize.oofm
</div>
*/

View File

@ -0,0 +1,151 @@
/** \page dw-out-of-flow-floats Handling Elements Out Of Flow: Floats
TODO: Much missing.
(Historical) Note: Floats make use of \ref dw-size-request-pos, which
reduces the complexity of a previous design.
Sorting floats
==============
Floats are sorted, to make binary search possible, in these lists:
- for each generator: dw::OutOfFlowMgr::TBInfo::leftFloatsGB and
dw::OutOfFlowMgr::TBInfo::rightFloatsGB;
- for the container: dw::OutOfFlowMgr::leftFloatsCB and
dw::OutOfFlowMgr::rightFloatsCB.
The other two lists, dw::OutOfFlowMgr::leftFloatsAll and
dw::OutOfFlowMgr::rightFloatsAll are not sorted at all.
New floats are always added to the end of either list; this order is
called *generation order*. See also above: *GB lists and CB lists*.
On the other hand, there are different sorting criteria, implemented
by different comparators, so that different kinds of keys may be used
for searching. These sorting criteria are equivalent to the generation
order.
dw::OutOfFlowMgr::Float::CompareSideSpanningIndex compares
*sideSpanningIndex* (used to compare floats to those on the respective
other side); if you look at the definition
(dw::OutOfFlowMgr::addWidgetOOF) it becomes clear that this order is
equivalent to the generation order.
dw::OutOfFlowMgr::Float::CompareGBAndExtIndex compares *externalIndex*
for floats with same generators, otherwise: (i) if one generator (T1)
is a direct ancestor of the other generator (T2), the child of T1,
which is an ancestor of, or identical to, T2 is compared to the float
generated by T1, using *externalIndex*, as in this example:
T1 -+-> child --> ... -> T2 -> Float
`-> Float
Otherwise, the two blocks are compared, according to their position in
dw::OutOfFlowMgr::tbInfos:
common ancestor -+-> ... --> T1 -> Float
`-> ... --> T2 -> Float
This is equivalent to the generation order, as long it is ensured that
*externalIndex* reflects the generation order within a generating
block, for both floats and child blocks.
dw::OutOfFlowMgr::Float::ComparePosition ...
Miscellaneous notes
===================
Handling collisions
-------------------
The CSS specification allows two strategies to deal with colliding
floats: placing the second float beside or below the first one. Many
other browsers implement the first approach, while dillo implements
the second one, which may cause problems when the author assumes the
first. Example: the "tabs" at the top of every page at Wikipedia
("Article", "Talk", ...).
Float containers in flow
------------------------
Consider the following HTML snippet:
<body>
<img src="....jpg" style="float:right">
<p style="overflow:hidden">Text</p>
</body>
Interestingly, dillo shows "Text" always *below* the image, even if
there is enough space left of it. An investigation shows that the
paragraph (&lt;p&gt;) is regarded as own floats container (because of
*overflow:hidden*), so the floats container above (&lt;body&gt;)
regards this block as widget which must be fit between the floats
(dw::Textblock::mustBorderBeRegarded &gt;
dw::Textblock::getWidgetRegardingBorderForLine). However, since a
textblock in flow always covers (at least) the whole available width,
which is defined *without* considering floats, the space left of the
float will always be to narrow, so that the paragraph is moved below
the float, by inserting an empty line before.
When searching for a solution, several difficulties show up:
1. The available width, which is used for the width of the textblock,
is defined independent of floats. Aside from problems when changing
this definition, a dependence on floats would be difficult to
implement, since *sizeRequest* is independent of a position. (See
also \ref dw-out-of-flow.)
2. I must admit that I do not remember the exact rationale and the
test case behind adding the exception in
dw::Textblock::getWidgetRegardingBorderForLine (see above), but
simply removing this exception will result in a possible
overlapping of floats from both containers, since no collisions are
tested for.
3. On the other hand, mixing the float containers (interaction of two
or more instances of dw::oof::OOFFloatsMgr), e.&nbsp;g. for
collision tests, would become too complex and possibly result in
performance problems.
Instead, this approach is focussed:
- Goal: the paragraph is narrowed so it fits, *as a whole*, between
the floats.
- The approach is to remove the exception in
dw::Textblock::getWidgetRegardingBorderForLine. A textblock, which
is a float container in flow (as this paragraph), is returned by
this method and so dw::Textblock::mustBorderBeRegarded returns
*true*. This will put this paragraph again at the correct position.
- To avoid overlappings, the linebreaking width of this paragraph
(which is also used for positioning of floats) is the available
width, minus the maximal float width on either side. (This is an
approach similar to the one dw::Ruler will use soon). Most likely,
some new methods will have to be added to calculate this.
- For paragraphs like this, dw::Textblock::borderChanged must rewrap
all lines; *y* is irrelevant in this case.
- Since the textblock will tend to become taller when getting
narrower, and so possibly cover more (wider) floats, and so become
narrower again etc., there may be multiple solutions for calculating
the size. Generally, a smaller height (and so larger width) is
preferred.
- There remains a problem: what if a word is too large? Should a
textblock of this kind then reard the floats in detail, to insert
empty lines when needed?
<b>Real-world cases:</b> *Overflow:hidden* is set for headings in
Wikipedia, and so this case occurs when there is a float (thumb image)
before a heading. See e.&nbsp;g.
<a href="http://de.wikipedia.org/wiki/Emmerich_am_Rhein#Ans.C3.A4ssige_Unternehmen">this page</a>
and scroll a bit up; the company logos should be right of this section.
<b>Priority:</b> Since this is not a regression, compared to not
supporting floats at all, a fix is not urgent for a new release.
Resizing
--------
Has the case that a float changes its position to be regarded? Probably yes, but
cases where no other mechanisms come into play are rather unlikely.
<b>Priority:</b> If this plays a role, this means a regression compared to not
supporting floats at all.
*/

View File

@ -0,0 +1,66 @@
/** \page dw-out-of-flow-positioned Handling Elements Out Of Flow: Positioned Elements
<div style="border: 2px solid #ffff00; margin: 1em 0; padding: 0.5em
1em; background-color: #ffffe0">Positioned elements have been
deactivated, during the development of \ref dw-size-request-pos, to
focus on floats. See dw::core::IMPL_POS in file dw/core.hh.</div>
Miscellaneous notes
===================
General
-------
(See also *relative positions* below.)
What about negative positions?
dw::oof::OutOfFlowMgr::tellPosition1 and
dw::oof::OutOfFlowMgr::tellPosition2 could be combined again, and
called in the respective circumstance, depending on
dw::oof::OutOfFlowMgr::mayAffectBordersAtAll.
Relative positions
------------------
**General Overview:** At the original position, a space as large as
the positioned element is left. This is implemented by assigning a
size to the widget *reference*. For this there are two new methods:
dw::oof::OutOfFlowMgr::calcWidgetRefSize and
dw::oof::OOFAwareWidget::widgetRefSizeChanged.
**Bug:** Since the size of a relatively positioned element should be
calculated as if it was in flow, the available width should be
delegated to the *generator*; however, since
dw::oof::OOFPosRelMgr::dealingWithSizeOfChild returns *false* in all
cases, it is delegated to the *container*. **Idea for fix:**
dw::oof::OOFPosRelMgr::dealingWithSizeOfChild should return *false* if
and only if the generator of the child is the container (to prevent an
endless recursion). In other cases,
dw::oof::OOFPosRelMgr::getAvailWidthOfChild and
dw::oof::OOFPosRelMgr::getAvailHeightOfChild should directly call the
respective methods of the *generator*, which must be made public then.
**Performance:** In many cases, the first call of
dw::oof::OOFPosRelMgr::sizeAllocateEnd will queue again the resize
idle, since some elements are not considered in
dw::oof::OOFPosRelMgr::getSize. One case could be removed: if a
positioned element has *left* = *top* = 0, and its total size (the
requisition) is equal to the space left at the original position, the
size of the widget *reference*
(dw::oof::OOFAwareWidget::getRequisitionWithoutOOF, see
dw::oof::OOFPosRelMgr::calcWidgetRefSize).
**Documentation:** Describe why the latter is not covered by
dw::oof::OOFPositionedMgr::doChildrenExceedContainer. (Is this really
the case?)
**Open:** Stacking order? Furthermore: a relatively positioned element
does not always constitute a containing block (see CSS specification).
Fixed positions
---------------
Currently, fixedly positioned elements are positioned relative to the
canvas, not to the viewport. For a complete implementation, see \ref
dw-fixed-positions.
*/

102
devdoc/dw-out-of-flow.doc Normal file
View File

@ -0,0 +1,102 @@
/** \page dw-out-of-flow Handling Elements Out Of Flow
Introduction
============
This texts deals with both floats and positioned elements, which have
in common that there is a distinction between generating block and
containing block (we are here using the same notation as in the
CSS&nbsp;2 specification). Consider this snippet (regarding floats):
<ul>
<li>Some text.</li>
<li>
<div style="float:right; width: 50%">Some longer text, so
that the effect described in this passage can be
demonstrated.
</div>
Some more and longer text.</li>
<li>Final text. Plus some more to demonstrate how text flows
around the float on the right side.</li>
</ul>
which may be rendered like this
\image html dw-floats-01.png
The float (the DIV section, yellow in the image) is defined
("generated") within the list item (green), so, in CSS 2 terms, the
list item is the generating block of the float. However, as the image
shows, the float is not contained by the list item, but another block,
several levels above (not shown here). In terms of ::dw, this means
that the dw::Textblock representing the float cannot be a child of the
dw::Textblock representing the generating block, the list item, since
the allocation of a child widget must be within the allocation of the
parent widget. Instead, to each dw::Textblock, another dw::Textblock
is assigned as the containing box.
(Notice also that other text blocks must regard floats to calculate
their borders, and so their size. In this example, the following list
item (gray) must consider the position of the float. This is discussed
in detail in the following section, _Implementation overview_.)
Implementation overview
=======================
- dw::oof::OOFAwareWidget is the base for both generators and containers.
dw::Textblock and dw::Table are based on this, but dw::Table is only relevant
for positioned elements, so much simpler than dw::Textblock.
- For a given textblock (or, generally, generator), the float container is not
necessary the same container as for positioned elements. For this reason,
dw::oof::OOFAwareWidget::oofContainer is an array.
- The containers are set in dw::oof::OOFAwareWidget::notifySetAsTopLevel and
dw::oof::OOFAwareWidget::notifySetParent.
- If a widget is out of flow, the generating widget keeps a reference with the
type dw::core::Content::WIDGET_OOF_REF, while the containing block refers to
it as dw::core::Content::WIDGET_OOF_CONT. For widgets within flow,
dw::core::Content::WIDGET_IN_FLOW is used. Notice that in the first case,
there are two pieces of content referring to the same widget. An application
of this distinction is iterators. (For selection and searching, the generating
hierarchy is used, which is different from the widget hierarchy.)
Handling widgets out of flow is partly the task of class implementing
dw::oof::OutOfFlowMgr, which is stored by dw::oof::OOFAwareWidget::outOfFlowMgr,
but only for containing blocks. Generating blocks should refer
to *container->outOfFlowMgr[...]*.
Overview of the dw::oof::OutOfFlowMgr hierarchy;
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="none", arrowtail="empty", dir="both"];
fontname=Helvetica; fontsize=8;
OutOfFlowMgr [URL="\ref dw::oof::OutOfFlowMgr"; color="#a0a0a0"];
OOFFloatsMgr [URL="\ref dw::oof::OOFFloatsMgr"];
OOFPositionedMgr [URL="\ref dw::oof::OOFPositionedMgr"; color="#a0a0a0"];
OOFPosAbsLikeMgr [URL="\ref dw::oof::OOFPosAbsLikeMgr"; color="#a0a0a0"];
OOFPosAbsMgr [URL="\ref dw::oof::OOFPosAbsMgr"];
OOFPosFixedMgr [URL="\ref dw::oof::OOFPosFixedMgr"];
OOFPosRelMgr [URL="\ref dw::oof::OOFPosRelMgr"];
OutOfFlowMgr -> OOFFloatsMgr;
OutOfFlowMgr -> OOFPositionedMgr;
OOFPositionedMgr -> OOFPosAbsLikeMgr;
OOFPosAbsLikeMgr -> OOFPosAbsMgr;
OOFPosAbsLikeMgr -> OOFPosFixedMgr;
OOFPositionedMgr -> OOFPosRelMgr;
}
\enddot
</div>
Further details
===============
- \ref dw-out-of-flow-floats
- \ref dw-out-of-flow-positioned
*/

158
devdoc/dw-overview.doc Normal file
View File

@ -0,0 +1,158 @@
/** \page dw-overview Dillo Widget Overview
Note: If you are already familiar with the Gtk+-based version of Dw,
read \ref dw-changes.
The module Dw (Dillo Widget) is responsible for the low-level rendering of
all resources, e.g. images, plain text, and HTML pages (this is the
most complex type). Dw is \em not responsible for parsing HTML, or
decoding image data. Furthermore, the document tree, which is planned
for CSS, is neither a part of Dw, instead, it is a new module on top
of Dw.
The rendering, as done by Dw, is split into two phases:
<ul>
<li> the \em layouting, this means calculating the exact positions of
words, lines, etc. (in pixel position), and
<li> the \em drawing, i.e. making the result of the layouting visible
on the screen.
</ul>
The result of the layouting allocates an area, which is called
\em canvas.
<h2>Structure</h2>
The whole Dw module can be split into the following parts:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="open", fontname=Helvetica, fontsize=10,
labelfontname=Helvetica, labelfontsize=10,
color="#404040", labelfontcolor="#000080"];
subgraph cluster_core {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="Platform independent core";
Layout [URL="\ref dw::core::Layout"];
Platform [URL="\ref dw::core::Platform", color="#ff8080"];
View [URL="\ref dw::core::View", color="#ff8080"];
Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
}
subgraph cluster_fltk {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="FLTK specific part (as an\nexample for the platform specific\n\
implementations)";
subgraph cluster_fltkcore {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="FLTK core";
FltkPlatform [URL="\ref dw::fltk::FltkPlatform"];
FltkView [URL="\ref dw::fltk::FltkView", color="#ff8080"];
}
FltkViewport [URL="\ref dw::fltk::FltkViewport"];
FltkPreview [URL="\ref dw::fltk::FltkPreview"];
}
subgraph cluster_widgets {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="Platform independent widgets";
Textblock [URL="\ref dw::Textblock"];
AlignedTextblock [URL="\ref dw::AlignedTextblock", color="#a0a0a0"];
Table [URL="\ref dw::Table"];
Image [URL="\ref dw::Image"];
etc1 [label="..."];
etc2 [label="..."];
}
Layout -> Platform [headlabel="1", taillabel="1"];
Layout -> View [headlabel="*", taillabel="1"];
Layout -> Widget [headlabel="1", taillabel="1", label="topLevel"];
Widget -> Widget [headlabel="*", taillabel="1", label="children"];
Widget -> Textblock [arrowhead="none", arrowtail="empty", dir="both"];
Widget -> Table [arrowhead="none", arrowtail="empty", dir="both"];
Widget -> Image [arrowhead="none", arrowtail="empty", dir="both"];
Widget -> etc1 [arrowhead="none", arrowtail="empty", dir="both"];
Textblock -> AlignedTextblock [arrowhead="none", arrowtail="empty",
dir="both"];
AlignedTextblock -> etc2 [arrowhead="none", arrowtail="empty", dir="both"];
Platform -> FltkPlatform [arrowhead="none", arrowtail="empty", dir="both",
style="dashed"];
FltkPlatform -> FltkView [headlabel="*", taillabel="1"];
View -> FltkView [arrowhead="none", arrowtail="empty", dir="both"];
FltkView -> FltkViewport [arrowhead="none", arrowtail="empty", dir="both",
style="dashed"];
FltkView -> FltkPreview [arrowhead="none", arrowtail="empty", dir="both",
style="dashed"];
}
\enddot
<center>[\ref uml-legend "legend"]</center>
\em Platform means in most cases the underlying UI toolkit
(e.g. FLTK). A layout is bound to a specific platform, but multiple
platforms may be handled in one program.
A short overview:
<ul>
<li> dw::core::Layout is the central class, it manages the widgets and the
view, and provides delegation methods for the platform.
<li> The layouting is done by a tree of widgets (details are described in
\ref dw-layout-widgets), also the drawing, which is finally delegated
to the view.
<li> The view (implementation of dw::core::View) provides primitive methods
for drawing, but also have an influence on
the canvas size (via size hints). See \ref dw-layout-views for details.
<li> Some platform dependencies are handled by implementations
of dw::core::Platform.
</ul>
<h3>Header Files</h3>
The structures mentioned above can be found in the following header
files:
<ul>
<li> Anything from the Dw core in core.hh. Do not include the single files.
<li> The single widgets can be found in the respective header files, e.g.
image.hh for dw::Image.
<li> The core of the FLTK implementation is defined in fltkcore.hh. This
includes dw::fltk::FltkPlatform, dw::fltk::FltkView, but not the concrete
view implementations.
<li> The views can be found in single header files, e.g fltkviewport.hh for
dw::fltk::FltkViewport.
</ul>
<h2>Further Documentations</h2>
A complete map can be found at \ref dw-map.
<ul>
<li> For learning, how to use Dw, read \ref dw-usage and related documents,
dw::core::style, dw::core::ui and \ref dw-images-and-backgrounds.
<li> Advanced topics are described in \ref dw-layout-widgets,
\ref dw-widget-sizes and \ref dw-layout-views.
</ul>
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,35 @@
<p style="margin-bottom: 5em"><i>This is the source of the image
<a href="dw-size-request-pos-01.png">dw-size-request-pos-01.png</a>.</i></p>
<table lang="en">
<td style="width: 220px">
<p>
<div style="float:right">
<img src="xhttps://www.gnu.org/graphics/heckert_gnu.small.png">
<p style="text-align: center"><i>A GNU</i></p>
</div>
Short.
</p>
<p>A longer paragraph, into which a float extends, which has been
defined in the previous paragraph. </p>
</td>
<td style="vertical-align: middle">
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://upload.wikimedia.org/wikipedia/commons/8/80/CorrespMtl5.png" style="width: 80px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td style="width: 220px">
<p>
<div style="float:right">
<img src="https://www.gnu.org/graphics/heckert_gnu.small.png">
<p style="text-align: center"><i>A GNU</i></p>
</div>
Short.
</p>
<p>A longer paragraph, into which a float extends, which has been
defined in the previous paragraph. </p>
</td>
</table>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,172 @@
/** \page dw-size-request-pos Size requisitions depending on positions
<div style="border: 2px solid #ffff00; margin: 1em 0;
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
sizes" is currently divided into three documents: \ref
dw-widget-sizes, \ref dw-grows, and **Size requisitions depending on
positions** (this document). Furthermore, there are some notes in
\ref dw-miscellaneous.</div>
Motivation
==========
As described in \ref dw-out-of-flow (*The sizeRequest/sizeAllocate
problem*), the principle that the size of a widget depends only on the
sizes of its children causes some problems with floats; the current
solution is a frequent correction by calling
dw::core::Widget::queueResize. In this document, an alternative
approach is presented.
<div style="border: 2px solid #ffff00; margin: 1em 0; padding: 0.5em 1em;
background-color: #ffffe0">This approach works very well for floats, but not
for positioned elements, which means that calling
dw::core::Widget::queueResize is still needed for the latter. On the other
hand, dw::oof::OOFFloatsMgr (which is much more complex than
dw::oof::OOFPositionedMgr) can be simplified quite much.</div>
General Idea
============
A widget size may depend on the position relative to an ancestor
widget. If a widget wants to get the size of a child widget, it should:
1. call the new methods dw::core::Widget::numSizeRequestReferences and
dw::core::Widget::sizeRequestReference, which return all widgets
relative to which the child's position must be calculated;
2. call dw::core::Widget::sizeRequest with the positions relative to
these widgets.
<div style="border: 2px solid #ffff00; margin: 1em 0;
padding: 0.5em 1em; background-color: #ffffe0">It is not sufficient
to work with *absolute* positions, since there may be an
interruption passing the positions so that absolute positions are
often not known.</div>
All positions passed to dw::core::Widget::sizeRequest must constitute
the position at which this child will be allocated.
There are situations where the parent widget is unable to determine
these positions before the size is known. An example: a textblock
widget cannot determine the positions of an inline widget (like an
image, or an inline block) before the line is finished; on the other
hand, finishing the line depends on knowing the sizes of the inline
widgets.
This may result in a conflict, when the size of an inline widget
depends on positions. Generally, the only widget whose size depends on
positions is dw::Textblock (the size will depend on the positions
within its oof container, see \ref dw-out-of-flow), so this conflict
occurs with inline blocks.
This conflict is handled in different ways:
1. Fortunately, this case is irrelevat for floats: an inline block
constitutes its own floats container, so that there is no dependence
on a position within another widget.
2. For positioned elements, this case is relevant, since an inline
block is in most cases not a container for positioned elements. In
this case, a generator will call the methods
dw::oof::OutOfFlowMgr::tellIncompletePosition1 and
dw::oof::OutOfFlowMgr::tellIncompletePosition2, instead of
dw::oof::OutOfFlowMgr::tellPosition and
dw::oof::OutOfFlowMgr::tellPosition2, respectively. (Since this
case is irrelevant for floats,
dw::oof::OOFFloatsMgr::tellIncompletePosition1 and
dw::oof::OOFFloatsMgr::tellIncompletePosition2 are not implemented but
simply abort.)
(This is not (yet) considered for borders: borders are only relevant
for floats, but conflicts do not occur for floats.)
Extremes
--------
Extremes may depend on the position in an analogue way, see:
- dw::core::Widget::numGetExtremesReferences,
- dw::core::Widget::getExtremesReference, and
- dw::core::Widget::getExtremes.
Resizing
--------
Currently, the size of a widget has to be recalculated when:
1. it has called dw::core::Widget::queueResize, or
2. the size of a child widget has to be recalculated.
Since for this new approach, the size does not only depend on the size of the
children, the second condition must be modified. See beginning of
dw::core::Widget::sizeRequest.
An implementation may have to consider, this too, especially when implementing
incremental resizing (see \ref dw-widget-sizes); see
dw::Textblock::sizeRequestImpl as an example.
Regarding changes of the position is not sufficient. Consider this example,
where a float size changes as soon as the image is loaded:
\image html dw-size-request-pos-01.png
The second paragraph ("A longer paragraph" ...) stays at the same position, both
absolute and relative to the float container, but has to be rewrapped because of
the float. Instead, this is handled by dw::oof::OutOfFlowMgr::markSizeChange
(and likewise dw::oof::OutOfFlowMgr::markExtremesChange), which is called by the
implementation of `markSizeChange` (or `markExtremesChange`, respectively) of
the OOF container. (See also the end of the comment of dw::oof::OOFAwareWidget.)
Plan
====
1. General design (dw::core::Widget::sizeRequestReference, changes to
dw::core::Widget::sizeRequest). Completed.
2. Implementation for dw::Textblock. Completed.
3. Change interface of dw::oof::OutOfFlowMgr (this affects mostly only
comments). Completed.
Affects methods dw::oof::OutOfFlowMgr::tellPosition1,
dw::oof::OutOfFlowMgr::tellPosition2,
dw::oof::OutOfFlowMgr::getLeftBorder,
dw::oof::OutOfFlowMgr::getRightBorder,
dw::oof::OutOfFlowMgr::hasFloatLeft,
dw::oof::OutOfFlowMgr::hasFloatRight,
dw::oof::OutOfFlowMgr::getLeftFloatHeight, and
dw::oof::OutOfFlowMgr::getRightFloatHeight.
4. Apply step 3 to calls within dw::Textblock. Completed.
<b>Attention:</b> After this step, and before completing the next steps, the
code is inconsistent and so incorrect.
5. Implement step 3 for floats (affects dw::oof::OOFFloatsMgr). **MOSTLY
COMPLETED.**
6. Implement step 3 for positioned elements (affects only
dw::oof::OOFPositionedMgr). **INCOMPLETE.** (But positioned elements are
currently deactivated.)
Issues
======
- Since the signature of dw::core::Widget::sizeRequestImpl changes quite often
during the development of *size requisitions depending on positions*, a
simpler option dw::core::Widget::sizeRequestSimpl has been added. May be
removed again, after the design is stable.
- As an alternative, passing the references may be done in a new method, which
is called *before* dw::core::Widget::sizeRequestImpl. This makes even more
sense, after dw::core::Widget::calcExtraSpace and
dw::core::Widget::calcExtraSpaceImpl have been extended by references.
- There may be inconsistencies for widget styles; see
[revision f797436687fe](http://flpsed.org/hgweb/dillo_grows/rev/f797436687fe)
as an example for a fix. Perhaps a different approach, where breaks are added,
_if `display` has the value `block`_ (or analogue), will work better.
*/

View File

@ -0,0 +1,114 @@
/** \page dw-stacking-context Handling stacking contexts
Stacking Context and dw::core::StackingContextMgr
=================================================
For the definition of stacking contexts, see CSS 2.1 specification,
- <a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
9.9.1: Specifying the stack level: the 'z-index' property</a> and
- <a href="http://www.w3.org/TR/CSS2/zindex.html">appendix E</a>.
A widget establishes a stacking context when it is positioned and its
style value of *z-index* is different from *auto* (see
dw::core::StackingContextMgr::isEstablishingStackingContext). In this
case, it is assigned an instance of dw::core::StackingContextMgr,
which has also access to the widgets establishing the child contexts.
Stacking Order
==============
The stacking order influences
1. the order in which child widgets are drawn (dw::core::Widget::draw),
and
2. the order in which mouse events are dispatched to child widgets
(dw::core::Widget::getWidgetAtPoint).
The first is done from bottom to top, the latter from top to bottom.
I'm here referring to the simplified description in
<a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
9.9.1</a>. The table shows a recommended order for the implementations
of dw::core::Widget::draw and dw::core::Widget::getWidgetAtPoint
(for the latter, read from bottom to top):
<table>
<tr>
<th> CSS specification <th> Drawing <th> Mouse events
<tr>
<td> *1. the background and borders of the element forming the
stacking context.*
<td> dw::core::Widget::drawBox
<td> Nothing necessary.
<tr>
<td> *2. the child stacking contexts with negative stack levels (most
negative first).*
<td> dw::core::StackingContextMgr::drawBottom (when defined)
<td> dw::core::StackingContextMgr::getBottomWidgetAtPoint (when defined)
<tr>
<td> *3. the in-flow, non-inline-level, non-positioned descendants.*
<td rowspan="4"> When (i) widget specific content is drawn, then (ii)
dw::oof::OOFAwareWidget::drawOOF is called, this will
have this effect:
1. all in-flow elements are drawn,
2. floats are drawn and
3. positioned elements with *z-index: auto* are drawn
(latter two done by
dw::oof::OOFAwareWidget::drawOOF, in this order).
This order differs from the specified order, but
since floats and in-flow elements do not overlap,
this difference has no effect.
Drawing in-line elements, floats and positioned
elements with *z-index: auto* and should avoid
duplicate calls: Widgets drawn by
dw::core::StackingContextMgr::drawBottom and by
dw::core::StackingContextMgr::drawTop should be
excluded here. This can be tested with
dw::core::StackingContextMgr::handledByStackingContextMgr.
<td rowspan="4"> Likewise, the implementation should (i) test
dw::oof::OOFAwareWidget::getWidgetOOFAtPoint, and
(ii) search through the children. Also, duplicate
calls should be avoided using
dw::core::StackingContextMgr::handledByStackingContextMgr.
There are already the implementations
dw::core::Widget::getWidgetAtPoint (ignoring
dw::oof::OutOfFlowMgr) and
dw::oof::OOFAwareWidget::getWidgetAtPoint (including
dw::oof::OutOfFlowMgr).
<tr>
<td> *4. the non-positioned floats.*
<tr>
<td> *5. the in-flow, inline-level, non-positioned descendants,
including inline tables and inline blocks.*
<tr>
<td> (What about positioned elements with *z-index: auto*? Seems to be
missing in
<a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
9.9.1</a>, but mentioned in
<a href="http://www.w3.org/TR/CSS2/zindex.html">appendix E</a>,
item 8.
<tr>
<td> *6. the child stacking contexts with stack level 0 and the
positioned descendants with stack level 0.*
<td rowspan="2"> dw::core::StackingContextMgr::drawTop (when defined)
<td rowspan="2"> dw::core::StackingContextMgr::getTopWidgetAtPoint
(when defined)
<tr>
<td> *7. the child stacking contexts with positive stack levels (least
positive first).*
</table>
Note: This is not quite in conformance with the specification: this
description refers to any widget, not only widgets establishing a
stacking context. Does this make a difference?
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

375
devdoc/dw-usage.doc Normal file
View File

@ -0,0 +1,375 @@
/** \page dw-usage Dillo Widget Usage
This document describes the usage of Dw, without going too much into
detail.
<h2>Getting Started</h2>
In this section, a small runnable example is described, based on the
FLTK implementation.
As described in \ref dw-overview, the following objects are needed:
<ul>
<li> dw::core::Layout,
<li> an implementation of dw::core::Platform (we will use
dw::fltk::FltkPlatform),
<li> at least one implementation of dw::core::View (dw::fltk::FltkViewport),
and
<li> some widgets (for this example, only a simple dw::Textblock).
</ul>
First of all, the necessary \#include's:
\code
#include <FL/Fl_Window.H>
#include <FL/Fl.H>
#include "dw/core.hh"
#include "dw/fltkcore.hh"
#include "dw/fltkviewport.hh"
#include "dw/textblock.hh"
\endcode
Everything is put into one function:
\code
int main(int argc, char **argv)
{
\endcode
As the first object, the platform is instantiated:
\code
dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
\endcode
Then, the layout is created, with the platform attached:
\code
dw::core::Layout *layout = new dw::core::Layout (platform);
\endcode
For the view, we first need a FLTK window:
\code
Fl_Window *window = new Fl_Window(200, 300, "Dw Example");
window->begin();
\endcode
After this, we can create a viewport, and attach it to the layout:
\code
dw::fltk::FltkViewport *viewport =
new dw::fltk::FltkViewport (0, 0, 200, 300);
layout->attachView (viewport);
\endcode
Each widget needs a style (dw::core::style::Style, see dw::core::style),
so we construct it here. For this, we need to fill a
dw::core::style::StyleAttrs structure with values, and call
dw::core::style::Style::create (latter is done further below):
\code
dw::core::style::StyleAttrs styleAttrs;
styleAttrs.initValues ();
styleAttrs.margin.setVal (5);
\endcode
dw::core::style::StyleAttrs::initValues sets several default
values. The last line sets a margin of 5 pixels. Next, we need a
font. Fonts are created in a similar way, first, the attributes are
defined:
\code
dw::core::style::FontAttrs fontAttrs;
fontAttrs.name = "Bitstream Charter";
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = dw::core::style::FONT_STYLE_NORMAL;
fontAttrs.letterSpacing = 0;
fontAttrs.fontVariant = dw::core::style::FONT_VARIANT_NORMAL;
\endcode
Now, the font can be created:
\code
styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
\endcode
As the last attributes, the background and foreground colors are
defined, here dw::core::style::Color::createSimple must be called:
\code
styleAttrs.color =
dw::core::style::Color::create (layout, 0x000000);
styleAttrs.backgroundColor =
dw::core::style::Color::create (layout, 0xffffff);
\endcode
Finally, the style for the widget is created:
\code
dw::core::style::Style *widgetStyle =
dw::core::style::Style::create (layout, &styleAttrs);
\endcode
Now, we create a widget, assign a style to it, and set it as the
toplevel widget of the layout:
\code
dw::Textblock *textblock = new dw::Textblock (false);
textblock->setStyle (widgetStyle);
layout->setWidget (textblock);
\endcode
The style is not needed anymore (a reference is added in
dw::core::Widget::setStyle), so it should be unreferred:
\code
widgetStyle->unref();
\endcode
Now, some text should be added to the textblock. For this, we first
need another style. \em styleAttrs can still be used for this. We set
the margin to 0, and the background color to "transparent":
\code
styleAttrs.margin.setVal (0);
styleAttrs.backgroundColor = NULL;
dw::core::style::Style *wordStyle =
dw::core::style::Style::create (layout, &styleAttrs);
\endcode
This loop adds some paragraphs:
\code
for(int i = 1; i <= 10; i++) {
char buf[4];
sprintf(buf, "%d.", i);
char *words[] = { "This", "is", "the", buf, "paragraph.",
"Here", "comes", "some", "more", "text",
"to", "demonstrate", "word", "wrapping.",
NULL };
for(int j = 0; words[j]; j++) {
textblock->addText(strdup(words[j]), wordStyle);
\endcode
Notice the \em strdup, dw::Textblock::addText will feel responsible
for the string, and free the text at the end. (This has been done to
avoid some overhead in the HTML parser.)
The rest is simple, it also includes spaces (which also have styles):
\code
textblock->addSpace(wordStyle);
}
\endcode
Finally, a paragraph break is added, which is 10 pixels high:
\code
textblock->addParbreak(10, wordStyle);
}
\endcode
Again, this style should be unreferred:
\code
wordStyle->unref();
\endcode
After adding text, this method should always be called (for faster
adding large text blocks):
\code
textblock->flush ();
\endcode
Some FLTK stuff to finally show the window:
\code
window->resizable(viewport);
window->show();
int errorCode = Fl::run();
\endcode
For cleaning up, it is sufficient to destroy the layout:
\code
delete layout;
\endcode
And the rest
\code
return errorCode;
}
\endcode
If you compile and start the program, you should see the following:
\image html dw-example-screenshot.png
Try to scroll, or to resize the window, you will see, that everything
is done automatically.
Of course, creating new widgets, adding text to widgets etc. can also
be done while the program is running, i.e. after fltk::run has been
called, within timeouts, idles, I/O functions etc. Notice that Dw is
not thread safe, so that everything should be done within one thread.
With the exception, that you have to call dw::Textblock::flush,
everything gets immediately visible, within reasonable times; Dw has
been optimized for frequent updates.
<h2>List of all Widgets</h2>
These widgets are used within dillo:
<ul>
<li>dw::core::ui::Embed
<li>dw::AlignedTextblock
<li>dw::Bullet
<li>dw::Ruler
<li>dw::Image
<li>dw::ListItem
<li>dw::Table
<li>dw::TableCell
<li>dw::Textblock
</ul>
If you want to create a new widget, refer to \ref dw-layout-widgets.
<h2>List of Views</h2>
There are three dw::core::View implementations for FLTK:
<ul>
<li> dw::fltk::FltkViewport implements a viewport, which is used in the
example above.
<li> dw::fltk::FltkPreview implements a preview window, together with
dw::fltk::FltkPreviewButton, it is possible to have a scaled down
overview of the whole canvas.
<li> dw::fltk::FltkFlatView is a "flat" view, i.e. it does not support
scrolling. It is used for HTML buttons, see
dw::fltk::ui::FltkComplexButtonResource and especially
dw::fltk::ui::FltkComplexButtonResource::createNewWidget for details.
</ul>
More information about views in general can be found in \ref
dw-layout-views.
<h2>Iterators</h2>
For examining generally the contents of widgets, there are iterators
(dw::core::Iterator), created by the method
dw::core::Widget::iterator (see there for more details).
These simple iterators only iterate through one widget, and return
child widgets as dw::core::Content::WIDGET. The next call of
dw::core::Iterator::next will return the piece of contents \em after
(not within) this child widget.
If you want to iterate through the whole widget trees, there are two
possibilities:
<ol>
<li> Use a recursive function. Of course, with this approach, you are
limited by the program flow.
<li> Maintain a stack of iterators, so you can freely pass this stack
around. This is already implemented, as dw::core::DeepIterator.
</ol>
As an example, dw::core::SelectionState represents the selected region
as two instances of dw::core::DeepIterator.
<h2>Finding Text</h2>
See dw::core::Layout::findtextState and dw::core::FindtextState
(details in the latter). There are delegation methods:
<ul>
<li> dw::core::Layout::search and
<li> dw::core::Layout::resetSearch.
</ul>
<h2>Anchors and Scrolling</h2>
In some cases, it is necessary to scroll to a given position, or to
an anchor, programmatically.
<h3>Anchors</h3>
Anchors are defined by widgets, e.g. dw::Textblock defines them, when
dw::Textblock::addAnchor is called. To jump to a specific anchor
within the current widget tree, use dw::core::Layout::setAnchor.
This can be done immediately after assignig a toplevel widget, even
when the anchor has not yet been defined. The layout will remember the
anchor, and jump to the respective position, as soon as possible. Even
if the anchor position changes (e.g., when an anchor is moved
downwards, since some space is needed for an image in the text above),
the position is corrected.
As soon as the user scrolls the viewport, this correction is not done
anymore. If in dillo, the user request a page with an anchor, which is
quite at the bottom of the page, he may be get interested in the text
at the beginning of the page, and so scrolling down. If then, after
the anchor has been read and added to the dw::Textblock, this anchor
would be jumped at, the user would become confused.
The anchor is dismissed, too, when the toplevel widget is removed
again.
\todo Currently, anchors only define vertical positions.
<h3>Scrolling</h3>
To scroll to a given position, use the method
dw::core::Layout::scrollTo. It expects several parameters:
<ul>
<li>a horizontal adjustment parameter, defined by dw::core::HPosition,
<li>a vertical adjustment parameter, defined by dw::core::VPosition, and
<li>a rectangle (\em x, \em y, \em width and \em height) of the region
to be adjusted.
</ul>
If you just want to move the canvas coordinate (\em x, \em y) into the
upper left corner of the viewport, you can call:
\code
dw::core::Layout *layout;
// ...
layout->scrollTo(dw::core::HPOS_LEFT, dw::core::VPOS_TOP, 0, 0, 0, 0);
\endcode
By using dw::core::HPOS_NO_CHANGE or dw::core::VPOS_NO_CHANGE, you can
change only one dimension. dw::core::HPOS_INTO_VIEW and
dw::core::VPOS_INTO_VIEW will cause the viewport to move as much as
necessary, that the region is visible in the viewport (this is
e.g. used for finding text).
<h2>Further Documentations</h2>
<ul>
<li> dw::core::style
<li> dw::core::ui
<li> \ref dw-images-and-backgrounds
</ul>
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

278
devdoc/dw-widget-sizes.doc Normal file
View File

@ -0,0 +1,278 @@
/** \page dw-widget-sizes Sizes of Dillo Widgets
<div style="border: 2px solid #ffff00; margin: 1em 0;
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
sizes" is currently divided into three documents: **Sizes of Dillo
Widgets** (this document), \ref dw-grows, and \ref
dw-size-request-pos. Furthermore, there are some notes in
\ref dw-miscellaneous.</div>
<div style="border: 2px solid #ff4040; margin: 1em 0;
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
Not up to date, see other documents.</div>
Allocation
==========
Each widget has an \em allocation at a given time, this includes
- the position (\em x, \em y) relative to the upper left corner of the
canvas, and
- the size (\em width, \em ascent, \em descent).
The \em canvas is the whole area available for the widgets, in most
cases, only a part is seen in a viewport. The allocation of the
toplevel widget is exactly the allocation of the canvas, i.e.
- the position of the toplevel widget is always (0, 0), and
- the canvas size is defined by the size of the toplevel widget.
The size of a widget is not simply defined by the width and the
height, instead, widgets may have a base line, and so are vertically
divided into an ascender (which height is called \em ascent), and a
descender (which height is called \em descent). The total height is so
the sum of \em ascent and \em descent.
Sizes of zero are allowed. The upper limit for the size of a widget is
defined by the limits of the C++ type \em int.
\image html dw-size-of-widget.png Allocation of a Widget
In the example in the image, the widget has the following allocation:
- \em x = 50
- \em y = 50
- \em width = 150
- \em ascent = 150
- \em descent = 100
The current allocation of a widget is hold in
dw::core::Widget::allocation. It can be set from outside by
calling dw::core::Widget::sizeAllocate. This is a concrete method,
which will call dw::core::Widget::sizeAllocateImpl (see code of
dw::core::Widget::sizeAllocate for details).
For trivial widgets (like dw::Bullet),
dw::core::Widget::sizeAllocateImpl does not need to be
implemented. For more complex widgets, the implementation should call
dw::core::Widget::sizeAllocate (not
dw::core::Widget::sizeAllocateImpl) on all child widgets, with
appropriate child allocations. dw::core::Widget::allocation should not
be changed here, this is already done in
dw::core::Widget::sizeAllocate.
Requisitions
============
A widget may prefer a given size for the allocation. This size, the
\em requisition, should be returned by the method
dw::core::Widget::sizeRequestImpl. In the simplest case, this is
independent of the context, e.g. for an
image. dw::Image::sizeRequestImpl returns the following size:
- If no buffer has yet been assigned (see dw::Image for more details),
the size necessary for the alternative text is returned. If no
alternative text has been set, zero is returned.
- If a buffer has been assigned (by dw::Image::setBuffer), the root
size is returned (i.e. the original size of the image to display).
This is a bit simplified, dw::Image::sizeRequestImpl should also deal
with margins, borders and paddings, see dw::core::style.
From the outside, dw::Image::sizeRequest should be called, which does
a bit of optimization. Notice that in dw::Image::sizeRequestImpl, no
optimization like lazy evaluation is necessary, this is already done
in dw::Image::sizeRequest.
A widget, which has children, will likely call dw::Image::sizeRequest
on its children, to calculate the total requisition.
The caller (this is either the dw::core::Layout, or the parent
widget), may, but also may not consider the requisition. Instead, a
widget must deal with any allocation. (For example, dw::Image scales
the image buffer when allocated at another size.)
Size Hints
==========
<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
Size hints have been removed, see \ref dw-grows.</div>
Width Extremes
==============
dw::Table uses width extremes for fast calculation of column
widths. The structure dw::core::Extremes represents the minimal and
maximal width of a widget, as defined by:
- the minimal width is the smallest width, at which a widget can still
display contents, and
- the maximal width is the largest width, above which increasing the
width- does not make any sense.
Especially the latter is vaguely defined, here are some examples:
- For those widgets, which do not depend on size hints, the minimal
and the maximal width is the inherent width (the one returned by
dw::core::Widget::sizeRequest).
- For a textblock, the minimal width is the width of the widest
(unbreakable) word, the maximal width is the width of the total
paragraph (stretching a paragraph further would only waste space).
Actually, the implementation of dw::Textblock::getExtremesImpl is a
bit more complex.
- dw::Table is an example, where the width extremes are calculated
from the width extremes of the children.
Handling width extremes is similar to handling requisitions, a widget
must implement dw::core::Widget::getExtremesImpl, but a caller will
use dw::core::Widget::getExtremes.
Resizing
========
When the widget changes its size (requisition), it should call
dw::core::Widget::queueResize. The next call of
dw::core::Widget::sizeRequestImpl should then return the new
size. See dw::Image::setBuffer as an example.
Interna are described in the code of dw::core::Widget::queueResize.
<h3>Incremental Resizing</h3>
A widget may calculate its size based on size calculations already
done before. In this case, a widget must exactly know the reasons, why
a call of dw::core::Widget::sizeRequestImpl is necessary. To make use
of this, a widget must implement the following:
1. There is a member dw::core::Widget::parentRef, which is totally
under control of the parent widget (and so sometimes not used at
all). It is necessary to define how parentRef is used by a specific
parent widget, and it has to be set to the correct value whenever
necessary.
2. The widget must implement dw::core::Widget::markSizeChange and
dw::core::Widget::markExtremesChange, these methods are called in
two cases:
1. directly after dw::core::Widget::queueResize, with the
argument ref was passed to dw::core::Widget::queueResize,
and
2. if a child widget has called dw::core::Widget::queueResize,
with the value of the parent_ref member of this child.
This way, a widget can exactly keep track on size changes, and so
implement resizing in a faster way. A good example on how to use this
is dw::Textblock.
Rules for Methods Related to Resizing
=====================================
Which method can be called, when the call of another method is not
finished? These rules are important in two circumstances:
1. To know which method can be called, and, especially, which methods
*must not* be called, within the implementation of
*sizeRequestImpl* (called by *sizeRequest*), *markSizeChange*, and
*markExtremesChange* (the latter two are called by *queueResize*).
2. On the other hand, to make sure that the calls, which are allowed,
are handled correctly, especially in implementations of
*sizeRequestImpl*, *markSizeChange*, *markExtremesChange*
Generally, the rules defined below are, in case of doubt, rather
strict; when changing the rules, loosening is simpler than to tighten
them, since this will make it necessary to review old code for calls
previously allowed but now forbidden.
Short recap:
- *QueueResize* directly calls *markSizeChange* and
*markExtremesChanges*, and queues an idle function for the actual
resizing (dw::core::Layout::resizeIdle). (The idle function is
called some time after *queueResize* is finished.)
- The resize idle function first calls *sizeRequest*, then
*sizeAllocate*, for the toplevel widget.
In the following table, the rules are defined in detail. "Within call
of ..." includes all methods called from the original method: the
first row (*queueResize*) defines also the rules for
*markExtremesChanges* and *markExtremesChanges*, and in the second row
(*sizeAllocate*), even *sizeRequest* has to be considered.
<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
Not up to date: *queueResize* can now be called recursively (so to
speak). See code there.</div>
<table>
<tr>
<th>Within call of ... ↓
<th>... is call allowed of ... ? →
<th>queueResize
<th>sizeAllocate
<th>sizeRequest
<th>getExtremes
<tr>
<th colspan=2>queueResize
<td>No
<td>No<sup>1</sup>
<td>No<sup>1</sup>
<td>No<sup>1</sup>
<tr>
<th colspan=2>sizeAllocate
<td>Yes
<td>Only for children<sup>2</sup>
<td>Yes(?)
<td>Yes(?)
<tr>
<th colspan=2>sizeRequest
<td>Yes<sup>3</sup>
<td>No
<td>Limited<sup>4</sup>
<td>Limited<sup>4</sup>
<tr>
<th colspan=2>getExtremes
<td>Yes<sup>3</sup>
<td>No
<td>Limited<sup>4</sup>
<td>Limited<sup>4</sup>
<tr>
<td colspan=6><sup>1</sup>) Otherwise, since these other methods
may be call *queueResize*, the limitation that *queueResize* must not
call *queueResize* can be violated.
<sup>2</sup>) Could perhaps be loosened as for *sizeRequest* and
*getExtremes*, but there is probably no need.
<sup>3</sup>) Therefore the distinction between *RESIZE_QUEUED* and
*NEEDS_RESIZE*, and *EXTREMES_QUEUED* and *EXTREMES_CHANGED*,
respectively.
<sup>4</sup>) Calls only for children are safe. In other cases, you
take a large responsibility to prevent endless recursions by
(typically indirectly) calling *sizeRequest* / *getExtremes* for
direct ancestors.
</table>
Furthermore, *sizeAllocate* can only be called within a call of
dw::core::Layout::resizeIdleId, so (if you do not touch dw::core) do
not call it outside of *sizeAllocateImpl*. The other methods can be
called outsize; e.&nbsp;g. *sizeRequest* is called in
dw::Textblock::addWidget.
To avoid painful debugging, there are some tests for the cases that
one method call is strictly forbidden while another method is called.
This could be done furthermore:
- The tests could be refined.
- Is it possible to define exacter rules, along with a proof that no
problems (like endless recursion) can occur?
*/

180
devdoc/fltk-problems.doc Normal file
View File

@ -0,0 +1,180 @@
/** \page fltk-problems Problems with FLTK
<h2>dw::fltk::FltkViewport</h2>
Current problems:
<ul>
<li> How should dw::fltk::FltkViewport::cancelQueueDraw be implemented?
<li> If the value of a scrollbar is changed by the program, not the user,
the callback seems not to be called. Can this be assured?
<li> The same for dw::fltk::FltkViewport::layout?
<li> Also, the problems with the widgets seems to work. Also sure?
<li> When drawing, clipping of 32 bit values is not working properly.
<li> The item group within a selection widget (menu) should not be selectable.
</ul>
<h2>dw::fltk::FltkPlatform</h2>
<ul>
<li> There is the problem, that fltk::font always returns a font, the
required one, or a replacements. The latter is not wanted in all
cases, e.g. when several fonts are tested. Perhaps, this could be
solved by searching in the font list. <i>[This was true of fltk2.
What is the state of font handling now with fltk-1.3?]</i>
<li> Distinction between italics and oblique would be nice
(dw::fltk::FltkFont::FltkFont).
</ul>
<h2>dw::fltk::ui::FltkCheckButtonResource</h2>
Groups of Fl_Radio_Button must be added to one Fl_Group, which is
not possible in this context. There are two alternatives:
<ol>
<li>there is a more flexible way to group radio buttons, or
<li>radio buttons are not grouped, instead, grouping (especially
unchecking other buttons) is done by the application.
</ol>
(This is mostly solved.)
<h2>dw::fltk::FltkImgbuf</h2>
Alpha transparency should be best abstracted by FLTK itself. If not,
perhaps different implementations for different window systems could
be used. Then, it is for X necessary to use GCs with clipping masks.
<h2>dw::fltk::ui::ComplexButton</h2>
Unfortunately, FLTK does not provide a button with Fl_Group as parent, so
that children may be added to the button. dw::fltk::ui::ComplexButton does
exactly this, and is, in an ugly way, a modified copy of the FLTK
button.
It would be nice, if this is merged with the standard FLTK
button. Furthermore, setting the type is strange.
If the files do not compile, it may be useful to create a new one from
the FLTK source:
<ol>
<li> Copy Fl_Button.H from FLTK to dw/fltkcomplexbutton.hh and
src/Button.cxx to dw/fltkcomplexbutton.cc.
<li> In both files, rename "Button" to "ComplexButton". Automatic replacing
should work.
<li> Apply the changes below.
</ol>
The following changes should be applied manually.
<h3>Changes in fltkcomplexbutton.hh</h3>
First of all, the \#define's for avoiding multiple includes:
\code
-#ifndef fltk_ComplexButton_h // fltk_Button_h formerly
-#define fltk_ComplexButton_h
+#ifndef __FLTK_COMPLEX_BUTTON_HH__
+#define __FLTK_COMPLEX_BUTTON_HH__
\endcode
at the beginning and
\code
-#endif
+#endif // __FLTK_COMPLEX_BUTTON_HH__
\endcode
at the end. Then, the namespace is changed:
\code
-namespace fltk {
+namespace dw {
+namespace fltk {
+namespace ui {
\endcode
at the beginning and
\code
-}
+} // namespace ui
+} // namespace fltk
+} // namespace dw
\endcode
at the end. Most important, the base class is changed:
\code
-#include "FL/Fl_Widget.H"
+#include <FL/Fl_Group.H>
\endcode
and
\code
-class FL_API ComplexButton : public Fl_Widget {
+class ComplexButton: public Fl_Group
+{
\endcode
Finally, for dw::fltk::ui::ComplexButton::default_style, there is a
namespace conflict:
\code
- static NamedStyle* default_style;
+ static ::fltk::NamedStyle* default_style;
\endcode
<h3>Changes in fltkcomplexbutton.cc</h3>
First, \#include's:
\code
#include <FL/Fl.H>
-#include <FL/ComplexButton.h> // <FL/Fl_Button.H> formerly
#include <FL/Fl_Group.H>
#include <FL/Fl_Window.H>
+
+#include "fltkcomplexbutton.hh"
\endcode
Second, namespaces:
\code
+using namespace dw::fltk::ui;
\endcode
Since the base class is now Fl_Group, the constructor must be changed:
\code
-ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) : Fl_Widget(x,y,w,h,l) {
+ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) :
+ Fl_Group(x,y,w,h,l)
+{
\endcode
Finally, the button must draw its children (end of
dw::fltk::ui::ComplexButton::draw()):
\code
+
+ for (int i = children () - 1; i >= 0; i--)
+ draw_child (*child (i));
}
\endcode
*/

48
devdoc/index.doc Normal file
View File

@ -0,0 +1,48 @@
/** \mainpage
<h2>Overview</h2>
This is a list of documents to start with:
<ul>
<li> \ref lout
<li> \ref dw-overview (map at \ref dw-map)
</ul>
Currently, a document \ref fltk-problems is maintained, ideally, it
will be removed soon.
<h2>Historical</h2>
<h3>Replacements for GTK+ and GLib</h3>
There are several classes etc., which are used for tasks formerly (in the GTK+
version of dillo) achieved by GtkObject (in 1.2.x, this is part of Gtk+) and
GLib. For an overview on all this, take a look at \ref lout.
GtkObject is replaced by the following:
<ul>
<li> lout::object::Object is a common base class for many classes used
dillo. In the namespace lout::object, there are also some more common
classes and interfaces.
<li> A sub class of lout::object::Object is
lout::identity::IdentifiableObject, which allows to determine the
class at run-time (equivalent to GTK_CHECK_CAST in GtkObject).
<li> For signals, there is the namespace lout::signal.
</ul>
Hash tables, linked lists etc. can be found in the lout::container namespace,
several useful macros from GLib have been implemented as inline functions
in the lout::misc namespace.
As an alternative to the macros defined in list.h, there is also a template
class, lout::misc::SimpleVector, which does the same.
<h3>Changes in Dw</h3>
If you have been familiar with Dw before, take a look at \ref dw-changes.
*/

95
devdoc/lout.doc Normal file
View File

@ -0,0 +1,95 @@
/** \page lout Lots of Useful Tools
In the "lout" directory, there are some common base functionality for
C++. Most is described as doxygen comments, this text gives an
overview.
<h2>Common Base Class</h2>
Many classes are derived from lout::object::Object, which defines some
general methods. See there for more information.
For the case, that you need primitive C++ types, there are some
wrappers:
<table>
<tr><th>C++ Type <th>Wrapper Class
<tr><td>void* <td>lout::object::Pointer
<tr><td>specific pointer <td>lout::object::TypedPointer (template class)
<tr><td>int <td>lout::object::Integer
<tr><td>const char* <td>lout::object::ConstString
<tr><td>char* <td>lout::object::String
</table>
<h2>Containers</h2>
In the namespace lout::container, several container classes are defined,
which all deal with instances of lout::object::Object.
<h3>Untyped Containers</h3>
In lout::container::untyped, there are the following containers:
<ul>
<li>lout::container::untyped::Vector, a dynamically increases array,
<li>lout::container::untyped::List, a linked list,
<li>lout::container::untyped::HashTable, a hash table, and
<li>lout::container::untyped::Stack, a stack.
</ul>
All provide specific methods, but since they have a common base class,
lout::container::untyped::Collection, they all provide iterators, by the
method lout::container::untyped::Collection::iterator.
<h3>Typed Containers</h3>
lout::container::typed provides wrappers for the container classes defined
in lout::container::untyped, which are more type safe, by using C++
templates.
<h2>Signals</h2>
For how to connect objects at run-time (to reduce dependencies), take a
look at the lout::signal namespace.
There is also a base class lout::signal::ObservedObject, which implements
signals for deletion.
<h2>Debugging</h2>
In debug.hh, there are some some useful macros for debugging messages,
see the file for mor information.
<h2>Identifying Classes at Runtime</h2>
If the class of an object must be identified at runtime,
lout::identity::IdentifiableObject should be used as the base class,
see there for more details.
<h2>Miscellaneous</h2>
The lout::misc namespace provides several miscellaneous stuff:
<ul>
<li> In some contexts, it is necessary to compare objects
(less/greater), for this, also lout::misc::Comparable must be
implemented. For example., lout::container::untyped::Vector::sort and
lout::container::typed::Vector::sort cast the elements to
lout::misc::Comparable. This can be mixed with lout::object::Object.
<li> lout::misc::SimpleVector, a simple, template based vector class
(not depending on lout::object::Object) (a variant for handling a
special case in an efficient way is lout::misc::NotSoSimpleVector),
<li> lout::misc::StringBuffer, class for fast concatenation of a large number
of strings,
<li> lout::misc::BitSet implements a bitset.
<li> useful (template) functions (lout::misc::min, lout::misc::max), and
<li> some functions useful for runtime checks (lout::misc::assert,
lout::misc::assertNotReached).
</ul>
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,785 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="not-so-simple-container.svg"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/not-so-simple-container.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mend"
style="overflow:visible;">
<path
id="path3998"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
transform="scale(0.4) rotate(180) translate(10,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Send"
style="overflow:visible;">
<path
id="path4004"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
transform="scale(0.2) rotate(180) translate(6,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lend"
style="overflow:visible;">
<path
id="path3992"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
transform="scale(0.8) rotate(180) translate(12.5,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-5"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-5"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-3"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-2"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-2"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-22"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="marker4917"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4919"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="marker4921"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4923"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-9"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-7"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-6"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-6"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-51"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-3"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="226.09436"
inkscape:cy="741.31258"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1598"
inkscape:window-height="876"
inkscape:window-x="0"
inkscape:window-y="22"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2985" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3010"
width="35.433071"
height="17.716536"
x="442.91339"
y="25.611128" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="496.06299"
y="38.581394"
id="text3012"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3014"
x="496.06299"
y="38.581394"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">unaffected (in main array)</tspan></text>
<flowRoot
xml:space="preserve"
id="flowRoot3800"
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Nimbus Mono L;font-style:normal;font-weight:normal;font-size:20px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Nimbus Mono L;font-stretch:normal;font-variant:normal"><flowRegion
id="flowRegion3802"><rect
id="rect3804"
width="276.7818"
height="34.345188"
x="424.26407"
y="20.996433" /></flowRegion><flowPara
id="flowPara3806" /></flowRoot> <rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812"
width="70.866142"
height="17.716536"
x="17.716558"
y="60.236198" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3814"
width="35.433071"
height="17.716536"
x="442.91339"
y="61.480198" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816"
width="35.433071"
height="17.716536"
x="442.91339"
y="96.913269" />
<rect
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3818"
width="35.433071"
height="17.716536"
x="442.91339"
y="131.10234" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2"
width="88.58268"
height="17.716536"
x="124.01577"
y="60.236198" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-5"
width="88.582687"
height="17.716536"
x="301.18112"
y="60.236198" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4"
width="35.433071"
height="17.716536"
x="442.91339"
y="96.913269" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2"
width="35.433071"
height="17.716536"
x="88.582703"
y="95.669266" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 17.716558,60.236198 70.866142,0 0,17.716536 -70.866142,0"
id="path3939"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 389.7638,60.236198 -88.58268,0 0,17.716536 88.58268,0"
id="path3941"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-3"
width="70.866142"
height="17.716536"
x="17.71656"
y="148.81888" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-5-7"
width="88.582687"
height="17.716537"
x="301.18112"
y="148.81888" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 17.716558,148.81888 70.86614,0 0,17.71653 -70.86614,0"
id="path3939-4"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 389.7638,148.81887 -88.58268,1e-5 0,17.71653 88.58268,0"
id="path3941-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2-6"
width="35.433071"
height="17.716536"
x="124.01577"
y="148.81888" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2-5"
width="53.149605"
height="17.716537"
x="159.44885"
y="148.81888" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2-50"
width="35.433067"
height="17.716537"
x="212.59845"
y="184.25195" />
<rect
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3818-4"
width="53.149609"
height="17.716534"
x="248.03152"
y="184.25195" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,148.81887 35.43307,0"
id="path4078-6"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,166.53541 35.43307,0"
id="path4078-4-7"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,60.236198 88.58267,0"
id="path4078-66"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,77.952734 88.58267,0"
id="path4078-4-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 88.5827,77.952734 0,17.716535"
id="path4156"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,77.952734 0,17.716535"
id="path4176"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 124.01577,95.669269 0,-17.716535"
id="path4178"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,166.53541 0,17.71654"
id="path4180"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 301.18112,184.25195 0,-17.71654"
id="path4182"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,77.952734 0,17.716535"
id="path4186"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 124.01577,95.669269 0,-17.716535"
id="path4188"
inkscape:connector-curvature="0"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,166.53541 0,17.71654"
id="path4190"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 301.18112,184.25195 0,-17.71654"
id="path4192"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,77.952734 0,17.716535"
id="path4078-6-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 124.01577,77.952734 0,17.716535"
id="path4078-6-0-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,166.53541 0,17.71654"
id="path4078-6-0-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 301.18112,166.53541 0,17.71654"
id="path4078-6-0-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 106.29923,116.92911 c 35.43307,28.34646 0,0 35.43307,28.34646"
id="path3803"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 148.81892,81.496041 c 35.43307,63.779529 0,0 35.43307,63.779529"
id="path3803-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 177.16537,60.236198 0,17.716535"
id="path4078-6-0-9-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 194.88191,81.496038 c 35.43307,99.212602 0,3e-6 35.43307,99.212602"
id="path3803-7-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="496.06299"
y="74.426468"
id="text3012-5"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3014-0"
x="496.06299"
y="74.426468"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">main array (moved)</tspan></text>
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
x="496.06299"
y="109.85954"
id="text4897"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4899"
x="496.06299"
y="109.85954"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">original extra array</tspan></text>
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="496.06299"
y="145.60861"
id="text4901"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4903"
x="496.06299"
y="145.60861"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">new inserted area</tspan></text>
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-37"
width="53.149609"
height="17.716536"
x="17.716558"
y="272.83463" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2-1"
width="35.433067"
height="17.716537"
x="159.44884"
y="272.83463" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-5-5"
width="53.149582"
height="17.716537"
x="336.61423"
y="272.83463" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2-3"
width="88.582664"
height="17.716534"
x="70.866173"
y="308.2677" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 17.716558,272.83462 53.149615,0 0,17.71654 -53.149615,0"
id="path3939-43"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 389.7638,272.83462 -53.14961,0 0,17.71654 53.14961,0"
id="path3941-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-3-3"
width="53.149624"
height="17.716515"
x="17.716558"
y="361.4173" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-5-7-9"
width="53.149582"
height="17.716543"
x="336.61423"
y="361.4173" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 17.716559,361.4173 53.149604,0 0,17.71653 -53.149604,1e-5"
id="path3939-4-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 389.7638,361.4173 -53.14961,0 0,17.71653 53.14961,1e-5"
id="path3941-8-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2-6-7"
width="35.433071"
height="17.716536"
x="159.44884"
y="361.4173" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2-50-6"
width="35.433067"
height="17.716537"
x="248.03151"
y="396.85037" />
<rect
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3818-4-9"
width="53.149609"
height="17.716534"
x="283.4646"
y="396.85037" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,361.41729 88.582667,1e-5"
id="path4078-6-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,379.13383 88.582667,1e-5"
id="path4078-4-7-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,272.83462 141.73227,0"
id="path4078-66-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,290.55116 141.73227,0"
id="path4078-4-0-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 70.866173,290.55116 0,17.71653"
id="path4156-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,290.55116 0,17.71653"
id="path4176-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 106.29924,308.26769 0,-17.71653"
id="path4178-9"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,379.13383 0,17.71654"
id="path4180-3"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 336.61419,396.85037 0,-17.71654"
id="path4182-8"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,290.55116 0,17.71653"
id="path4186-1"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 106.29924,308.26769 0,-17.71653"
id="path4188-2"
inkscape:connector-curvature="0"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,379.13383 0,17.71654"
id="path4190-8"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 336.61419,396.85037 0,-17.71654"
id="path4192-4"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,290.55116 0,17.71653"
id="path4078-6-0-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 159.44883,290.55116 0,17.71653"
id="path4078-6-0-9-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,379.13383 0,17.71654"
id="path4078-6-0-0-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 336.61419,379.13383 0,17.71654"
id="path4078-6-0-6-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2-6-7-8"
width="53.149616"
height="17.716539"
x="194.88191"
y="396.85037" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 106.29924,308.26769 0,17.71654"
id="path4078-6-0-9-5-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 88.5827,329.52754 c 88.58267,28.34645 0,0 88.58267,28.34645"
id="path3803-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 131.10238,329.52754 c 70.86614,24.80315 53.14961,3.5433 88.58268,63.77952"
id="path3803-4-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 177.16537,294.09447 c 88.58268,99.21259 0,0 88.58268,99.21259"
id="path3803-4-01"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="17.716536"
y="42.519665"
id="text3012-5-7"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3014-0-2"
x="17.716536"
y="42.519665"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">Example 1:</tspan></text>
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="528"
y="389.36218"
id="text5205"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5207"
x="528"
y="389.36218" /></text>
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="17.716536"
y="255.11809"
id="text3012-5-7-9"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="17.716536"
y="255.11809"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
id="tspan5230">Example 2:</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,35 @@
/** \page rounding-errors How to Avoid Rounding Errors
(Probably, this is a standard algorithm, so if someone knows the name,
drop me a note.)
If something like
\f[y_i = {x_i a \over b}\f]
is to be calculated, and all numbers are integers, a naive
implementation would result in something, for which
\f[\sum y_i \ne {(\sum x_i) a \over b}\f]
because of rounding errors, due to the integer division. This can be
avoided by transforming the formula into
\f[y_i = {(\sum_{j=0}^{j=i} x_j) a \over b} - \sum_{j=0}^{j=i-1} y_j\f]
Of course, when all \f$y_i\f$ are calculated in a sequence,
\f$\sum_{j=0}^{j=i} x_j\f$ and \f$\sum_{j=0}^{j=i-1} y_j\f$ can be
accumulated in the same loop. Regard this as sample:
\code
int n, x[n], a, b; // Should all be initialized.
int y[n], cumX = 0, cumY = 0;
for (int i = 0; i < n; i++) {
cumX += x[i]
y[i] = (cumX * a) / b - cumY;
cumY += y[i];
}
\endcode
*/

195
devdoc/uml-legend.doc Normal file
View File

@ -0,0 +1,195 @@
/** \page uml-legend UML Legend
This page describes the notation for several diagrams used in the
documentation, which is a slight variation of UML.
<h2>Classes</h2>
Classes are represented by boxes, containing there names:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
fontname=Helvetica; fontsize=8;
"Concrete Class";
"Abstract Class" [color="#a0a0a0"];
Interface [color="#ff8080"];
}
\enddot
(In most cases, the attributes and operations are left away, for
better readability. Just click on it, to get to the detailed
description.)
Of course, in C++, there are no interfaces, but here, we call a class,
which has only virtual abstract methods, and so does not provide any
functionality, an interface.
Templates get a yellow background color:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10,
fillcolor="#ffffc0", style="filled"];
fontname=Helvetica; fontsize=8;
"Concrete Class Template";
"Abstract Class Template" [color="#a0a0a0"];
"Interface Template" [color="#ff8080"];
}
\enddot
<h2>Objects</h2>
In some cases, an example for a concrete constellation of objects is
shown. An object is represented by a box containing a name and the
class, separated by a colon.
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
color="#404040", labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
"x: A" -> "y1: B";
"x: A" -> "y2: B";
}
\enddot
The names (\em x, \em y, and \em z) are only meant within the context
of the diagram, there needs not to be a relation to the actual names
in the program. They should be unique within the diagram.
Classes and objects may be mixed in one diagram.
<h2>Associations</h2>
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
color="#404040", labelfontcolor="#000080",
fontname=Helvetica, fontsize=10, fontcolor="#000080"];
fontname=Helvetica; fontsize=10;
A -> B [headlabel="*", taillabel="1", label="x"];
}
\enddot
In this example, one instance of A refers to an arbitrary number of B
instances (denoted by the "*"), and each instance of B is referred by
exactly one ("1") A. The label \em x is the name of the association,
in most cases the name of the field, e.g. A::x.
Possible other values for the \em multiplicity:
<ul>
<li> a concrete number, in most cases "1",
<li> a range, e.g. "0..1",
<li> "*", denoting an arbitrary number.
</ul>
<h2>Implementations and Inheritance</h2>
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="none", dir="both", arrowtail="empty",
labelfontname=Helvetica, labelfontsize=10, color="#404040",
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
A[color="#ff8080"];
B[color="#ff8080"];
C;
D;
A -> B;
A -> C [style="dashed"];
C -> D;
}
\enddot
In this example,
<ul>
<li> the interface B extends the interface A,
<li> the class C implements the interface A, and
<li> the class D extends the class C.
</ul>
<h2>Template Instantiations</h2>
Template instantiations are shown as own classes/interfaces, the
instantiation by the template is shown by a yellow dashed arrow:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="none", arrowtail="empty", dir="both",
labelfontname=Helvetica, labelfontsize=10, color="#404040",
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
A[color="#ff8080"];
B[color="#ff8080"];
C[color="#ff8080", fillcolor="#ffffc0", style="filled"];
C_A[color="#ff8080", label="C \<A\>"];
C_B[color="#ff8080", label="C \<A\>"];
D;
C -> C_A [arrowhead="open", arrowtail="none", style="dashed",
color="#808000"];
C -> C_B [arrowhead="open", arrowtail="none", style="dashed",
color="#808000"];
A -> C_A;
B -> C_B;
C_A -> D [style="dashed"];
}
\enddot
In this example, the interface template C uses the template argument
as super interface.
<h2>Packages</h2>
Packages are presented by dashed rectangles:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="none", arrowtail="empty", dir="both",
labelfontname=Helvetica, labelfontsize=10, color="#404040",
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
subgraph cluster_1 {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="package 1";
A;
B [color="#a0a0a0"];
}
subgraph cluster_2 {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="package 2";
C;
D [color="#a0a0a0"];
E
}
A -> C;
B -> D;
D -> E;
E -> A [arrowhead="open", arrowtail="none"];
}
\enddot
Packages may be nested.
*/