The Almara Photo Album is a school software project of four students and of the leader and author of the project (and also advisor) from the university stuff. We all study informatics at Charles University, Faculty of Mathematics and Physics, Prague.
Project started (and we met together first time) on autumn 2003. After a
short period of planning (inter alia a coding style was ratified, see Appendix
D for details), we started programming in november. The main programming
language is C, the auxiliary and supporting scripting language is Python. We
use GTK 2.0 and ImageMagick 6.0 (recomended) as the basic libraries (and Cygwin
on MS Windows as well). We use threetimes vim
and once
emacs
as "IDE". Also strace
and gdb
were
excellent helpers. Then we used valgrind
, Doxygen for generating a
part of this documentation and for example archzoom
We founded an account on SourceForge, internal mailing list (with only 5 members), wiki web pages with all closed matters, documents, protocols, howtos, links, tutorials etc., bug track system and in the end we founded also an IRC channel for operative bug fixes and problem reporting. We used to have regular meetings for a few hours every week. We have more than 50 detailed text records which were distributed in e-mail after every meeting. There were also all tasks in those e-mails.
As a concurency system we chosed arch. It was continuosly mirrored. Here are some necessary commands:
tla my-id "Name <email>"
-- registers your IDtla register-archive almara-devel@nongnu.org--almara
sftp://<login_name_on_SF>@shell.sourceforge.net/home/
groups/a/al/almara/htdocs/arch
-- registers your archive (this is ONE command, so write it together on one
line and don't insert a space before groups
tla my-default-archive almara-devel@nongnu.org--almara
-- set
this archive as defaulttla get almara--project--0.1 <directory>
-- gives you
whole last version of project and places it into directory
tla get www--mainline--0.1
-- gives you project webpagestla missing
-- tells you if some new patches exist you haven't
gottla update
-- download you new patchestla commit
-- after you fill the changelog form, it commits
your changes as a new patchAlmara should be the image viewer which is very simple and fast to
manipulate with, which is completely controlled from keyboard (only operations
such as crop needs mouse), which provides operations on more images at once.
Yes, this viewer can do some simple operations with images (rotate, crop,
scale, saturation, flip, ...); there's gimp
for more complicated
transformations, which can be run from Almara. Moreover Almara is suited for
photos from digital cameras, so Almara understands Exif, can do some
categorization, can group photos to albums, ...
Simply, Almara is a fast thing, which you import your new photos to, look over them, rotate landscape or portrait, crop off void places, increase brightness to the second half of photos and export whole album with thumbnails to web.
Additionally all these things can be undo, the undo list is unlimited. All operations are logged, so image is (for example) croped only in a flag. You will see it always croped -- in Almara. But other viewers will prove that the image wasn't modified. It will be untouched, until you explicitely say that this is exactly what you want. Then, when operations are really executed, they are executed on background, in one of subprocesses.
Almara provides also special work with motion jpegs. (Motion jpeg is cut up on frames and is considered album.) Almara can print. Almara can by run under MS Windows (in Cygwin).
Almara can do many things also when controlled from shell, not from GUI.
The Almara Photo Album consists of three main modules. These modules can cooperate, but they can also run separately (with one exception). Further, there are many smaller parts in the package which make the work with Almara more comfortable and also very adjustable. Those three modules are little chaotic and messy in terminology. So we must explain the names of modules and a little history first.
The most visible module was called (logically) almara
after the
name of whole project. Almara is the graphic user interface for work with
pictures. Because there were some interesting requirements on internal work
with pictures (e.g. unlimited undo, logging of operations, work with metadata,
smart printing, applying opperations on group of pictures, not to be limited by
size of memory etc. -- more about this later in Chapter 4), we decided for some
kernel module which will provide a support for GUI, for almara
.
This inner module -- surrounded later by all other modules (such as
almara_print
, mjpeg2jpegs
,
almara_webexport
, ...) -- was named chaerwottoch
(a.
k. a. CHT). (The reasons: "Almara" means an old cupboard in Czech, and
"chaerwottoch" means a wood-worm.) Name chaerwottoch
is the only
name which has survived up to now. The third big module is
hallmara
, The Hall of Almara. It was needed to operate on whole
sets of pictures, albums, since almara
is conditioned only for
pictures itself.
But... almara
is now called almara_album
, while
hallmara
, the entering (and therefore first and great) binary was
changed its name to almara
(or Almara Hall). Because the reasons
for name change was mainly simplicity and readibility for common users, in this
programmer's guide (on the contrary of the user's guide) we will keep the
original designation (with warnings in danger parts). So, we have kernel
chaerwottoch
, GUI for pictures almara
and GUI for
directories of pictures hallmara
. Almara is primarily designed for
photos, so hallmara
takes care of photo albums.
And there is a list of reused components here:
In typical using, hallmara
is run first. When the album is
choosed in hallmara
, almara
is run and covers
hallmara
. almara
being run execs CHT and starts
communication through two pipes (there and back). Now you know the most
important three parts of the project, in cooperation.
CHT providing support to GUI needs some services. This services are provided
by those forementioned minor modules, small parts of almara project. A library
for pictures (using some functions from ImageMagick) libimg
. A
print utility almara_print
that can be used to advantage also from
shell. A python scripts almara_mkalb
for creating new album from
directory with photos and almara_mergalb
for merging two albums
together. Python script almara_webexport1_bin
, which allows export
whole album for web. mjpeg2jpegs
and jpegs2mjpeg
convertor of motion jpegs. Some other shell scripts provide function of CHT
without need of almara
, just take control over the CHT and send it
commands, because whole communication is designed in such way (see appendix B).
It shows that almost any module is replaceable and/or can be connected to some
other modules. That's how the modules can exist separately, and so you know
both, now. (The exception mentioned in the beginning is that
almara
requires CHT -- and it is used to launch it.)
CHT manages many processes. It has them for operations needed by commands. Most of them are its own forked subprocesses (small chaerwottoches), run on photos to satisfy some commands. No more than one process operates on one photo, so no conflicts may come. There are many difficult componants in CHT, such as scheduler with priority queues, command abort, compound commands. The protocol between GUI and CHT is in appendix A. It is able to run many external programs, both our own and the others, such as gimp. The special protocol AEP (Almara exernal protocol) is added as Appendix B. CHT also supports films from cameras, motion jpegs, which are cut frame by frame and give as many photos in album.
For details on chaerwottoch
see chapter 4.
It has asynchronous communication with CHT on two pipes, so there is a separate thread in GUI dedicated to writing data to avoid deadlocking. Communication is held in "almara_io protocol", which uses spaces to separate individual tokens and newlines to separate commands. Special characters like spaces, newlines, etc. are escaped by backslash. It has the advantage that this protocol can be used also in shellscripts.
For details on almara
see chapter 5.
hallmara
is responsible for creating and managing albumfiles
and launching almara (both on individual albums and on movies). It can be
easily extended to support much wider range of operations with albums.
For details on hallmara
see chapter 6.
ChT
(Chaerwottoch) is a "server" spawned by
almara_album
, but it can be used by shell scripts as well. It
receives commands from standard input and sends responses to standard output.
Standard error is used for debug messages.
ChT
is responsible for actual working with album. Only one
album can be opened at a time but ChT
is able to work with another
album after previous one is closed. For complete list of commands
ChT
recognizes, see appendix.
The main goal when implementing Cht
was to assure that
ChT
is always ready to receive command and that every
time-consuming command will be handled in the background. For the sake of this
a "scheduler" was implemented. Every command received is turned into
request. Request is held in a structure containing information about command
ID, the command and its parameters, photo this request operates upon, and
number of internal fields. Requests are of several types depending on a
command:
edit_audiotrack
command only) prevents all next requests operating on any photo to wait until
the blocking request is finished.Division of commands to above types is defined in
cht/commands.h
file.
Scheduler works with four levels of priorities (the number of priorities is
adjustable in cht/scheduler.h
- MAX_PRIORITY
constant; max_working
array in cht/scheduler.c
needs
to be modified appropriatelly in such case). Priority 0 is special and will be
discussed later.
Every priority level has the maximum count of running requests defined (only requests running in subprocess at this or higher priority are counted). When this maximum is reached, no more requests of this priority are started. Requests handled in main process are assumed not to be time-consuming, so they are honoured regardless of number of requests running.
Priority 0 is special to the intent that no "left over" requests are allowed. So if the maximum count of running requests for priority 0 is reached, any other request at this priority is aborted. Note that maximum count for priority 0 is by default set to zero, so setting priority to 0 is effectively equal to aborting the command.
When request is considered to be run, it is checked that there exists no previous request operating on given photo. If such a request exists, considered request cannot be run. This check is made efficient by keeping linked list of so called "dependand requests", ie. requests operating on the same photo.
Aborting of request not yet running is simple - it is discarded from scheduler lists and response about aborting is emitted. When request is running (note that it means it is running in subprocess, as other requests are considered not time-consuming and are handled in main process) the situation is more complicated. SIGTERM signal is sent to the process and response about aborting is emitted. The process itself is responsible for proper clean-up of temporary files.
When command corresponding to compound request is received, one internal request for every photo compound request is for is generated and scheduled as usual. Scheduler keeps track of requests that are part of compound command (it calls it "group"). When all requests from a group are finished, callback is called by scheduler and appropriate message is emitted. All errors occuring during run of compound command are gathered and just one error message is sent when group finishes.
There is also possibility to run external command after group is finished. This external program has to communicate in AEP protocol (see appendix) and appropriate message is send as late as external program requests it.
This request prevents all requests received later (only request operating on some photo, as there exist requests operating on no photo) to wait until the blocking request is finished. Only one blocking request can be scheduled at a time (error message is emitted when second such command is received).
Configuration can be changed at any time. But it contains also export format, work size atc., which are parameters that must not alter previous request. For the sake of this every request has its own copy of configuration. It is freed as soon as request is finished.
If request is undoable (this flag is set when adding request to scheduler) and is finished, its structure is not destroyed. It stays in undo list instead. Undo list is linked list of requests (both finished and unfinished) in the reverse order they were received. When request for undo is received, the first request in undo list is undone. It means it is:
As no operation is actually performed and operations are logged, removing of effect means removing of last log entry. A client is responsbile for actual rerendering.
Irreversible operation such as commit removes appropriate requests from undo list.
All operations are logged only. That means that getting image dimensions is not as simple as inspecting image file header. Of course image dimensions can be always got by performing operations from log, but it can be very slow if we just want image dimensions. So image dimension are counted from size of original image and logged operations and rendering is used only as a fallback when dimensions cannot be counted (it happens for some unusual operations performed by third-party libraries, like rotation by any angle). Counting of dimension is complicated by lossless JPEG transformations, as they may affect dimensions depending on size of MCU block. The algorithm for dimensions counting deals correctly with it.
When operation on image needs to be performed, appropriate function from
image library is called. ChT
needs to decide when particular
operation should be performed losslessly and when not, as it needs to compute
metadata and perform rendering in the same way.
All images are rendered at "work size". It means they are scaled down to this size (they are not enlarged). It gives noticeable speed-up to image rendering.
Thumbnails are cached automatically. This cache is made persistent when the album is not temporary. Symlink to directory with cached thumbnails is created in directory with album when album is being closed.
Images are cached (at work size). Maximum cache size is adjustable at run time. When the maximum size is hit, the last used item is deleted from the cache. For maximal speed-up, images are saved in uncompressed format to the cache (currently BMP format is used).
EXIFs are supported through libexif
library. When JPEG image is
exported or committed, EXIF is injected to the destination file and appropriate
changes are made inside it - Orientation tag and image width and height tags
are modified. Nearly all libraries which preserve EXIFs incorrectly preserve
them as a whole (eg. ImageMagick in later versions), so original EXIF is always
overwritten.
ChT
supports MJPEG files in both AVI and QuickTime format. When
command for opening of album is received, it is checked if given filename
corresponds to MJPEG file (by content, not by name - except for
album.alb
, which is always taken as an album name). If movie file
was passed, it is cut to individual frames and temporary album file is created.
In response to client it is indicated that opened album is a movie.
Cutting movie to individual frames and pasting them together is performed
through external AEP programs mjpeg2jpegs
and
jpegs2mjpeg
. First of them takes MJPEG file and generates a
directory with individual frames and corresponding parts of audio track (ie.
audiotrack is cut to pieces correspondent to fps). The second one takes a list
of files and generates MJPEG movie, preserving all of metadata from the
original movie.
mjpeg2jpegs
can workaround bugs of some types of digital camera
(Minolta seems to misinterpret QuickTime standard), jpegs2mjpeg
can workaround some bugs in mplayer
program (mplayer
seems to have problems with QuickTime MJPEGs with 16bit audiotrack).
Originally its name has been almara, but then hallmara assumes this name. But in the source code original names have been preserved, so this is the explanation for probably confused users.
GUI is divided to several components: image widget, filelist, iconlist, exif
widget a menu and toolbar. Thanks to dock system it is possible to dock, float
and hide these components independently. Each component keeps information about
its position and after next start of the system uses the saved data. Config
file path can be found at ~/.almara/almara_album.conf
and
~/.almara/exif_widget.conf
. The config file name can be changed in
cfg.h
:
#define WINDOW_GEOMETRY_FILE "almara_album"
#define EXIF_WIDGET_CONFIG_FILE "exif_widget"
Filelist and iconlist contain rendered thumbnails and decide according to current cursor position which photo will be displayed. Filelist is based on gtktreeview and iconlist is based on gtkiconlist (this structure is available only in gtk version >2.5) and both are based on the common model GtkTreeModelSort, which is based on GtkTreeModelFilter, which allows to control visibility in hierarchic groups. GtkTreeModel is built on GtkListSort containing pixbufs of thumbnails, name of an original photo and a description of a photo, and can sort according to these three items. The selection of photos using regular expressions is built on this structure too.
Every photograph in GtkListSort has an associated structure Picture, where additional information about joined audio record, exif data, advanced data and list of pending requests to CHT are stored. If you are curious what advanced data is, see CHT protocol (but generally it is data like compression method, volume, sound, description, keywords and user defined data).
These request are essential for GUI functionality and since the communication between CHT and GUI is asynchronous it is necessary to register callback for each GUI operation.
Communication with the CHT is provided by functions in the
almara/kernel.[hc]
. There is implemented hash table of requests
sent to CHT, but not yet processed. There are pointers to four different
callback in the table representing four different answers of CHT to
requests:
done
: request has been successfully processed.error
: some error has occurred.abort
: user abort.progress
: request is still active, progress bar should be
updated.If an answer from CHT comes and a callback for this request is registered, then callback is executed and request (except progress) is removed from the hash table.
Commands
render
/get_metadata
/get_adv_metadata
are
a bit more complicated, because GUI wants to abort them sometimes and they are
sometimes processed in advance, before their results are requested. In the
struct image are therefore lists of request for
render
/get_metadata
/get_adv_metadata
,
for each request GUI keeps information about its parameters.
Before request for any action like moving to the next photograph, then before the request to CHT is sent, first check is performed, whether the request with these parameters is not already solved or being processed and according to this the request is sent again or just its priority is adjusted (increased).
On the other side when it becomes clear that some preprocessed request is no more needed, the request is aborted and the piece of memory with the already rendered picture is released.
Another problem with rendering pictures is that we don't know its proportions at first and have to find out them with the get_metadata request. And after we received proportions from CHT, we can send render request with proper dimensions, revised according to current zoom. Therefore in the structure Picture we have to keep a list of callbacks, which we need to call for getting the metadata. Same problem and solution appears in case of display data in an exif_widget.
This code is a mesh of mutually tangled functions, especially functions
show_image
, show_image2
, render_image*
,
get_metadata*
, get_exifdata*
. You are advised not to
touch it unless you 100% understands it.
Here are some clues if you need to understand it:
Parameters for render requests are in the member list render_params of the
structure Picture. Parameters
get_metadata
/get_adv_metadata
are stored in the
structure pointed by
metadata_request
/exifdata_request
. If they point to
NULL
, there is no active request.
It is very important that data exchanged through register_cmd
(struct pending_request
) contain picture id. In the beginning of
the callback function it is necessary to check whether picture still exists
(get_pic(id) != NULL
). Pending_request also contains pointer to
structure with real parameters of the operation. It is necessary to check
whether this structure is still valid (if it is referred from the list in struct
Picture). If it is not, the structure is already released (probably because the
request has been aborted).
The code deallocating structure with parameters
render
/get_metadata
/ get_exifdata
has to
clear the reference in the proper list in the struct Picture, otherwise other
callback functions will use it.
One of the main modules working with the requests for CHT is the operations
module, where general callbacks for progress
, done
,
error
and abort
actions are defined. This is far
enough for simple operations, other more complex operations like
render_image
, render_thumb
have to implement these
callbacks themselves, since they have to be able to react to GUI request to
pre-render next and previous photographs with the lower priority and to update
this priority along with the movement of a cursor in a filelist or abort if
this is not necessary for current pictures at this time.
Progress callback allows to display the state of separate operations and to distinguish with different colors, which operations are processed now, and while the operation with a higher priority (with different color) doesn't finish yet, the progress bar of the operation with the lower priority isn't visible.
Since the operations are not processed immediately, but after render requirement of given picture, it is not necessary to allow user to abort them, because it is happen automatically in CHT along with the current requirements.
GUI offers repeating of the last operation on the picture. So achive this all function that change the picture have to register their name and their parameters. The registered function is also exploited when given operation is called on selected group of pictures. Then the registered operation is applied stepwise to each selected picture.
Since some operations make possible preview rendering, it is necessary to register this function (last repeat operation) after the eventual confirmation. Preview is implemented as any other operation, but if user aborts it, undo operation is called.
The most important function is an image rendering. It is achieved through the
named pipe. The name is sent by CHT in callback render_done. GUI data reads and
deserializes. Data serialization uses the gdk structure GdkPixdata and
gdk_pixdata_serialize()
and gdk_pixdata_deserialize()
functions to do the coding. In a similar way histograms are sent and when
histograms have to be rendered, they are composed transparently over the given
picture. Functions dealing with histograms are in lib/histogram.h
and GUI functions for rendering histograms are in "histogram issue" in
main_window.c
(functions draw_histogram()
and
draw_image()
)
One of the last requirements of the task specification was the possibility of
quick control with hotkeys. Here the need to adapt the GUI has arisen, because
by default it works with the focus and without it, many of functions are not
accessible. For the management of hotkeys the GtkUIManager has been used, where
GtkAction are defined some callbacks are associated with them and it is possible
to group callbacks to logical units which can be enabled and disabled at
once. Then using the xml, definition menu is created and a toolbar with assigned
actions too. With this concept it is also easy to change the menu
structure. Internal keys of gtk (arrows, page up/down, etc.) have to be handled
extra in each window (this is the docking system work) to accomplish the same
control keys for all docked widgets. Each hotkeys has associated some actions so
that it is easy to customize the widget control with assigning any other key to
any action (see the User guide and gtk-can-change-accels = 1
).
Further we have to control that all the keys should be disabled while in actions
like inplace edit mode using exif widget.
This widget (see almara/exif_widget/
) extends properties of
GtkTreeView and allows to edit advanced data and to display exif
information. Since the default keys for controlling GtkTreeView are filtered by
the main window it is necessary to simulate this control with actions and right
away it was extended by step by step edit mode (it edits editable items one by
one). Paging with the movement of the cursor without the need of focus
(shortcoming of gtk_utils). Data displayed by exif widget are stored in hash
table with the string key, exif data are stored under their id, which is
returned by libexif and the name and description are stored by an exif widget
itself using the libexif. While display data of the next image, data of the
previous image are invalidated by set of invalid flag but not deleted because
information about the visibility and rank of items and edit-ability etc. are
still necessary. If all data are set invalid information about the current
position of the cursor is preserved and used after new data are loaded. During
inplace editing all actions of GUI are forbidden and are allowed after the
editing finishes. Position, visibility of items and columns and saved in config
file.
The runtime of hallmara
is based on GTK main loop. Before this
main loop is started, there is only a few things to do:
The main loop started just after this initialization rests till the end.
hallmara
allows all the functionality mentioned (and explained) in
chapter "Provides (what and how)".
The main window of hallmara
was designed in Glade, but all the
next features and look was done manually. Glade files were removed.
Libraries developed for almara project, which are used by
hallmara
are:
Besides well known headers such as stdio.h
,
string.h
or sys/stat
, these less common are
included:.
This won't (and neither cannot) be long. We just want to say some terms used in documentation and also in comments in source code.
Album list is the left part of the window. It consists of two
columns, first with picture (sometimes called thumbnail), second with
some text lines with some information on album (simply info). The
second, right part of the window, is called album preview. It has three
vertically organized segments. At the top, there is big red title of the
album. Below, there is a photo index, many small thumbnails from the
album. At the bottom, there is fullinfo list with complete set of
information about album (including those from info column and some extra).
Current album or focused album is the album under the cursor, the
album, on which all the commands are applied. Temporary album -- album
created for photos in read-only directory. Because the album.alb file cannot be
written there, it is written into /tmp
and this enable user to
view the album and export eventual changes to another destination. The
directory ~/.almara/albums
, where all the albums are linked to, is
called albums home.
Some data structures and variables interesting to mention:
struct alb_header
-- belongs to one album and carries whole
information about it: both from album.alb (such as title or user defined info)
and from file system (such as path or disk size of the album). Four items from
album.alb are default (title, description, date and album-owner), the rest is
undefined. These items are stored in linked list, in structures included
"name", "value" and the "next" pointer. They split into two groups: user
defined (user conceives them, sees them, edits them) and inner (user doesn't
either know that they exist). Both are in the structure, in the same list, but
only those from first group are displayed, changable, searched, ...album_table
-- a hash table, which keeps all struct
alb_header
s shown in album list. Key is a directory name, value is a
whole struct.current_album
-- is a pointer on struct
alb_header
belonged to currently focused album.album_list_store
and album_list
-- GTK list store
and GTK tree view; keeps data for and shows data in album list. It has three
columns: first pixbuf column for thumbnail, second markup text for short info
and third hiden for identifier, the directory name, which is the key in the
album_table
hash table.fullinfo_list_store
and fullinfo_list
-- the
same, just for data in fullinfo list. It has two text columns, for name of
the item and for its value.nof_albums
-- number of albums displayed in album list and
stored in album_table
.nof_recents
-- number of recent opened albums to save. It is
saved also into the configuration file (this number as well as albums
themselves).recents[]
-- is an array of nof_recents
pathes to
recent opened albums.rec_nam[]
-- is an array of nof_recents
names of
recent opened albums. This string is displayed in menu.struct running_almara
-- includes only pid of the almara
process and the path to the album, which is changed by this process.almaras
-- glib single list (GSList) of all running almaras.
Includes struct running_almara
s.struct search_strc
-- keeps just searched pattern. Detailed
description in doxygen, file hallmara/album_list.c
.Hallmara
can show known albums: All albums which were
ever opened (and was not temporarily neither deleted by user) have their own
link in ~/.almara/albums/
. This link points to the directory,
where all the photos are and where album.alb file was created. At every start
of hallmara
, this directory is walked and all correct albums are
shown in the album list. (Incorrect and/or without album.alb are reported and
unlinked.) "Shown in the album list" represents reading of album file,
filling the struct alb_header
, pushing this struct into the
album_table
, inserting a row into the album list and displaying
the thumbnail and the information.
Hallmara
can delete an album: Removing of album means
only unlinking it from albums home and removing it from
album_table
. Deleting means also delete all files in the directory
(no subdirectories) and delete a cache directory; then unlink.
Hallmara
can open album: Hallmara
find the
current album, takes its path and run almara_album
on it. Before
this, it creates new struct running_almara
, prepend it to the
almaras
list and register a callback to know, when this Almara
ends.
Hallmara
can open unknown album: If you remove album
from album list or you have album from elsewhere, you can open it. The
directory is searched for album.alb, and if it is found, it is linked into the
albums home. The name of the link is the last part of the path. If this name
already exists (and doesn't link at the same directory), the "name_2" is tried
(and so on). And then it is shown same as in show known albums. At the
end, almara
is run on new album.
Hallmara
can create (and open) new album: If it is as in
previous case but no album.alb is found, the directory is tested for write
permission. Then album.alb is created (using almara_mkalb) either standardly in
the photo directory or (in case that this directory is read-only) in temp. If
album.alb was not created only temporarily, the album is linked (as we know
from previous paragraphs). And (in both cases) almara_album
is
run.
Hallmara
can open recent: Every opened album (not movie
and not a temporary album) is appended to recents. That means, that it appears
in File|Recent albums menu at the top (pushing older albums down and removing
the oldest recent album). Recents are keeped in two arrays,
recents
includes the paths and rec_nam
includes the
titles. Title is shown in menu, path is opened if it is clicked (and path is
also compared when inserting new). These two arrays are filled at the start of
hallmara
from almara.conf
file and also saved there
at the closing of program. Inserting of new starts with searching, if this
album isn't there yet. If so, the oldest recent album isn't removed and only
small shuffle is done. Album "survives" in recents either its removing, so you
can open still existing, but removing album in this way.
Hallmara
can open mpegs: Its not hallmara
's
credit. It simply runs almara_album
and album argument replaced
with movie. almara
runs CHT and CHT runs mjpeg2jpegs
.
And on this result almara
works then.
Hallmara
can show casual photo index: The photo index is
created very simply: hallmara
only takes first PHINDEX_H *
PHINDEX_W pregenerated thumbnails from an album cache. If there are less
thumbnails (or none), it doesn't matter, fewer (or nothing) will be displayed.
This index is only as actual as the cache is.
Hallmara
can edit album info: Editing album informations
is done through a simple dialog. After this dialog is closed (by OK), all
informations are rewritten in struct alb_header
and immediately
written into the album.alb. It is done in that way, that album.alb is opened
twice (for reading and for writting). New header is written "over" old
album.alb and then the rest of the old file is read and rewritten into the new.
The album_parser library allows this. The edit dialog also provides
potentiality to add own user defined information. The new list (see
struct alb_header
) is created from this user defined items, it is
merged with old list to keep the inner items and then the old is freed and is
replaced by this new.
Hallmara
can reload new album info: As it was mentioned
in open album, the list of all running almaras is kept and whenever some
almara ends, the callback is called. This (dead) almara is removed from an
almaras
list and reload is undertaken. It aggregates reloading of
title, photo index, short info and fullinfo.
Hallmara
can block more almaras on one album: Before any
almara is run, the almaras
list is searched. Only if the current
album is not served by almara
, only then almara_album
is run. Identically, if editing should start, this list is tested.
Hallmara
can search: It searched regexp in one of four
default items, in path or in user defined items (those given by regexp, again),
or in several of them. After first search, the patterns and the found position
is kept, so next search searched next matching pattern. More detailed info in
doxygen, file hallmara/album_list.c
.
Module almara_print_bin
is individual binary, which is run from
either shell or (through the wrapper almara_print
) from
almara_album
, GUI. This module gets many parameters and
descriptions and can print for example 6 images on a page, all images with a
filename and a description and an exposition time.
mjpeg2jpegs
This binary takes an input movie, cut it into separately frames (.jpg) and
do the same thing with eventual audio track (.wav). Then any operations on
frames are permitted. And jpegs2mjpeg
can be run after that.
jpegs2mjpeg
It takes a directory of same sized photos and stick them together into a movie. Also put together the audio track, if there are any parts of it.
almara_mkalb
This script -- using for creating album files (see Appendix C for more info)
in image directories -- is written in Python. All its outputs are converted to
UTF-8. Because almara_mkalb
accepts many arguments (see user's
guide or type almara_mkalb --help
for them), large part of script
parses them.
Variable destdir
keeps the important directory (either where
album is, or where album will be -- in case of --copy-to or --move-to
argument). Variable existing
includes all photos (with all
information) from old album (if --append was set) or is empty (otherwise). Then
it places all participating files (depends on --recursive option and on list of
file masks) into files
, but removes those that was already in
album file (if --append).
Then the album file itself is creating. Title of album is a name of directory by default, Album-owner is login name by default. After header is written, file by file is taken, .wav sound is assigned (and other items from arguments).
File .welcome.jpg (shown in hallmara in an album list) is also created by
almara_mkalb
. It is first image scaled into the box 120x100
px.
almara_mergalb
This script -- using for merging one album file into another -- is written
it Python too. It takes image by image from source album file. If
image.jpg
already exists in destination directory, it tries
image-1.jpg
, then -2
etc. When the free suffix is
found, the <first>/image.jpg
is copy (or move: depends on
--move
argument) to
<second>/image-<suffix>.jpg
. And -- if exists -- also
sound.wav
to sound-<suffix>.wav
. The
appropriate rows from first album file are append to the destination album
file.
almara_mkalb
and almara_mergalb
can be run
directly from shell and also almara_mkalb
is and
almara_mergalb
will be used by hallmara
, which calls
them.
almara_webexport1_bin
This script (also written in Python) is on contrary run only from CHT.
Because it expects on stdin data in special format. Is is list of couples of
key and value. These couples are separated by newlines and spaces, tabelators
and newlines inside one line are replaced by strings \_
,
\t
and \n
respectively. First set of couples carries
information about entire album, then empty line follows and all next sets
belong to individual images. CHT sends this information from album file. Now it
sends all of them, but it may send only those items, which are to place on web.
In first set (about album) the item "Title" is guaranted, in other set
(about image) items "File" and "Filename" must be included.
CHT must export the album to the special directory, before it calls
almara_webexport1_bin
. All this files (usually with thumbnails) it
gives to the script in Filename: path/file
and Thumbnail:
path/file
on stdin. From this, almara_webexport1_bin
gets
almost everything -- and the destination directory is the only argument of this
script.
almara_webexport1_bin
create index.html and n
html
pages for n
images (and main.css). All these pages are valid XHTML
1.0 Strict. Yes, the style of pages is wired in. But there are two possitive
points. 1) Style is given, but appereance of the style is user defined as much
as Cascade Styles permit. Every tag has its own class with an apposite name and
those classes are all prepared for defining in main.css (only some of them are
defined and are intended for reedit). 2) There will be also
almara_webexport2_bin
and almara_webexport3_bin
and
others some day, which will provide more styles.
almara_webexport
This script is finally not written in Python, but in shell. Is very simple
and its only mission is to run CHT and gives it commands as if it isn't shell
script but almara GUI. Then CHT executes all the work necessary for export for
web (mentioned in previous paragraphs) and runs
almara_webexport1_bin
.
It sets handlers for SIGINT and SIGTERM, creates two named pipes in almara
temp directory in /tmp
and runs CHT with these two pipes as
stdin
and stdout
. Then sends to CHT command for
openning album, for web export, waits until CHT is done and returns
done
and then closes album and send exit
to CHT.
That's all. Everybody can write his own script for
chaerwottoch
.
Image library (see libimg
or a source code in the
lib
subdirectory) makes possible all the work with images of
different graphic formats. It utilizes functions from other graphic libraries,
mainly ImageMagick library, which does most of the operations (see
almara/operations.h
or doxygen docs to get the list of all
supported operations) on an image.
It handles cases, when external program expects data on standard input, transparently saves the data to temporary files using almara's tmp support. It also backs up files if an extern program modifies directly the input file. Thanks to this support of extern programs it has been possible since the beginning of the project to implement lossless operations with the available lossless programs jpegtran and exiftran, which are able to do lossless operations like rotate, flip and crop. Now this functionality provides already library libjpegtran which has been created from the source code of jpegtran and allows lossless operations on pictures located either in a file or in a memory and thus makes the image processing faster. In addition lossless support using this library allows to find out if given operations on the given picture can be processed without sideffects inherent to an implementation of the jpeg format, which stores data in blocks, so operations are correctly processed only if the size of an image is a multiple of the size of a block.
The library itself provides computation and rendering of a histogram, converts image data between different bpp (bit per pixel), serializes data for transfer through pipe (this operation is used by CHT when sending data to GUI).
Most of the operations support display of progress bar with the name of the currently processed operation and phase the operation is in and react to user abort if possible.
Since we do not want to link GUI with this library, we provide in file
img_formats.h
all information about supported formats, color
channels and scale filters including explaining description.
Auxiliary functions used by other components are stored in directory
common
.
album_parser.h
This library provides simple and uniform access to album.alb in reading (same as in writting) from (into) it in all parts of Almara. Returns line by line splited into the couple key and value.
config_parser.h
All modules of Almara uses the same library config_parser
for
accessing configuration from their configuration files. They are stored in
.almara
directory in user's home. Configuration library allows
users to make changes in configuration -- without losing comments they were
made -- when configuration files are rewritten.
debug.h
Useful for debugging of Almara, allows simple print-out of debugging
messages, if the project is compiled with the DEBUG
macro. It can
be easily configured using ALMARA_DEBUG
environment variables (see
description on the file common/debug.h
). Messages are divided
according to domains and it is easy to set on only that ones we need. Domains
can be dynamically created or default domain for given component can be used.
Default domain is registered in Makefile
. It is possible also to
add to each message the current time, number of process and name of the function
and line in the source code the debug message is called from. In addition
messages can be sent to a file or to stderr
. If you want to use it
you have to call debug_init()
first and then use macro
DEBUG_MSG("domain name", "debug message\n");
. For further info
about debug configuration see doxygen description of debug.h
.
file_utils.h
Includes functions with similar effect as mkdir -p
,
cp
, rm -rf
and mv
. And functions to
split extension and to create a file with a unique name (adding increased
infix).
gtk_utils.h
Functions for joining non-empty strings and for manipulating cursor of GtkTreeView (for moving to a next, previous, first and last image and previous and next page without change of the GtkTreeView focus).
io_parser.h
It provides comfortable reading and writing according to IO protocol between
chaerwottoch
and the GUI. It handles all neccessery escaping (for
these escape sequences see Appendix A).
pixmap.h
Functions for register directories with pixmaps and their load or register as icons for toolbars and menus.
tmp.h
Functions for creating global temporary directory and local directories for separate processes. Also helps the library to reserve temporary files for external programs, when the file origins after the execution of given program and is reserved before using locks, which blocks creation of temporary files with the same name originating after the reservation.
type_convert.h
It converts little and big endians to host byte order and vice versa. It is
need for mjpeg2jpegs
and jpegs2mjpeg
.
Project takes advantage of the modern development tools
autoconf
and automake
to check whether proper
versions of all necessary libraries and other development tools for compiling
are present in the system and then to achieve conditional compiling according
to their presence and easy configuration to match user requirements. We then
utilize libtool to let user choose if she wants to link with static or shared
libraries.
To use only shared libraries use configure --enable-shared
--disable-static
for static only libraries configure
--enable-static --disable-shared
.
First version GUI was designed in Glade, but because of its breakneck control and especially lack of support of some desired features, we decided to write all dialogs on our own without the help of the Glade and finally stop using the Glade at all.
Also hallmara
was started in Glade. But after creating main
window and impressing the figure, there were o more need of Glade. So it was
removed later.
Almara supports gettext and therefore it is very easy to translate all texts
to another language and thanks to automake also easy to maintain. Basic file
po/almara.pot
for localization is created and updated using
Makefile
(make almara.pot
). When .po
file for given language doesn't exist yet, copy the file
almara.pot
to a file id_of_the_language.po
(e.g. for
czech cs.po
) and add record to the file po/LINGUAS
.
When the .po
does exist it is enough to update it using make
update-po
in po directory and to translate all sentences (we recommend
kbabel to do this).
Every command or respond is terminated with newline ('\n'
character).
Command or respond consists of one or more tokens. Tokens are separated by any
amount of spaces (' '
) or tabs ('\t'
) or any of their
combinations.
If there is a need to include reserved character ('\n'
, ' '
,
'\t'
, '\'
) in a token, it has to be escaped by backslash
('\'
) character:
\
{space}\t
\n
\
have to represented as \\
Token can be a string or a number. Numbers are represented as strings in ASCII in C locale (eg. "3.14159").
Special token \0
(ie. token containing exactly '\\'
followed
by '0'
) means empty string (""
).
Communication is done through stdin and stdout. Command or respond is written to stdout in whole, so it can be read in blocking mode at the other side.
When ChT successfuly starts, it sends "OK
" command (ie. token
"OK
" followed by newline).
Commands for ChT have typically this form:
<string command> <int cid> [<parameter1>
<parameter2> ...]
Where <cid>
is integer choosen by sender of the command. It has to be unique
through the time ChT is run (or at least unique through the time one album is
opened).
Possible responses from ChT:
<cid> <string respond> [<parameter1> <parameter2>
...]
Where <cid>
identifies command. <respond>
is one of these strings:
done
error
aborted
progress
"done
" response:
<cid> done [<parameter1> <parameter2> ...]
- Tokens following "done" are command-specific and are explained below.
"error
" response:
<cid> error <string message> [<parameter1> <parameter2> ...]
- "error" token is followed by token containing localized error message
possible followed by other parameters (see commands explanation below).
"aborted
" response:
<cid> aborted
<cid>
, priority of that command was set to zero or that
command was undone while still running.<cid>
. It is caused by
asynchronous command execution."progress
" response:
<cid> progress <int amount> <string comment>
<amount>
is integer in range 0 to 100 indicating state of progress in
percents or number greater than 100 if percentual amount of progress
cannot be counted. <comment>
denotes current operation.Every command is described at several lines. First line contains format of command. This line can be followed by possible responses from ChT. Following lines contain description of command.
Possible responses follow these rules:
open <cid> <string albumname>
<cid> done <int album_type> <string title>
- Opens album.
- <album_type>
is 0 when album is a normal album, 1 when album belongs to
MJPEG.
close <cid> <int no_save>
<cid> done
- Closes album. May take some time.
- <no_save>
should be 0. If close fails, it means album cannot be written.
In such case it is allowed to recall "close" with <no_save>
set to 1.
save <cid>
<cid> done
- Saves album to disk. Can be called periodically to minimize risc of data
loss.
- It should be called after every "commit".
exit <cid>
<cid> done
- After sending "done" ChT will terminate.
abort <cid>
- Aborts request with given cid. No response is given (except for
"<cid> aborted
" sent by aborted request itself).
set_priority <cid> <int priority>
- Sets new priority to request with <cid>
. No response is given by ChT.
- <priority>
can be in range 3 (highest) to 0 (lowest). Requests at
priority 0 can be aborted at ChT's discretion.
list <cid>
<cid> done <phid1> <filename1> <description1>
<phid2> <fname2> <desc2> ...
<cid> done <<photo1>> <<photo2>> ...
where every <<photo>>
consists of:
<int phid> <int order-id> <string filename>
<string desc> <string soundfile> <string keywords>
- Returns list of photos in album. <phid>
is integer which uniquely
identifies photo, <order-id>
order identificator of the photo, <filename>
physical name of file with photo, <desc>
textual description of the photo
and <soundfile>
physical name of attached sound or empty string if there
is no sound attached
list_one <cid> <phid>
<cid> done <order-id> <filename> <description> <soundfile> <keywords>
- One-photo-only version of "list" command.
render <cid> <phid> <priority> <int w> <int h>
<cid> done <filename>
- Renders image in specified dimensions. Filename is choosen by ChT and
typically it will be named pipe. Rendering works at work size.
render_full <cid> <phid> <priority> <int w> <int h>
<cid> done <filename> <int size>
- Like "render", but works at full size (and not at work size) and if source
image is JPEG, compression level is also applied.
- If source image is JPEG, a size of saved JPEG file is returned in <size>
.
In other cases, zero is returned in <size>
.
thumb <cid> <phid> <priority> <int width> <int height>
<cid> done <filename>
- Renders thumbnail with specified maximal dimensions. Although dimensions
can be specified, it is assumed they will be constant and they will never
change.
histogram <cid> <phid> <priority> <int channel> <int width> <int height>
<cid> done <filename>
- Renders histogram and returns it twice. The first returned histogram is
linear, the second one is logarithmic.
get_metadata <cid> <phid> <priority>
<cid> done <string key1> <value1> <key2> <value2> ...
- Returns metadata for given photo. Most noticable keys are "width",
"height" and "depth".
get_adv_metadata <cid> <phid> <priority>
<cid> done <string key1> <value1> <key2> <value2> ...
- Returns advanced metadata, ie. exif and user-defined data. For exif, keys
are represented numerically, values are localized strings. For
user-defined data, keys are in form "User-XXX". "Compression" and "Volume"
may be returned too. If photo has sound attached, metadata always contain
"Volume" (and vice versa).
rotate <cid> <phid> <int degrees>
<cid> done
flip_horizontally <cid> <phid>
<cid> done
flip_vertically <cid> <phid>
<cid> done
crop <cid> <phid> <int x> <int y> <int w> <int h>
<cid> done
scale <cid> <phid> <int w> <int h> <int filter> <int blur>
<cid> done
- For <filter>
and <blur>
values see lib/img_formats.h
(note that they are
sent as integers, not as symbolic constants).
scale_adv <cid> <phid> <int w_type> <int h_type> <double width> <double height>
<bool aspect_ratio> <bool enlargeable> <int filter> <int blur>
- Advanced scale. <w_type>
and <h_type>
can be:
0: scale to given size (like simple "scale", but takes into account also
<aspect_ratio>
)
1: scale to given ratio (<width>
or <height>
contains value in range
0.0 to 1.0, <aspect_ratio>
is taken into account)
2: scale image to fit in given bounding box (<width>
or <height>
contatins bounding box, <aspect_ratio>
and <enlargeable>
is taken
into account)
3: ignore <width>
or <height>
modulate <cid> <phid> <int brightness> <int saturation> <int hue>
<cid> done
despeckle <cid> <phid>
<cid> done
enhance <cid> <phid>
<cid> done
equalize <cid> <phid>
<cid> done
invert <cid> <phid>
<cid> done
normalize <cid> <phid>
<cid> done
sharpen_contrast <cid> <phid>
<cid> done
dull_contrast <cid> <phid>
<cid> done
change_metadata <cid> <phid> <string key1> <value1> <key2> <value2> ...
<cid> done
- Sets metadata. Accepted keys are:
= "Description"
= "Compression" (sets per-photo compression level)
= "Volume" (sets volume of attached sound in percents)
= "User-XXX" (where XXX represents any value)
= no other keys are accepted.
- Passing empty value means deleting of a metadata.
reorder <cid> <phid> <oid>
<cid> done
- Changes order-id of a photo.
delete <cid> <phid>
<cid> done
- Marks photo as deleted.
delete_sound <cid> <phid>
<cid> done
- Marks attached audio file as deleted.
undelete <cid>
<cid> done <phid1> <phid2> ...
- Undeletes all photos. Returns added phids or phids with added sound file.
edit_external <cid> <phid> <string prg_name> <string parm1> <parm2> ...
<cid> done <int deleted>
- Runs external program at given photo. Photo is committed first (so no undo
is possible). Program doesn't need to be AEP conformant.
- <prg_name>
is name of program to run, <parm>
s are not mandatory and are
passed to program as parameters. Parameter consisting of "{}" is replaced
by photo filename. If no such parameter is specified, photo filename is
passed as a last parameter.
- "done" is returned when specified program finishes. If photo was deleted
during running of external program, <deleted>
is 1.
- After every edit_external, "save" should be called.
- Even if error is returned, photo might be committed! Call undo_list_global
to update undo list.
edit_audiotrack <cid> <string prg_name> <string param> <phid1> <phid2> ...
<cid> done
- Runs external program at audiotrack. Sounds attached to passed photos are
pasted together and passed to given program.
- If no phids are listed, all photos are used.
undo_list_global <cid> <int max>
<cid> done <int undoid1> <string name1> <undoid2> <name2> ...
- Gets list of undoable operations, last used first. Returned names are
localized. <max>
is maximal number of returned undo items.
undo_group_start <unused>
- Starts undo group. No response is sent by ChT. <unused>
parameter should
be 0.
undo_group_end <unused>
- Ends undo group. See undo_group_start for explanation.
undo_global <cid> <int undoid>
<cid> done <<list1>> <<list2>> <<list3>> <<list4>> <int relist>
where every <<list>>
consists of:
<n> <phid1> <phid2> ... <phid_n>
- Undos one or more operations. <undoid> contains number of operations to be
undone and it corresponds to number returned by undo_list_global.
- <<list1>>
is a list of photos removed by undo.
<<list2>>
is a list of photos whose metadata was modified by undo.
<<list3>>
is a list of photos modified by undo,
<<list4>>
is a list of photos added by undo and
<relist>
is 1 when new "list" command should be performed (order-ids
changed)
debug <parm1> <parm2> ...
<parm1> <parm2>
- Local echo :-)
export <cid> <priority> <string directory> <phid1> <phid2> ...
<cid> done
<cid> error <message> <phid1> <phid2> ...
- Exports photos to given directory. If no phid is given, all photos in
album are exported.
- If error occured, not exported photo ids are returned.
external <cid> <priority> <string prg_name> <int thmb_x> <int thmb_y> <string dir> <phid1> <phid2> ...
<cid> done
<cid> error <message> <phid1> <phid2> ...
- Exports photos through given external program. If no phid is given, all
photos in album are exported. Program MUST be AEP (Almara external
protocol) conformant.
- <thmb_x>
and <thmb_y>
contain sizes (bounding box) of thumbnail. If they
are equal to 0, no thumbnail is generated. For some programs, thumbnails
have no sense (eg. almara_print, jpegs2mjpeg).
- <dir>
is passed to external program. Not every program uses it.
- Use "almara_print_bin" as <prg_name>
to print photos. almaraprint-bin
doesn't make use of <dir>
parameter.
- If error occurred during exporting photos for external program, not
exported photo ids are returned and external program was not run. If error
occurred during run of external program, no phids are returned.
commit <cid> <priority> <reserved> <phid1> <phid2> ...
<cid> done
<cid> error <message> <phid1> <phid2> ...
- Commits given or all photos (ie. performs image log in place).
- Like "export". <reserved>
should be 0.
- When no phids are specified, commit is performed also on deleted photos
(ie. they are physically deleted).
- After every commit, "save" should be called.
play_sound <cid> <phid> <priority> <string prg_name> <string parm1> <parm2> ...
<cid> done
- Plays sound through given external program.
- For explanation of <prg_name>
and <parm>
s see edit_external.
copy_from <cid> <phid>
<cid> done <string key1> <string value1> <key2> <value2> ...
- Sends data needed for copy and paste between ChTs.
paste_to <cid> <string key1> <string value1> <key2> <value2> ...
<cid> done <phid>
- Adds new photo to album. Keys and values are those obtained from
"copy_from" command (order of keys MUST be preserved).
- As a result, photo id of newly added image is returned.
set_globals <cid> <string key1> <value1> <key2> <value2> ...
<cid> done
- Sets global options for ChT. It is not necessary to wait for "done",
settings are guaranteed to have effect right after sending command (but
for new requests only, old requests are not affected).
- As some of settings are loaded from/saved to album file, set_globals can
be called only when album is opened.
- Currently allowed settings (<key>
), saved in album file:
= "compression": sets compression level (0 to 100, 100 is the best)
= "export_fmt": "copy" or ".extension" (see lib/img_formats.c for full
list)
- Currently allowed settings, not saved in album file and not returned
by "get_globals":
= "gamma_r", "gamma_g", "gamma_b": sets gamma value (in doubles)
= "work_width", "work_height": sets size of work image (warning: this
can come in effect very slowly because already cached images are not
affected even if new operations are performed on them)
= "cache_size": sets maximum size of cache
= "scheduler": sets scheduler behaviour to given level. The level roughly
corresponds to number of processors (but it has a sense to use lower or
higher number depending on computer performance).
get_globals <cid>
<cid> done <string key1> <value1> <key2> <value2> ...
- Returns actual global options. This include:
= Title of album (key "title").
= Settings saved in album file (see set_globals).
= Other informations found in album header.
- Can be called only when album is opened.
Almara uses a number of external programs. To communicate effectively with them but allowing also their independent usage, Almara External Protocol (AEP) was defined.
Examples of AEP-conforming programs are mjpeg2jpegs, jpeg2mjpegs, almara_mkalb, almara_print, almara_webexport, etc.
AEP program sends its status to stderr and receives input (when requested) from stdin. Stdout is reserved for application use and is usualy connected to a terminal.
Empty lines are ignored.
Every line should start with a keyword, possibly followed by a space character (' ') and code (depending on the keyword), followed by colon (:). The rest of line is ignored by reader and is used to output detailed information when running independently. Every line is terminated by newline ('\n').
Possible keywords are:
DEBUG
INFO
WARNING
ERROR
DONE
PROGRESS
READING
When PROGRESS
have been sent, the line can also consist of number, space and
percent sign (%).
DEBUG
keyword
Used to output debug messages. They should be ignored by caller.
INFO
keyword
Used to output informational messages for user running AEP program independently. Caller should ignore it.
ERROR
keyword
ERROR
keyword is followed by error code. Error codes are defined and reserved
for individual applications as follows:
almara_mkalb
and almara_mergalb
).almara_print
.almara_webexport
.Program must not emit any message after ERROR
.
WARNING
keyword
WARNING
is similar to ERROR
(and it shares the same error codes), but problems
reported by them are nonfatal. Caller should inform user about this warnings.
DONE
keyword
Program finished its job successfully. No message may be emitted after DONE
.
PROGRESS
keyword
When seen for the first time, this keyword indicates start of progress. When seen second time, it indicates end of progress. Every program can emit at most one progress during its run.
<number> %
Progress is sent as number followed by space (' '
) followed by percent sign
('%'
) followed by carriage return ('\r'
). Line is NOT terminated by newline
('\n'
). <number>
can go from 0 to 100.
It is allowed for DEBUG
, INFO
, ERROR
and WARNING
keywords to appear during
progress.
READING
keyword
AEP program wants data at its stdin. AEP program may not send anything to stderr (else deadlock will occur). Program may close stdin anytime during reading, if it detects some error.
READING
has two variants:
"READING:"
- program wants data in almara_io format."READING filenames:"
- program wants data in simple-input format (one
filename at a line and nothing else).Album file is a text file named album.alb (in some rare cases it can have another name - eg. album temporary made for read-only directory).
Lines beginning with '#' are ignored. Empty lines delimit sections in album
file. All other lines consist of key, followed by separator - colon and
space (": "
), followed by a value. If value spreads more lines (ie. it
should contain '\n'
), the key and separator are repeated at every line.
First section in album contains information about album. There is only one mandatory item: "Title". It contains the name of album.
Next sections contain information about photos, one photo per section, up to the end of file.
This is the header of album.alb
with information about
album.
Title: title of album Description: detailed description of album Album-owner: name of author of album Date: date when album was made Eport-format: default export format Compression: default compression Compression: Compression is integer 0-100. User-blahblah: user defined item and its value
This is a structure of the rest of album.alb
.
File: filename (without path) Description: description of image Order-id: order of photo in list of photos Author: author of the photo Sound: name of attached sound file (without path) Volume: volume of attached sound file, integer 0-100. Operation: log of operations performed at image, Operation: one per line (for list of operations, see Operation: Appendix A) Deleted: photo is marked as deleted Sound-deleted: attached sound is marked as deleted User-XXX: user-defined fields
GPL header, in every file, like this:
/** @file relativ/path/with/this/file/name.c * Short description of this file. * @author Author1 * @author Author2 * * Optionaly detailed description of this file * on more lines. */ /* * This file is a part of Almara project * (http://almara.sourceforge.net/). * Copyright (C) 2004 Almara team * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software * Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will * be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public * License for more details. */
Use whole sentences. For each modification use new line. Or you write the theme, colon and then every change on new line, begin with "- ".
Example:
I done some bugfixes. Library: - I change the interface - added some new functions
Use Doxygen! Style JavaDoc.
if ([cond]) { [command]; ... [command]; } else { [command]; ... [command]; } if ([cond very very very very very very very very long] && [cond2very very very very very very very very very long]) [the only command]; switch ([var]) { case 0: [command]; [command]; break; case 1: [command]; break; }
void bar([type var], [type var]) { [command]; ... [command]; return 0; // i.e. SUCCESS; if error, you must return // minus error number; }
#ifndef _FILENAME_H
Use spaces around every operator (except ".
", "->
", "++
" and "--
");
unary operator '*
' and ampersand are missing the space from right;
(and also unary '-
').
As you can see above, parentheses are closed to inside, i.e. " (blah blah) "
In "function([var])
" there is no space before '('.
You MAY use more spaces than you OUGHT TO (according to this CodingStyle), if it makes your code nicer in being verticaly indented.
(From docs/templates/template.c)
/** @file [[[ relativ/path/with/this/file/name.c ]]] * [[[ Short description of this file. ]]] * @author [[[ Author1 ]]] */ /* * [[[ GPL Licence ]]] */ [[[ #include "almara.h" ]]] [[[ #include "template.h" ]]] [[[ declarations here ... ]]]
(From docs/templates/template.h)
/** @file [[[ relativ/path/with/this/file/name.h ]]] * [[[ Short description of this file. ]]] * @author [[[ Author1 ]]] * @author [[[ Author2 ]]] * * [[[ Optionaly detailed description of this file * on more lines. ]]] */ /* * GPL Licence */ #ifndef _[[[ relativ_path_with_this_file_name.h \ (slashes converted to _ and everything \ is uppercase) ]]] #define [[[ same as above ]]] /* include almara's global header file */ #include "almara.h" [[[ definitions here ... /** * First sentence is Brief description of following * sample_function. * Continued with Detailed description of this function * on more lines (sentences). * * @param param1 is for ... * @param param2 is a something ... * @return description of return values */ int sample_function(int param1, int param2); /** * @defgroup NameOfGroup First sentence is Brief * description of group of something (e.g. special * macros...). Continued with Detailed description of * this function on more lines (sentences). */ /*@{*/ #define MACRO1 1 /**< description of group member1. */ #define MACRO2 1 /**< description of group member2. */ #define MACRO3 1 /**< description of group member3. */ /*@}*/ /** * First sentence is Brief description of following enum. * Continued with Detailed description of this enum * on more lines (sentences). */ typedef enum { ITEM_1, /**< description of enum item 1 */ ITEM_2, /**< description of enum item 2 */ ITEM_3, /**< description of enum item 3 */ } SampleEnum; ]]] #endif