~ubuntu-branches/ubuntu/hardy/gnome-commander/hardy

« back to all changes in this revision

Viewing changes to doc/internal_viewer_hacking.txt

  • Committer: Bazaar Package Importer
  • Author(s): Michael Vogt
  • Date: 2006-06-13 15:39:48 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20060613153948-gvrt3mb2ddk5u62o
Tags: 1.2.0-3
added --disable-scrollkeeper on build

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Internel-Viewer Internals
 
2
-------------------------
 
3
 
 
4
This short document will detail the internals of the 'libgviewer' library,
 
5
which implements GnomeCommander's Internal-Viewer.
 
6
 
 
7
If you plan to hack the internal viewer, add features or just browse the source
 
8
for fun, I recommend reading this document first.
 
9
 
 
10
(However note that the source files are always more updated than this document)
 
11
 
 
12
Table of Content
 
13
----------------
 
14
1.  General
 
15
    1.1. Some Coding Conventions
 
16
2.  Modules
 
17
    2.1.  File Operations module (fileops.c)
 
18
    2.2.  Input mode translations module (inputmodes.c)
 
19
          2.2.1.  CP437 encoding utilities (cp437.c)
 
20
    2.3.  Data presentation module(datapresentation.c)
 
21
    2.4.  Scroll Box widget (scroll-box.c)
 
22
    2.5.  Text Render widget (text-render.c)
 
23
    2.6.  Image Render widget (image-render.c)
 
24
    2.7.  Viewer widget (viewer-widget.c)
 
25
    2.8.  Viewer window (viewer-window.c)
 
26
3.  Module Testers
 
27
    3.1. test-fileops
 
28
    3.2. test-inputmodes
 
29
    3.3. test-datapresentation
 
30
    3.4. test-textrender
 
31
    3.5. test-imagerender
 
32
    3.6. test-viewerwidget
 
33
    3.7. text-viewerwindow
 
34
4.  Using the internal viewer in your project
 
35
    4.1.  External Viewer Window
 
36
    4.2.  Embedding the Viewer Widget
 
37
    4.3.  Using a single module
 
38
 
 
39
1.  General
 
40
-----------
 
41
The 'libgviewer' library aims to be a complete, fast, useable, user-friendly
 
42
viewer window for the GnomeCommander project.
 
43
 
 
44
Key Features:
 
45
  * Fast loading -
 
46
      If possible, does not load the entire file into memory.
 
47
  * Text Handling -
 
48
      Multiple character-set encodings,  including UTF8 and Code page 437.
 
49
  * Versatile Text display -
 
50
      Fixed and Variable width fonts, line wrapping, hex dump.
 
51
  * Versatile Image display -
 
52
      Every GDKable image format can be displayed (jpg,png,gif,svg, etc).
 
53
  * Image Manipulations -
 
54
      Rotating, Flipping, Zooming
 
55
  * Usability -
 
56
      easy keyboard navigation for (almost) all operations
 
