Initial import of Dillo
153
devdoc/CCCwork.txt
Normal 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
@ -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
@ -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
@ -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
@ -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 '<' and '>' 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
@ -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
@ -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
@ -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
@ -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
105
devdoc/dw-changes.doc
Normal 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>
|
||||
|
||||
*/
|
BIN
devdoc/dw-example-screenshot.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
63
devdoc/dw-fixed-positions.doc
Normal 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
|
||||
<thead> and <tbody>
|
||||
<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 <tfoot>, which is not discussed here, for reasons
|
||||
of simplicity. However, it is obvious that <tfoot> should be
|
||||
dealt with in an analogue way as <thead>.
|
||||
|
||||
|
||||
*/
|
BIN
devdoc/dw-floats-01.png
Normal file
After Width: | Height: | Size: 42 KiB |
209
devdoc/dw-grows.doc
Normal 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*;
|
||||
- <button>.
|
||||
|
||||
|
||||
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. 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. 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.
|
||||
|
||||
*/
|
235
devdoc/dw-images-and-backgrounds.doc
Normal 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.)
|
||||
|
||||
*/
|
BIN
devdoc/dw-interrupted-drawing-1.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
devdoc/dw-interrupted-drawing-2.png
Normal file
After Width: | Height: | Size: 42 KiB |
706
devdoc/dw-interrupted-drawing-2.svg
Normal 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 |
121
devdoc/dw-interrupted-drawing.doc
Normal 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
@ -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>
|
||||
|
||||
*/
|
267
devdoc/dw-layout-widgets.doc
Normal 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
@ -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. 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. 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. 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. 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. 93--94). The value for the stretchability of the line
|
||||
is slightly less, 1 em (ibid., see also p. 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. 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> - <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> - <i>i</i><sub>1</sub>) +
|
||||
(<i>n</i> - <i>i</i><sub>2</sub>) + ..., with
|
||||
<i>i</i><sub>1</sub> < <i>i</i><sub>2</sub> < ...,
|
||||
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
|
||||
"Abtei-<span></span>-[new line]Stadt". 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
@ -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
|
||||
*/
|
97
devdoc/dw-miscellaneous.doc
Normal 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>
|
||||
|
||||
*/
|
151
devdoc/dw-out-of-flow-floats.doc
Normal 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 (<p>) is regarded as own floats container (because of
|
||||
*overflow:hidden*), so the floats container above (<body>)
|
||||
regards this block as widget which must be fit between the floats
|
||||
(dw::Textblock::mustBorderBeRegarded >
|
||||
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. 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. 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.
|
||||
|
||||
*/
|
66
devdoc/dw-out-of-flow-positioned.doc
Normal 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
@ -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 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
@ -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>
|
||||
|
||||
*/
|
BIN
devdoc/dw-size-of-widget.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
35
devdoc/dw-size-request-pos-01.html
Normal 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/>
|
||||
<img src="http://upload.wikimedia.org/wikipedia/commons/8/80/CorrespMtl5.png" style="width: 80px"> </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>
|
BIN
devdoc/dw-size-request-pos-01.png
Normal file
After Width: | Height: | Size: 31 KiB |
172
devdoc/dw-size-request-pos.doc
Normal 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.
|
||||
|
||||
*/
|
114
devdoc/dw-stacking-context.doc
Normal 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?
|
||||
|
||||
*/
|
BIN
devdoc/dw-style-box-model.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
devdoc/dw-style-length-absolute.png
Normal file
After Width: | Height: | Size: 575 B |
BIN
devdoc/dw-style-length-percentage.png
Normal file
After Width: | Height: | Size: 890 B |
BIN
devdoc/dw-style-length-relative.png
Normal file
After Width: | Height: | Size: 868 B |
BIN
devdoc/dw-textblock-collapsing-spaces-1-1.png
Normal file
After Width: | Height: | Size: 641 B |
BIN
devdoc/dw-textblock-collapsing-spaces-1-2.png
Normal file
After Width: | Height: | Size: 521 B |
BIN
devdoc/dw-textblock-collapsing-spaces-2-1.png
Normal file
After Width: | Height: | Size: 802 B |
BIN
devdoc/dw-textblock-collapsing-spaces-2-2.png
Normal file
After Width: | Height: | Size: 586 B |
375
devdoc/dw-usage.doc
Normal 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>
|
||||
|
||||
*/
|
BIN
devdoc/dw-viewport-with-scrollbar.png
Normal file
After Width: | Height: | Size: 755 B |
BIN
devdoc/dw-viewport-without-scrollbar.png
Normal file
After Width: | Height: | Size: 542 B |
278
devdoc/dw-widget-sizes.doc
Normal 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. 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
@ -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
@ -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
@ -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>
|
||||
|
||||
*/
|
BIN
devdoc/not-so-simple-container.png
Normal file
After Width: | Height: | Size: 19 KiB |
785
devdoc/not-so-simple-container.svg
Normal 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 |
35
devdoc/rounding-errors.doc
Normal 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
@ -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.
|
||||
|
||||
*/
|