1
Internel-Viewer Internals
2
-------------------------
4
This short document will detail the internals of the 'libgviewer' library,
5
which implements GnomeCommander's Internal-Viewer.
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.
10
(However note that the source files are always more updated than this document)
15
1.1. Some Coding Conventions
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)
29
3.3. test-datapresentation
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
41
The 'libgviewer' library aims to be a complete, fast, useable, user-friendly
42
viewer window for the GnomeCommander project.
46
If possible, does not load the entire file into memory.
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
56
easy keyboard navigation for (almost) all operations
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
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 :-) )
73
1.1. Some Coding Conventions
74
-----------------------
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.
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),
83
* All text handling is UTF8 based.
84
(To be consistent with Pango, the GTK+ text rendering engine).
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).
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.
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
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....)
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.
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).
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).
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.
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).
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.
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.
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
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).
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".)
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".
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
182
This modules tries to hide away the nasty details of line wrapping (but it only
183
works for fixed-width fonts).
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.
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.
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
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.
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
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.
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).
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.
225
* character width is calculated in "get_max_char_width".
227
* Pango fails to draw some valid UTF8 characters, and using
228
"text_render_filter_undisplayable_chars" we make sure they are not displayed.
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.
235
TextRender emits one signal (status changed), whenever the view is updated.
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).
244
This widget loads the entire file into memory (unlike the text-render widget).
246
Read "image-render.h" to see possible operations (they are self-explainatory).
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.
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
271
ImageRender emits one signal (status changed), whenever the view is updated.
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.
278
See "viewer-widget.h" for possible operations.
279
Switching between TextRender and ImageRender is done in "gviewer_set_display_mode".
281
ViewerWidget catches the "status-changed" from both renderers, and combines them
282
into one signal ("status-line-changed").
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.
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.
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.
305
Inside the "gviewer_test" sub-directory you'll find module testers, one for each
306
module (as specified in section 2, above).
308
The learn how to use each module, check the appropriate tester:
312
Uses the "fileops" module to load and display the file.
315
Run without command line parameters to see usage instructions.
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
323
This is very similar to using "iconv" with "--to-code" set to UTF8.
325
Run without command line parameters to see usage instructions.
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.
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.
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.
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.
355
ViewerWidget combines TextRender and ImageRender widgets, so you can specify a
356
display mode (Text/Binary/Hex/Image) or use auto detection.
358
Run without command line parameters to see usage instructions.
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.
365
4. Using the internal viewer in your project
366
---------------------------------------------
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
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.
379
4.2. Embedding the Viewer Widget
380
---------------------------------
381
To embed a ViewerWidget inside your own window/widget, see the "test-viewerwidget"
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.
387
4.3. Using a single module
388
---------------------------
389
The best place to start is to look in the relevant module tester.