57
 
 
58
Some other viewers that you can try (if you're not pleased with 'libgviewer'):
 
59
  * "Total Commander" has an excellent internal viewer.
 
60
    Very power full and fast. If you use That-Other-Operation-System,
 
61
    I suggest you give it a try.
 
62
    You can also use Total Commander on *nix systems under Wine.
 
63
  * "Midnight Commander" (MC) has a very good internal viewer.
 
64
    (actually, the file operation module is largely based on midnight
 
65
    commander's internal viewer code).
 
66
    MC is a console application, so UTF8 and multiple charset encodings are not
 
67
    easily displayed.
 
68
  * "Krusader" (KDE's twin-panel file manager) has a good,
 
69
    feature-packed internal viewer. The downside (IMHO) is that it tries to
 
70
    load the entire file into memory before displaying anything.
 
71
    (And the fact that it uses Qt :-) )
 
72
 
 
73
1.1. Some Coding Conventions
 
74
-----------------------
 
75
  * The modules:
 
76
      Scroll-Box, Text-Render, Image-Render, Viewer-Widget, Viewer-Widget
 
77
    are all full-fledged GTK+2.0 objects. Know your GTK+ before hacking those.
 
78
 
 
79
  * The modules: inputmodes, datapresentation
 
80
    are semi-object oriented but they are not GObjects in any sense.
 
81
    (internally the use function pointers for polymorphism),
 
82
 
 
83
  * All text handling is UTF8 based.
 
84
    (To be consistent with Pango, the GTK+ text rendering engine).
 
85
 
 
86
  * "char_type" is the internal representation of a single UTF-8 character, in
 
87
    a 32bit variable. (This might be a problem: UTF8 characters which occupy 5 or
 
88
    6 bytes cannot be handled by the current internal viewer code).
 
89
 
 
90
  * If the UTF8 character is one byte long, the LSB of the 'char_type'
 
91
    variable contains the value, the other three bytes are zero.
 
92
    If the UTF8 character is two byte long, the two LSBs of the 'char_type'
 
93
    variable contain the value, the other two are zero.
 
94
    The same goes for three and four bytes long UTF8 characters.
 
95
    You can use the macros GV_{FIRST,SEONCD,etc}_BYTE (defined in 'gvtypes.h')
 
96
    To check the 'char_type' variable.
 
97
 
 
98
  * "offset_type", whenever used, ALWAYS means BYTE offset in the file.
 
99
    It never means Character offset (this is important for UTF8 files,
 
100
    where a single character is not necessarily a single byte).
 
101
    The "inputmodes" module provides functions to move to the next and previous
 
102
    character offsets.
 
103
 
 
104
    Currently it is "unsigned long", so the file size limit is 4GB.
 
105
    (Actually, some internal code might be using a "signed long", so anything
 
106
    bigger than 2GB is not supported....)
 
107
 
 
108
  * There are no static variables in to modules (except the GTK+'s parent_class)
 
109
    So using multiple viewers widgets in the same application should be OK.
 
110
    However, the modules are not guaranteed to be thread-safe, so the same
 
111
    viewer-widget should not be accessed from two different threads.
 
112
 
 
113
2.  Modules
 
114
-----------
 
115
 
 
116
2.1.  File Operations module (fileops.c)
 
117
----------------------------------------
 
118
File operation module is largely based on Midnight Commander's "view.c".
 
119
When opening a file, it tries to "MMAP" it, and only if this fails tries to load
 
120
entirely into memory (and if there's not enough memory, use "growing buffer",
 
121
where each read operation loads a new part of the file into memory).
 
122
 
 
123
Midnight Commander has a nice feature of using the same code (with growing
 
124
buffers) to read directly from a pipe (e.g. running an external program and
 
125
sending the STDOUT directly to the viewer). This part was not ported to the
 
126
"libgviewer", but it can be later added (if need arises).
 
127
 
 
128
Read "fileops.h" for the list of functions (they are pretty self-explainatoty).
 
129
Use "gv_file_get_byte" to read a single byte from the file.
 
130
 
 
131
2.2.  Input mode translations module (inputmodes.c)
 
132
---------------------------------------------------
 
133
The Input Mode module is responsible for reading raw bytes from a file, and
 
134
translating them to UTF8 characters(the rest of 'libgviewer' is always UTF8).
 
135
 
 
136
If using a one-byte-per-character encoding, a quick translation table is built
 
137
(in "inputmode_ascii_activate"), with a UTF8 value for each possible byte value.
 
138
Later, when a character is requested (with "gv_input_mode_get_utf8_char"),
 
139
The byte from the file is translated to the corresponding UTF8 character.
 
140
 
 
141
If using a multibyte-per-character encoding (currently only UTF8 is supported),
 
142
The "inputmode_utf8_get_char" is used to read the required number of bytes from
 
143
the file and return a single UTF8 character.
 
144
 
 
145
When higher levels/other modules wish to read the file, they use:
 
146
 * "gv_input_mode_get_utf8_char" to read a single UTF8 char from the file, at
 
147
   the requested offset (ALWAYS byte offset).
 
148
 * "gv_input_mode_get_next_char_offset","gv_input_mode_get_previous_char_offset"
 
149
   To move across the file. These functions translate from character offset to
 
150
   byte offset.
 
151
   Never use "offset++" or "offset--" to move around the file.
 
152
   (Although, I admit, I do it in with the hex dump code... - but only because I
 
153
   made sure I'm using a byte offset, not character offset).
 
154
 
 
155
A note about control characters (tab='\t', CR='\r', LF='\n'):
 
156
The inputmodes modules NEVER translates these control characters.
 
157
"gv_input_mode_get_utf8_char" will return these control characters without
 
158
translating them, even if they can be translated to a valid UTF8 character
 
159
(for example, when using CP437 encoding).
 
160
Other modules which want to display the UTF8 equivalents of these control
 
161
characters can use "gv_input_mode_byte_to_utf8" to get the actual UTF8 value.
 
162
(See "text_render_display_line" in "text-render.c".)
 
163
 
 
164
2.2.1.  CP437 encoding helpers (cp437.c)
 
165
------------------------------------------
 
166
Except Codepage 437, all inputmodes' convertions are made with "g_iconv".
 
167
Codepage 437 (a.k.a IBM437, CP437) is the "terminal font" which is able to
 
168
display graphic representation of the control characters (ASCII<32), and nice
 
169
extended graphic characters (ASCII>128).
 
170
I like it very much, it is useful for viewing binary files.
 
171
I could not get g_iconv to translate characters correctly into this codepage.
 
172
So I made special helper functions to make it work.
 
173
(See "inputmode_ascii_activate" in "inputmodes.c").
 
174
More details on CP437 can be found at "http://en.wikipedia.org/wiki/CP437".
 
175
 
 
176
2.3.  Data presentation module(datapresentation.c)
 
177
--------------------------------------------------
 
178
The data presentation module is responsible for moving around the file,
 
179
calculating the offsets of the start of the line and
 
180
the end of the line.
 
181
 
 
182
This modules tries to hide away the nasty details of line wrapping (but it only
 
183
works for fixed-width fonts).
 
184
 
 
185
The are several modes of calculations:
 
186
 BIN_FIXED - The simplest mode. Uses fixed number of characters per line.
 
187
        16 characters per line is used for HEXDUMPs.
 
188
        20,40,80 characters per line are used for binary display.
 
189
 NO_WRAP - Simple text mode. A line starts a offset 0, or at a CR/LF character.
 
190
        A line ends at the next CR/LF, or at the end of the file.
 
191
 WRAP    - More complex calculations to display text files with line wrapping.
 
192
 
 
193
Note: The calculations are about number of CHARACTERS per line (it uses the
 
194
Inputmode module for moving to the next and previous characters).
 
195
BUT the return offsets from the functions are ALWAYS byte offsets in the file.
 
196
 
 
197
2.4.  Scroll Box widget (scroll-box.c)
 
198
--------------------------------------
 
199
The ScrollBox widget is a simple composite widget.
 
200
It packs a GtkTable, with two scroll bars (horizontal and vertical) and one
 
201
"client" widget.
 
202
The client widget should be connected to the GtkAdjustments of the scrollbars.
 
203
This saves some work, because both the text-render and the image-render requires
 
204
scroll bars with GtkAdjustments.
 
205
 
 
206
2.5.  Text Render widget (text-render.c)
 
207
----------------------------------------
 
208
Text Rener the main text displaying widget.
 
209
It uses fileops, inpumodes and datapresentation modules to handle the most of the
 
210
text formatting.
 
211
 
 
212
Three display modes are supported:
 
213
* Text - normal text, with or without line wrapping.
 
214
* Binary - fixed number of characters per line.
 
215
* Hex Dump - regular hex dump, 16 bytes per line.
 
216
 
 
217
Displaying and selecting text in the three display modes are implemented using
 
218
function call backs (the functions are at the end of the file).
 
219
 
 
220
The is some black voodoo there trying to block forbidden conbinations
 
221
(Like UTF8 with Binary mode, or Line Wrapping with Variable width font).
 
222
This should be fixed in future versions.
 
223
 
 
224
Pango-ism:
 
225
* character width is calculated in "get_max_char_width".
 
226
 
 
227
* Pango fails to draw some valid UTF8 characters, and using
 
228
  "text_render_filter_undisplayable_chars" we make sure they are not displayed.
 
229
 
 
230
* Text-Render uses "char_type" to hold each UTF8 character (always 4 bytes),
 
231
  But Pango wants real UTF8 strings (variable number of bytes per character).
 
232
  "text_render_utf8_printf" and "text_render_utf8_print_char" take care of that.
 
233
 
 
234
Signals:
 
235
TextRender emits one signal (status changed), whenever the view is updated.
 
236
 
 
237
 
 
238
2.6.  Image Render widget (image-render.c)
 
239
------------------------------------------
 
240
Image Render is a GdkPixBuf wrapper widget.
 
241
(The GtkImage widget has some annoying behaviours, so I didn't use it, but
 
242
maybe I just failed to utilize it correctly).
 
243
 
 
244
This widget loads the entire file into memory (unlike the text-render widget).
 
245
 
 
246
Read "image-render.h" to see possible operations (they are self-explainatory).
 
247
 
 
248
Image loading process:
 
249
1. upon "image_render_load_file", the file name is stored for later, and
 
250
   nothing else happens.
 
251
2. On the first "realize" event, the file is loaded in
 
252
   "image_render_load_scaled_pixbuf" using "gdk_pixbuf_new_from_file_at_scale".
 
253
   This is because "file_at_scale" is the fastest loader around.
 
254
   The reason for the delayed loading (after the "realize") is because
 
255
   "file_at_scale" requires a scale size (before "realize" the widget doesn't
 
256
   have an allocated size).
 
257
3. On the first "expose" event, a background thread is started, loading the file
 
258
   (in "image_render_start_background_pixbuf_loading").
 
259
4. On subsequent "expose" or "size_allocate", the atomic int "orig_pixbuf_loaded"
 
260
   is checked to see if the background loader thread completed loading the image.
 
261
   if not, the scaled image (from step 2) is displayed, regardless of the zoom mode.
 
262
 
 
263
Image freeing details:
 
264
If the user is very quick (and the image is very big), the viewer might be closed
 
265
(by the user) before the background thread is finished. For better user-experience,
 
266
"destroy" doesn't block until the thread is done.
 
267
See "image_render_destroy" and "image_render_wait_for_loader_thread" for the
 
268
messy details.
 
269
 
 
270
Signals:
 
271
ImageRender emits one signal (status changed), whenever the view is updated.
 
272
 
 
273
2.7.  Viewer widget (viewer-widget.c)
 
274
-------------------------------------
 
275
ViewerWidget is a composite widget, which multiplexes a TextRender widget and
 
276
an ImageRender widget into one, easy to use GtkWidget.
 
277
 
 
278
See "viewer-widget.h" for possible operations.
 
279
Switching between TextRender and ImageRender is done in "gviewer_set_display_mode".
 
280
 
 
281
ViewerWidget catches the "status-changed" from both renderers, and combines them
 
282
into one signal ("status-line-changed").
 
283
 
 
284
 
 
285
2.8.  Viewer window (viewer-window.c)
 
286
-------------------------------------
 
287
ViewerWindot is a full-blown Top-Level GtkWindow, displaying a Viewer Widget,
 
288
With user-friendly menu items.
 
289
All operations on ViewerWidget/TextRender/ImageRender are possible from the menus.
 
290
 
 
291
Extra features (on top of the ViewerWidget's features)
 
292
* saves/loads user settings (position, wrapping, etc).
 
293
* displays EXIF/IPTC information in a split screen in image display mode.
 
294
 
 
295
One simple function "gviewer_window_file_view" can be used to create the window
 
296
and load the file (including display mode autodetection).
 
297
The function returns a "GtkWidget" (which is actually a GtkWindow).
 
298
You need to "gtk_widget_show" the widget.
 
299
You can also the the window's Icon.
 
300
See "do_view_file" in "gnome-cmd-file.c" for an example.
 
301
 
 
302
 
 
303
3. Module Testers
 
304
-----------------
 
305
Inside the "gviewer_test" sub-directory you'll find module testers, one for each
 
306
module (as specified in section 2, above).
 
307
 
 
308
The learn how to use each module, check the appropriate tester:
 
309
 
 
310
3.1. test-fileops
 
311
-----------------
 
312
Uses the "fileops" module to load and display the file.
 
313
Exactly like "cat".
 
314
 
 
315
Run without command line parameters to see usage instructions.
 
316
 
 
317
3.2. test-inputmodes
 
318
--------------------
 
319
Uses the "inputmodes" module (on top of "fileops") to load the file,
 
320
and translate it from the requested charset to UTF8.
 
321
Output is sent to stdout ALWAYS in UTF-8 (because the whole viewer library uses
 
322
UTF8 internally).
 
323
This is very similar to using "iconv" with "--to-code" set to UTF8.
 
324
 
 
325
Run without command line parameters to see usage instructions.
 
326
 
 
327
3.3. test-datapresentation
 
328
---------------------------
 
329
Uses "datapresentation" module to display the input file in various formats.
 
330
Output format can be text (with or without wrapping), binary or hex.
 
331
Input mode charset translation can also be specified.
 
332
Output is sent to stdout.
 
333
Run without command line parameters to see usage instructions.
 
334
 
 
335
3.4. test-textrender
 
336
--------------------
 
337
Create a primitive GtkWindow and puts a TextRender widget in it.
 
338
There is almost no user interface (menus and such), so you must set the options
 
339
(display mode, charset encoding, wrapping etc) from the command line.
 
340
Run without command line parameters to see usage instructions.
 
341
 
 
342
3.5. test-imagerender
 
343
---------------------
 
344
Create a primitive GtkWindow and puts an ImageRender widget in it.
 
345
There is almost no user interface (menus and alike), so you must set the options
 
346
(scaling/best-fit) from the command line.
 
347
Run without command line parameters to see usage instructions.
 
348
 
 
349
3.6. test-viewerwidget
 
350
----------------------
 
351
Create a primitive GtkWindow and puts a Viewer widget in it.
 
352
There is almost no user interface (menus and alike), so you must set the options
 
353
(display mode, wrapping, scaling, etc.) from the command line.
 
354
 
 
355
ViewerWidget combines TextRender and ImageRender widgets, so you can specify a
 
356
display mode (Text/Binary/Hex/Image) or use auto detection.
 
357
 
 
358
Run without command line parameters to see usage instructions.
 
359
 
 
360
3.7. text-viewerwindow
 
361
----------------------
 
362
This is complete, stand-alone viewer window, with menus and everything.
 
363
Specify a file to view on command line.
 
364
 
 
365
4.  Using the internal viewer in your project
 
366
---------------------------------------------
 
367
 
 
368
4.1.  External Viewer Window
 
369
----------------------------
 
370
To include an external Viewer window in your project (meaning a new top level
 
371
window will be displayed with the desired file), simply link with the libgviewer
 
372
library,
 
373
add
 
374
"#include <libgviewer/libgviewer.h>"
 
375
to your file, and call "gviewer_window_file_view" (don't forget "gtk_widget_show").
 
376
See "test-viewerwindow.c" for a simple example.
 
377
See "gnome-cmd-file.c" for an example with icon changing.
 
378
 
 
379
4.2.  Embedding the Viewer Widget
 
380
---------------------------------
 
381
To embed a ViewerWidget inside your own window/widget, see the "test-viewerwidget"
 
382
example code.
 
383
The ViewerWidget doesn't include any user interface (menus and such), so you must
 
384
add them yourself. See the source file "viewer-window.c" for a reference
 
385
implementation of user interface relating to the viewer widget.
 
386
 
 
387
4.3.  Using a single module
 
388
---------------------------
 
389
The best place to start is to look in the relevant module tester.