2013-05-31 13:05:15 +04:00
|
|
|
/*
|
|
|
|
* Copyright 2012 - 2013 Michael Drake <tlsa@netsurf-browser.org>
|
|
|
|
*
|
|
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
|
|
*
|
|
|
|
* NetSurf 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; version 2 of the License.
|
|
|
|
*
|
|
|
|
* NetSurf 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.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
* Treeview handling (interface).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _NETSURF_DESKTOP_TREEVIEW_H_
|
|
|
|
#define _NETSURF_DESKTOP_TREEVIEW_H_
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include "desktop/core_window.h"
|
2013-07-01 14:47:46 +04:00
|
|
|
#include "desktop/textinput.h"
|
2013-05-31 13:05:15 +04:00
|
|
|
#include "utils/types.h"
|
|
|
|
|
2013-07-03 16:48:53 +04:00
|
|
|
typedef struct treeview treeview;
|
|
|
|
typedef struct treeview_node treeview_node;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-07-25 19:06:34 +04:00
|
|
|
enum treeview_node_type {
|
|
|
|
TREE_NODE_ROOT = (1 << 0),
|
|
|
|
TREE_NODE_FOLDER = (1 << 1),
|
|
|
|
TREE_NODE_ENTRY = (1 << 2)
|
|
|
|
};
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
enum treeview_relationship {
|
2013-06-04 15:24:14 +04:00
|
|
|
TREE_REL_FIRST_CHILD,
|
|
|
|
TREE_REL_NEXT_SIBLING
|
2013-06-18 19:02:31 +04:00
|
|
|
}; /**< Relationship between nodes */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-07-02 20:30:52 +04:00
|
|
|
typedef enum {
|
2013-08-30 15:51:40 +04:00
|
|
|
TREE_OPTION_NONE = (0), /* No flags set */
|
|
|
|
TREE_OPTION_SUPPRESS_RESIZE = (1 << 0), /* Suppress callback */
|
|
|
|
TREE_OPTION_SUPPRESS_REDRAW = (1 << 1) /* Suppress callback */
|
2013-08-30 16:29:19 +04:00
|
|
|
} treeview_node_options_flags; /**< Node change handling options */
|
2013-07-03 14:19:16 +04:00
|
|
|
|
|
|
|
typedef enum {
|
2013-07-03 14:50:23 +04:00
|
|
|
TREEVIEW_NO_FLAGS = (0), /**< No flags set */
|
|
|
|
TREEVIEW_NO_MOVES = (1 << 0), /**< No node drags */
|
|
|
|
TREEVIEW_NO_DELETES = (1 << 1), /**< No node deletes */
|
|
|
|
TREEVIEW_READ_ONLY = TREEVIEW_NO_MOVES | TREEVIEW_NO_DELETES,
|
|
|
|
TREEVIEW_DEL_EMPTY_DIRS = (1 << 2) /**< Delete dirs on empty */
|
2013-07-03 14:19:16 +04:00
|
|
|
} treeview_flags;
|
2013-07-02 20:30:52 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
enum treeview_msg {
|
2013-06-18 19:02:31 +04:00
|
|
|
TREE_MSG_NODE_DELETE, /**< Node to be deleted */
|
|
|
|
TREE_MSG_NODE_EDIT, /**< Node to be edited */
|
|
|
|
TREE_MSG_NODE_LAUNCH /**< Node to be launched */
|
2013-05-31 13:05:15 +04:00
|
|
|
};
|
|
|
|
struct treeview_node_msg {
|
|
|
|
enum treeview_msg msg; /**< The message type */
|
|
|
|
union {
|
2013-07-15 17:34:56 +04:00
|
|
|
struct {
|
|
|
|
bool user; /* True iff delete by user interaction */
|
|
|
|
} delete;
|
2013-05-31 13:05:15 +04:00
|
|
|
struct {
|
2013-08-20 18:44:24 +04:00
|
|
|
lwc_string *field; /* The field being edited */
|
2013-06-04 18:03:07 +04:00
|
|
|
const char *text; /* The proposed new value */
|
|
|
|
} node_edit; /* Client may call treeview_update_node_* */
|
2013-05-31 13:05:15 +04:00
|
|
|
struct {
|
2013-06-04 18:03:07 +04:00
|
|
|
browser_mouse_state mouse; /* Button / modifier used */
|
|
|
|
} node_launch;
|
2013-05-31 13:05:15 +04:00
|
|
|
} data; /**< The message data. */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum treeview_field_flags {
|
|
|
|
TREE_FLAG_NONE = 0, /**< No flags set */
|
|
|
|
TREE_FLAG_ALLOW_EDIT = (1 << 0), /**< Whether allow edit field */
|
|
|
|
TREE_FLAG_DEFAULT = (1 << 1), /**< Whether field is default */
|
|
|
|
TREE_FLAG_SHOW_NAME = (1 << 2) /**< Whether field name shown */
|
|
|
|
|
|
|
|
};
|
|
|
|
struct treeview_field_desc {
|
2013-06-18 19:02:31 +04:00
|
|
|
lwc_string *field; /**< A treeview field name */
|
|
|
|
enum treeview_field_flags flags; /**< Flags for field */
|
|
|
|
}; /**< Treeview field description */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
struct treeview_field_data {
|
2013-06-18 19:02:31 +04:00
|
|
|
lwc_string *field; /**< Field name */
|
|
|
|
const char *value; /**< Field value */
|
|
|
|
size_t value_len; /**< Field value length (bytes) */
|
2013-05-31 13:05:15 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct treeview_callback_table {
|
|
|
|
nserror (*folder)(struct treeview_node_msg msg, void *data);
|
|
|
|
nserror (*entry)(struct treeview_node_msg msg, void *data);
|
2013-06-18 19:02:31 +04:00
|
|
|
}; /**< Client callbacks for events concerning nodes */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Prepare treeview module for treeview usage
|
|
|
|
*
|
2013-09-09 16:11:36 +04:00
|
|
|
* \param font_pt_size Treeview text size in pt. Set to <= 0 for default.
|
2013-06-18 19:02:31 +04:00
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*/
|
2013-09-09 16:11:36 +04:00
|
|
|
nserror treeview_init(int font_pt_size);
|
2013-06-18 19:02:31 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Finalise the treeview module (all treeviews must have been destroyed first)
|
|
|
|
*
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
nserror treeview_fini(void);
|
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Create a treeview
|
|
|
|
*
|
|
|
|
* \param tree Returns created treeview object
|
|
|
|
* \param callbacks Treeview client node event callbacks
|
|
|
|
* \param n_fields Number of treeview fields (see description)
|
|
|
|
* \param fields Array of treeview fields
|
|
|
|
* \param cw_t Callback table for core_window containing the treeview
|
|
|
|
* \param cw The core_window in which the treeview is shown
|
2013-07-03 14:19:16 +04:00
|
|
|
* \param flags Treeview behaviour flags
|
2013-06-18 19:02:31 +04:00
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* The fields array order is as follows (N = n_fields):
|
|
|
|
*
|
|
|
|
* fields[0] Main field for entries (shown when not expanded)
|
|
|
|
* fields[1]...fields[N-2] Additional fields for entries
|
|
|
|
* fields[N-1] Field for folder nodes
|
|
|
|
*
|
|
|
|
* So fields[0] and fields[N-1] have TREE_FLAG_DEFAULT set.
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
nserror treeview_create(treeview **tree,
|
2013-05-31 13:05:15 +04:00
|
|
|
const struct treeview_callback_table *callbacks,
|
|
|
|
int n_fields, struct treeview_field_desc fields[],
|
|
|
|
const struct core_window_callback_table *cw_t,
|
2013-07-03 14:19:16 +04:00
|
|
|
struct core_window *cw, treeview_flags flags);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Destroy a treeview object
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to destroy
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* Will emit folder and entry deletion msg callbacks for all nodes in treeview.
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
nserror treeview_destroy(treeview *tree);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-08-27 23:56:20 +04:00
|
|
|
/**
|
|
|
|
* Find a releation for node creation.
|
|
|
|
*
|
|
|
|
* \param tree Treeview object in which to create folder
|
|
|
|
* \param relation Existing node to insert as relation of, or NULL
|
|
|
|
* \param rel Folder's relationship to relation
|
|
|
|
* \param at_y Iff true, insert at y-offest
|
|
|
|
* \param y Y-offset in px from top of hotlist. Ignored if (!at_y).
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* If at_y is set, we find a relation that will put the created node at that
|
|
|
|
* position.
|
|
|
|
*
|
|
|
|
* If at_y is unset, we find a relation that would put the node below the first
|
|
|
|
* selected node, or at the end of the treeview if no nodes selected.
|
|
|
|
*/
|
|
|
|
nserror treeview_get_relation(treeview *tree, treeview_node **relation,
|
|
|
|
enum treeview_relationship *rel, bool at_y, int y);
|
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Create a folder node in given treeview
|
|
|
|
*
|
|
|
|
* \param tree Treeview object in which to create folder
|
|
|
|
* \param folder Returns created folder node
|
|
|
|
* \param relation Existing node to insert as relation of, or NULL
|
|
|
|
* \param rel Folder's relationship to relation
|
|
|
|
* \param field Field data
|
|
|
|
* \param data Client data for node event callbacks
|
2013-08-30 15:51:40 +04:00
|
|
|
* \param flags Treeview node options flags
|
2013-06-18 19:02:31 +04:00
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* Field name must match name past in treeview_create fields[N-1].
|
|
|
|
*
|
|
|
|
* If relation is NULL, will insert as child of root node.
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
nserror treeview_create_node_folder(treeview *tree,
|
|
|
|
treeview_node **folder,
|
|
|
|
treeview_node *relation,
|
2013-05-31 13:05:15 +04:00
|
|
|
enum treeview_relationship rel,
|
|
|
|
const struct treeview_field_data *field,
|
2013-08-30 15:51:40 +04:00
|
|
|
void *data, treeview_node_options_flags flags);
|
2013-06-18 19:02:31 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an entry node in given treeview
|
|
|
|
*
|
|
|
|
* \param tree Treeview object in which to create entry
|
|
|
|
* \param entry Returns created entry node
|
|
|
|
* \param relation Existing node to insert as relation of, or NULL
|
|
|
|
* \param rel Folder's relationship to relation
|
|
|
|
* \param fields Array of field data
|
|
|
|
* \param data Client data for node event callbacks
|
2013-08-30 15:51:40 +04:00
|
|
|
* \param flags Treeview node options flags
|
2013-06-18 19:02:31 +04:00
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* Fields array names must match names past in treeview_create fields[0...N-2].
|
|
|
|
*
|
|
|
|
* If relation is NULL, will insert as child of root node.
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
nserror treeview_create_node_entry(treeview *tree,
|
|
|
|
treeview_node **entry,
|
|
|
|
treeview_node *relation,
|
2013-05-31 13:05:15 +04:00
|
|
|
enum treeview_relationship rel,
|
|
|
|
const struct treeview_field_data fields[],
|
2013-08-30 15:51:40 +04:00
|
|
|
void *data, treeview_node_options_flags flags);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-08-20 22:44:14 +04:00
|
|
|
/**
|
|
|
|
* Update an folder node in given treeview
|
|
|
|
*
|
|
|
|
* \param tree Treeview object in which to create entry
|
|
|
|
* \param folder Folder node to update
|
|
|
|
* \param fields New field data
|
|
|
|
* \param data Client data for node event callbacks
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* Field name must match name past in treeview_create fields[N-1].
|
|
|
|
*/
|
|
|
|
nserror treeview_update_node_folder(treeview *tree,
|
|
|
|
treeview_node *folder,
|
|
|
|
const struct treeview_field_data *field,
|
|
|
|
void *data);
|
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Update an entry node in given treeview
|
|
|
|
*
|
|
|
|
* \param tree Treeview object in which to create entry
|
|
|
|
* \param entry Entry node to update
|
|
|
|
* \param fields Array of new field data
|
|
|
|
* \param data Client data for node event callbacks
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* Fields array names must match names past in treeview_create fields[0...N-2].
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
nserror treeview_update_node_entry(treeview *tree,
|
|
|
|
treeview_node *entry,
|
2013-05-31 13:05:15 +04:00
|
|
|
const struct treeview_field_data fields[],
|
|
|
|
void *data);
|
|
|
|
|
2013-07-25 19:06:34 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Client callback for treeview_walk
|
|
|
|
*
|
|
|
|
* \param ctx Client context
|
|
|
|
* \param node_data Client data for the current treeview node
|
|
|
|
* \param type The node type
|
|
|
|
* \param abort Set to true to abort treeview walk prematurely
|
|
|
|
* \return NSERROR_OK on success, or appropriate error otherwise
|
|
|
|
*/
|
2013-08-22 19:05:03 +04:00
|
|
|
typedef nserror (*treeview_walk_cb)(void *ctx, void *node_data,
|
2013-07-25 19:06:34 +04:00
|
|
|
enum treeview_node_type type, bool *abort);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Walk (depth first) a treeview subtree, calling a callback at each node of
|
|
|
|
* required type.
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to walk
|
|
|
|
* \param root Root node to walk tree from (or NULL for tree root)
|
2013-08-22 19:05:03 +04:00
|
|
|
* \param enter_cb Function to call on entering nodes, or NULL
|
|
|
|
* \param leave_cb Function to call on leaving nodes, or NULL
|
2013-07-25 19:06:34 +04:00
|
|
|
* \param ctx Client context, passed back to callback function
|
|
|
|
* \param type The node type(s) of interest
|
|
|
|
* \return NSERROR_OK on success, or appropriate error otherwise
|
|
|
|
*
|
2013-08-22 19:05:03 +04:00
|
|
|
* Example usage: To export a treeview as XML, XML elements can be opened in
|
|
|
|
* enter_cb, and closed in leave_cb.
|
|
|
|
*
|
|
|
|
* Note, if deleting returned node in enter_cb, the walk must be terminated by
|
2013-07-25 19:06:34 +04:00
|
|
|
* setting abort to true.
|
|
|
|
*/
|
|
|
|
nserror treeview_walk(treeview *tree, treeview_node *root,
|
2013-08-22 19:05:03 +04:00
|
|
|
treeview_walk_cb enter_cb, treeview_walk_cb leave_cb,
|
|
|
|
void *ctx, enum treeview_node_type type);
|
2013-07-25 19:06:34 +04:00
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Delete a treeview node
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to delete node from
|
|
|
|
* \param n Node to delete
|
2013-08-30 15:51:40 +04:00
|
|
|
* \param flags Treeview node options flags
|
2013-06-18 19:02:31 +04:00
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* Will emit folder or entry deletion msg callback.
|
|
|
|
*/
|
2013-08-30 15:51:40 +04:00
|
|
|
nserror treeview_delete_node(treeview *tree, treeview_node *n,
|
|
|
|
treeview_node_options_flags flags);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Expand a treeview node
|
|
|
|
*
|
2013-09-03 14:06:07 +04:00
|
|
|
* \param tree Treeview object to expand node in
|
2013-06-18 19:02:31 +04:00
|
|
|
* \param node Node to expand
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
nserror treeview_node_expand(treeview *tree, treeview_node *node);
|
2013-06-18 19:02:31 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Contract a treeview node
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to contract node in
|
|
|
|
* \param node Node to contract
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
nserror treeview_node_contract(treeview *tree, treeview_node *node);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-09-03 14:06:07 +04:00
|
|
|
/**
|
|
|
|
* Expand a treeview's nodes
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to expand nodes in
|
|
|
|
* \param only_folders Iff true, only folders are expanded.
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*/
|
|
|
|
nserror treeview_expand(treeview *tree, bool only_folders);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contract a treeview's nodes
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to contract nodes in
|
|
|
|
* \param all Iff false, only entries are contracted.
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*/
|
|
|
|
nserror treeview_contract(treeview *tree, bool all);
|
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Redraw a treeview object
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to render
|
|
|
|
* \param x X coordinate to render treeview at
|
|
|
|
* \param x Y coordinate to render treeview at
|
2013-06-18 19:08:21 +04:00
|
|
|
* \param clip Current clip rectangle (wrt tree origin)
|
2013-06-18 19:02:31 +04:00
|
|
|
* \param ctx Current redraw context
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
void treeview_redraw(treeview *tree, int x, int y, struct rect *clip,
|
2013-05-31 13:05:15 +04:00
|
|
|
const struct redraw_context *ctx);
|
|
|
|
|
2013-07-01 14:47:46 +04:00
|
|
|
/**
|
|
|
|
* Key press handling for treeviews.
|
|
|
|
*
|
|
|
|
* \param tree The treeview which got the keypress
|
|
|
|
* \param key The ucs4 character codepoint
|
|
|
|
* \return true if the keypress is dealt with, false otherwise.
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
bool treeview_keypress(treeview *tree, uint32_t key);
|
2013-07-01 14:47:46 +04:00
|
|
|
|
2013-05-31 16:24:27 +04:00
|
|
|
/**
|
|
|
|
* Handles all kinds of mouse action
|
|
|
|
*
|
2013-06-18 19:02:31 +04:00
|
|
|
* \param tree Treeview object
|
|
|
|
* \param mouse The current mouse state
|
|
|
|
* \param x X coordinate
|
|
|
|
* \param y Y coordinate
|
2013-05-31 16:24:27 +04:00
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
void treeview_mouse_action(treeview *tree,
|
2013-05-31 16:24:27 +04:00
|
|
|
browser_mouse_state mouse, int x, int y);
|
|
|
|
|
2013-06-18 19:02:31 +04:00
|
|
|
/**
|
|
|
|
* Determine whether treeview has a selection
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to delete node from
|
|
|
|
* \return true iff treeview has a selection
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
bool treeview_has_selection(treeview *tree);
|
2013-06-03 20:06:47 +04:00
|
|
|
|
2013-09-13 17:24:25 +04:00
|
|
|
/**
|
|
|
|
* Get the first selected node
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to get selected node in
|
|
|
|
* \param node_data Client data for the selected treeview node, or NULL
|
|
|
|
*/
|
|
|
|
void treeview_get_selection(treeview *tree, void **node_data);
|
|
|
|
|
2013-08-28 15:58:41 +04:00
|
|
|
/**
|
|
|
|
* Edit the first selected node
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to edit selected node in
|
|
|
|
*/
|
|
|
|
void treeview_edit_selection(treeview *tree);
|
|
|
|
|
2013-07-01 19:16:11 +04:00
|
|
|
/**
|
|
|
|
* Find current height of a treeview
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to find height of
|
|
|
|
* \return height of treeview in px
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
int treeview_get_height(treeview *tree);
|
2013-07-01 19:16:11 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
#endif
|