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/>.
|
|
|
|
*/
|
|
|
|
|
2014-10-13 14:56:31 +04:00
|
|
|
/**
|
|
|
|
* \file
|
|
|
|
*
|
|
|
|
* Treeview handling implementation.
|
2013-05-31 13:05:15 +04:00
|
|
|
*/
|
|
|
|
|
2017-09-10 14:02:36 +03:00
|
|
|
#include <string.h>
|
|
|
|
|
2017-01-20 00:15:08 +03:00
|
|
|
#include "utils/utils.h"
|
2014-02-03 21:56:24 +04:00
|
|
|
#include "utils/log.h"
|
2016-06-06 10:59:23 +03:00
|
|
|
#include "utils/nsurl.h"
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
#include "utils/nsoption.h"
|
2017-09-10 14:02:36 +03:00
|
|
|
#include "utils/config.h"
|
2016-05-27 01:01:03 +03:00
|
|
|
#include "netsurf/bitmap.h"
|
2016-06-06 10:59:23 +03:00
|
|
|
#include "netsurf/content.h"
|
2016-06-30 18:12:06 +03:00
|
|
|
#include "netsurf/plotters.h"
|
|
|
|
#include "netsurf/clipboard.h"
|
|
|
|
#include "netsurf/layout.h"
|
2016-07-01 16:27:04 +03:00
|
|
|
#include "netsurf/keypress.h"
|
2016-08-18 23:07:04 +03:00
|
|
|
#include "netsurf/core_window.h"
|
2014-02-03 21:56:24 +04:00
|
|
|
#include "content/hlcache.h"
|
2016-06-06 16:47:27 +03:00
|
|
|
#include "css/utils.h"
|
2014-02-03 21:56:24 +04:00
|
|
|
|
2014-01-10 18:32:29 +04:00
|
|
|
#include "desktop/system_colour.h"
|
2013-05-31 13:05:15 +04:00
|
|
|
#include "desktop/knockout.h"
|
2013-08-19 17:23:44 +04:00
|
|
|
#include "desktop/textarea.h"
|
2013-05-31 13:05:15 +04:00
|
|
|
#include "desktop/treeview.h"
|
2015-01-03 03:15:58 +03:00
|
|
|
#include "desktop/gui_internal.h"
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* The maximum horizontal size a treeview can possibly be.
|
|
|
|
*
|
|
|
|
* \todo get rid of REDRAW_MAX -- need to be able to know window size
|
|
|
|
*/
|
2013-06-03 23:21:00 +04:00
|
|
|
#define REDRAW_MAX 8000
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Treeview handling global context
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview_globals {
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
unsigned initialised;
|
2013-06-03 22:49:36 +04:00
|
|
|
int line_height;
|
2013-05-31 13:05:15 +04:00
|
|
|
int furniture_width;
|
|
|
|
int step_width;
|
|
|
|
int window_padding;
|
2013-08-17 14:48:16 +04:00
|
|
|
int icon_size;
|
2013-05-31 13:05:15 +04:00
|
|
|
int icon_step;
|
2013-08-16 23:06:08 +04:00
|
|
|
int move_offset;
|
2013-05-31 13:05:15 +04:00
|
|
|
} tree_g;
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Section type of a treeview at a point
|
|
|
|
*/
|
2013-08-16 14:15:16 +04:00
|
|
|
enum treeview_node_part {
|
2017-03-01 02:41:48 +03:00
|
|
|
TV_NODE_PART_TOGGLE, /**< Expansion toggle */
|
|
|
|
TV_NODE_PART_ON_NODE, /**< Node content (text, icon) */
|
|
|
|
TV_NODE_PART_NONE /**< Empty area */
|
|
|
|
};
|
2013-06-18 17:58:43 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Text within a treeview field or node
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview_text {
|
2013-06-18 19:49:42 +04:00
|
|
|
const char *data; /**< Text string */
|
2015-01-03 03:15:06 +03:00
|
|
|
uint32_t len; /**< Length of string in bytes */
|
2013-06-18 19:49:42 +04:00
|
|
|
int width; /**< Width of text in px */
|
2013-05-31 13:05:15 +04:00
|
|
|
};
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* a treeview field
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview_field {
|
2017-03-01 02:41:48 +03:00
|
|
|
/** flags controlling how field is interpreted */
|
2013-05-31 13:05:15 +04:00
|
|
|
enum treeview_field_flags flags;
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
lwc_string *field; /**< field contents */
|
|
|
|
struct treeview_text value; /**< field text */
|
2013-05-31 13:05:15 +04:00
|
|
|
};
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* flags indicating render state of node.
|
|
|
|
*/
|
2013-05-31 16:24:27 +04:00
|
|
|
enum treeview_node_flags {
|
2017-03-01 02:41:48 +03:00
|
|
|
TV_NFLAGS_NONE = 0, /**< No node flags set */
|
|
|
|
TV_NFLAGS_EXPANDED = (1 << 0), /**< Whether node is expanded */
|
|
|
|
TV_NFLAGS_SELECTED = (1 << 1), /**< Whether node is selected */
|
2017-09-10 14:02:36 +03:00
|
|
|
TV_NFLAGS_SPECIAL = (1 << 2), /**< Render as special node */
|
|
|
|
TV_NFLAGS_MATCHED = (1 << 3), /**< Whether node matches search */
|
2013-05-31 16:24:27 +04:00
|
|
|
};
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Treeview target position
|
|
|
|
*/
|
2013-08-16 23:34:41 +04:00
|
|
|
enum treeview_target_pos {
|
|
|
|
TV_TARGET_ABOVE,
|
|
|
|
TV_TARGET_INSIDE,
|
|
|
|
TV_TARGET_BELOW,
|
|
|
|
TV_TARGET_NONE
|
|
|
|
};
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Treeview node
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview_node {
|
2013-06-18 19:49:42 +04:00
|
|
|
enum treeview_node_flags flags; /**< Node flags */
|
|
|
|
enum treeview_node_type type; /**< Node type */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-18 19:49:42 +04:00
|
|
|
int height; /**< Includes height of any descendants (pixels) */
|
|
|
|
int inset; /**< Node's inset depending on tree depth (pixels) */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_node *parent; /**< parent node */
|
|
|
|
treeview_node *prev_sib; /**< previous sibling node */
|
|
|
|
treeview_node *next_sib; /**< next sibling node */
|
|
|
|
treeview_node *children; /**< first child node */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-18 19:49:42 +04:00
|
|
|
void *client_data; /**< Passed to client on node event msg callback */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
struct treeview_text text; /** Text to show for node (default field) */
|
2017-03-01 02:41:48 +03:00
|
|
|
};
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Node entry
|
|
|
|
*
|
|
|
|
* node entry contains a base node at the beginning allowing for
|
|
|
|
* trivial containerof by cast and some number of fields.
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview_node_entry {
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_node base; /**< Entry class inherits node base class */
|
2013-12-07 04:10:57 +04:00
|
|
|
struct treeview_field fields[FLEX_ARRAY_LEN_DECL];
|
2017-03-01 02:41:48 +03:00
|
|
|
};
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* A mouse position wrt treeview
|
|
|
|
*/
|
2013-06-18 17:58:43 +04:00
|
|
|
struct treeview_pos {
|
2013-06-18 19:49:42 +04:00
|
|
|
int x; /**< Mouse X coordinate */
|
|
|
|
int y; /**< Mouse Y coordinate */
|
|
|
|
int node_y; /**< Top of node at y */
|
|
|
|
int node_h; /**< Height of node at y */
|
2017-03-01 02:41:48 +03:00
|
|
|
};
|
|
|
|
|
2013-06-18 17:58:43 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Treeview drag state
|
|
|
|
*/
|
2013-06-18 17:58:43 +04:00
|
|
|
struct treeview_drag {
|
|
|
|
enum {
|
|
|
|
TV_DRAG_NONE,
|
|
|
|
TV_DRAG_SELECTION,
|
|
|
|
TV_DRAG_MOVE,
|
2017-09-09 23:18:19 +03:00
|
|
|
TV_DRAG_TEXTAREA,
|
|
|
|
TV_DRAG_SEARCH,
|
2013-06-18 19:49:42 +04:00
|
|
|
} type; /**< Drag type */
|
2013-07-03 16:48:53 +04:00
|
|
|
treeview_node *start_node; /**< Start node */
|
2013-08-16 14:15:16 +04:00
|
|
|
bool selected; /**< Start node is selected */
|
|
|
|
enum treeview_node_part part; /**< Node part at start */
|
|
|
|
struct treeview_pos start; /**< Start pos */
|
|
|
|
struct treeview_pos prev; /**< Previous pos */
|
2017-03-01 02:41:48 +03:00
|
|
|
};
|
|
|
|
|
2013-06-18 17:58:43 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Treeview node move details
|
|
|
|
*/
|
2013-08-17 00:40:45 +04:00
|
|
|
struct treeview_move {
|
2013-08-19 17:23:44 +04:00
|
|
|
treeview_node *root; /**< Head of yanked node list */
|
2013-08-17 00:40:45 +04:00
|
|
|
treeview_node *target; /**< Move target */
|
2013-08-17 14:48:16 +04:00
|
|
|
struct rect target_area; /**< Pos/size of target indicator */
|
2013-08-17 00:40:45 +04:00
|
|
|
enum treeview_target_pos target_pos; /**< Pos wrt render node */
|
2017-03-01 02:41:48 +03:00
|
|
|
};
|
2013-08-17 00:40:45 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Treeview node edit details
|
|
|
|
*/
|
2013-08-19 17:23:44 +04:00
|
|
|
struct treeview_edit {
|
|
|
|
treeview_node *node; /**< Node being edited, or NULL */
|
|
|
|
struct textarea *textarea; /**< Textarea for edit, or NULL */
|
2013-08-20 18:44:24 +04:00
|
|
|
lwc_string *field; /**< The field being edited, or NULL */
|
2013-08-19 17:23:44 +04:00
|
|
|
int x; /**< Textarea x position */
|
|
|
|
int y; /**< Textarea y position */
|
|
|
|
int w; /**< Textarea width */
|
|
|
|
int h; /**< Textarea height */
|
2017-03-01 02:41:48 +03:00
|
|
|
};
|
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
|
2017-09-09 20:35:24 +03:00
|
|
|
/**
|
|
|
|
* Treeview search box details
|
|
|
|
*/
|
|
|
|
struct treeview_search {
|
2017-09-10 14:02:36 +03:00
|
|
|
struct textarea *textarea; /**< Search box. */
|
|
|
|
bool active; /**< Whether the search box has focus. */
|
|
|
|
bool search; /**< Whether we have a search term. */
|
2017-09-14 22:07:42 +03:00
|
|
|
int height; /**< Current search display height. */
|
2017-09-09 20:35:24 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* The treeview context
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview {
|
2017-03-01 02:41:48 +03:00
|
|
|
uint32_t view_width; /**< Viewport horizontal size */
|
2013-06-18 19:49:42 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_flags flags; /**< Treeview behaviour settings */
|
2013-07-03 14:19:16 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_node *root; /**< Root node */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-18 19:49:42 +04:00
|
|
|
struct treeview_field *fields; /**< Array of fields */
|
|
|
|
int n_fields; /**< fields[n_fields] is folder, lower are entry fields */
|
|
|
|
int field_width; /**< Max width of shown field names */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
struct treeview_drag drag; /**< Drag state */
|
|
|
|
struct treeview_move move; /**< Move drag details */
|
|
|
|
struct treeview_edit edit; /**< Edit details */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-09-09 20:35:24 +03:00
|
|
|
struct treeview_search search; /**< Treeview search box */
|
|
|
|
|
2013-06-18 19:49:42 +04:00
|
|
|
const struct treeview_callback_table *callbacks; /**< For node events */
|
2013-06-18 17:58:43 +04:00
|
|
|
|
2013-06-18 19:49:42 +04:00
|
|
|
const struct core_window_callback_table *cw_t; /**< Window cb table */
|
2013-06-01 00:02:29 +04:00
|
|
|
struct core_window *cw_h; /**< Core window handle */
|
2013-05-31 13:05:15 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Treeview furniture states.
|
|
|
|
*/
|
2013-10-03 02:34:34 +04:00
|
|
|
enum treeview_furniture_id {
|
|
|
|
TREE_FURN_EXPAND = 0,
|
|
|
|
TREE_FURN_CONTRACT,
|
|
|
|
TREE_FURN_LAST
|
|
|
|
};
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* style for a node
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview_node_style {
|
2017-03-01 02:41:48 +03:00
|
|
|
plot_style_t bg; /**< Background */
|
|
|
|
plot_font_style_t text; /**< Text */
|
|
|
|
plot_font_style_t itext; /**< Entry field text */
|
|
|
|
|
|
|
|
plot_style_t sbg; /**< Selected background */
|
|
|
|
plot_font_style_t stext; /**< Selected text */
|
|
|
|
plot_font_style_t sitext; /**< Selected entry field text */
|
|
|
|
|
|
|
|
struct {
|
|
|
|
int size;
|
|
|
|
struct bitmap *bmp;
|
|
|
|
struct bitmap *sel;
|
|
|
|
} furn[TREE_FURN_LAST];
|
|
|
|
};
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Plot style for odd rows
|
|
|
|
*/
|
|
|
|
struct treeview_node_style plot_style_odd;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Plot style for even rows
|
|
|
|
*/
|
|
|
|
struct treeview_node_style plot_style_even;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Treeview content resource data
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview_resource {
|
|
|
|
const char *url;
|
|
|
|
struct hlcache_handle *c;
|
|
|
|
int height;
|
|
|
|
bool ready;
|
2017-03-01 02:41:48 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* treeview resource indexes
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
enum treeview_resource_id {
|
2013-08-13 15:41:04 +04:00
|
|
|
TREE_RES_ARROW = 0,
|
|
|
|
TREE_RES_CONTENT,
|
2013-05-31 13:05:15 +04:00
|
|
|
TREE_RES_FOLDER,
|
2013-11-08 22:53:34 +04:00
|
|
|
TREE_RES_FOLDER_SPECIAL,
|
2013-05-31 13:05:15 +04:00
|
|
|
TREE_RES_SEARCH,
|
|
|
|
TREE_RES_LAST
|
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treeview content resources
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
static struct treeview_resource treeview_res[TREE_RES_LAST] = {
|
2013-08-13 15:41:04 +04:00
|
|
|
{ "resource:icons/arrow-l.png", NULL, 0, false },
|
2013-05-31 13:05:15 +04:00
|
|
|
{ "resource:icons/content.png", NULL, 0, false },
|
|
|
|
{ "resource:icons/directory.png", NULL, 0, false },
|
2013-11-08 22:53:34 +04:00
|
|
|
{ "resource:icons/directory2.png", NULL, 0, false },
|
2013-05-31 13:05:15 +04:00
|
|
|
{ "resource:icons/search.png", NULL, 0, false }
|
2017-03-01 02:41:48 +03:00
|
|
|
};
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
|
2017-09-14 22:07:42 +03:00
|
|
|
/**
|
|
|
|
* Get the display height of the treeview data component of the display.
|
|
|
|
*
|
|
|
|
* \param[in] tree Treeview to get the height of.
|
|
|
|
* \return the display height in pixels.
|
|
|
|
*/
|
|
|
|
static inline int treeview__get_display_height(treeview *tree)
|
|
|
|
{
|
|
|
|
return (tree->search.search == false) ?
|
|
|
|
tree->root->height :
|
|
|
|
tree->search.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-29 17:29:48 +03:00
|
|
|
/**
|
|
|
|
* Corewindow callback wrapper: Request a redraw of the window
|
|
|
|
*
|
2017-02-15 02:05:30 +03:00
|
|
|
* \param[in] tree The treeview to request redraw on.
|
2016-12-29 17:29:48 +03:00
|
|
|
* \param[in] r rectangle to redraw
|
|
|
|
*/
|
2017-09-11 20:29:54 +03:00
|
|
|
static inline void treeview__cw_invalidate_area(
|
|
|
|
const struct treeview *tree,
|
|
|
|
const struct rect *r)
|
2016-12-29 17:29:48 +03:00
|
|
|
{
|
2016-12-29 17:40:54 +03:00
|
|
|
if (tree->cw_t != NULL) {
|
2017-05-23 11:41:29 +03:00
|
|
|
tree->cw_t->invalidate(tree->cw_h, r);
|
2016-12-29 17:40:54 +03:00
|
|
|
}
|
2016-12-29 17:29:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Corewindow callback wrapper: Update the limits of the window
|
|
|
|
*
|
2017-02-15 02:05:30 +03:00
|
|
|
* \param[in] tree The treeview to update size for.
|
2016-12-29 17:29:48 +03:00
|
|
|
* \param[in] width the width in px, or negative if don't care
|
|
|
|
* \param[in] height the height in px, or negative if don't care
|
|
|
|
*/
|
|
|
|
static inline void treeview__cw_update_size(
|
2017-03-01 02:41:48 +03:00
|
|
|
const struct treeview *tree,
|
|
|
|
int width, int height)
|
2016-12-29 17:29:48 +03:00
|
|
|
{
|
2017-09-10 13:55:25 +03:00
|
|
|
int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ?
|
|
|
|
tree_g.line_height : 0;
|
|
|
|
|
2016-12-29 17:40:54 +03:00
|
|
|
if (tree->cw_t != NULL) {
|
2017-09-10 13:55:25 +03:00
|
|
|
tree->cw_t->update_size(tree->cw_h, width,
|
|
|
|
height + search_height);
|
2016-12-29 17:40:54 +03:00
|
|
|
}
|
2016-12-29 17:29:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-11 20:27:10 +03:00
|
|
|
/**
|
|
|
|
* Corewindow callback_wrapper: Scroll to top of window.
|
|
|
|
*
|
|
|
|
* \param[in] tree The treeview to scroll.
|
|
|
|
*/
|
|
|
|
static inline void treeview__cw_scroll_top(
|
|
|
|
const struct treeview *tree)
|
|
|
|
{
|
|
|
|
struct rect r = {
|
|
|
|
.x0 = 0,
|
|
|
|
.y0 = 0,
|
|
|
|
.x1 = tree_g.window_padding,
|
|
|
|
.y1 = tree_g.line_height,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (tree->cw_t != NULL) {
|
|
|
|
tree->cw_t->scroll_visible(tree->cw_h, &r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-29 17:29:48 +03:00
|
|
|
/**
|
|
|
|
* Corewindow callback wrapper: Get window viewport dimensions
|
|
|
|
*
|
2017-02-15 02:05:30 +03:00
|
|
|
* \param[in] tree The treeview to get dimensions for.
|
2016-12-29 17:29:48 +03:00
|
|
|
* \param[out] width to be set to viewport width in px
|
|
|
|
* \param[out] height to be set to viewport height in px
|
|
|
|
*/
|
|
|
|
static inline void treeview__cw_get_window_dimensions(
|
2017-03-01 02:41:48 +03:00
|
|
|
const struct treeview *tree,
|
|
|
|
int *width, int *height)
|
2016-12-29 17:29:48 +03:00
|
|
|
{
|
2016-12-29 17:40:54 +03:00
|
|
|
if (tree->cw_t != NULL) {
|
|
|
|
tree->cw_t->get_window_dimensions(tree->cw_h, width, height);
|
|
|
|
}
|
2016-12-29 17:29:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Corewindow callback wrapper: Inform corewindow owner of drag status
|
|
|
|
*
|
2017-02-15 02:05:30 +03:00
|
|
|
* \param[in] tree The treeview to report status on.
|
2016-12-29 17:29:48 +03:00
|
|
|
* \param[in] ds the current drag status
|
|
|
|
*/
|
|
|
|
static inline void treeview__cw_drag_status(
|
2017-03-01 02:41:48 +03:00
|
|
|
const struct treeview *tree,
|
|
|
|
core_window_drag_status ds)
|
2016-12-29 17:29:48 +03:00
|
|
|
{
|
2016-12-29 17:40:54 +03:00
|
|
|
if (tree->cw_t != NULL) {
|
|
|
|
tree->cw_t->drag_status(tree->cw_h, ds);
|
|
|
|
}
|
2016-12-29 17:29:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Helper function to access the given field of a node
|
2015-01-03 03:15:58 +03:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview that node belongs to
|
|
|
|
* \param n Node to get field from
|
|
|
|
* \param i Index of field of interest
|
2015-01-03 03:15:58 +03:00
|
|
|
* \return text entry for field or NULL.
|
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static inline struct treeview_text *
|
|
|
|
treeview_get_text_for_field(treeview *tree, treeview_node *n, int i)
|
2015-01-03 03:15:58 +03:00
|
|
|
{
|
|
|
|
if (i == 0) {
|
|
|
|
return &n->text;
|
|
|
|
|
|
|
|
} else if (i < tree->n_fields && n->type == TREE_NODE_ENTRY) {
|
|
|
|
struct treeview_node_entry *e = (struct treeview_node_entry *)n;
|
|
|
|
return &e->fields[i - 1].value;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(0 && "Bad field index for node");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Find the next node in depth first tree order
|
2013-08-29 22:05:04 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param node Start node
|
|
|
|
* \param full Iff true, visit children of collapsed nodes
|
|
|
|
* \return next node, or NULL if \a node is last node
|
2013-08-29 22:05:04 +04:00
|
|
|
*/
|
|
|
|
static inline treeview_node * treeview_node_next(treeview_node *node, bool full)
|
|
|
|
{
|
|
|
|
assert(node != NULL);
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if ((full || (node->flags & TV_NFLAGS_EXPANDED)) &&
|
2017-03-01 02:41:48 +03:00
|
|
|
node->children != NULL) {
|
2013-08-29 22:05:04 +04:00
|
|
|
/* Next node is child */
|
|
|
|
node = node->children;
|
|
|
|
} else {
|
|
|
|
/* No children. As long as we're not at the root,
|
|
|
|
* go to next sibling if present, or nearest ancestor
|
|
|
|
* with a next sibling. */
|
|
|
|
|
2013-10-23 14:36:42 +04:00
|
|
|
while (node->parent != NULL && node->next_sib == NULL) {
|
2013-08-29 22:05:04 +04:00
|
|
|
node = node->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->type == TREE_NODE_ROOT) {
|
|
|
|
node = NULL;
|
|
|
|
|
2013-10-23 17:40:46 +04:00
|
|
|
} else {
|
2013-08-29 22:05:04 +04:00
|
|
|
node = node->next_sib;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Find node at given y-position
|
2013-08-29 22:05:04 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to delete node from
|
|
|
|
* \param target_y Target y-position
|
2013-08-29 22:05:04 +04:00
|
|
|
* \return node at y_target
|
|
|
|
*/
|
|
|
|
static treeview_node * treeview_y_node(treeview *tree, int target_y)
|
|
|
|
{
|
|
|
|
treeview_node *n;
|
|
|
|
int y = 0;
|
|
|
|
int h;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
n = treeview_node_next(tree->root, false);
|
|
|
|
|
|
|
|
while (n != NULL) {
|
|
|
|
h = (n->type == TREE_NODE_ENTRY) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
n->height : tree_g.line_height;
|
2013-08-29 22:05:04 +04:00
|
|
|
if (target_y >= y && target_y < y + h)
|
|
|
|
return n;
|
|
|
|
y += h;
|
|
|
|
|
|
|
|
n = treeview_node_next(n, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Find y position of the top of a node
|
2013-08-29 22:05:04 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to delete node from
|
|
|
|
* \param node Node to get position of
|
2013-08-29 22:05:04 +04:00
|
|
|
* \return node's y position
|
|
|
|
*/
|
|
|
|
static int treeview_node_y(treeview *tree, treeview_node *node)
|
|
|
|
{
|
|
|
|
treeview_node *n;
|
|
|
|
int y = 0;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
n = treeview_node_next(tree->root, false);
|
|
|
|
|
|
|
|
while (n != NULL && n != node) {
|
2013-08-29 23:43:25 +04:00
|
|
|
y += (n->type == TREE_NODE_ENTRY) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
n->height : tree_g.line_height;
|
2013-08-29 22:05:04 +04:00
|
|
|
|
|
|
|
n = treeview_node_next(n, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
/**
|
|
|
|
* The treeview walk mode. Controls which nodes are visited in a walk.
|
|
|
|
*/
|
|
|
|
enum treeview_walk_mode {
|
|
|
|
/**
|
|
|
|
* Walk to all nodes in the (sub)tree.
|
|
|
|
*/
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Walk to expanded nodes in the (sub)tree only. Children of
|
|
|
|
* collapsed nodes are not visited.
|
|
|
|
*/
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_EXPANDED,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Walk displayed nodes. This differs from the
|
|
|
|
* `TREEVIEW_WALK_MODE_LOGICAL_EXPANDED` mode when there is
|
|
|
|
* an active search filter display.
|
|
|
|
*/
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Walk a treeview subtree, calling a callback at each node (depth first)
|
2013-08-17 22:39:17 +04:00
|
|
|
*
|
2017-09-14 22:10:30 +03:00
|
|
|
* \param tree Treeview being walked.
|
2013-08-17 22:39:17 +04:00
|
|
|
* \param root Root to walk tree from (doesn't get a callback call)
|
2017-09-14 22:10:30 +03:00
|
|
|
* \param mode The treeview walk mode to use.
|
2013-08-17 22:39:17 +04:00
|
|
|
* \param callback_bwd Function to call on each node in backwards order
|
|
|
|
* \param callback_fwd Function to call on each node in forwards order
|
|
|
|
* \param ctx Context to pass to callback
|
|
|
|
* \return NSERROR_OK on success, or appropriate error otherwise
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \note Any node deletion must happen in callback_bwd.
|
2013-08-17 22:39:17 +04:00
|
|
|
*/
|
2017-09-14 22:10:30 +03:00
|
|
|
static nserror treeview_walk_internal(
|
|
|
|
treeview *tree,
|
|
|
|
treeview_node *root,
|
|
|
|
enum treeview_walk_mode mode,
|
|
|
|
nserror (*callback_bwd)(
|
|
|
|
treeview_node *n,
|
|
|
|
void *ctx,
|
|
|
|
bool *end),
|
|
|
|
nserror (*callback_fwd)(
|
|
|
|
treeview_node *n,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end),
|
|
|
|
void *ctx)
|
2013-08-17 22:39:17 +04:00
|
|
|
{
|
|
|
|
treeview_node *node, *child, *parent, *next_sibling;
|
2017-09-14 22:10:30 +03:00
|
|
|
bool walking_search = (mode == TREEVIEW_WALK_MODE_DISPLAY &&
|
|
|
|
tree->search.search == true);
|
2013-08-17 22:39:17 +04:00
|
|
|
bool skip_children = false;
|
2017-09-14 22:10:30 +03:00
|
|
|
bool abort = false;
|
|
|
|
bool full = false;
|
2013-08-17 22:39:17 +04:00
|
|
|
nserror err;
|
2017-09-14 22:10:30 +03:00
|
|
|
bool entry;
|
2013-08-17 22:39:17 +04:00
|
|
|
|
2013-10-23 14:40:22 +04:00
|
|
|
assert(root != NULL);
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
if (mode == TREEVIEW_WALK_MODE_LOGICAL_COMPLETE || walking_search) {
|
|
|
|
/* We need to visit children of collapsed folders. */
|
|
|
|
full = true;
|
|
|
|
}
|
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
node = root;
|
|
|
|
parent = node->parent;
|
|
|
|
next_sibling = node->next_sib;
|
2017-09-11 21:52:07 +03:00
|
|
|
child = (full || (node->flags & TV_NFLAGS_EXPANDED)) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
node->children : NULL;
|
2013-08-17 22:39:17 +04:00
|
|
|
|
|
|
|
while (node != NULL) {
|
|
|
|
|
2017-09-11 21:52:07 +03:00
|
|
|
if (child != NULL && !skip_children) {
|
2013-08-17 22:39:17 +04:00
|
|
|
/* Down to children */
|
|
|
|
node = child;
|
|
|
|
} else {
|
|
|
|
/* No children. As long as we're not at the root,
|
|
|
|
* go to next sibling if present, or nearest ancestor
|
|
|
|
* with a next sibling. */
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
while (node != root && next_sibling == NULL) {
|
|
|
|
entry = (node->type == TREE_NODE_ENTRY);
|
|
|
|
if (callback_bwd != NULL &&
|
|
|
|
(entry || !walking_search)) {
|
2013-08-17 22:39:17 +04:00
|
|
|
/* Backwards callback */
|
|
|
|
err = callback_bwd(node, ctx, &abort);
|
|
|
|
|
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
|
|
|
|
} else if (abort) {
|
|
|
|
/* callback requested early
|
|
|
|
* termination */
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node = parent;
|
|
|
|
parent = node->parent;
|
|
|
|
next_sibling = node->next_sib;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node == root)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (callback_bwd != NULL) {
|
|
|
|
/* Backwards callback */
|
|
|
|
err = callback_bwd(node, ctx, &abort);
|
|
|
|
|
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
|
|
|
|
} else if (abort) {
|
|
|
|
/* callback requested early
|
|
|
|
* termination */
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node = next_sibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(node != NULL);
|
|
|
|
assert(node != root);
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
entry = (node->type == TREE_NODE_ENTRY);
|
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
parent = node->parent;
|
|
|
|
next_sibling = node->next_sib;
|
2014-05-24 18:09:20 +04:00
|
|
|
child = (full || (node->flags & TV_NFLAGS_EXPANDED)) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
node->children : NULL;
|
2013-08-17 22:39:17 +04:00
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
if (walking_search && (!entry ||
|
|
|
|
!(node->flags & TV_NFLAGS_MATCHED))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
if (callback_fwd != NULL) {
|
|
|
|
/* Forwards callback */
|
|
|
|
err = callback_fwd(node, ctx, &skip_children, &abort);
|
|
|
|
|
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
|
|
|
|
} else if (abort) {
|
|
|
|
/* callback requested early termination */
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-18 22:19:00 +03:00
|
|
|
/**
|
|
|
|
* Data used when doing a treeview walk for search.
|
|
|
|
*/
|
|
|
|
struct treeview_search_walk_data {
|
|
|
|
treeview *tree; /**< The treeview to search. */
|
|
|
|
const char *text; /**< The string being searched for. */
|
|
|
|
const unsigned int len; /**< Length of string being searched for. */
|
|
|
|
int window_height; /**< Accumulate height for matching entries. */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node callback for handling search.
|
|
|
|
*
|
|
|
|
* \param[in] n Current node.
|
|
|
|
* \param[in] ctx Treeview search context.
|
|
|
|
* \param[in,out] skip_children Flag to allow children to be skipped.
|
|
|
|
* \param[in,out] end Flag to allow iteration to be finished early.
|
|
|
|
* \return NSERROR_OK on success else error code.
|
|
|
|
*/
|
|
|
|
static nserror treeview__search_walk_cb(
|
|
|
|
treeview_node *n,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end)
|
|
|
|
{
|
|
|
|
struct treeview_search_walk_data *sw = ctx;
|
|
|
|
|
|
|
|
if (n->type != TREE_NODE_ENTRY) {
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sw->len == 0) {
|
|
|
|
n->flags &= ~TV_NFLAGS_MATCHED;
|
|
|
|
} else {
|
|
|
|
struct treeview_node_entry *entry =
|
|
|
|
(struct treeview_node_entry *)n;
|
|
|
|
bool matched = false;
|
|
|
|
|
|
|
|
for (int i = 0; i < sw->tree->n_fields; i++) {
|
|
|
|
struct treeview_field *ef = &(sw->tree->fields[i + 1]);
|
|
|
|
if (ef->flags & TREE_FLAG_SEARCHABLE) {
|
|
|
|
if (strcasestr(entry->fields[i].value.data,
|
|
|
|
sw->text) != NULL) {
|
|
|
|
matched = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!matched && strcasestr(n->text.data, sw->text) != NULL) {
|
|
|
|
matched = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (matched) {
|
|
|
|
n->flags |= TV_NFLAGS_MATCHED;
|
|
|
|
sw->window_height += n->height;
|
|
|
|
} else {
|
|
|
|
n->flags &= ~TV_NFLAGS_MATCHED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Search treeview for text.
|
|
|
|
*
|
|
|
|
* \param[in] tree Treeview to search.
|
|
|
|
* \param[in] text UTF-8 string to search for. (NULL-terminated.)
|
|
|
|
* \param[in] len Byte length of UTF-8 string.
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise.
|
|
|
|
*/
|
|
|
|
static nserror treeview__search(
|
|
|
|
treeview *tree,
|
|
|
|
const char *text,
|
|
|
|
unsigned int len)
|
|
|
|
{
|
|
|
|
nserror err;
|
|
|
|
uint32_t height;
|
|
|
|
uint32_t prev_height = treeview__get_display_height(tree);
|
|
|
|
int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ?
|
|
|
|
tree_g.line_height : 0;
|
|
|
|
struct treeview_search_walk_data sw = {
|
|
|
|
.len = len,
|
|
|
|
.text = text,
|
|
|
|
.tree = tree,
|
|
|
|
.window_height = 0,
|
|
|
|
};
|
|
|
|
struct rect r = {
|
|
|
|
.x0 = 0,
|
|
|
|
.y0 = search_height,
|
|
|
|
.x1 = REDRAW_MAX,
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(text[len] == '\0');
|
|
|
|
|
2017-09-19 01:11:51 +03:00
|
|
|
if (tree->root == NULL) {
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
2017-09-18 22:19:00 +03:00
|
|
|
err = treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, NULL,
|
|
|
|
treeview__search_walk_cb, &sw);
|
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len > 0) {
|
|
|
|
tree->search.height = sw.window_height;
|
|
|
|
tree->search.search = true;
|
|
|
|
height = sw.window_height;
|
|
|
|
} else {
|
|
|
|
tree->search.search = false;
|
|
|
|
height = tree->root->height;
|
|
|
|
}
|
|
|
|
|
|
|
|
r.y1 = ((height > prev_height) ? height : prev_height) + search_height;
|
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2017-09-19 00:53:06 +03:00
|
|
|
treeview__cw_update_size(tree, -1, height);
|
2017-09-18 22:19:00 +03:00
|
|
|
treeview__cw_scroll_top(tree);
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 11:37:25 +03:00
|
|
|
/**
|
|
|
|
* Cancel a treeview search, optionally droping focus from search widget.
|
|
|
|
*
|
|
|
|
* \param[in] tree Treeview to cancel search in.
|
|
|
|
* \param[in] drop_focus Iff true, drop input focus from search widget.
|
|
|
|
*/
|
|
|
|
static void treeview__search_cancel(treeview *tree, bool drop_focus)
|
|
|
|
{
|
|
|
|
struct rect r = {
|
|
|
|
.x0 = tree_g.window_padding + tree_g.icon_size,
|
|
|
|
.x1 = 600,
|
|
|
|
.y0 = 0,
|
|
|
|
.y1 = tree_g.line_height,
|
|
|
|
};
|
|
|
|
|
2017-09-25 14:02:14 +03:00
|
|
|
tree->search.search = false;
|
2017-09-22 11:37:25 +03:00
|
|
|
if (tree->search.active == false) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (drop_focus) {
|
|
|
|
tree->search.active = false;
|
|
|
|
textarea_set_caret(tree->search.textarea, -1);
|
|
|
|
} else {
|
|
|
|
textarea_set_caret(tree->search.textarea, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
textarea_set_text(tree->search.textarea, "");
|
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-18 22:19:00 +03:00
|
|
|
/**
|
|
|
|
* Callback for textarea_create, in desktop/treeview.h
|
|
|
|
*
|
|
|
|
* \param data treeview context
|
|
|
|
* \param msg textarea message
|
|
|
|
*/
|
|
|
|
static void treeview_textarea_search_callback(void *data,
|
|
|
|
struct textarea_msg *msg)
|
|
|
|
{
|
|
|
|
treeview *tree = data;
|
|
|
|
struct rect *r;
|
|
|
|
|
2017-09-19 00:53:06 +03:00
|
|
|
if (tree->search.active == false || tree->root == NULL) {
|
2017-09-18 22:19:00 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (msg->type) {
|
|
|
|
case TEXTAREA_MSG_DRAG_REPORT:
|
|
|
|
if (msg->data.drag == TEXTAREA_DRAG_NONE) {
|
|
|
|
/* Textarea drag finished */
|
|
|
|
tree->drag.type = TV_DRAG_NONE;
|
|
|
|
} else {
|
|
|
|
/* Textarea drag started */
|
|
|
|
tree->drag.type = TV_DRAG_SEARCH;
|
|
|
|
}
|
|
|
|
treeview__cw_drag_status(tree, tree->drag.type);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TEXTAREA_MSG_REDRAW_REQUEST:
|
|
|
|
r = &msg->data.redraw;
|
|
|
|
r->x0 += tree_g.window_padding + tree_g.icon_size;
|
|
|
|
r->y0 += 0;
|
|
|
|
r->x1 += 600;
|
|
|
|
r->y1 += tree_g.line_height;
|
|
|
|
|
|
|
|
/* Redraw the textarea */
|
|
|
|
treeview__cw_invalidate_area(tree, r);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TEXTAREA_MSG_TEXT_MODIFIED:
|
|
|
|
/* Textarea length includes trailing NULL, so subtract it. */
|
|
|
|
treeview__search(tree,
|
|
|
|
msg->data.modified.text,
|
|
|
|
msg->data.modified.len - 1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the layout for any active search.
|
|
|
|
*
|
|
|
|
* \param[in] tree The tree to update.
|
|
|
|
*/
|
|
|
|
static void treeview__search_update_display(
|
|
|
|
treeview *tree)
|
|
|
|
{
|
|
|
|
const char *string;
|
|
|
|
unsigned int len;
|
|
|
|
|
|
|
|
if (tree->search.search == false) {
|
|
|
|
/* No active search to update view for. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
string = textarea_data(tree->search.textarea, &len);
|
|
|
|
if (string == NULL || len == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
treeview__search(tree, string, len - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 20:08:35 +04:00
|
|
|
/**
|
|
|
|
* Create treeview's root node
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param[out] root Returns root node
|
2013-06-18 20:08:35 +04:00
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
static nserror treeview_create_node_root(treeview_node **root)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
2013-07-03 16:48:53 +04:00
|
|
|
treeview_node *n;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
n = malloc(sizeof(struct treeview_node));
|
|
|
|
if (n == NULL) {
|
|
|
|
return NSERROR_NOMEM;
|
|
|
|
}
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
n->flags = TV_NFLAGS_EXPANDED;
|
2013-05-31 13:05:15 +04:00
|
|
|
n->type = TREE_NODE_ROOT;
|
|
|
|
|
|
|
|
n->height = 0;
|
2013-06-04 13:37:16 +04:00
|
|
|
n->inset = tree_g.window_padding - tree_g.step_width;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
n->text.data = NULL;
|
|
|
|
n->text.len = 0;
|
|
|
|
n->text.width = 0;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
n->parent = NULL;
|
2013-08-17 16:27:38 +04:00
|
|
|
n->next_sib = NULL;
|
|
|
|
n->prev_sib = NULL;
|
2013-05-31 13:05:15 +04:00
|
|
|
n->children = NULL;
|
|
|
|
|
|
|
|
n->client_data = NULL;
|
|
|
|
|
|
|
|
*root = n;
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
/**
|
2017-03-01 02:41:48 +03:00
|
|
|
* Set a node's inset from its parent
|
|
|
|
*
|
|
|
|
* This may be used as treeview walk callback
|
|
|
|
*
|
|
|
|
* \param[in] n node to set inset on
|
|
|
|
* \param[in] ctx context unused
|
|
|
|
* \param[out] skip_children set to false so child nodes are not skipped.
|
|
|
|
* \param[out] end unused flag so treewalk in not terminated early.
|
2013-08-17 22:39:17 +04:00
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static nserror
|
|
|
|
treeview_set_inset_from_parent(treeview_node *n,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end)
|
2013-08-17 22:39:17 +04:00
|
|
|
{
|
|
|
|
if (n->parent != NULL)
|
|
|
|
n->inset = n->parent->inset + tree_g.step_width;
|
|
|
|
|
|
|
|
*skip_children = false;
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
/**
|
|
|
|
* Insert a treeview node into a treeview
|
|
|
|
*
|
2017-09-14 22:10:30 +03:00
|
|
|
* \param tree the treeview to insert node into.
|
|
|
|
* \param a parentless node to insert
|
|
|
|
* \param b tree node to insert a as a relation of
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param rel The relationship between \a a and \a b
|
2013-05-31 13:05:15 +04:00
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static inline void
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_insert_node(
|
|
|
|
treeview *tree,
|
|
|
|
treeview_node *a,
|
|
|
|
treeview_node *b,
|
|
|
|
enum treeview_relationship rel)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
|
|
|
assert(a != NULL);
|
|
|
|
assert(a->parent == NULL);
|
|
|
|
assert(b != NULL);
|
|
|
|
|
|
|
|
switch (rel) {
|
2013-06-04 15:24:14 +04:00
|
|
|
case TREE_REL_FIRST_CHILD:
|
2013-05-31 13:05:15 +04:00
|
|
|
assert(b->type != TREE_NODE_ENTRY);
|
|
|
|
a->parent = b;
|
2013-08-17 16:27:38 +04:00
|
|
|
a->next_sib = b->children;
|
|
|
|
if (a->next_sib)
|
|
|
|
a->next_sib->prev_sib = a;
|
2013-05-31 13:05:15 +04:00
|
|
|
b->children = a;
|
|
|
|
break;
|
|
|
|
|
2013-06-04 15:24:14 +04:00
|
|
|
case TREE_REL_NEXT_SIBLING:
|
2013-05-31 13:05:15 +04:00
|
|
|
assert(b->type != TREE_NODE_ROOT);
|
2013-08-17 16:27:38 +04:00
|
|
|
a->prev_sib = b;
|
|
|
|
a->next_sib = b->next_sib;
|
2013-05-31 13:05:15 +04:00
|
|
|
a->parent = b->parent;
|
2013-08-17 16:27:38 +04:00
|
|
|
b->next_sib = a;
|
|
|
|
if (a->next_sib)
|
|
|
|
a->next_sib->prev_sib = a;
|
2013-05-31 13:05:15 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(a->parent != NULL);
|
|
|
|
|
2013-06-04 13:37:16 +04:00
|
|
|
a->inset = a->parent->inset + tree_g.step_width;
|
2013-08-17 22:39:17 +04:00
|
|
|
if (a->children != NULL) {
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, a,
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_COMPLETE, NULL,
|
|
|
|
treeview_set_inset_from_parent, NULL);
|
2013-08-17 22:39:17 +04:00
|
|
|
}
|
2013-06-04 13:37:16 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (a->parent->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-20 18:02:09 +04:00
|
|
|
int height = a->height;
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Parent is expanded, so inserted node will be visible and
|
|
|
|
* affect layout */
|
2013-08-19 17:23:44 +04:00
|
|
|
if (a->text.width == 0) {
|
2016-04-24 01:32:21 +03:00
|
|
|
guit->layout->width(&plot_style_odd.text,
|
|
|
|
a->text.data,
|
|
|
|
a->text.len,
|
|
|
|
&(a->text.width));
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
2013-08-20 18:02:09 +04:00
|
|
|
|
|
|
|
do {
|
|
|
|
a->parent->height += height;
|
|
|
|
a = a->parent;
|
|
|
|
} while (a->parent != NULL);
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_create_node_folder(treeview *tree,
|
|
|
|
treeview_node **folder,
|
|
|
|
treeview_node *relation,
|
|
|
|
enum treeview_relationship rel,
|
|
|
|
const struct treeview_field_data *field,
|
|
|
|
void *data,
|
|
|
|
treeview_node_options_flags flags)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
2013-07-03 16:48:53 +04:00
|
|
|
treeview_node *n;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
assert(data != NULL);
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
if (relation == NULL) {
|
|
|
|
relation = tree->root;
|
2013-06-04 15:24:14 +04:00
|
|
|
rel = TREE_REL_FIRST_CHILD;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
n = malloc(sizeof(struct treeview_node));
|
|
|
|
if (n == NULL) {
|
|
|
|
return NSERROR_NOMEM;
|
|
|
|
}
|
|
|
|
|
2013-11-08 22:53:34 +04:00
|
|
|
n->flags = (flags & TREE_OPTION_SPECIAL_DIR) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
TV_NFLAGS_SPECIAL : TV_NFLAGS_NONE;
|
2013-05-31 13:05:15 +04:00
|
|
|
n->type = TREE_NODE_FOLDER;
|
|
|
|
|
|
|
|
n->height = tree_g.line_height;
|
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
n->text.data = field->value;
|
|
|
|
n->text.len = field->value_len;
|
|
|
|
n->text.width = 0;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
n->parent = NULL;
|
2013-08-17 16:27:38 +04:00
|
|
|
n->next_sib = NULL;
|
|
|
|
n->prev_sib = NULL;
|
2013-05-31 13:05:15 +04:00
|
|
|
n->children = NULL;
|
|
|
|
|
|
|
|
n->client_data = data;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_insert_node(tree, n, relation, rel);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->parent->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-29 23:43:25 +04:00
|
|
|
/* Inform front end of change in dimensions */
|
2013-08-30 15:51:40 +04:00
|
|
|
if (!(flags & TREE_OPTION_SUPPRESS_RESIZE))
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_update_size(tree, -1,
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->root->height);
|
2013-08-29 23:43:25 +04:00
|
|
|
|
|
|
|
/* Redraw */
|
2013-08-30 15:51:40 +04:00
|
|
|
if (!(flags & TREE_OPTION_SUPPRESS_REDRAW)) {
|
2013-08-29 23:43:25 +04:00
|
|
|
struct rect r;
|
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = treeview_node_y(tree, n);
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
r.y1 = tree->root->height;
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-29 23:43:25 +04:00
|
|
|
}
|
2013-08-29 22:05:04 +04:00
|
|
|
}
|
|
|
|
|
2013-08-29 23:43:25 +04:00
|
|
|
*folder = n;
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-20 22:44:14 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_update_node_folder(treeview *tree,
|
|
|
|
treeview_node *folder,
|
|
|
|
const struct treeview_field_data *field,
|
|
|
|
void *data)
|
2013-08-20 22:44:14 +04:00
|
|
|
{
|
|
|
|
bool match;
|
|
|
|
|
|
|
|
assert(data != NULL);
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(folder != NULL);
|
|
|
|
assert(data == folder->client_data);
|
|
|
|
assert(folder->parent != NULL);
|
|
|
|
|
|
|
|
assert(field != NULL);
|
|
|
|
assert(lwc_string_isequal(tree->fields[tree->n_fields].field,
|
2017-03-01 02:41:48 +03:00
|
|
|
field->field, &match) == lwc_error_ok &&
|
|
|
|
match == true);
|
2013-08-20 22:44:14 +04:00
|
|
|
folder->text.data = field->value;
|
|
|
|
folder->text.len = field->value_len;
|
|
|
|
folder->text.width = 0;
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (folder->parent->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-20 22:44:14 +04:00
|
|
|
/* Text will be seen, get its width */
|
2016-04-24 01:32:21 +03:00
|
|
|
guit->layout->width(&plot_style_odd.text,
|
2017-03-01 02:41:48 +03:00
|
|
|
folder->text.data,
|
|
|
|
folder->text.len,
|
|
|
|
&(folder->text.width));
|
2013-08-20 22:44:14 +04:00
|
|
|
} else {
|
|
|
|
/* Just invalidate the width, since it's not needed now */
|
|
|
|
folder->text.width = 0;
|
|
|
|
}
|
|
|
|
|
2013-08-29 23:43:25 +04:00
|
|
|
/* Redraw */
|
2014-05-24 18:09:20 +04:00
|
|
|
if (folder->parent->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-29 23:43:25 +04:00
|
|
|
struct rect r;
|
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = treeview_node_y(tree, folder);
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
r.y1 = r.y0 + tree_g.line_height;
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-29 23:43:25 +04:00
|
|
|
}
|
|
|
|
|
2013-08-20 22:44:14 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_update_node_entry(treeview *tree,
|
|
|
|
treeview_node *entry,
|
|
|
|
const struct treeview_field_data fields[],
|
|
|
|
void *data)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
|
|
|
bool match;
|
|
|
|
struct treeview_node_entry *e = (struct treeview_node_entry *)entry;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(data != NULL);
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(entry != NULL);
|
|
|
|
assert(data == entry->client_data);
|
|
|
|
assert(entry->parent != NULL);
|
|
|
|
|
|
|
|
assert(fields != NULL);
|
|
|
|
assert(fields[0].field != NULL);
|
|
|
|
assert(lwc_string_isequal(tree->fields[0].field,
|
2017-03-01 02:41:48 +03:00
|
|
|
fields[0].field, &match) == lwc_error_ok &&
|
|
|
|
match == true);
|
2013-08-19 17:23:44 +04:00
|
|
|
entry->text.data = fields[0].value;
|
|
|
|
entry->text.len = fields[0].value_len;
|
|
|
|
entry->text.width = 0;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (entry->parent->flags & TV_NFLAGS_EXPANDED) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Text will be seen, get its width */
|
2016-04-24 01:32:21 +03:00
|
|
|
guit->layout->width(&plot_style_odd.text,
|
2017-03-01 02:41:48 +03:00
|
|
|
entry->text.data,
|
|
|
|
entry->text.len,
|
|
|
|
&(entry->text.width));
|
2013-05-31 13:05:15 +04:00
|
|
|
} else {
|
|
|
|
/* Just invalidate the width, since it's not needed now */
|
2013-08-19 17:23:44 +04:00
|
|
|
entry->text.width = 0;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i < tree->n_fields; i++) {
|
|
|
|
assert(fields[i].field != NULL);
|
|
|
|
assert(lwc_string_isequal(tree->fields[i].field,
|
2017-03-01 02:41:48 +03:00
|
|
|
fields[i].field, &match) == lwc_error_ok &&
|
|
|
|
match == true);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-03 20:06:47 +04:00
|
|
|
e->fields[i - 1].value.data = fields[i].value;
|
|
|
|
e->fields[i - 1].value.len = fields[i].value_len;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (entry->flags & TV_NFLAGS_EXPANDED) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Text will be seen, get its width */
|
2016-04-24 01:32:21 +03:00
|
|
|
guit->layout->width(&plot_style_odd.text,
|
2017-03-01 02:41:48 +03:00
|
|
|
e->fields[i - 1].value.data,
|
|
|
|
e->fields[i - 1].value.len,
|
|
|
|
&(e->fields[i - 1].value.width));
|
2013-05-31 13:05:15 +04:00
|
|
|
} else {
|
|
|
|
/* Invalidate the width, since it's not needed yet */
|
2013-06-03 20:06:47 +04:00
|
|
|
e->fields[i - 1].value.width = 0;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-18 22:19:00 +03:00
|
|
|
treeview__search_update_display(tree);
|
|
|
|
|
2013-08-29 23:43:25 +04:00
|
|
|
/* Redraw */
|
2014-05-24 18:09:20 +04:00
|
|
|
if (entry->parent->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-29 23:43:25 +04:00
|
|
|
struct rect r;
|
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = treeview_node_y(tree, entry);
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
r.y1 = r.y0 + entry->height;
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-29 23:43:25 +04:00
|
|
|
}
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_create_node_entry(treeview *tree,
|
|
|
|
treeview_node **entry,
|
|
|
|
treeview_node *relation,
|
|
|
|
enum treeview_relationship rel,
|
|
|
|
const struct treeview_field_data fields[],
|
|
|
|
void *data,
|
|
|
|
treeview_node_options_flags flags)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
|
|
|
bool match;
|
|
|
|
struct treeview_node_entry *e;
|
2013-07-03 16:48:53 +04:00
|
|
|
treeview_node *n;
|
2013-05-31 13:05:15 +04:00
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(data != NULL);
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
if (relation == NULL) {
|
|
|
|
relation = tree->root;
|
2013-06-04 15:24:14 +04:00
|
|
|
rel = TREE_REL_FIRST_CHILD;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
e = malloc(sizeof(struct treeview_node_entry) +
|
2017-09-10 13:57:02 +03:00
|
|
|
(tree->n_fields - 1) * sizeof(struct treeview_field));
|
2013-05-31 13:05:15 +04:00
|
|
|
if (e == NULL) {
|
|
|
|
return NSERROR_NOMEM;
|
|
|
|
}
|
|
|
|
|
2013-06-03 20:06:47 +04:00
|
|
|
|
2013-07-03 16:48:53 +04:00
|
|
|
n = (treeview_node *) e;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
n->flags = TV_NFLAGS_NONE;
|
2013-05-31 13:05:15 +04:00
|
|
|
n->type = TREE_NODE_ENTRY;
|
|
|
|
|
|
|
|
n->height = tree_g.line_height;
|
|
|
|
|
|
|
|
assert(fields != NULL);
|
|
|
|
assert(fields[0].field != NULL);
|
|
|
|
assert(lwc_string_isequal(tree->fields[0].field,
|
2017-09-10 13:57:02 +03:00
|
|
|
fields[0].field, &match) == lwc_error_ok &&
|
|
|
|
match == true);
|
2013-08-19 17:23:44 +04:00
|
|
|
n->text.data = fields[0].value;
|
|
|
|
n->text.len = fields[0].value_len;
|
|
|
|
n->text.width = 0;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
n->parent = NULL;
|
2013-08-17 16:27:38 +04:00
|
|
|
n->next_sib = NULL;
|
|
|
|
n->prev_sib = NULL;
|
2013-05-31 13:05:15 +04:00
|
|
|
n->children = NULL;
|
|
|
|
|
|
|
|
n->client_data = data;
|
|
|
|
|
|
|
|
for (i = 1; i < tree->n_fields; i++) {
|
|
|
|
assert(fields[i].field != NULL);
|
|
|
|
assert(lwc_string_isequal(tree->fields[i].field,
|
2017-09-10 13:57:02 +03:00
|
|
|
fields[i].field, &match) == lwc_error_ok &&
|
|
|
|
match == true);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-03 20:06:47 +04:00
|
|
|
e->fields[i - 1].value.data = fields[i].value;
|
|
|
|
e->fields[i - 1].value.len = fields[i].value_len;
|
|
|
|
e->fields[i - 1].value.width = 0;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_insert_node(tree, n, relation, rel);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->parent->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-29 23:43:25 +04:00
|
|
|
/* Inform front end of change in dimensions */
|
2013-08-30 15:51:40 +04:00
|
|
|
if (!(flags & TREE_OPTION_SUPPRESS_RESIZE))
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_update_size(tree, -1,
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->root->height);
|
2013-08-29 23:43:25 +04:00
|
|
|
|
|
|
|
/* Redraw */
|
2013-08-30 15:51:40 +04:00
|
|
|
if (!(flags & TREE_OPTION_SUPPRESS_REDRAW)) {
|
2013-08-29 23:43:25 +04:00
|
|
|
struct rect r;
|
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = treeview_node_y(tree, n);
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
r.y1 = tree->root->height;
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-29 23:43:25 +04:00
|
|
|
}
|
2013-08-17 17:37:24 +04:00
|
|
|
}
|
|
|
|
|
2017-09-18 22:19:00 +03:00
|
|
|
treeview__search_update_display(tree);
|
|
|
|
|
2013-08-29 23:43:25 +04:00
|
|
|
*entry = n;
|
|
|
|
|
2013-08-29 22:05:04 +04:00
|
|
|
return NSERROR_OK;
|
2013-08-17 17:37:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* Treewalk iterator context
|
|
|
|
*/
|
2013-07-25 19:06:34 +04:00
|
|
|
struct treeview_walk_ctx {
|
2013-08-22 19:05:03 +04:00
|
|
|
treeview_walk_cb enter_cb;
|
|
|
|
treeview_walk_cb leave_cb;
|
2013-07-25 19:06:34 +04:00
|
|
|
void *ctx;
|
|
|
|
enum treeview_node_type type;
|
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node enter callback.
|
|
|
|
*
|
|
|
|
* \param n current node
|
|
|
|
* \param ctx treewalk context
|
|
|
|
* \param skip_children set if child nodes should be skipped
|
|
|
|
* \param end set if iteration should end early
|
|
|
|
*/
|
|
|
|
static nserror
|
|
|
|
treeview_walk_fwd_cb(treeview_node *n,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end)
|
2013-07-25 19:06:34 +04:00
|
|
|
{
|
|
|
|
struct treeview_walk_ctx *tw = ctx;
|
|
|
|
|
|
|
|
if (n->type & tw->type) {
|
2013-08-22 19:05:03 +04:00
|
|
|
return tw->enter_cb(tw->ctx, n->client_data, n->type, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node leave callback.
|
|
|
|
*
|
|
|
|
* \param n current node
|
|
|
|
* \param ctx treewalk context
|
|
|
|
* \param end set if iteration should end early
|
|
|
|
*/
|
2013-08-22 19:05:03 +04:00
|
|
|
static nserror treeview_walk_bwd_cb(treeview_node *n, void *ctx, bool *end)
|
|
|
|
{
|
|
|
|
struct treeview_walk_ctx *tw = ctx;
|
|
|
|
|
|
|
|
if (n->type & tw->type) {
|
|
|
|
return tw->leave_cb(tw->ctx, n->client_data, n->type, end);
|
2013-07-25 19:06:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
2013-07-25 19:06:34 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_walk(treeview *tree,
|
|
|
|
treeview_node *root,
|
|
|
|
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
|
|
|
{
|
|
|
|
struct treeview_walk_ctx tw = {
|
2013-08-22 19:05:03 +04:00
|
|
|
.enter_cb = enter_cb,
|
|
|
|
.leave_cb = leave_cb,
|
2013-07-25 19:06:34 +04:00
|
|
|
.ctx = ctx,
|
|
|
|
.type = type
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
if (root == NULL)
|
|
|
|
root = tree->root;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
return treeview_walk_internal(tree, root,
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
|
2017-09-10 13:57:02 +03:00
|
|
|
(leave_cb != NULL) ? treeview_walk_bwd_cb : NULL,
|
|
|
|
(enter_cb != NULL) ? treeview_walk_fwd_cb : NULL,
|
|
|
|
&tw);
|
2013-07-25 19:06:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
/**
|
|
|
|
* Unlink a treeview node
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param n Node to unlink
|
2013-08-17 22:39:17 +04:00
|
|
|
* \return true iff ancestor heights need to be reduced
|
|
|
|
*/
|
|
|
|
static inline bool treeview_unlink_node(treeview_node *n)
|
2013-07-07 02:00:43 +04:00
|
|
|
{
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Unlink node from tree */
|
|
|
|
if (n->parent != NULL && n->parent->children == n) {
|
|
|
|
/* Node is a first child */
|
2013-08-17 16:27:38 +04:00
|
|
|
n->parent->children = n->next_sib;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-08-17 16:27:38 +04:00
|
|
|
} else if (n->prev_sib != NULL) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Node is not first child */
|
2013-08-17 16:27:38 +04:00
|
|
|
n->prev_sib->next_sib = n->next_sib;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
2013-08-17 16:27:38 +04:00
|
|
|
if (n->next_sib != NULL) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Always need to do this */
|
2013-08-17 16:27:38 +04:00
|
|
|
n->next_sib->prev_sib = n->prev_sib;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
2013-07-01 01:07:53 +04:00
|
|
|
/* Reduce ancestor heights */
|
2017-03-01 02:41:48 +03:00
|
|
|
if ((n->parent != NULL) &&
|
|
|
|
(n->parent->flags & TV_NFLAGS_EXPANDED)) {
|
2013-08-17 22:39:17 +04:00
|
|
|
return true;
|
2013-07-01 01:07:53 +04:00
|
|
|
}
|
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
/**
|
|
|
|
* Cancel the editing of a treeview node
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to cancel node editing in
|
|
|
|
* \param redraw Set true iff redraw of removed textarea area required
|
2013-08-19 17:23:44 +04:00
|
|
|
*/
|
|
|
|
static void treeview_edit_cancel(treeview *tree, bool redraw)
|
|
|
|
{
|
|
|
|
struct rect r;
|
|
|
|
|
|
|
|
if (tree->edit.textarea == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
textarea_destroy(tree->edit.textarea);
|
|
|
|
|
|
|
|
tree->edit.textarea = NULL;
|
|
|
|
tree->edit.node = NULL;
|
|
|
|
|
|
|
|
if (tree->drag.type == TV_DRAG_TEXTAREA)
|
|
|
|
tree->drag.type = TV_DRAG_NONE;
|
|
|
|
|
|
|
|
if (redraw) {
|
|
|
|
r.x0 = tree->edit.x;
|
|
|
|
r.y0 = tree->edit.y;
|
|
|
|
r.x1 = tree->edit.x + tree->edit.w;
|
|
|
|
r.y1 = tree->edit.y + tree->edit.h;
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-19 17:23:44 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-01 02:41:48 +03:00
|
|
|
* Complete a treeview edit
|
2013-08-19 17:23:44 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* Complete edit by informing the client with a change request msg
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to complete edit in
|
2013-08-19 17:23:44 +04:00
|
|
|
*/
|
|
|
|
static void treeview_edit_done(treeview *tree)
|
|
|
|
{
|
2013-08-20 18:44:24 +04:00
|
|
|
int len, error;
|
|
|
|
char* new_text;
|
|
|
|
treeview_node *n = tree->edit.node;
|
|
|
|
struct treeview_node_msg msg;
|
|
|
|
msg.msg = TREE_MSG_NODE_EDIT;
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
if (tree->edit.textarea == NULL) {
|
2013-08-19 17:23:44 +04:00
|
|
|
return;
|
2017-03-01 02:41:48 +03:00
|
|
|
}
|
2013-08-19 17:23:44 +04:00
|
|
|
|
2013-08-20 18:44:24 +04:00
|
|
|
assert(n != NULL);
|
|
|
|
|
|
|
|
/* Get new text length */
|
|
|
|
len = textarea_get_text(tree->edit.textarea, NULL, 0);
|
|
|
|
|
|
|
|
new_text = malloc(len);
|
|
|
|
if (new_text == NULL) {
|
|
|
|
/* TODO: don't just silently ignore */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the new text from textarea */
|
|
|
|
error = textarea_get_text(tree->edit.textarea, new_text, len);
|
|
|
|
if (error == -1) {
|
|
|
|
/* TODO: don't just silently ignore */
|
|
|
|
free(new_text);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
/* Inform the treeview client with change request message */
|
2013-08-20 18:44:24 +04:00
|
|
|
msg.data.node_edit.field = tree->edit.field;
|
|
|
|
msg.data.node_edit.text = new_text;
|
|
|
|
|
|
|
|
switch (n->type) {
|
|
|
|
case TREE_NODE_ENTRY:
|
|
|
|
tree->callbacks->entry(msg, n->client_data);
|
|
|
|
break;
|
|
|
|
case TREE_NODE_FOLDER:
|
|
|
|
tree->callbacks->folder(msg, n->client_data);
|
|
|
|
break;
|
|
|
|
case TREE_NODE_ROOT:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finished with the new text */
|
|
|
|
free(new_text);
|
2013-08-19 17:23:44 +04:00
|
|
|
|
|
|
|
/* Finally, destroy the treeview, and redraw */
|
|
|
|
treeview_edit_cancel(tree, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* context for treeview node deletion iterator
|
|
|
|
*/
|
2013-08-17 22:39:17 +04:00
|
|
|
struct treeview_node_delete {
|
|
|
|
treeview *tree;
|
2013-08-30 15:51:40 +04:00
|
|
|
int h_reduction;
|
2013-08-17 22:39:17 +04:00
|
|
|
bool user_interaction;
|
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node callback deleting nodes.
|
|
|
|
*/
|
|
|
|
static nserror
|
|
|
|
treeview_delete_node_walk_cb(treeview_node *n, void *ctx, bool *end)
|
2013-08-17 22:39:17 +04:00
|
|
|
{
|
|
|
|
struct treeview_node_delete *nd = (struct treeview_node_delete *)ctx;
|
|
|
|
struct treeview_node_msg msg;
|
2017-03-01 02:41:48 +03:00
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
msg.msg = TREE_MSG_NODE_DELETE;
|
|
|
|
msg.data.delete.user = nd->user_interaction;
|
|
|
|
|
|
|
|
assert(n->children == NULL);
|
|
|
|
|
|
|
|
if (treeview_unlink_node(n))
|
2013-08-30 15:51:40 +04:00
|
|
|
nd->h_reduction += (n->type == TREE_NODE_ENTRY) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
n->height : tree_g.line_height;
|
2013-08-17 22:39:17 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Handle any special treatment */
|
|
|
|
switch (n->type) {
|
|
|
|
case TREE_NODE_ENTRY:
|
2013-07-07 02:00:43 +04:00
|
|
|
nd->tree->callbacks->entry(msg, n->client_data);
|
2013-05-31 13:05:15 +04:00
|
|
|
break;
|
2017-03-01 02:41:48 +03:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
case TREE_NODE_FOLDER:
|
2013-07-07 02:00:43 +04:00
|
|
|
nd->tree->callbacks->folder(msg, n->client_data);
|
2013-05-31 13:05:15 +04:00
|
|
|
break;
|
2017-03-01 02:41:48 +03:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
case TREE_NODE_ROOT:
|
|
|
|
break;
|
2017-03-01 02:41:48 +03:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
default:
|
|
|
|
return NSERROR_BAD_PARAMETER;
|
|
|
|
}
|
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
/* Cancel any edit of this node */
|
|
|
|
if (nd->tree->edit.textarea != NULL &&
|
2017-03-01 02:41:48 +03:00
|
|
|
nd->tree->edit.node == n) {
|
2013-08-19 17:23:44 +04:00
|
|
|
treeview_edit_cancel(nd->tree, false);
|
2017-03-01 02:41:48 +03:00
|
|
|
}
|
2013-08-19 17:23:44 +04:00
|
|
|
|
2013-07-07 02:00:43 +04:00
|
|
|
/* Free the node */
|
|
|
|
free(n);
|
2013-07-03 15:37:58 +04:00
|
|
|
|
2013-07-07 02:00:43 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
2013-07-07 02:00:43 +04:00
|
|
|
/**
|
|
|
|
* Delete a treeview node
|
|
|
|
*
|
|
|
|
* Will emit folder or entry deletion msg callback.
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \note this can be called from inside a treeview_walk fwd callback.
|
2013-07-07 02:00:43 +04:00
|
|
|
* For example walking the tree and calling this for any node that's selected.
|
|
|
|
*
|
|
|
|
* This function does not delete empty nodes, so if TREEVIEW_DEL_EMPTY_DIRS is
|
|
|
|
* set, caller must also call treeview_delete_empty.
|
2017-03-01 02:41:48 +03:00
|
|
|
*
|
|
|
|
* \param tree Treeview object to delete node from
|
|
|
|
* \param n Node to delete
|
|
|
|
* \param interaction Delete is result of user interaction with treeview
|
|
|
|
* \param flags Treeview node options flags
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
2013-07-07 02:00:43 +04:00
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static nserror
|
|
|
|
treeview_delete_node_internal(treeview *tree,
|
|
|
|
treeview_node *n,
|
|
|
|
bool interaction,
|
|
|
|
treeview_node_options_flags flags)
|
2013-07-07 02:00:43 +04:00
|
|
|
{
|
|
|
|
nserror err;
|
|
|
|
treeview_node *p = n->parent;
|
|
|
|
struct treeview_node_delete nd = {
|
|
|
|
.tree = tree,
|
2013-08-30 15:51:40 +04:00
|
|
|
.h_reduction = 0,
|
2013-07-15 17:34:56 +04:00
|
|
|
.user_interaction = interaction
|
2013-07-07 02:00:43 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
if (interaction && (tree->flags & TREEVIEW_NO_DELETES)) {
|
|
|
|
return NSERROR_OK;
|
2013-07-03 15:37:58 +04:00
|
|
|
}
|
2013-06-30 20:53:41 +04:00
|
|
|
|
2013-07-07 02:00:43 +04:00
|
|
|
/* Delete any children first */
|
2017-09-14 22:10:30 +03:00
|
|
|
err = treeview_walk_internal(tree, n,
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
|
|
|
|
treeview_delete_node_walk_cb, NULL, &nd);
|
2013-07-07 02:00:43 +04:00
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now delete node */
|
|
|
|
if (n == tree->root)
|
|
|
|
tree->root = NULL;
|
|
|
|
err = treeview_delete_node_walk_cb(n, &nd, false);
|
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-08-30 15:51:40 +04:00
|
|
|
n = p;
|
2013-07-07 02:00:43 +04:00
|
|
|
/* Reduce ancestor heights */
|
2014-05-24 18:09:20 +04:00
|
|
|
while (n != NULL && n->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-30 15:51:40 +04:00
|
|
|
n->height -= nd.h_reduction;
|
|
|
|
n = n->parent;
|
2013-07-07 02:00:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Inform front end of change in dimensions */
|
2014-05-24 18:09:20 +04:00
|
|
|
if (tree->root != NULL && p != NULL && p->flags & TV_NFLAGS_EXPANDED &&
|
2017-03-01 02:41:48 +03:00
|
|
|
nd.h_reduction > 0 &&
|
|
|
|
!(flags & TREE_OPTION_SUPPRESS_RESIZE)) {
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_update_size(tree, -1,
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->root->height);
|
2013-08-30 15:51:40 +04:00
|
|
|
}
|
2013-07-07 02:00:43 +04:00
|
|
|
|
2017-09-18 22:19:00 +03:00
|
|
|
treeview__search_update_display(tree);
|
|
|
|
|
2013-07-07 02:00:43 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-30 15:51:40 +04:00
|
|
|
* Delete any empty treeview folder nodes
|
2013-07-07 02:00:43 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to delete empty nodes from
|
|
|
|
* \param interaction Delete is result of user interaction with treeview
|
2013-07-07 02:00:43 +04:00
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*
|
|
|
|
* Note this must not be called within a treeview_walk. It may delete the
|
|
|
|
* walker's 'current' node, making it impossible to move on without invalid
|
|
|
|
* reads.
|
|
|
|
*/
|
2013-07-15 17:34:56 +04:00
|
|
|
static nserror treeview_delete_empty_nodes(treeview *tree, bool interaction)
|
2013-07-07 02:00:43 +04:00
|
|
|
{
|
2013-08-30 15:51:40 +04:00
|
|
|
treeview_node *node, *child, *parent, *next_sibling, *p;
|
2013-07-07 02:00:43 +04:00
|
|
|
bool abort = false;
|
|
|
|
nserror err;
|
|
|
|
struct treeview_node_delete nd = {
|
|
|
|
.tree = tree,
|
2013-08-30 15:51:40 +04:00
|
|
|
.h_reduction = 0,
|
2013-07-15 17:34:56 +04:00
|
|
|
.user_interaction = interaction
|
2013-07-07 02:00:43 +04:00
|
|
|
};
|
|
|
|
|
2013-10-23 14:25:40 +04:00
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
2013-09-05 14:34:38 +04:00
|
|
|
node = tree->root;
|
2013-07-07 02:00:43 +04:00
|
|
|
parent = node->parent;
|
2013-08-17 16:27:38 +04:00
|
|
|
next_sibling = node->next_sib;
|
2014-05-24 18:09:20 +04:00
|
|
|
child = (node->flags & TV_NFLAGS_EXPANDED) ? node->children : NULL;
|
2013-07-07 02:00:43 +04:00
|
|
|
|
|
|
|
while (node != NULL) {
|
|
|
|
|
|
|
|
if (child != NULL) {
|
|
|
|
/* Down to children */
|
|
|
|
node = child;
|
|
|
|
} else {
|
|
|
|
/* No children. As long as we're not at the root,
|
|
|
|
* go to next sibling if present, or nearest ancestor
|
|
|
|
* with a next sibling. */
|
|
|
|
|
2013-09-05 14:34:38 +04:00
|
|
|
while (node->parent != NULL &&
|
2017-03-01 02:41:48 +03:00
|
|
|
next_sibling == NULL) {
|
2013-07-07 02:00:43 +04:00
|
|
|
if (node->type == TREE_NODE_FOLDER &&
|
2017-03-01 02:41:48 +03:00
|
|
|
node->children == NULL) {
|
2013-07-07 02:00:43 +04:00
|
|
|
/* Delete node */
|
2013-08-30 15:51:40 +04:00
|
|
|
p = node->parent;
|
2013-07-07 02:00:43 +04:00
|
|
|
err = treeview_delete_node_walk_cb(
|
2017-03-01 02:41:48 +03:00
|
|
|
node, &nd, &abort);
|
2013-07-07 02:00:43 +04:00
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
}
|
2013-08-30 15:51:40 +04:00
|
|
|
|
|
|
|
/* Reduce ancestor heights */
|
|
|
|
while (p != NULL &&
|
2017-03-01 02:41:48 +03:00
|
|
|
p->flags &
|
|
|
|
TV_NFLAGS_EXPANDED) {
|
2013-08-30 15:51:40 +04:00
|
|
|
p->height -= nd.h_reduction;
|
|
|
|
p = p->parent;
|
|
|
|
}
|
|
|
|
nd.h_reduction = 0;
|
2013-07-07 02:00:43 +04:00
|
|
|
}
|
|
|
|
node = parent;
|
|
|
|
parent = node->parent;
|
2013-08-17 16:27:38 +04:00
|
|
|
next_sibling = node->next_sib;
|
2013-07-07 02:00:43 +04:00
|
|
|
}
|
|
|
|
|
2013-09-05 14:34:38 +04:00
|
|
|
if (node->parent == NULL)
|
2013-07-07 02:00:43 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (node->type == TREE_NODE_FOLDER &&
|
2017-03-01 02:41:48 +03:00
|
|
|
node->children == NULL) {
|
2013-07-07 02:00:43 +04:00
|
|
|
/* Delete node */
|
2013-08-30 15:51:40 +04:00
|
|
|
p = node->parent;
|
2013-07-07 02:00:43 +04:00
|
|
|
err = treeview_delete_node_walk_cb(
|
2017-03-01 02:41:48 +03:00
|
|
|
node, &nd, &abort);
|
2013-07-07 02:00:43 +04:00
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
}
|
2013-08-30 15:51:40 +04:00
|
|
|
|
|
|
|
/* Reduce ancestor heights */
|
|
|
|
while (p != NULL &&
|
2017-03-01 02:41:48 +03:00
|
|
|
p->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-30 15:51:40 +04:00
|
|
|
p->height -= nd.h_reduction;
|
|
|
|
p = p->parent;
|
|
|
|
}
|
|
|
|
nd.h_reduction = 0;
|
2013-07-07 02:00:43 +04:00
|
|
|
}
|
|
|
|
node = next_sibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(node != NULL);
|
2013-09-05 14:34:38 +04:00
|
|
|
assert(node->parent != NULL);
|
2013-07-07 02:00:43 +04:00
|
|
|
|
|
|
|
parent = node->parent;
|
2013-08-17 16:27:38 +04:00
|
|
|
next_sibling = node->next_sib;
|
2014-05-24 18:09:20 +04:00
|
|
|
child = (node->flags & TV_NFLAGS_EXPANDED) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
node->children : NULL;
|
2013-07-07 02:00:43 +04:00
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-03 14:50:23 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_delete_node(treeview *tree,
|
|
|
|
treeview_node *n,
|
|
|
|
treeview_node_options_flags flags)
|
2013-07-03 14:50:23 +04:00
|
|
|
{
|
2013-07-07 02:00:43 +04:00
|
|
|
nserror err;
|
2013-08-29 22:58:43 +04:00
|
|
|
struct rect r;
|
2013-10-03 03:13:18 +04:00
|
|
|
bool visible;
|
2013-08-29 23:43:25 +04:00
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(n != NULL);
|
|
|
|
assert(n->parent != NULL);
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
visible = n->parent->flags & TV_NFLAGS_EXPANDED;
|
2013-10-03 03:13:18 +04:00
|
|
|
|
2013-08-29 22:58:43 +04:00
|
|
|
r.y0 = treeview_node_y(tree, n);
|
|
|
|
r.y1 = tree->root->height;
|
2013-07-07 02:00:43 +04:00
|
|
|
|
2013-08-30 15:51:40 +04:00
|
|
|
err = treeview_delete_node_internal(tree, n, false, flags);
|
2013-07-07 02:00:43 +04:00
|
|
|
if (err != NSERROR_OK)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (tree->flags & TREEVIEW_DEL_EMPTY_DIRS) {
|
2013-08-30 16:29:19 +04:00
|
|
|
int h = tree->root->height;
|
2013-07-07 02:00:43 +04:00
|
|
|
/* Delete any empty nodes */
|
2013-07-15 17:34:56 +04:00
|
|
|
err = treeview_delete_empty_nodes(tree, false);
|
2013-07-07 02:00:43 +04:00
|
|
|
if (err != NSERROR_OK)
|
|
|
|
return err;
|
2013-08-30 16:29:19 +04:00
|
|
|
|
|
|
|
/* Inform front end of change in dimensions */
|
|
|
|
if (tree->root->height != h) {
|
|
|
|
r.y0 = 0;
|
|
|
|
if (!(flags & TREE_OPTION_SUPPRESS_RESIZE)) {
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_update_size(tree, -1,
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->root->height);
|
2013-08-30 16:29:19 +04:00
|
|
|
}
|
|
|
|
}
|
2013-08-30 15:51:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Redraw */
|
2013-10-03 03:13:18 +04:00
|
|
|
if (visible && !(flags & TREE_OPTION_SUPPRESS_REDRAW)) {
|
2013-08-29 23:43:25 +04:00
|
|
|
r.x0 = 0;
|
|
|
|
r.x1 = REDRAW_MAX;
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-29 23:43:25 +04:00
|
|
|
}
|
2013-08-29 22:58:43 +04:00
|
|
|
|
2013-07-07 02:00:43 +04:00
|
|
|
return NSERROR_OK;
|
2013-07-03 14:50:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-09 20:35:24 +03:00
|
|
|
/**
|
|
|
|
* Helper to create a textarea.
|
|
|
|
*
|
|
|
|
* \param[in] tree The treeview we're creating the textarea for.
|
|
|
|
* \param[in] width The width of the textarea.
|
|
|
|
* \param[in] height The height of the textarea.
|
|
|
|
* \param[in] border The border colour to use.
|
|
|
|
* \param[in] background The background colour to use.
|
|
|
|
* \param[in] foreground The foreground colour to use.
|
|
|
|
* \param[in] text The text style to use for the text area.
|
|
|
|
* \param[in] ta_callback The textarea callback function to give the textarea.
|
|
|
|
* \return the textarea pointer on success, or NULL on failure.
|
|
|
|
*/
|
|
|
|
static struct textarea *treeview__create_textarea(
|
|
|
|
treeview *tree,
|
|
|
|
int width,
|
|
|
|
int height,
|
|
|
|
colour border,
|
|
|
|
colour background,
|
|
|
|
colour foreground,
|
|
|
|
plot_font_style_t text,
|
|
|
|
textarea_client_callback ta_callback)
|
|
|
|
{
|
|
|
|
/* Configure the textarea */
|
|
|
|
textarea_flags ta_flags = TEXTAREA_INTERNAL_CARET;
|
|
|
|
textarea_setup ta_setup = {
|
|
|
|
.text = text,
|
|
|
|
.width = width,
|
|
|
|
.height = height,
|
|
|
|
.pad_top = 0,
|
|
|
|
.pad_left = 2,
|
|
|
|
.pad_right = 2,
|
|
|
|
.pad_bottom = 0,
|
|
|
|
.border_width = 1,
|
|
|
|
.border_col = border,
|
|
|
|
.selected_bg = foreground,
|
|
|
|
.selected_text = background,
|
|
|
|
};
|
|
|
|
|
|
|
|
ta_setup.text.foreground = foreground;
|
|
|
|
ta_setup.text.background = background;
|
|
|
|
|
|
|
|
/* Create text area */
|
|
|
|
return textarea_create(ta_flags, &ta_setup, ta_callback, tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_create(treeview **tree,
|
2013-05-31 13:05:15 +04:00
|
|
|
const struct treeview_callback_table *callbacks,
|
2017-03-01 02:41:48 +03:00
|
|
|
int n_fields,
|
|
|
|
struct treeview_field_desc fields[],
|
2013-05-31 13:05:15 +04:00
|
|
|
const struct core_window_callback_table *cw_t,
|
2017-03-01 02:41:48 +03:00
|
|
|
struct core_window *cw,
|
|
|
|
treeview_flags flags)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
|
|
|
nserror error;
|
|
|
|
int i;
|
|
|
|
|
2016-12-29 17:42:25 +03:00
|
|
|
assert((cw_t == NULL && cw == NULL) || (cw_t != NULL && cw != NULL));
|
2013-05-31 13:05:15 +04:00
|
|
|
assert(callbacks != NULL);
|
|
|
|
|
|
|
|
assert(fields != NULL);
|
|
|
|
assert(fields[0].flags & TREE_FLAG_DEFAULT);
|
|
|
|
assert(fields[n_fields - 1].flags & TREE_FLAG_DEFAULT);
|
|
|
|
assert(n_fields >= 2);
|
|
|
|
|
|
|
|
*tree = malloc(sizeof(struct treeview));
|
|
|
|
if (*tree == NULL) {
|
|
|
|
return NSERROR_NOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*tree)->fields = malloc(sizeof(struct treeview_field) * n_fields);
|
|
|
|
if ((*tree)->fields == NULL) {
|
|
|
|
free(tree);
|
|
|
|
return NSERROR_NOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = treeview_create_node_root(&((*tree)->root));
|
|
|
|
if (error != NSERROR_OK) {
|
|
|
|
free((*tree)->fields);
|
|
|
|
free(*tree);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*tree)->field_width = 0;
|
|
|
|
for (i = 0; i < n_fields; i++) {
|
|
|
|
struct treeview_field *f = &((*tree)->fields[i]);
|
|
|
|
|
|
|
|
f->flags = fields[i].flags;
|
|
|
|
f->field = lwc_string_ref(fields[i].field);
|
|
|
|
f->value.data = lwc_string_data(fields[i].field);
|
|
|
|
f->value.len = lwc_string_length(fields[i].field);
|
|
|
|
|
2016-04-24 01:32:21 +03:00
|
|
|
guit->layout->width(&plot_style_odd.text, f->value.data,
|
2017-03-01 02:41:48 +03:00
|
|
|
f->value.len, &(f->value.width));
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
if (f->flags & TREE_FLAG_SHOW_NAME)
|
|
|
|
if ((*tree)->field_width < f->value.width)
|
|
|
|
(*tree)->field_width = f->value.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*tree)->field_width += tree_g.step_width;
|
|
|
|
|
|
|
|
(*tree)->callbacks = callbacks;
|
|
|
|
(*tree)->n_fields = n_fields - 1;
|
|
|
|
|
2013-06-18 17:58:43 +04:00
|
|
|
(*tree)->drag.type = TV_DRAG_NONE;
|
|
|
|
(*tree)->drag.start_node = NULL;
|
|
|
|
(*tree)->drag.start.x = 0;
|
|
|
|
(*tree)->drag.start.y = 0;
|
|
|
|
(*tree)->drag.start.node_y = 0;
|
|
|
|
(*tree)->drag.start.node_h = 0;
|
|
|
|
(*tree)->drag.prev.x = 0;
|
|
|
|
(*tree)->drag.prev.y = 0;
|
|
|
|
(*tree)->drag.prev.node_y = 0;
|
|
|
|
(*tree)->drag.prev.node_h = 0;
|
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
(*tree)->move.root = NULL;
|
2013-08-17 00:40:45 +04:00
|
|
|
(*tree)->move.target = NULL;
|
|
|
|
(*tree)->move.target_pos = TV_TARGET_NONE;
|
2013-08-16 23:06:08 +04:00
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
(*tree)->edit.textarea = NULL;
|
|
|
|
(*tree)->edit.node = NULL;
|
|
|
|
|
2017-09-09 20:35:24 +03:00
|
|
|
if (flags & TREEVIEW_SEARCHABLE) {
|
|
|
|
(*tree)->search.textarea = treeview__create_textarea(
|
|
|
|
*tree, 600, tree_g.line_height,
|
|
|
|
plot_style_even.text.background,
|
|
|
|
plot_style_even.text.background,
|
|
|
|
plot_style_even.text.foreground,
|
|
|
|
plot_style_odd.text,
|
|
|
|
treeview_textarea_search_callback);
|
|
|
|
if ((*tree)->search.textarea == NULL) {
|
|
|
|
treeview_destroy(*tree);
|
|
|
|
return NSERROR_NOMEM;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(*tree)->search.textarea = NULL;
|
|
|
|
}
|
|
|
|
(*tree)->search.active = false;
|
2017-09-10 14:02:36 +03:00
|
|
|
(*tree)->search.search = false;
|
2017-09-09 20:35:24 +03:00
|
|
|
|
2013-07-03 14:19:16 +04:00
|
|
|
(*tree)->flags = flags;
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
(*tree)->cw_t = cw_t;
|
|
|
|
(*tree)->cw_h = cw;
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
|
2016-12-29 17:43:38 +03:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_cw_attach(treeview *tree,
|
|
|
|
const struct core_window_callback_table *cw_t,
|
|
|
|
struct core_window *cw)
|
2016-12-29 17:43:38 +03:00
|
|
|
{
|
|
|
|
assert(cw_t != NULL);
|
|
|
|
assert(cw != NULL);
|
|
|
|
|
|
|
|
if (tree->cw_t != NULL || tree->cw_h != NULL) {
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Treeview already attached.");
|
2016-12-29 17:43:38 +03:00
|
|
|
return NSERROR_UNKNOWN;
|
|
|
|
}
|
|
|
|
tree->cw_t = cw_t;
|
|
|
|
tree->cw_h = cw;
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
nserror treeview_cw_detach(treeview *tree)
|
|
|
|
{
|
|
|
|
tree->cw_t = NULL;
|
|
|
|
tree->cw_h = NULL;
|
|
|
|
|
2017-09-22 11:37:50 +03:00
|
|
|
treeview__search_cancel(tree, true);
|
|
|
|
|
2016-12-29 17:43:38 +03:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2013-07-03 16:48:53 +04:00
|
|
|
nserror treeview_destroy(treeview *tree)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
|
|
|
int f;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
|
2017-09-19 01:32:36 +03:00
|
|
|
if (tree->search.textarea != NULL) {
|
|
|
|
tree->search.active = false;
|
|
|
|
tree->search.search = false;
|
|
|
|
textarea_destroy(tree->search.textarea);
|
|
|
|
}
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Destroy nodes */
|
2013-08-30 15:51:40 +04:00
|
|
|
treeview_delete_node_internal(tree, tree->root, false,
|
2017-03-01 02:41:48 +03:00
|
|
|
TREE_OPTION_SUPPRESS_RESIZE |
|
|
|
|
TREE_OPTION_SUPPRESS_REDRAW);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
/* Destroy feilds */
|
|
|
|
for (f = 0; f <= tree->n_fields; f++) {
|
|
|
|
lwc_string_unref(tree->fields[f].field);
|
|
|
|
}
|
|
|
|
free(tree->fields);
|
|
|
|
|
|
|
|
/* Free treeview */
|
|
|
|
free(tree);
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-03 17:08:44 +04:00
|
|
|
/**
|
|
|
|
* Expand a treeview's nodes
|
|
|
|
*
|
2014-11-08 19:49:32 +03:00
|
|
|
* \param tree Treeview object to expand nodes in
|
|
|
|
* \param node The node to expand.
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise.
|
2013-09-03 17:08:44 +04:00
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static nserror
|
|
|
|
treeview_node_expand_internal(treeview *tree, treeview_node *node)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
2013-07-03 16:48:53 +04:00
|
|
|
treeview_node *child;
|
2013-05-31 13:05:15 +04:00
|
|
|
struct treeview_node_entry *e;
|
|
|
|
int additional_height = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(node != NULL);
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (node->flags & TV_NFLAGS_EXPANDED) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* What madness is this? */
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Tried to expand an expanded node.");
|
2013-05-31 13:05:15 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (node->type) {
|
|
|
|
case TREE_NODE_FOLDER:
|
|
|
|
child = node->children;
|
|
|
|
if (child == NULL) {
|
2013-07-03 15:14:07 +04:00
|
|
|
/* Allow expansion of empty folders */
|
|
|
|
break;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
2013-08-19 17:23:44 +04:00
|
|
|
if (child->text.width == 0) {
|
2016-04-24 01:32:21 +03:00
|
|
|
guit->layout->width(&plot_style_odd.text,
|
2017-03-01 02:41:48 +03:00
|
|
|
child->text.data,
|
|
|
|
child->text.len,
|
|
|
|
&(child->text.width));
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
additional_height += child->height;
|
|
|
|
|
2013-08-17 16:27:38 +04:00
|
|
|
child = child->next_sib;
|
2013-05-31 13:05:15 +04:00
|
|
|
} while (child != NULL);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TREE_NODE_ENTRY:
|
|
|
|
assert(node->children == NULL);
|
|
|
|
|
|
|
|
e = (struct treeview_node_entry *)node;
|
|
|
|
|
2013-06-03 20:06:47 +04:00
|
|
|
for (i = 0; i < tree->n_fields - 1; i++) {
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
if (e->fields[i].value.width == 0) {
|
2016-04-24 01:32:21 +03:00
|
|
|
guit->layout->width(&plot_style_odd.text,
|
2017-03-01 02:41:48 +03:00
|
|
|
e->fields[i].value.data,
|
|
|
|
e->fields[i].value.len,
|
|
|
|
&(e->fields[i].value.width));
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add height for field */
|
|
|
|
additional_height += tree_g.line_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TREE_NODE_ROOT:
|
2014-05-24 18:09:20 +04:00
|
|
|
case TREE_NODE_NONE:
|
2013-05-31 13:05:15 +04:00
|
|
|
assert(node->type != TREE_NODE_ROOT);
|
2014-05-24 18:09:20 +04:00
|
|
|
assert(node->type != TREE_NODE_NONE);
|
2013-05-31 13:05:15 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update the node */
|
2014-05-24 18:09:20 +04:00
|
|
|
node->flags |= TV_NFLAGS_EXPANDED;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-09-19 00:53:06 +03:00
|
|
|
/* And node heights */
|
|
|
|
for (struct treeview_node *n = node;
|
|
|
|
(n != NULL) && (n->flags & TV_NFLAGS_EXPANDED);
|
|
|
|
n = n->parent) {
|
|
|
|
n->height += additional_height;
|
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-09-22 11:39:47 +03:00
|
|
|
if (tree->search.search &&
|
|
|
|
node->type == TREE_NODE_ENTRY &&
|
|
|
|
node->flags & TV_NFLAGS_MATCHED) {
|
2017-09-19 00:53:06 +03:00
|
|
|
tree->search.height += additional_height;
|
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-30 20:53:41 +04:00
|
|
|
/* Inform front end of change in dimensions */
|
2017-03-01 02:41:48 +03:00
|
|
|
if (additional_height != 0) {
|
2017-09-19 00:53:06 +03:00
|
|
|
treeview__cw_update_size(tree, -1,
|
|
|
|
treeview__get_display_height(tree));
|
2017-03-01 02:41:48 +03:00
|
|
|
}
|
2013-06-30 20:53:41 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-03 17:08:44 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
nserror treeview_node_expand(treeview *tree, treeview_node *node)
|
|
|
|
{
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror res;
|
2013-09-03 17:08:44 +04:00
|
|
|
struct rect r;
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
res = treeview_node_expand_internal(tree, node);
|
|
|
|
if (res == NSERROR_OK) {
|
|
|
|
/* expansion was successful, attempt redraw */
|
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = treeview_node_y(tree, node);
|
|
|
|
r.x1 = REDRAW_MAX;
|
2017-09-19 00:53:06 +03:00
|
|
|
r.y1 = treeview__get_display_height(tree);
|
2013-09-03 17:08:44 +04:00
|
|
|
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2017-03-01 02:41:48 +03:00
|
|
|
}
|
2013-09-03 17:08:44 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
return res;
|
2013-09-03 17:08:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* context for treeview contraction callback
|
|
|
|
*/
|
2013-09-03 16:32:46 +04:00
|
|
|
struct treeview_contract_data {
|
2017-09-19 00:53:06 +03:00
|
|
|
treeview *tree;
|
2013-09-03 16:32:46 +04:00
|
|
|
bool only_entries;
|
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node callback for handling node contraction.
|
|
|
|
*
|
|
|
|
* \param n node
|
|
|
|
* \param ctx contract iterator context
|
|
|
|
* \param end flag to end iteration now
|
|
|
|
* \return NSERROR_OK on success else appropriate error code
|
|
|
|
*/
|
2013-09-03 15:36:29 +04:00
|
|
|
static nserror treeview_node_contract_cb(treeview_node *n, void *ctx, bool *end)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
2013-09-03 16:32:46 +04:00
|
|
|
struct treeview_contract_data *data = ctx;
|
2013-08-30 15:51:40 +04:00
|
|
|
int h_reduction;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-07-07 02:00:43 +04:00
|
|
|
assert(n != NULL);
|
|
|
|
assert(n->type != TREE_NODE_ROOT);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
n->flags &= ~TV_NFLAGS_SELECTED;
|
2013-08-18 02:52:48 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if ((n->flags & TV_NFLAGS_EXPANDED) == false ||
|
2017-03-01 02:41:48 +03:00
|
|
|
(n->type == TREE_NODE_FOLDER && data->only_entries)) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Nothing to do. */
|
2013-07-03 17:18:33 +04:00
|
|
|
return NSERROR_OK;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
2013-08-30 15:51:40 +04:00
|
|
|
h_reduction = n->height - tree_g.line_height;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-08-30 15:51:40 +04:00
|
|
|
assert(h_reduction >= 0);
|
2017-09-19 00:53:06 +03:00
|
|
|
for (struct treeview_node *node = n;
|
|
|
|
(node != NULL) && (node->flags & TV_NFLAGS_EXPANDED);
|
|
|
|
node = node->parent) {
|
|
|
|
node->height -= h_reduction;
|
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-09-19 00:53:06 +03:00
|
|
|
if (data->tree->search.search) {
|
|
|
|
data->tree->search.height -= h_reduction;
|
|
|
|
}
|
|
|
|
|
|
|
|
n->flags ^= TV_NFLAGS_EXPANDED;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-07-03 17:18:33 +04:00
|
|
|
return NSERROR_OK;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
2013-09-03 17:08:44 +04:00
|
|
|
/**
|
|
|
|
* Contract a treeview node
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to contract node in
|
|
|
|
* \param node Node to contract
|
2013-09-03 17:08:44 +04:00
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static nserror
|
|
|
|
treeview_node_contract_internal(treeview *tree, treeview_node *node)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
2013-09-03 16:32:46 +04:00
|
|
|
struct treeview_contract_data data;
|
2013-08-18 02:52:48 +04:00
|
|
|
bool selected;
|
2013-05-31 13:05:15 +04:00
|
|
|
assert(node != NULL);
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if ((node->flags & TV_NFLAGS_EXPANDED) == false) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* What madness is this? */
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Tried to contract a contracted node.");
|
2013-05-31 13:05:15 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
2017-09-19 00:53:06 +03:00
|
|
|
data.tree = tree;
|
2013-09-03 16:32:46 +04:00
|
|
|
data.only_entries = false;
|
2014-05-24 18:09:20 +04:00
|
|
|
selected = node->flags & TV_NFLAGS_SELECTED;
|
2013-08-18 02:52:48 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Contract children. */
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, node, TREEVIEW_WALK_MODE_LOGICAL_EXPANDED,
|
|
|
|
treeview_node_contract_cb, NULL, &data);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
/* Contract node */
|
2013-09-03 16:32:46 +04:00
|
|
|
treeview_node_contract_cb(node, &data, false);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-08-18 02:52:48 +04:00
|
|
|
if (selected)
|
2014-05-24 18:09:20 +04:00
|
|
|
node->flags |= TV_NFLAGS_SELECTED;
|
2013-08-18 02:52:48 +04:00
|
|
|
|
2013-06-30 20:53:41 +04:00
|
|
|
/* Inform front end of change in dimensions */
|
2017-09-19 00:53:06 +03:00
|
|
|
treeview__cw_update_size(tree, -1, treeview__get_display_height(tree));
|
2013-06-30 20:53:41 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
2013-06-18 20:08:35 +04:00
|
|
|
|
2013-09-03 17:08:44 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
nserror treeview_node_contract(treeview *tree, treeview_node *node)
|
|
|
|
{
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror res;
|
2013-09-03 17:08:44 +04:00
|
|
|
struct rect r;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
res = treeview_node_contract_internal(tree, node);
|
|
|
|
if (res == NSERROR_OK) {
|
2017-03-01 15:50:30 +03:00
|
|
|
/* successful contraction, request redraw */
|
2017-03-01 02:41:48 +03:00
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = treeview_node_y(tree, node);
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
r.y1 = tree->root->height;
|
2013-09-03 17:08:44 +04:00
|
|
|
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2017-03-01 02:41:48 +03:00
|
|
|
}
|
2013-09-03 17:08:44 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
return res;
|
2013-09-03 17:08:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-03 14:06:07 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
nserror treeview_contract(treeview *tree, bool all)
|
|
|
|
{
|
2013-09-03 16:32:46 +04:00
|
|
|
struct treeview_contract_data data;
|
2013-09-03 14:06:07 +04:00
|
|
|
bool selected;
|
|
|
|
treeview_node *n;
|
2013-09-03 17:08:44 +04:00
|
|
|
struct rect r;
|
2013-09-03 14:06:07 +04:00
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
2013-09-03 17:08:44 +04:00
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = 0;
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
r.y1 = tree->root->height;
|
|
|
|
|
2017-09-22 11:39:02 +03:00
|
|
|
data.tree = tree;
|
2013-09-03 16:32:46 +04:00
|
|
|
data.only_entries = !all;
|
|
|
|
|
2013-09-03 14:06:07 +04:00
|
|
|
for (n = tree->root->children; n != NULL; n = n->next_sib) {
|
2014-05-24 18:09:20 +04:00
|
|
|
if ((n->flags & TV_NFLAGS_EXPANDED) == false) {
|
2013-09-03 14:06:07 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
selected = n->flags & TV_NFLAGS_SELECTED;
|
2013-09-03 14:06:07 +04:00
|
|
|
|
2013-09-03 16:32:46 +04:00
|
|
|
/* Contract children. */
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, n,
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_EXPANDED,
|
|
|
|
treeview_node_contract_cb, NULL, &data);
|
2013-09-03 14:06:07 +04:00
|
|
|
|
2013-09-03 16:32:46 +04:00
|
|
|
/* Contract node */
|
|
|
|
treeview_node_contract_cb(n, &data, false);
|
2013-09-03 14:06:07 +04:00
|
|
|
|
2013-09-03 16:32:46 +04:00
|
|
|
if (selected)
|
2014-05-24 18:09:20 +04:00
|
|
|
n->flags |= TV_NFLAGS_SELECTED;
|
2013-09-03 14:06:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Inform front end of change in dimensions */
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_update_size(tree, -1, tree->root->height);
|
2013-09-03 14:06:07 +04:00
|
|
|
|
2013-09-03 17:08:44 +04:00
|
|
|
/* Redraw */
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-09-03 17:08:44 +04:00
|
|
|
|
2013-09-03 14:06:07 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* context data for treeview expansion
|
|
|
|
*/
|
2013-09-03 16:32:46 +04:00
|
|
|
struct treeview_expand_data {
|
2013-09-03 14:06:07 +04:00
|
|
|
treeview *tree;
|
|
|
|
bool only_folders;
|
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node callback for handling recursive node expansion.
|
|
|
|
*
|
|
|
|
* \param n current node
|
|
|
|
* \param ctx node expansion context
|
|
|
|
* \param skip_children flag to allow children to be skipped
|
|
|
|
* \param end flag to allow iteration to be finished early.
|
2017-03-01 15:50:30 +03:00
|
|
|
* \return NSERROR_OK on success else error code.
|
2017-03-01 02:41:48 +03:00
|
|
|
*/
|
|
|
|
static nserror
|
|
|
|
treeview_expand_cb(treeview_node *n,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end)
|
2013-09-03 14:06:07 +04:00
|
|
|
{
|
2013-09-03 16:32:46 +04:00
|
|
|
struct treeview_expand_data *data = ctx;
|
2013-09-03 14:06:07 +04:00
|
|
|
nserror err;
|
|
|
|
|
|
|
|
assert(n != NULL);
|
|
|
|
assert(n->type != TREE_NODE_ROOT);
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->flags & TV_NFLAGS_EXPANDED ||
|
2017-03-01 02:41:48 +03:00
|
|
|
(data->only_folders && n->type != TREE_NODE_FOLDER)) {
|
2013-09-03 14:06:07 +04:00
|
|
|
/* Nothing to do. */
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
2013-09-03 17:08:44 +04:00
|
|
|
err = treeview_node_expand_internal(data->tree, n);
|
2013-09-03 14:06:07 +04:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
2013-09-03 14:06:07 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
nserror treeview_expand(treeview *tree, bool only_folders)
|
|
|
|
{
|
2013-09-03 16:32:46 +04:00
|
|
|
struct treeview_expand_data data;
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror res;
|
2013-09-03 17:08:44 +04:00
|
|
|
struct rect r;
|
2013-09-03 14:06:07 +04:00
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
data.tree = tree;
|
|
|
|
data.only_folders = only_folders;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
res = treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
|
|
|
|
NULL, treeview_expand_cb, &data);
|
2017-03-01 02:41:48 +03:00
|
|
|
if (res == NSERROR_OK) {
|
2017-03-01 15:50:30 +03:00
|
|
|
/* expansion succeeded, schedule redraw */
|
2013-09-03 17:08:44 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = 0;
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
r.y1 = tree->root->height;
|
2013-09-03 17:08:44 +04:00
|
|
|
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2017-03-01 02:41:48 +03:00
|
|
|
}
|
|
|
|
return res;
|
2013-09-03 14:06:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-10 15:12:35 +03:00
|
|
|
/**
|
|
|
|
* Draw a treeview normally, in tree mode.
|
|
|
|
*
|
|
|
|
* \param[in] tree The treeview we're rendering.
|
|
|
|
* \param[in] x X coordinate we're rendering the treeview at.
|
|
|
|
* \param[in] y Y coordinate we're rendering the treeview at.
|
|
|
|
* \param[in,out] render_y Current vertical position in tree, updated on exit.
|
|
|
|
* \param[in] r Clip rectangle.
|
|
|
|
* \param[in] data Redraw data for rendering contents.
|
|
|
|
* \param[in] ctx Current render context.
|
|
|
|
*/
|
|
|
|
static void treeview_redraw_tree(
|
|
|
|
treeview *tree,
|
2017-03-01 02:41:48 +03:00
|
|
|
const int x,
|
|
|
|
const int y,
|
2017-09-10 15:12:35 +03:00
|
|
|
int *render_y_in_out,
|
|
|
|
struct rect *r,
|
|
|
|
struct content_redraw_data *data,
|
2017-03-01 02:41:48 +03:00
|
|
|
const struct redraw_context *ctx)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
|
|
|
struct treeview_node_style *style = &plot_style_odd;
|
2013-06-03 21:42:02 +04:00
|
|
|
enum treeview_resource_id res = TREE_RES_CONTENT;
|
2017-09-10 15:12:35 +03:00
|
|
|
int baseline = (tree_g.line_height * 3 + 2) / 4;
|
2013-06-04 14:15:44 +04:00
|
|
|
plot_font_style_t *infotext_style;
|
2017-09-10 15:12:35 +03:00
|
|
|
treeview_node *root = tree->root;
|
|
|
|
treeview_node *node = tree->root;
|
|
|
|
int render_y = *render_y_in_out;
|
|
|
|
plot_font_style_t *text_style;
|
|
|
|
plot_style_t *bg_style;
|
2013-06-18 17:58:43 +04:00
|
|
|
int sel_min, sel_max;
|
2017-09-10 15:12:35 +03:00
|
|
|
uint32_t count = 0;
|
|
|
|
struct rect rect;
|
|
|
|
int inset;
|
|
|
|
int x0;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-18 17:58:43 +04:00
|
|
|
if (tree->drag.start.y > tree->drag.prev.y) {
|
|
|
|
sel_min = tree->drag.prev.y;
|
|
|
|
sel_max = tree->drag.start.y;
|
|
|
|
} else {
|
|
|
|
sel_min = tree->drag.start.y;
|
|
|
|
sel_max = tree->drag.prev.y;
|
|
|
|
}
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
while (node != NULL) {
|
2017-09-10 15:12:35 +03:00
|
|
|
struct treeview_node_entry *entry;
|
|
|
|
struct bitmap *furniture;
|
|
|
|
bool invert_selection;
|
|
|
|
treeview_node *next;
|
|
|
|
int height;
|
2013-05-31 13:05:15 +04:00
|
|
|
int i;
|
2017-09-10 15:12:35 +03:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
next = (node->flags & TV_NFLAGS_EXPANDED) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
node->children : NULL;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
if (next != NULL) {
|
|
|
|
/* down to children */
|
|
|
|
node = next;
|
|
|
|
} else {
|
2013-06-03 21:54:19 +04:00
|
|
|
/* No children. As long as we're not at the root,
|
|
|
|
* go to next sibling if present, or nearest ancestor
|
|
|
|
* with a next sibling. */
|
|
|
|
|
|
|
|
while (node != root &&
|
2017-03-01 02:41:48 +03:00
|
|
|
node->next_sib == NULL) {
|
2013-06-03 21:54:19 +04:00
|
|
|
node = node->parent;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
2013-06-03 21:54:19 +04:00
|
|
|
|
|
|
|
if (node == root)
|
|
|
|
break;
|
|
|
|
|
2013-08-17 16:27:38 +04:00
|
|
|
node = node->next_sib;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
assert(node != NULL);
|
|
|
|
assert(node != root);
|
2013-06-03 21:42:02 +04:00
|
|
|
assert(node->type == TREE_NODE_FOLDER ||
|
2017-03-01 02:41:48 +03:00
|
|
|
node->type == TREE_NODE_ENTRY);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
count++;
|
2013-10-27 01:39:48 +04:00
|
|
|
inset = x + node->inset;
|
2013-06-03 20:06:47 +04:00
|
|
|
height = (node->type == TREE_NODE_ENTRY) ? node->height :
|
2017-03-01 02:41:48 +03:00
|
|
|
tree_g.line_height;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-09-10 15:12:35 +03:00
|
|
|
if ((render_y + height) < r->y0) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* This node's line is above clip region */
|
2013-06-03 20:06:47 +04:00
|
|
|
render_y += height;
|
2013-05-31 13:05:15 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
style = (count & 0x1) ? &plot_style_odd : &plot_style_even;
|
2013-06-18 17:58:43 +04:00
|
|
|
if (tree->drag.type == TV_DRAG_SELECTION &&
|
2017-03-01 02:41:48 +03:00
|
|
|
(render_y + height >= sel_min &&
|
|
|
|
render_y < sel_max)) {
|
2013-06-18 17:58:43 +04:00
|
|
|
invert_selection = true;
|
|
|
|
} else {
|
|
|
|
invert_selection = false;
|
|
|
|
}
|
2014-05-24 18:09:20 +04:00
|
|
|
if ((node->flags & TV_NFLAGS_SELECTED && !invert_selection) ||
|
2017-03-01 02:41:48 +03:00
|
|
|
(!(node->flags & TV_NFLAGS_SELECTED) &&
|
|
|
|
invert_selection)) {
|
2013-06-04 14:15:44 +04:00
|
|
|
bg_style = &style->sbg;
|
|
|
|
text_style = &style->stext;
|
|
|
|
infotext_style = &style->sitext;
|
2014-05-24 18:09:20 +04:00
|
|
|
furniture = (node->flags & TV_NFLAGS_EXPANDED) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
style->furn[TREE_FURN_CONTRACT].sel :
|
|
|
|
style->furn[TREE_FURN_EXPAND].sel;
|
2013-05-31 16:24:27 +04:00
|
|
|
} else {
|
2013-06-04 14:15:44 +04:00
|
|
|
bg_style = &style->bg;
|
|
|
|
text_style = &style->text;
|
|
|
|
infotext_style = &style->itext;
|
2014-05-24 18:09:20 +04:00
|
|
|
furniture = (node->flags & TV_NFLAGS_EXPANDED) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
style->furn[TREE_FURN_CONTRACT].bmp :
|
|
|
|
style->furn[TREE_FURN_EXPAND].bmp;
|
2013-05-31 16:24:27 +04:00
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
/* Render background */
|
2017-09-10 15:12:35 +03:00
|
|
|
rect.x0 = r->x0;
|
2017-02-11 16:54:08 +03:00
|
|
|
rect.y0 = render_y;
|
2017-09-10 15:12:35 +03:00
|
|
|
rect.x1 = r->x1;
|
2017-02-11 16:54:08 +03:00
|
|
|
rect.y1 = render_y + height;
|
2017-09-10 15:12:35 +03:00
|
|
|
ctx->plot->rectangle(ctx, bg_style, &rect);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
/* Render toggle */
|
2017-09-10 15:12:35 +03:00
|
|
|
ctx->plot->bitmap(ctx,
|
|
|
|
furniture,
|
|
|
|
inset,
|
|
|
|
render_y + tree_g.line_height / 4,
|
|
|
|
style->furn[TREE_FURN_EXPAND].size,
|
|
|
|
style->furn[TREE_FURN_EXPAND].size,
|
|
|
|
bg_style->fill_colour,
|
|
|
|
BITMAPF_NONE);
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Render icon */
|
2017-02-11 16:54:08 +03:00
|
|
|
if (node->type == TREE_NODE_ENTRY) {
|
2013-05-31 13:05:15 +04:00
|
|
|
res = TREE_RES_CONTENT;
|
2017-02-11 16:54:08 +03:00
|
|
|
} else if (node->flags & TV_NFLAGS_SPECIAL) {
|
2013-11-08 22:53:34 +04:00
|
|
|
res = TREE_RES_FOLDER_SPECIAL;
|
2017-02-11 16:54:08 +03:00
|
|
|
} else {
|
2013-06-03 22:51:46 +04:00
|
|
|
res = TREE_RES_FOLDER;
|
2017-02-11 16:54:08 +03:00
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
if (treeview_res[res].ready) {
|
|
|
|
/* Icon resource is available */
|
2017-09-10 15:12:35 +03:00
|
|
|
data->x = inset + tree_g.step_width;
|
|
|
|
data->y = render_y + ((tree_g.line_height -
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_res[res].height + 1) / 2);
|
2017-09-10 15:12:35 +03:00
|
|
|
data->background_colour = bg_style->fill_colour;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-09-10 15:12:35 +03:00
|
|
|
content_redraw(treeview_res[res].c, data, r, ctx);
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Render text */
|
|
|
|
x0 = inset + tree_g.step_width + tree_g.icon_step;
|
2017-09-10 15:12:35 +03:00
|
|
|
ctx->plot->text(ctx,
|
|
|
|
text_style,
|
|
|
|
x0, render_y + baseline,
|
|
|
|
node->text.data,
|
|
|
|
node->text.len);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
/* Rendered the node */
|
|
|
|
render_y += tree_g.line_height;
|
2017-09-10 15:12:35 +03:00
|
|
|
if (render_y > r->y1) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Passed the bottom of what's in the clip region.
|
|
|
|
* Done. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-31 16:24:27 +04:00
|
|
|
if (node->type != TREE_NODE_ENTRY ||
|
2017-03-01 02:41:48 +03:00
|
|
|
!(node->flags & TV_NFLAGS_EXPANDED))
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Done everything for this node */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Render expanded entry fields */
|
|
|
|
entry = (struct treeview_node_entry *)node;
|
2013-06-03 20:06:47 +04:00
|
|
|
for (i = 0; i < tree->n_fields - 1; i++) {
|
|
|
|
struct treeview_field *ef = &(tree->fields[i + 1]);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
if (ef->flags & TREE_FLAG_SHOW_NAME) {
|
2013-06-03 20:06:47 +04:00
|
|
|
int max_width = tree->field_width;
|
|
|
|
|
2017-09-10 15:12:35 +03:00
|
|
|
ctx->plot->text(ctx,
|
|
|
|
infotext_style,
|
|
|
|
x0 + max_width - ef->value.width - tree_g.step_width,
|
|
|
|
render_y + baseline,
|
|
|
|
ef->value.data,
|
|
|
|
ef->value.len);
|
|
|
|
|
|
|
|
ctx->plot->text(ctx,
|
|
|
|
infotext_style,
|
|
|
|
x0 + max_width,
|
|
|
|
render_y + baseline,
|
|
|
|
entry->fields[i].value.data,
|
|
|
|
entry->fields[i].value.len);
|
2013-05-31 13:05:15 +04:00
|
|
|
} else {
|
2017-09-10 15:12:35 +03:00
|
|
|
ctx->plot->text(ctx,
|
|
|
|
infotext_style,
|
|
|
|
x0, render_y + baseline,
|
|
|
|
entry->fields[i].value.data,
|
|
|
|
entry->fields[i].value.len);
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Rendered the expanded entry field */
|
|
|
|
render_y += tree_g.line_height;
|
|
|
|
}
|
|
|
|
|
2017-03-01 15:50:30 +03:00
|
|
|
/* Finished rendering expanded entry */
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2017-09-10 15:12:35 +03:00
|
|
|
if (render_y > r->y1) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Passed the bottom of what's in the clip region.
|
|
|
|
* Done. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-10 15:12:35 +03:00
|
|
|
*render_y_in_out = render_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-10 17:50:35 +03:00
|
|
|
/**
|
|
|
|
* Draw a treeview normally, in tree mode.
|
|
|
|
*
|
|
|
|
* \param[in] tree The treeview we're rendering.
|
|
|
|
* \param[in] x X coordinate we're rendering the treeview at.
|
|
|
|
* \param[in] y Y coordinate we're rendering the treeview at.
|
|
|
|
* \param[in,out] render_y Current vertical position in tree, updated on exit.
|
|
|
|
* \param[in] r Clip rectangle.
|
|
|
|
* \param[in] data Redraw data for rendering contents.
|
|
|
|
* \param[in] ctx Current render context.
|
|
|
|
*/
|
|
|
|
static void treeview_redraw_search(
|
|
|
|
treeview *tree,
|
|
|
|
const int x,
|
|
|
|
const int y,
|
|
|
|
int *render_y_in_out,
|
|
|
|
struct rect *r,
|
|
|
|
struct content_redraw_data *data,
|
|
|
|
const struct redraw_context *ctx)
|
|
|
|
{
|
|
|
|
struct treeview_node_style *style = &plot_style_odd;
|
|
|
|
enum treeview_resource_id res = TREE_RES_CONTENT;
|
|
|
|
int baseline = (tree_g.line_height * 3 + 2) / 4;
|
|
|
|
plot_font_style_t *infotext_style;
|
|
|
|
treeview_node *root = tree->root;
|
|
|
|
treeview_node *node = tree->root;
|
|
|
|
int render_y = *render_y_in_out;
|
|
|
|
plot_font_style_t *text_style;
|
|
|
|
plot_style_t *bg_style;
|
|
|
|
int sel_min, sel_max;
|
|
|
|
uint32_t count = 0;
|
|
|
|
struct rect rect;
|
|
|
|
int inset;
|
|
|
|
int x0;
|
|
|
|
|
|
|
|
if (tree->drag.start.y > tree->drag.prev.y) {
|
|
|
|
sel_min = tree->drag.prev.y;
|
|
|
|
sel_max = tree->drag.start.y;
|
|
|
|
} else {
|
|
|
|
sel_min = tree->drag.start.y;
|
|
|
|
sel_max = tree->drag.prev.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (node != NULL) {
|
|
|
|
struct treeview_node_entry *entry;
|
|
|
|
struct bitmap *furniture;
|
|
|
|
bool invert_selection;
|
|
|
|
treeview_node *next;
|
|
|
|
int height;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
next = node->children;
|
|
|
|
|
|
|
|
if (next != NULL) {
|
|
|
|
/* down to children */
|
|
|
|
node = next;
|
|
|
|
} else {
|
|
|
|
/* No children. As long as we're not at the root,
|
|
|
|
* go to next sibling if present, or nearest ancestor
|
|
|
|
* with a next sibling. */
|
|
|
|
|
|
|
|
while (node != root &&
|
|
|
|
node->next_sib == NULL) {
|
|
|
|
node = node->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node == root)
|
|
|
|
break;
|
|
|
|
|
|
|
|
node = node->next_sib;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(node != NULL);
|
|
|
|
assert(node != root);
|
|
|
|
assert(node->type == TREE_NODE_FOLDER ||
|
|
|
|
node->type == TREE_NODE_ENTRY);
|
|
|
|
|
|
|
|
if (node->type == TREE_NODE_FOLDER ||
|
|
|
|
!(node->flags & TV_NFLAGS_MATCHED)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
count++;
|
|
|
|
inset = x + tree_g.window_padding;
|
2017-09-14 22:07:42 +03:00
|
|
|
height = node->height;
|
2017-09-10 17:50:35 +03:00
|
|
|
|
|
|
|
if ((render_y + height) < r->y0) {
|
|
|
|
/* This node's line is above clip region */
|
|
|
|
render_y += height;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
style = (count & 0x1) ? &plot_style_odd : &plot_style_even;
|
|
|
|
if (tree->drag.type == TV_DRAG_SELECTION &&
|
|
|
|
(render_y + height >= sel_min &&
|
|
|
|
render_y < sel_max)) {
|
|
|
|
invert_selection = true;
|
|
|
|
} else {
|
|
|
|
invert_selection = false;
|
|
|
|
}
|
|
|
|
if ((node->flags & TV_NFLAGS_SELECTED && !invert_selection) ||
|
|
|
|
(!(node->flags & TV_NFLAGS_SELECTED) &&
|
|
|
|
invert_selection)) {
|
|
|
|
bg_style = &style->sbg;
|
|
|
|
text_style = &style->stext;
|
|
|
|
infotext_style = &style->sitext;
|
|
|
|
furniture = (node->flags & TV_NFLAGS_EXPANDED) ?
|
|
|
|
style->furn[TREE_FURN_CONTRACT].sel :
|
|
|
|
style->furn[TREE_FURN_EXPAND].sel;
|
|
|
|
} else {
|
|
|
|
bg_style = &style->bg;
|
|
|
|
text_style = &style->text;
|
|
|
|
infotext_style = &style->itext;
|
|
|
|
furniture = (node->flags & TV_NFLAGS_EXPANDED) ?
|
|
|
|
style->furn[TREE_FURN_CONTRACT].bmp :
|
|
|
|
style->furn[TREE_FURN_EXPAND].bmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Render background */
|
|
|
|
rect.x0 = r->x0;
|
|
|
|
rect.y0 = render_y;
|
|
|
|
rect.x1 = r->x1;
|
|
|
|
rect.y1 = render_y + height;
|
|
|
|
ctx->plot->rectangle(ctx, bg_style, &rect);
|
|
|
|
|
|
|
|
/* Render toggle */
|
|
|
|
ctx->plot->bitmap(ctx,
|
|
|
|
furniture,
|
|
|
|
inset,
|
|
|
|
render_y + tree_g.line_height / 4,
|
|
|
|
style->furn[TREE_FURN_EXPAND].size,
|
|
|
|
style->furn[TREE_FURN_EXPAND].size,
|
|
|
|
bg_style->fill_colour,
|
|
|
|
BITMAPF_NONE);
|
|
|
|
|
|
|
|
/* Render icon */
|
|
|
|
if (node->type == TREE_NODE_ENTRY) {
|
|
|
|
res = TREE_RES_CONTENT;
|
|
|
|
} else if (node->flags & TV_NFLAGS_SPECIAL) {
|
|
|
|
res = TREE_RES_FOLDER_SPECIAL;
|
|
|
|
} else {
|
|
|
|
res = TREE_RES_FOLDER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (treeview_res[res].ready) {
|
|
|
|
/* Icon resource is available */
|
|
|
|
data->x = inset + tree_g.step_width;
|
|
|
|
data->y = render_y + ((tree_g.line_height -
|
|
|
|
treeview_res[res].height + 1) / 2);
|
|
|
|
data->background_colour = bg_style->fill_colour;
|
|
|
|
|
|
|
|
content_redraw(treeview_res[res].c, data, r, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Render text */
|
|
|
|
x0 = inset + tree_g.step_width + tree_g.icon_step;
|
|
|
|
ctx->plot->text(ctx,
|
|
|
|
text_style,
|
|
|
|
x0, render_y + baseline,
|
|
|
|
node->text.data,
|
|
|
|
node->text.len);
|
|
|
|
|
|
|
|
/* Rendered the node */
|
|
|
|
render_y += tree_g.line_height;
|
|
|
|
if (render_y > r->y1) {
|
|
|
|
/* Passed the bottom of what's in the clip region.
|
|
|
|
* Done. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (node->type != TREE_NODE_ENTRY ||
|
|
|
|
!(node->flags & TV_NFLAGS_EXPANDED))
|
|
|
|
/* Done everything for this node */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Render expanded entry fields */
|
|
|
|
entry = (struct treeview_node_entry *)node;
|
|
|
|
for (i = 0; i < tree->n_fields - 1; i++) {
|
|
|
|
struct treeview_field *ef = &(tree->fields[i + 1]);
|
|
|
|
|
|
|
|
if (ef->flags & TREE_FLAG_SHOW_NAME) {
|
|
|
|
int max_width = tree->field_width;
|
|
|
|
|
|
|
|
ctx->plot->text(ctx,
|
|
|
|
infotext_style,
|
|
|
|
x0 + max_width - ef->value.width - tree_g.step_width,
|
|
|
|
render_y + baseline,
|
|
|
|
ef->value.data,
|
|
|
|
ef->value.len);
|
|
|
|
|
|
|
|
ctx->plot->text(ctx,
|
|
|
|
infotext_style,
|
|
|
|
x0 + max_width,
|
|
|
|
render_y + baseline,
|
|
|
|
entry->fields[i].value.data,
|
|
|
|
entry->fields[i].value.len);
|
|
|
|
} else {
|
|
|
|
ctx->plot->text(ctx,
|
|
|
|
infotext_style,
|
|
|
|
x0, render_y + baseline,
|
|
|
|
entry->fields[i].value.data,
|
|
|
|
entry->fields[i].value.len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Rendered the expanded entry field */
|
|
|
|
render_y += tree_g.line_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finished rendering expanded entry */
|
|
|
|
|
|
|
|
if (render_y > r->y1) {
|
|
|
|
/* Passed the bottom of what's in the clip region.
|
|
|
|
* Done. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*render_y_in_out = render_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-10 15:12:35 +03:00
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
void
|
|
|
|
treeview_redraw(treeview *tree,
|
|
|
|
const int x,
|
|
|
|
const int y,
|
|
|
|
struct rect *clip,
|
|
|
|
const struct redraw_context *ctx)
|
|
|
|
{
|
|
|
|
struct redraw_context new_ctx = *ctx;
|
|
|
|
struct content_redraw_data data;
|
|
|
|
struct rect r;
|
|
|
|
struct rect rect;
|
|
|
|
int render_y = y;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
assert(tree->root->flags & TV_NFLAGS_EXPANDED);
|
|
|
|
|
|
|
|
/* Start knockout rendering if it's available for this plotter */
|
|
|
|
if (ctx->plot->option_knockout) {
|
|
|
|
knockout_plot_start(ctx, &new_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up clip rectangle */
|
|
|
|
r.x0 = clip->x0 + x;
|
|
|
|
r.y0 = clip->y0 + y;
|
|
|
|
r.x1 = clip->x1 + x;
|
|
|
|
r.y1 = clip->y1 + y;
|
|
|
|
new_ctx.plot->clip(&new_ctx, &r);
|
|
|
|
|
|
|
|
/* Setup common content redraw data */
|
|
|
|
data.width = tree_g.icon_size;
|
|
|
|
data.height = tree_g.icon_size;
|
|
|
|
data.scale = 1;
|
|
|
|
data.repeat_x = false;
|
|
|
|
data.repeat_y = false;
|
|
|
|
|
|
|
|
if (tree->flags & TREEVIEW_SEARCHABLE) {
|
|
|
|
if (render_y < r.y1) {
|
|
|
|
enum treeview_resource_id icon = TREE_RES_SEARCH;
|
|
|
|
|
|
|
|
/* Fill the blank area at the bottom */
|
|
|
|
rect.x0 = r.x0;
|
|
|
|
rect.y0 = render_y;
|
|
|
|
rect.x1 = r.x1;
|
|
|
|
rect.y1 = render_y + tree_g.line_height;
|
|
|
|
new_ctx.plot->rectangle(&new_ctx, &plot_style_even.bg,
|
|
|
|
&rect);
|
|
|
|
|
|
|
|
if (treeview_res[icon].ready) {
|
|
|
|
/* Icon resource is available */
|
|
|
|
data.x = tree_g.window_padding;
|
|
|
|
data.y = render_y + ((tree_g.line_height -
|
|
|
|
treeview_res[icon].height + 1) /
|
|
|
|
2);
|
|
|
|
data.background_colour = plot_style_even.bg.
|
|
|
|
fill_colour;
|
|
|
|
|
|
|
|
content_redraw(treeview_res[icon].c,
|
|
|
|
&data, &r, &new_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
textarea_redraw(tree->search.textarea,
|
|
|
|
x + tree_g.window_padding +
|
|
|
|
tree_g.icon_step, y,
|
|
|
|
plot_style_even.bg.fill_colour, 1.0,
|
|
|
|
&r, &new_ctx);
|
|
|
|
}
|
|
|
|
render_y += tree_g.line_height;
|
|
|
|
}
|
|
|
|
|
2017-09-10 17:50:35 +03:00
|
|
|
/* Render the treeview data */
|
|
|
|
if (tree->search.search == true) {
|
|
|
|
treeview_redraw_search(tree, x, y,
|
|
|
|
&render_y, &r, &data, &new_ctx);
|
|
|
|
} else {
|
|
|
|
treeview_redraw_tree(tree, x, y,
|
|
|
|
&render_y, &r, &data, &new_ctx);
|
|
|
|
}
|
2017-09-10 15:12:35 +03:00
|
|
|
|
2013-06-03 23:00:23 +04:00
|
|
|
if (render_y < r.y1) {
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Fill the blank area at the bottom */
|
2017-02-11 16:54:08 +03:00
|
|
|
rect.x0 = r.x0;
|
|
|
|
rect.y0 = render_y;
|
|
|
|
rect.x1 = r.x1;
|
|
|
|
rect.y1 = r.y1;
|
|
|
|
new_ctx.plot->rectangle(&new_ctx, &plot_style_even.bg, &rect);
|
2013-08-17 02:08:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* All normal treeview rendering is done; render any overlays */
|
2017-02-11 16:54:08 +03:00
|
|
|
if ((tree->move.target_pos != TV_TARGET_NONE) &&
|
|
|
|
(treeview_res[TREE_RES_ARROW].ready)) {
|
2013-08-17 02:08:57 +04:00
|
|
|
/* Got a MOVE drag; render move indicator arrow */
|
2013-10-27 01:39:48 +04:00
|
|
|
data.x = tree->move.target_area.x0 + x;
|
|
|
|
data.y = tree->move.target_area.y0 + y;
|
2013-08-17 02:08:57 +04:00
|
|
|
data.background_colour = plot_style_even.bg.fill_colour;
|
|
|
|
|
2017-02-11 16:54:08 +03:00
|
|
|
content_redraw(treeview_res[TREE_RES_ARROW].c, &data, &r, &new_ctx);
|
2013-08-19 17:23:44 +04:00
|
|
|
|
|
|
|
} else if (tree->edit.textarea != NULL) {
|
|
|
|
/* Edit in progress; render textarea */
|
|
|
|
textarea_redraw(tree->edit.textarea,
|
2013-10-27 01:39:48 +04:00
|
|
|
tree->edit.x + x, tree->edit.y + y,
|
2013-08-19 17:23:44 +04:00
|
|
|
plot_style_even.bg.fill_colour, 1.0,
|
|
|
|
&r, &new_ctx);
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Rendering complete */
|
2017-02-11 16:54:08 +03:00
|
|
|
if (ctx->plot->option_knockout) {
|
2017-02-11 16:47:39 +03:00
|
|
|
knockout_plot_end(ctx);
|
2017-02-11 16:54:08 +03:00
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* context for treeview selection
|
|
|
|
*/
|
2013-06-03 20:06:47 +04:00
|
|
|
struct treeview_selection_walk_data {
|
|
|
|
enum {
|
|
|
|
TREEVIEW_WALK_HAS_SELECTION,
|
2013-08-27 23:56:20 +04:00
|
|
|
TREEVIEW_WALK_GET_FIRST_SELECTED,
|
2013-06-03 20:06:47 +04:00
|
|
|
TREEVIEW_WALK_CLEAR_SELECTION,
|
2013-06-18 17:58:43 +04:00
|
|
|
TREEVIEW_WALK_SELECT_ALL,
|
2013-07-01 16:00:46 +04:00
|
|
|
TREEVIEW_WALK_COMMIT_SELECT_DRAG,
|
2013-08-17 15:51:08 +04:00
|
|
|
TREEVIEW_WALK_DELETE_SELECTION,
|
2013-08-17 22:39:17 +04:00
|
|
|
TREEVIEW_WALK_PROPAGATE_SELECTION,
|
2015-01-03 03:15:58 +03:00
|
|
|
TREEVIEW_WALK_YANK_SELECTION,
|
|
|
|
TREEVIEW_WALK_COPY_SELECTION
|
2013-06-03 20:06:47 +04:00
|
|
|
} purpose;
|
|
|
|
union {
|
|
|
|
bool has_selection;
|
|
|
|
struct {
|
|
|
|
bool required;
|
|
|
|
struct rect *rect;
|
|
|
|
} redraw;
|
2013-06-18 17:58:43 +04:00
|
|
|
struct {
|
|
|
|
int sel_min;
|
|
|
|
int sel_max;
|
|
|
|
} drag;
|
2013-08-17 22:39:17 +04:00
|
|
|
struct {
|
|
|
|
treeview_node *prev;
|
|
|
|
} yank;
|
2013-08-27 23:56:20 +04:00
|
|
|
struct {
|
|
|
|
treeview_node *n;
|
|
|
|
} first;
|
2015-01-03 03:15:58 +03:00
|
|
|
struct {
|
|
|
|
char *text;
|
|
|
|
uint32_t len;
|
|
|
|
} copy;
|
2013-06-03 20:06:47 +04:00
|
|
|
} data;
|
|
|
|
int current_y;
|
2013-07-03 16:48:53 +04:00
|
|
|
treeview *tree;
|
2013-06-03 20:06:47 +04:00
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node callback for handling selection related actions.
|
|
|
|
*
|
|
|
|
* \param n current node
|
|
|
|
* \param ctx node selection context
|
|
|
|
* \param skip_children flag to allow children to be skipped
|
|
|
|
* \param end flag to allow iteration to be finished early.
|
2017-03-01 15:50:30 +03:00
|
|
|
* \return NSERROR_OK on success else error code.
|
2017-03-01 02:41:48 +03:00
|
|
|
*/
|
|
|
|
static nserror
|
|
|
|
treeview_node_selection_walk_cb(treeview_node *n,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end)
|
2013-06-03 20:06:47 +04:00
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data *sw = ctx;
|
|
|
|
int height;
|
|
|
|
bool changed = false;
|
2013-07-03 17:18:33 +04:00
|
|
|
nserror err;
|
2013-06-03 20:06:47 +04:00
|
|
|
|
2013-07-07 02:00:43 +04:00
|
|
|
height = (n->type == TREE_NODE_ENTRY) ? n->height : tree_g.line_height;
|
2013-06-03 20:06:47 +04:00
|
|
|
sw->current_y += height;
|
|
|
|
|
|
|
|
switch (sw->purpose) {
|
|
|
|
case TREEVIEW_WALK_HAS_SELECTION:
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->flags & TV_NFLAGS_SELECTED) {
|
2013-06-03 20:06:47 +04:00
|
|
|
sw->data.has_selection = true;
|
2013-07-03 17:18:33 +04:00
|
|
|
*end = true; /* Can abort tree walk */
|
|
|
|
return NSERROR_OK;
|
2013-06-03 20:06:47 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-08-27 23:56:20 +04:00
|
|
|
case TREEVIEW_WALK_GET_FIRST_SELECTED:
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->flags & TV_NFLAGS_SELECTED) {
|
2013-08-27 23:56:20 +04:00
|
|
|
sw->data.first.n = n;
|
|
|
|
*end = true; /* Can abort tree walk */
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-07-01 16:00:46 +04:00
|
|
|
case TREEVIEW_WALK_DELETE_SELECTION:
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->flags & TV_NFLAGS_SELECTED) {
|
2013-08-30 15:51:40 +04:00
|
|
|
err = treeview_delete_node_internal(sw->tree, n, true,
|
2017-03-01 02:41:48 +03:00
|
|
|
TREE_OPTION_NONE);
|
2013-07-03 17:18:33 +04:00
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
}
|
2013-07-07 02:00:43 +04:00
|
|
|
*skip_children = true;
|
2013-07-01 16:00:46 +04:00
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-08-17 15:51:08 +04:00
|
|
|
case TREEVIEW_WALK_PROPAGATE_SELECTION:
|
|
|
|
if (n->parent != NULL &&
|
2017-03-01 02:41:48 +03:00
|
|
|
n->parent->flags & TV_NFLAGS_SELECTED &&
|
|
|
|
!(n->flags & TV_NFLAGS_SELECTED)) {
|
2014-05-24 18:09:20 +04:00
|
|
|
n->flags ^= TV_NFLAGS_SELECTED;
|
2013-08-17 15:51:08 +04:00
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-06-03 20:06:47 +04:00
|
|
|
case TREEVIEW_WALK_CLEAR_SELECTION:
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->flags & TV_NFLAGS_SELECTED) {
|
|
|
|
n->flags ^= TV_NFLAGS_SELECTED;
|
2013-06-03 20:06:47 +04:00
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TREEVIEW_WALK_SELECT_ALL:
|
2014-05-24 18:09:20 +04:00
|
|
|
if (!(n->flags & TV_NFLAGS_SELECTED)) {
|
|
|
|
n->flags ^= TV_NFLAGS_SELECTED;
|
2013-06-03 20:06:47 +04:00
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
break;
|
2013-06-18 17:58:43 +04:00
|
|
|
|
|
|
|
case TREEVIEW_WALK_COMMIT_SELECT_DRAG:
|
2013-08-28 18:59:00 +04:00
|
|
|
if (sw->current_y >= sw->data.drag.sel_min &&
|
2017-03-01 02:41:48 +03:00
|
|
|
sw->current_y - height <
|
|
|
|
sw->data.drag.sel_max) {
|
2014-05-24 18:09:20 +04:00
|
|
|
n->flags ^= TV_NFLAGS_SELECTED;
|
2013-06-18 17:58:43 +04:00
|
|
|
}
|
2013-07-03 17:18:33 +04:00
|
|
|
return NSERROR_OK;
|
2013-08-17 22:39:17 +04:00
|
|
|
|
|
|
|
case TREEVIEW_WALK_YANK_SELECTION:
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->flags & TV_NFLAGS_SELECTED) {
|
2013-08-17 22:39:17 +04:00
|
|
|
treeview_node *p = n->parent;
|
|
|
|
int h = 0;
|
|
|
|
|
|
|
|
if (treeview_unlink_node(n))
|
|
|
|
h = n->height;
|
|
|
|
|
|
|
|
/* Reduce ancestor heights */
|
2014-05-24 18:09:20 +04:00
|
|
|
while (p != NULL && p->flags & TV_NFLAGS_EXPANDED) {
|
2013-08-17 22:39:17 +04:00
|
|
|
p->height -= h;
|
|
|
|
p = p->parent;
|
|
|
|
}
|
|
|
|
if (sw->data.yank.prev == NULL) {
|
|
|
|
sw->tree->move.root = n;
|
|
|
|
n->parent = NULL;
|
|
|
|
n->prev_sib = NULL;
|
|
|
|
n->next_sib = NULL;
|
|
|
|
} else {
|
|
|
|
n->parent = NULL;
|
|
|
|
n->prev_sib = sw->data.yank.prev;
|
|
|
|
n->next_sib = NULL;
|
|
|
|
sw->data.yank.prev->next_sib = n;
|
|
|
|
}
|
|
|
|
sw->data.yank.prev = n;
|
|
|
|
|
|
|
|
*skip_children = true;
|
|
|
|
}
|
|
|
|
break;
|
2015-01-03 03:15:58 +03:00
|
|
|
|
|
|
|
case TREEVIEW_WALK_COPY_SELECTION:
|
|
|
|
if (n->flags & TV_NFLAGS_SELECTED &&
|
2017-03-01 02:41:48 +03:00
|
|
|
n->type == TREE_NODE_ENTRY) {
|
2015-01-03 03:15:58 +03:00
|
|
|
int i;
|
|
|
|
char *temp;
|
|
|
|
uint32_t len;
|
|
|
|
const char *text;
|
|
|
|
struct treeview_field *ef;
|
|
|
|
struct treeview_text *val;
|
|
|
|
|
|
|
|
for (i = 0; i < sw->tree->n_fields; i++) {
|
|
|
|
ef = &(sw->tree->fields[i]);
|
|
|
|
|
|
|
|
if (!(ef->flags & TREE_FLAG_COPY_TEXT)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
val = treeview_get_text_for_field(sw->tree,
|
2017-03-01 02:41:48 +03:00
|
|
|
n, i);
|
2015-01-03 03:15:58 +03:00
|
|
|
text = val->data;
|
|
|
|
len = val->len;
|
|
|
|
|
|
|
|
temp = realloc(sw->data.copy.text,
|
2017-03-01 02:41:48 +03:00
|
|
|
sw->data.copy.len + len + 1);
|
2015-01-03 03:15:58 +03:00
|
|
|
if (temp == NULL) {
|
|
|
|
free(sw->data.copy.text);
|
|
|
|
sw->data.copy.text = NULL;
|
|
|
|
sw->data.copy.len = 0;
|
|
|
|
return NSERROR_NOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sw->data.copy.len != 0) {
|
|
|
|
temp[sw->data.copy.len - 1] = '\n';
|
|
|
|
}
|
|
|
|
memcpy(temp + sw->data.copy.len, text, len);
|
|
|
|
temp[sw->data.copy.len + len] = '\0';
|
|
|
|
sw->data.copy.len += len + 1;
|
|
|
|
sw->data.copy.text = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2013-06-03 20:06:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
if (sw->data.redraw.required == false) {
|
|
|
|
sw->data.redraw.required = true;
|
|
|
|
sw->data.redraw.rect->y0 = sw->current_y - height;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sw->current_y > sw->data.redraw.rect->y1) {
|
|
|
|
sw->data.redraw.rect->y1 = sw->current_y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-03 17:18:33 +04:00
|
|
|
return NSERROR_OK;
|
2013-06-03 20:06:47 +04:00
|
|
|
}
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
|
|
|
|
/* Exported interface, documented in treeview.h */
|
2013-07-03 16:48:53 +04:00
|
|
|
bool treeview_has_selection(treeview *tree)
|
2013-06-03 20:06:47 +04:00
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_HAS_SELECTION;
|
|
|
|
sw.data.has_selection = false;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2013-06-03 20:06:47 +04:00
|
|
|
|
|
|
|
return sw.data.has_selection;
|
|
|
|
}
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
|
2013-08-27 23:56:20 +04:00
|
|
|
/**
|
|
|
|
* Get first selected node (in any)
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object in which to create folder
|
2013-08-27 23:56:20 +04:00
|
|
|
* \return the first selected treeview node, or NULL
|
|
|
|
*/
|
|
|
|
static treeview_node * treeview_get_first_selected(treeview *tree)
|
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_GET_FIRST_SELECTED;
|
|
|
|
sw.data.first.n = NULL;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2013-08-27 23:56:20 +04:00
|
|
|
|
|
|
|
return sw.data.first.n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-13 17:24:25 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2014-05-24 18:09:20 +04:00
|
|
|
enum treeview_node_type treeview_get_selection(treeview *tree,
|
2017-03-01 02:41:48 +03:00
|
|
|
void **node_data)
|
2013-09-13 17:24:25 +04:00
|
|
|
{
|
2013-09-15 02:22:13 +04:00
|
|
|
treeview_node *n;
|
|
|
|
|
2013-09-13 17:24:25 +04:00
|
|
|
assert(tree != NULL);
|
|
|
|
|
2013-09-15 02:22:13 +04:00
|
|
|
n = treeview_get_first_selected(tree);
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n != NULL && n->type & (TREE_NODE_ENTRY | TREE_NODE_FOLDER)) {
|
|
|
|
*node_data = n->client_data;
|
|
|
|
return n->type;
|
|
|
|
}
|
|
|
|
|
|
|
|
*node_data = NULL;
|
|
|
|
return TREE_NODE_NONE;
|
2013-09-13 17:24:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-28 16:36:15 +04:00
|
|
|
/**
|
|
|
|
* Clear any selection in a treeview
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to clear selection in
|
|
|
|
* \param rect Redraw rectangle (if redraw required)
|
2013-08-28 16:36:15 +04:00
|
|
|
* \return true iff redraw required
|
|
|
|
*/
|
|
|
|
static bool treeview_clear_selection(treeview *tree, struct rect *rect)
|
2013-06-03 20:06:47 +04:00
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
|
|
|
|
rect->x0 = 0;
|
|
|
|
rect->y0 = 0;
|
2013-06-03 23:21:00 +04:00
|
|
|
rect->x1 = REDRAW_MAX;
|
2013-06-03 20:06:47 +04:00
|
|
|
rect->y1 = 0;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_CLEAR_SELECTION;
|
|
|
|
sw.data.redraw.required = false;
|
|
|
|
sw.data.redraw.rect = rect;
|
2017-09-09 20:45:22 +03:00
|
|
|
sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ?
|
|
|
|
tree_g.line_height : 0;
|
2013-06-03 20:06:47 +04:00
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2013-06-03 20:06:47 +04:00
|
|
|
|
|
|
|
return sw.data.redraw.required;
|
|
|
|
}
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
|
2013-08-28 16:36:15 +04:00
|
|
|
/**
|
|
|
|
* Select all in a treeview
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to select all in
|
|
|
|
* \param rect Redraw rectangle (if redraw required)
|
2013-08-28 16:36:15 +04:00
|
|
|
* \return true iff redraw required
|
|
|
|
*/
|
|
|
|
static bool treeview_select_all(treeview *tree, struct rect *rect)
|
2013-06-03 20:06:47 +04:00
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
|
|
|
|
rect->x0 = 0;
|
|
|
|
rect->y0 = 0;
|
2013-06-03 23:21:00 +04:00
|
|
|
rect->x1 = REDRAW_MAX;
|
2013-06-03 20:06:47 +04:00
|
|
|
rect->y1 = 0;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_SELECT_ALL;
|
|
|
|
sw.data.redraw.required = false;
|
|
|
|
sw.data.redraw.rect = rect;
|
2017-09-09 20:45:22 +03:00
|
|
|
sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ?
|
|
|
|
tree_g.line_height : 0;
|
2013-06-03 20:06:47 +04:00
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2013-06-03 20:06:47 +04:00
|
|
|
|
|
|
|
return sw.data.redraw.required;
|
|
|
|
}
|
|
|
|
|
2013-06-18 20:08:35 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Commit a current selection drag, modifying the node's selection state.
|
2013-07-08 16:01:00 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to commit drag selection in
|
2013-06-18 20:08:35 +04:00
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
static void treeview_commit_selection_drag(treeview *tree)
|
2013-06-18 17:58:43 +04:00
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_COMMIT_SELECT_DRAG;
|
2017-09-09 20:45:22 +03:00
|
|
|
sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ?
|
|
|
|
tree_g.line_height : 0;
|
2013-06-18 17:58:43 +04:00
|
|
|
|
|
|
|
if (tree->drag.start.y > tree->drag.prev.y) {
|
|
|
|
sw.data.drag.sel_min = tree->drag.prev.y;
|
|
|
|
sw.data.drag.sel_max = tree->drag.start.y;
|
|
|
|
} else {
|
|
|
|
sw.data.drag.sel_min = tree->drag.start.y;
|
|
|
|
sw.data.drag.sel_max = tree->drag.prev.y;
|
|
|
|
}
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2013-06-18 17:58:43 +04:00
|
|
|
}
|
|
|
|
|
2013-07-01 14:47:46 +04:00
|
|
|
|
2013-08-16 23:06:08 +04:00
|
|
|
/**
|
2013-08-17 22:39:17 +04:00
|
|
|
* Yank a selection to the node move list.
|
2013-08-16 23:06:08 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to yank selection from
|
2013-08-16 23:06:08 +04:00
|
|
|
*/
|
2013-08-17 22:39:17 +04:00
|
|
|
static void treeview_move_yank_selection(treeview *tree)
|
2013-08-16 23:06:08 +04:00
|
|
|
{
|
2013-08-17 22:39:17 +04:00
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_YANK_SELECTION;
|
|
|
|
sw.data.yank.prev = NULL;
|
|
|
|
sw.tree = tree;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2013-08-16 23:06:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-03 03:15:58 +03:00
|
|
|
/**
|
|
|
|
* Copy a selection to the clipboard.
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to yank selection from
|
2015-01-03 03:15:58 +03:00
|
|
|
*/
|
|
|
|
static void treeview_copy_selection(treeview *tree)
|
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
nserror err;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_COPY_SELECTION;
|
|
|
|
sw.data.copy.text = NULL;
|
|
|
|
sw.data.copy.len = 0;
|
|
|
|
sw.tree = tree;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
err = treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2015-01-03 03:15:58 +03:00
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sw.data.copy.text != NULL) {
|
|
|
|
guit->clipboard->set(sw.data.copy.text,
|
2017-03-01 02:41:48 +03:00
|
|
|
sw.data.copy.len - 1, NULL, 0);
|
2015-01-03 03:15:58 +03:00
|
|
|
free(sw.data.copy.text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-01 16:00:46 +04:00
|
|
|
/**
|
2013-07-03 17:18:33 +04:00
|
|
|
* Delete a selection.
|
2013-07-08 16:01:00 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to delete selected nodes from
|
|
|
|
* \param rect Updated to redraw rectangle
|
2013-07-08 16:01:00 +04:00
|
|
|
* \return true iff redraw required.
|
2013-07-01 16:00:46 +04:00
|
|
|
*/
|
2013-07-03 16:48:53 +04:00
|
|
|
static bool treeview_delete_selection(treeview *tree, struct rect *rect)
|
2013-07-01 16:00:46 +04:00
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
rect->x0 = 0;
|
|
|
|
rect->y0 = 0;
|
|
|
|
rect->x1 = REDRAW_MAX;
|
|
|
|
rect->y1 = tree->root->height;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_DELETE_SELECTION;
|
|
|
|
sw.data.redraw.required = false;
|
|
|
|
sw.data.redraw.rect = rect;
|
|
|
|
sw.current_y = 0;
|
|
|
|
sw.tree = tree;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2013-07-01 16:00:46 +04:00
|
|
|
|
|
|
|
return sw.data.redraw.required;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-17 15:51:08 +04:00
|
|
|
/**
|
|
|
|
* Propagate selection to visible descendants of selected nodes.
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to propagate selection in
|
|
|
|
* \param rect Redraw rectangle (if redraw required)
|
2013-08-17 15:51:08 +04:00
|
|
|
* \return true iff redraw required
|
|
|
|
*/
|
|
|
|
static bool treeview_propagate_selection(treeview *tree, struct rect *rect)
|
|
|
|
{
|
|
|
|
struct treeview_selection_walk_data sw;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
rect->x0 = 0;
|
|
|
|
rect->y0 = 0;
|
|
|
|
rect->x1 = REDRAW_MAX;
|
|
|
|
rect->y1 = 0;
|
|
|
|
|
|
|
|
sw.purpose = TREEVIEW_WALK_PROPAGATE_SELECTION;
|
|
|
|
sw.data.redraw.required = false;
|
|
|
|
sw.data.redraw.rect = rect;
|
|
|
|
sw.current_y = 0;
|
|
|
|
sw.tree = tree;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_selection_walk_cb, &sw);
|
2013-08-17 15:51:08 +04:00
|
|
|
|
|
|
|
return sw.data.redraw.required;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-17 22:39:17 +04:00
|
|
|
/**
|
|
|
|
* Move a selection according to the current move drag.
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to move selected nodes in
|
|
|
|
* \param rect Redraw rectangle
|
2017-03-01 15:50:30 +03:00
|
|
|
* \return NSERROR_OK on success else appropriate error code
|
2013-08-17 22:39:17 +04:00
|
|
|
*/
|
|
|
|
static nserror treeview_move_selection(treeview *tree, struct rect *rect)
|
|
|
|
{
|
|
|
|
treeview_node *node, *next, *parent;
|
|
|
|
treeview_node *relation;
|
|
|
|
enum treeview_relationship relationship;
|
|
|
|
int height;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
assert(tree->root->children != NULL);
|
|
|
|
assert(tree->move.target_pos != TV_TARGET_NONE);
|
|
|
|
|
|
|
|
height = tree->root->height;
|
|
|
|
|
|
|
|
/* Identify target location */
|
|
|
|
switch (tree->move.target_pos) {
|
|
|
|
case TV_TARGET_ABOVE:
|
|
|
|
if (tree->move.target == NULL) {
|
|
|
|
/* Target: After last child of root */
|
|
|
|
relation = tree->root->children;
|
|
|
|
while (relation->next_sib != NULL) {
|
|
|
|
relation = relation->next_sib;
|
|
|
|
}
|
|
|
|
relationship = TREE_REL_NEXT_SIBLING;
|
|
|
|
|
|
|
|
} else if (tree->move.target->prev_sib != NULL) {
|
|
|
|
/* Target: After previous sibling */
|
|
|
|
relation = tree->move.target->prev_sib;
|
|
|
|
relationship = TREE_REL_NEXT_SIBLING;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* Target: Target: First child of parent */
|
|
|
|
assert(tree->move.target->parent != NULL);
|
|
|
|
relation = tree->move.target->parent;
|
|
|
|
relationship = TREE_REL_FIRST_CHILD;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TV_TARGET_INSIDE:
|
|
|
|
assert(tree->move.target != NULL);
|
|
|
|
relation = tree->move.target;
|
|
|
|
relationship = TREE_REL_FIRST_CHILD;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TV_TARGET_BELOW:
|
|
|
|
assert(tree->move.target != NULL);
|
|
|
|
relation = tree->move.target;
|
|
|
|
relationship = TREE_REL_NEXT_SIBLING;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Bad drop target for move.");
|
2013-08-17 22:39:17 +04:00
|
|
|
return NSERROR_BAD_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (relationship == TREE_REL_FIRST_CHILD) {
|
|
|
|
parent = relation;
|
|
|
|
} else {
|
|
|
|
parent = relation->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The node that we're moving selection to can't itself be selected */
|
2014-05-24 18:09:20 +04:00
|
|
|
assert(!(relation->flags & TV_NFLAGS_SELECTED));
|
2013-08-17 22:39:17 +04:00
|
|
|
|
|
|
|
/* Move all selected nodes from treeview to tree->move.root */
|
|
|
|
treeview_move_yank_selection(tree);
|
|
|
|
|
|
|
|
/* Move all nodes on tree->move.root to target location */
|
|
|
|
for (node = tree->move.root; node != NULL; node = next) {
|
|
|
|
next = node->next_sib;
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (!(parent->flags & TV_NFLAGS_EXPANDED)) {
|
|
|
|
if (node->flags & TV_NFLAGS_EXPANDED)
|
2013-09-03 17:08:44 +04:00
|
|
|
treeview_node_contract_internal(tree, node);
|
2014-05-24 18:09:20 +04:00
|
|
|
node->flags &= ~TV_NFLAGS_SELECTED;
|
2013-08-17 22:39:17 +04:00
|
|
|
}
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_insert_node(tree, node, relation, relationship);
|
2013-08-17 22:39:17 +04:00
|
|
|
|
|
|
|
relation = node;
|
|
|
|
relationship = TREE_REL_NEXT_SIBLING;
|
|
|
|
}
|
|
|
|
tree->move.root = NULL;
|
|
|
|
|
|
|
|
/* Tell window, if height has changed */
|
|
|
|
if (height != tree->root->height)
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_update_size(tree, -1, tree->root->height);
|
2013-08-17 22:39:17 +04:00
|
|
|
|
|
|
|
/* TODO: Deal with redraw area properly */
|
|
|
|
rect->x0 = 0;
|
|
|
|
rect->y0 = 0;
|
|
|
|
rect->x1 = REDRAW_MAX;
|
|
|
|
rect->y1 = REDRAW_MAX;
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* context for treeview launch action
|
|
|
|
*/
|
2013-07-08 15:53:19 +04:00
|
|
|
struct treeview_launch_walk_data {
|
|
|
|
int selected_depth;
|
|
|
|
treeview *tree;
|
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node walk backward callback for tracking folder selection.
|
|
|
|
*/
|
|
|
|
static nserror
|
|
|
|
treeview_node_launch_walk_bwd_cb(treeview_node *n, void *ctx, bool *end)
|
2013-07-08 15:53:19 +04:00
|
|
|
{
|
|
|
|
struct treeview_launch_walk_data *lw = ctx;
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->type == TREE_NODE_FOLDER && n->flags == TV_NFLAGS_SELECTED) {
|
2013-07-08 15:53:19 +04:00
|
|
|
lw->selected_depth--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node walk forward callback for launching nodes.
|
|
|
|
*
|
|
|
|
* \param n current node
|
|
|
|
* \param ctx node launch context
|
|
|
|
* \param skip_children flag to allow children to be skipped
|
|
|
|
* \param end flag to allow iteration to be finished early.
|
2017-03-01 15:50:30 +03:00
|
|
|
* \return NSERROR_OK on success else error code.
|
2017-03-01 02:41:48 +03:00
|
|
|
*/
|
|
|
|
static nserror
|
|
|
|
treeview_node_launch_walk_fwd_cb(treeview_node *n,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end)
|
2013-07-08 15:53:19 +04:00
|
|
|
{
|
|
|
|
struct treeview_launch_walk_data *lw = ctx;
|
2014-10-31 01:43:16 +03:00
|
|
|
nserror ret = NSERROR_OK;
|
2013-07-08 15:53:19 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (n->type == TREE_NODE_FOLDER && n->flags & TV_NFLAGS_SELECTED) {
|
2013-07-08 15:53:19 +04:00
|
|
|
lw->selected_depth++;
|
|
|
|
|
|
|
|
} else if (n->type == TREE_NODE_ENTRY &&
|
2017-03-01 02:41:48 +03:00
|
|
|
(n->flags & TV_NFLAGS_SELECTED ||
|
|
|
|
lw->selected_depth > 0)) {
|
2013-07-08 15:53:19 +04:00
|
|
|
struct treeview_node_msg msg;
|
|
|
|
msg.msg = TREE_MSG_NODE_LAUNCH;
|
|
|
|
msg.data.node_launch.mouse = BROWSER_MOUSE_HOVER;
|
2014-10-31 01:43:16 +03:00
|
|
|
ret = lw->tree->callbacks->entry(msg, n->client_data);
|
2013-07-08 15:53:19 +04:00
|
|
|
}
|
|
|
|
|
2014-10-31 01:43:16 +03:00
|
|
|
return ret;
|
2013-07-08 15:53:19 +04:00
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
2013-07-08 15:53:19 +04:00
|
|
|
/**
|
|
|
|
* Launch a selection.
|
2013-07-08 16:01:00 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \note Selected entries are launched. Entries that are descendants
|
|
|
|
* of selected folders are also launched.
|
2013-07-08 16:01:00 +04:00
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to launch selected nodes in
|
|
|
|
* \return NSERROR_OK on success, appropriate error otherwise
|
2013-07-08 15:53:19 +04:00
|
|
|
*/
|
|
|
|
static nserror treeview_launch_selection(treeview *tree)
|
|
|
|
{
|
|
|
|
struct treeview_launch_walk_data lw;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
lw.selected_depth = 0;
|
|
|
|
lw.tree = tree;
|
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
return treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_LOGICAL_COMPLETE,
|
|
|
|
treeview_node_launch_walk_bwd_cb,
|
|
|
|
treeview_node_launch_walk_fwd_cb, &lw);
|
2013-07-08 15:53:19 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-27 23:56:20 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
nserror
|
|
|
|
treeview_get_relation(treeview *tree,
|
|
|
|
treeview_node **relation,
|
|
|
|
enum treeview_relationship *rel,
|
|
|
|
bool at_y,
|
|
|
|
int y)
|
2013-08-27 23:56:20 +04:00
|
|
|
{
|
|
|
|
treeview_node *n;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
|
|
|
|
if (at_y) {
|
|
|
|
n = treeview_y_node(tree, y);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
n = treeview_get_first_selected(tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n != NULL && n->parent != NULL) {
|
|
|
|
if (n == n->parent->children) {
|
|
|
|
/* First child */
|
|
|
|
*relation = n->parent;
|
|
|
|
*rel = TREE_REL_FIRST_CHILD;
|
|
|
|
} else {
|
|
|
|
/* Not first child */
|
|
|
|
*relation = n->prev_sib;
|
|
|
|
*rel = TREE_REL_NEXT_SIBLING;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (tree->root->children == NULL) {
|
|
|
|
/* First child of root */
|
|
|
|
*relation = tree->root;
|
|
|
|
*rel = TREE_REL_FIRST_CHILD;
|
|
|
|
} else {
|
|
|
|
/* Last child of root */
|
|
|
|
n = tree->root->children;
|
|
|
|
while (n->next_sib != NULL)
|
|
|
|
n = n->next_sib;
|
|
|
|
*relation = n;
|
|
|
|
*rel = TREE_REL_NEXT_SIBLING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* context for treeview keyboard action
|
|
|
|
*/
|
2013-07-09 15:42:22 +04:00
|
|
|
struct treeview_nav_state {
|
|
|
|
treeview *tree;
|
|
|
|
treeview_node *prev;
|
|
|
|
treeview_node *curr;
|
|
|
|
treeview_node *next;
|
|
|
|
treeview_node *last;
|
|
|
|
int n_selected;
|
2013-07-09 17:24:48 +04:00
|
|
|
int prev_n_selected;
|
2013-07-09 15:42:22 +04:00
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node callback for handling mouse action.
|
|
|
|
*
|
2017-03-01 15:50:30 +03:00
|
|
|
* \param node current node
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param ctx node context
|
|
|
|
* \param skip_children flag to allow children to be skipped
|
|
|
|
* \param end flag to allow iteration to be finished early.
|
2017-03-01 15:50:30 +03:00
|
|
|
* \return NSERROR_OK on success else error code.
|
2017-03-01 02:41:48 +03:00
|
|
|
*/
|
|
|
|
static nserror
|
|
|
|
treeview_node_nav_cb(treeview_node *node,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end)
|
2013-07-09 15:42:22 +04:00
|
|
|
{
|
|
|
|
struct treeview_nav_state *ns = ctx;
|
|
|
|
|
|
|
|
if (node == ns->tree->root)
|
|
|
|
return NSERROR_OK;
|
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (node->flags & TV_NFLAGS_SELECTED) {
|
2013-07-09 15:42:22 +04:00
|
|
|
ns->n_selected++;
|
|
|
|
if (ns->curr == NULL) {
|
|
|
|
ns->curr = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (ns->n_selected == 0) {
|
|
|
|
ns->prev = node;
|
|
|
|
|
2013-07-09 17:24:48 +04:00
|
|
|
} else if (ns->prev_n_selected < ns->n_selected) {
|
2013-07-09 15:42:22 +04:00
|
|
|
ns->next = node;
|
2013-07-09 17:24:48 +04:00
|
|
|
ns->prev_n_selected = ns->n_selected;
|
2013-07-09 15:42:22 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ns->last = node;
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
2013-07-09 15:46:30 +04:00
|
|
|
/**
|
|
|
|
* Handle keyboard navigation.
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \note Selected entries are launched.
|
2013-07-09 15:46:30 +04:00
|
|
|
* Entries that are descendants of selected folders are also launched.
|
2017-03-01 02:41:48 +03:00
|
|
|
*
|
|
|
|
* \param tree Treeview object to launch selected nodes in
|
|
|
|
* \param key The ucs4 character codepoint
|
|
|
|
* \param rect Updated to redraw rectangle
|
|
|
|
* \return true if treeview needs redraw, false otherwise
|
2013-07-09 15:46:30 +04:00
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static bool
|
|
|
|
treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
|
2013-07-09 15:42:22 +04:00
|
|
|
{
|
|
|
|
struct treeview_nav_state ns = {
|
|
|
|
.tree = tree,
|
|
|
|
.prev = NULL,
|
|
|
|
.curr = NULL,
|
|
|
|
.next = NULL,
|
|
|
|
.last = NULL,
|
2013-07-09 17:24:48 +04:00
|
|
|
.n_selected = 0,
|
|
|
|
.prev_n_selected = 0
|
2013-07-09 15:42:22 +04:00
|
|
|
};
|
2013-08-17 16:40:05 +04:00
|
|
|
int h = tree->root->height;
|
2013-07-09 15:42:22 +04:00
|
|
|
bool redraw = false;
|
|
|
|
|
|
|
|
/* Fill out the nav. state struct, by examining the current selection
|
|
|
|
* state */
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_nav_cb, &ns);
|
|
|
|
|
|
|
|
if (tree->search.search == false) {
|
|
|
|
if (ns.next == NULL)
|
|
|
|
ns.next = tree->root->children;
|
|
|
|
if (ns.prev == NULL)
|
|
|
|
ns.prev = ns.last;
|
|
|
|
}
|
2013-07-09 15:42:22 +04:00
|
|
|
|
|
|
|
/* Clear any existing selection */
|
|
|
|
redraw = treeview_clear_selection(tree, rect);
|
|
|
|
|
|
|
|
switch (key) {
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_LEFT:
|
2017-09-14 22:10:30 +03:00
|
|
|
if (tree->search.search == true) {
|
|
|
|
break;
|
|
|
|
}
|
2013-07-09 15:42:22 +04:00
|
|
|
if (ns.curr != NULL &&
|
2017-03-01 02:41:48 +03:00
|
|
|
ns.curr->parent != NULL &&
|
|
|
|
ns.curr->parent->type != TREE_NODE_ROOT) {
|
2013-07-09 15:42:22 +04:00
|
|
|
/* Step to parent */
|
2014-05-24 18:09:20 +04:00
|
|
|
ns.curr->parent->flags |= TV_NFLAGS_SELECTED;
|
2013-07-09 15:42:22 +04:00
|
|
|
|
|
|
|
} else if (ns.curr != NULL && tree->root->children != NULL) {
|
|
|
|
/* Select first node in tree */
|
2014-05-24 18:09:20 +04:00
|
|
|
tree->root->children->flags |= TV_NFLAGS_SELECTED;
|
2013-07-09 15:42:22 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_RIGHT:
|
2013-07-09 16:47:06 +04:00
|
|
|
if (ns.curr != NULL) {
|
2014-05-24 18:09:20 +04:00
|
|
|
if (!(ns.curr->flags & TV_NFLAGS_EXPANDED)) {
|
2013-07-09 16:47:06 +04:00
|
|
|
/* Toggle node to expanded */
|
2013-09-03 17:08:44 +04:00
|
|
|
treeview_node_expand_internal(tree, ns.curr);
|
2013-07-09 16:47:06 +04:00
|
|
|
if (ns.curr->children != NULL) {
|
|
|
|
/* Step to first child */
|
|
|
|
ns.curr->children->flags |=
|
2017-03-01 02:41:48 +03:00
|
|
|
TV_NFLAGS_SELECTED;
|
2013-07-09 16:47:06 +04:00
|
|
|
} else {
|
|
|
|
/* Retain current node selection */
|
2014-05-24 18:09:20 +04:00
|
|
|
ns.curr->flags |= TV_NFLAGS_SELECTED;
|
2013-07-09 16:47:06 +04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Toggle node to contracted */
|
2013-09-03 17:08:44 +04:00
|
|
|
treeview_node_contract_internal(tree, ns.curr);
|
2013-07-09 16:47:06 +04:00
|
|
|
/* Retain current node selection */
|
2014-05-24 18:09:20 +04:00
|
|
|
ns.curr->flags |= TV_NFLAGS_SELECTED;
|
2013-07-09 15:42:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
} else if (ns.curr != NULL) {
|
|
|
|
/* Retain current node selection */
|
2014-05-24 18:09:20 +04:00
|
|
|
ns.curr->flags |= TV_NFLAGS_SELECTED;
|
2013-07-09 15:42:22 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_UP:
|
2013-07-09 15:42:22 +04:00
|
|
|
if (ns.prev != NULL) {
|
|
|
|
/* Step to previous node */
|
2014-05-24 18:09:20 +04:00
|
|
|
ns.prev->flags |= TV_NFLAGS_SELECTED;
|
2013-07-09 15:42:22 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_DOWN:
|
2013-07-09 15:42:22 +04:00
|
|
|
if (ns.next != NULL) {
|
|
|
|
/* Step to next node */
|
2014-05-24 18:09:20 +04:00
|
|
|
ns.next->flags |= TV_NFLAGS_SELECTED;
|
2013-07-09 15:42:22 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Deal with redraw area properly */
|
|
|
|
rect->x0 = 0;
|
|
|
|
rect->y0 = 0;
|
|
|
|
rect->x1 = REDRAW_MAX;
|
2013-08-17 16:40:05 +04:00
|
|
|
if (tree->root->height > h)
|
2013-08-17 16:27:38 +04:00
|
|
|
rect->y1 = tree->root->height;
|
2013-08-17 16:40:05 +04:00
|
|
|
else
|
|
|
|
rect->y1 = h;
|
2013-07-09 15:42:22 +04:00
|
|
|
redraw = true;
|
|
|
|
|
|
|
|
return redraw;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-17 16:40:05 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
bool treeview_keypress(treeview *tree, uint32_t key)
|
|
|
|
{
|
|
|
|
struct rect r; /**< Redraw rectangle */
|
|
|
|
bool redraw = false;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
|
2017-09-09 23:23:32 +03:00
|
|
|
/* Pass to any textarea, if editing in progress */
|
2013-08-19 17:23:44 +04:00
|
|
|
if (tree->edit.textarea != NULL) {
|
|
|
|
switch (key) {
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_ESCAPE:
|
2013-08-19 17:23:44 +04:00
|
|
|
treeview_edit_cancel(tree, true);
|
|
|
|
return true;
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_NL:
|
|
|
|
case NS_KEY_CR:
|
2013-08-19 17:23:44 +04:00
|
|
|
treeview_edit_done(tree);
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return textarea_keypress(tree->edit.textarea, key);
|
|
|
|
}
|
2017-09-09 23:23:32 +03:00
|
|
|
} else if (tree->search.active == true) {
|
|
|
|
switch (key) {
|
|
|
|
case NS_KEY_ESCAPE:
|
2017-09-22 11:37:25 +03:00
|
|
|
treeview__search_cancel(tree, false);
|
2017-09-09 23:23:32 +03:00
|
|
|
return true;
|
|
|
|
case NS_KEY_NL:
|
|
|
|
case NS_KEY_CR:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return textarea_keypress(tree->search.textarea, key);
|
|
|
|
}
|
2013-08-19 17:23:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Keypress to be handled by treeview */
|
2013-08-17 16:40:05 +04:00
|
|
|
switch (key) {
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_SELECT_ALL:
|
2013-08-17 16:40:05 +04:00
|
|
|
redraw = treeview_select_all(tree, &r);
|
|
|
|
break;
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_COPY_SELECTION:
|
2015-01-03 03:15:58 +03:00
|
|
|
treeview_copy_selection(tree);
|
2013-08-17 16:40:05 +04:00
|
|
|
break;
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_DELETE_LEFT:
|
|
|
|
case NS_KEY_DELETE_RIGHT:
|
2013-08-17 16:40:05 +04:00
|
|
|
redraw = treeview_delete_selection(tree, &r);
|
|
|
|
|
|
|
|
if (tree->flags & TREEVIEW_DEL_EMPTY_DIRS) {
|
2013-08-30 16:29:19 +04:00
|
|
|
int h = tree->root->height;
|
2013-08-17 16:40:05 +04:00
|
|
|
/* Delete any empty nodes */
|
2013-09-05 14:27:33 +04:00
|
|
|
treeview_delete_empty_nodes(tree, false);
|
2013-08-30 16:29:19 +04:00
|
|
|
|
|
|
|
/* Inform front end of change in dimensions */
|
|
|
|
if (tree->root->height != h) {
|
|
|
|
r.y0 = 0;
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_update_size(tree, -1,
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->root->height);
|
2013-08-30 16:29:19 +04:00
|
|
|
}
|
2013-08-17 16:40:05 +04:00
|
|
|
}
|
|
|
|
break;
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_CR:
|
|
|
|
case NS_KEY_NL:
|
2013-09-05 14:27:33 +04:00
|
|
|
treeview_launch_selection(tree);
|
2013-08-17 16:40:05 +04:00
|
|
|
break;
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_ESCAPE:
|
|
|
|
case NS_KEY_CLEAR_SELECTION:
|
2013-08-17 16:40:05 +04:00
|
|
|
redraw = treeview_clear_selection(tree, &r);
|
|
|
|
break;
|
2015-03-27 12:36:19 +03:00
|
|
|
case NS_KEY_LEFT:
|
|
|
|
case NS_KEY_RIGHT:
|
|
|
|
case NS_KEY_UP:
|
|
|
|
case NS_KEY_DOWN:
|
2013-08-17 16:40:05 +04:00
|
|
|
redraw = treeview_keyboard_navigation(tree, key, &r);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redraw) {
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-17 16:40:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-16 23:06:08 +04:00
|
|
|
/**
|
|
|
|
* Set the drag&drop drop indicator
|
|
|
|
*
|
|
|
|
* \param tree Treeview object to set node indicator in
|
2013-08-17 15:51:08 +04:00
|
|
|
* \param need_redraw True iff we already have a redraw region
|
2013-08-17 14:48:16 +04:00
|
|
|
* \param target The treeview node with mouse pointer over it
|
2013-08-16 23:06:08 +04:00
|
|
|
* \param node_height The height of node
|
2013-08-17 14:48:16 +04:00
|
|
|
* \param node_y The Y coord of the top of target node
|
2013-08-17 02:08:57 +04:00
|
|
|
* \param mouse_y Y coord of mouse position
|
2013-08-16 23:06:08 +04:00
|
|
|
* \param rect Redraw rectangle (if redraw required)
|
|
|
|
* \return true iff redraw required
|
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static bool
|
|
|
|
treeview_set_move_indicator(treeview *tree,
|
|
|
|
bool need_redraw,
|
|
|
|
treeview_node *target,
|
|
|
|
int node_height,
|
|
|
|
int node_y,
|
|
|
|
int mouse_y,
|
|
|
|
struct rect *rect)
|
2013-08-16 23:06:08 +04:00
|
|
|
{
|
2013-08-17 17:37:24 +04:00
|
|
|
treeview_node *orig = target;
|
2013-08-16 23:34:41 +04:00
|
|
|
enum treeview_target_pos target_pos;
|
2013-08-17 02:08:57 +04:00
|
|
|
int mouse_pos = mouse_y - node_y;
|
2013-08-17 14:48:16 +04:00
|
|
|
int x;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
assert(tree->root->children != NULL);
|
2013-10-22 18:22:42 +04:00
|
|
|
assert(target != NULL);
|
2013-08-17 02:08:57 +04:00
|
|
|
|
2014-05-24 18:09:20 +04:00
|
|
|
if (target->flags & TV_NFLAGS_SELECTED) {
|
2013-08-17 16:22:40 +04:00
|
|
|
/* Find top selected ancestor */
|
|
|
|
while (target->parent &&
|
2017-03-01 02:41:48 +03:00
|
|
|
target->parent->flags & TV_NFLAGS_SELECTED) {
|
2013-08-17 16:22:40 +04:00
|
|
|
target = target->parent;
|
|
|
|
}
|
|
|
|
|
2017-03-01 15:50:30 +03:00
|
|
|
/* Find top adjacent selected sibling */
|
2013-08-17 16:27:38 +04:00
|
|
|
while (target->prev_sib &&
|
2017-03-01 02:41:48 +03:00
|
|
|
target->prev_sib->flags & TV_NFLAGS_SELECTED) {
|
2013-08-17 16:27:38 +04:00
|
|
|
target = target->prev_sib;
|
2013-08-17 16:22:40 +04:00
|
|
|
}
|
|
|
|
target_pos = TV_TARGET_ABOVE;
|
|
|
|
|
|
|
|
} else switch (target->type) {
|
2017-03-01 02:41:48 +03:00
|
|
|
case TREE_NODE_FOLDER:
|
|
|
|
if (mouse_pos <= node_height / 4) {
|
|
|
|
target_pos = TV_TARGET_ABOVE;
|
|
|
|
} else if (mouse_pos <= (3 * node_height) / 4 ||
|
|
|
|
target->flags & TV_NFLAGS_EXPANDED) {
|
|
|
|
target_pos = TV_TARGET_INSIDE;
|
|
|
|
} else {
|
|
|
|
target_pos = TV_TARGET_BELOW;
|
|
|
|
}
|
|
|
|
break;
|
2013-08-16 23:06:08 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
case TREE_NODE_ENTRY:
|
|
|
|
if (mouse_pos <= node_height / 2) {
|
|
|
|
target_pos = TV_TARGET_ABOVE;
|
|
|
|
} else {
|
|
|
|
target_pos = TV_TARGET_BELOW;
|
|
|
|
}
|
|
|
|
break;
|
2013-08-16 23:06:08 +04:00
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
default:
|
|
|
|
assert(target->type != TREE_NODE_ROOT);
|
|
|
|
return false;
|
|
|
|
}
|
2013-08-16 23:06:08 +04:00
|
|
|
|
2013-08-17 02:08:57 +04:00
|
|
|
if (target_pos == tree->move.target_pos &&
|
2017-03-01 02:41:48 +03:00
|
|
|
target == tree->move.target) {
|
2013-08-16 23:34:41 +04:00
|
|
|
/* No change */
|
2013-08-17 15:51:08 +04:00
|
|
|
return need_redraw;
|
2013-08-16 23:34:41 +04:00
|
|
|
}
|
|
|
|
|
2013-08-17 14:48:16 +04:00
|
|
|
if (tree->move.target_pos != TV_TARGET_NONE) {
|
|
|
|
/* Need to clear old indicator position */
|
2013-08-17 15:51:08 +04:00
|
|
|
if (need_redraw) {
|
|
|
|
if (rect->x0 > tree->move.target_area.x0)
|
|
|
|
rect->x0 = tree->move.target_area.x0;
|
|
|
|
if (tree->move.target_area.x1 > rect->x1)
|
|
|
|
rect->x1 = tree->move.target_area.x1;
|
|
|
|
if (rect->y0 > tree->move.target_area.y0)
|
|
|
|
rect->y0 = tree->move.target_area.y0;
|
|
|
|
if (tree->move.target_area.y1 > rect->y1)
|
|
|
|
rect->y1 = tree->move.target_area.y1;
|
|
|
|
} else {
|
|
|
|
*rect = tree->move.target_area;
|
|
|
|
need_redraw = true;
|
|
|
|
}
|
2013-08-17 14:48:16 +04:00
|
|
|
}
|
|
|
|
|
2013-08-17 17:37:24 +04:00
|
|
|
/* Offset for ABOVE / BELOW */
|
2013-08-17 02:08:57 +04:00
|
|
|
if (target_pos == TV_TARGET_ABOVE) {
|
2013-08-17 17:37:24 +04:00
|
|
|
if (target != orig) {
|
|
|
|
node_y = treeview_node_y(tree, target);
|
|
|
|
}
|
2013-08-17 02:08:57 +04:00
|
|
|
node_y -= (tree_g.line_height + 1) / 2;
|
|
|
|
} else if (target_pos == TV_TARGET_BELOW) {
|
|
|
|
node_y += node_height - (tree_g.line_height + 1) / 2;
|
|
|
|
}
|
|
|
|
|
2013-08-17 17:37:24 +04:00
|
|
|
/* Oftsets are all relative to centred (INSIDE) */
|
|
|
|
node_y += (tree_g.line_height -
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_res[TREE_RES_ARROW].height + 1) / 2;
|
2013-08-17 17:37:24 +04:00
|
|
|
|
2013-10-23 17:36:46 +04:00
|
|
|
x = target->inset + tree_g.move_offset;
|
2013-08-17 14:48:16 +04:00
|
|
|
|
|
|
|
/* Update target details */
|
2013-08-17 00:40:45 +04:00
|
|
|
tree->move.target = target;
|
|
|
|
tree->move.target_pos = target_pos;
|
2013-08-17 14:48:16 +04:00
|
|
|
tree->move.target_area.x0 = x;
|
|
|
|
tree->move.target_area.y0 = node_y;
|
|
|
|
tree->move.target_area.x1 = tree_g.icon_size + x;
|
|
|
|
tree->move.target_area.y1 = tree_g.icon_size + node_y;
|
|
|
|
|
|
|
|
if (target_pos != TV_TARGET_NONE) {
|
|
|
|
/* Need to draw new indicator position */
|
|
|
|
if (need_redraw) {
|
2013-08-17 15:51:08 +04:00
|
|
|
if (rect->x0 > tree->move.target_area.x0)
|
|
|
|
rect->x0 = tree->move.target_area.x0;
|
|
|
|
if (tree->move.target_area.x1 > rect->x1)
|
|
|
|
rect->x1 = tree->move.target_area.x1;
|
2013-08-17 14:48:16 +04:00
|
|
|
if (rect->y0 > tree->move.target_area.y0)
|
|
|
|
rect->y0 = tree->move.target_area.y0;
|
|
|
|
if (tree->move.target_area.y1 > rect->y1)
|
|
|
|
rect->y1 = tree->move.target_area.y1;
|
|
|
|
} else {
|
|
|
|
*rect = tree->move.target_area;
|
|
|
|
need_redraw = true;
|
|
|
|
}
|
|
|
|
}
|
2013-08-16 23:06:08 +04:00
|
|
|
|
2013-08-17 14:48:16 +04:00
|
|
|
return need_redraw;
|
2013-08-16 23:06:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
/**
|
|
|
|
* Callback for textarea_create, in desktop/treeview.h
|
2017-03-01 02:41:48 +03:00
|
|
|
*
|
|
|
|
* \param data treeview context
|
|
|
|
* \param msg textarea message
|
2013-08-19 17:23:44 +04:00
|
|
|
*/
|
|
|
|
static void treeview_textarea_callback(void *data, struct textarea_msg *msg)
|
|
|
|
{
|
|
|
|
treeview *tree = data;
|
|
|
|
struct rect *r;
|
|
|
|
|
|
|
|
switch (msg->type) {
|
|
|
|
case TEXTAREA_MSG_DRAG_REPORT:
|
|
|
|
if (msg->data.drag == TEXTAREA_DRAG_NONE) {
|
|
|
|
/* Textarea drag finished */
|
|
|
|
tree->drag.type = TV_DRAG_NONE;
|
|
|
|
} else {
|
|
|
|
/* Textarea drag started */
|
|
|
|
tree->drag.type = TV_DRAG_TEXTAREA;
|
|
|
|
}
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_drag_status(tree, tree->drag.type);
|
2013-08-19 17:23:44 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TEXTAREA_MSG_REDRAW_REQUEST:
|
|
|
|
r = &msg->data.redraw;
|
|
|
|
r->x0 += tree->edit.x;
|
|
|
|
r->y0 += tree->edit.y;
|
|
|
|
r->x1 += tree->edit.x;
|
|
|
|
r->y1 += tree->edit.y;
|
|
|
|
|
|
|
|
/* Redraw the textarea */
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, r);
|
2013-08-19 17:23:44 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start edit of node field, at given y-coord, if editable
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param tree Treeview object to consider editing in
|
|
|
|
* \param n The treeview node to try editing
|
|
|
|
* \param node_y The Y coord of the top of n
|
|
|
|
* \param mouse_x X coord of mouse position
|
|
|
|
* \param mouse_y Y coord of mouse position
|
|
|
|
* \param rect Redraw rectangle (if redraw required)
|
2013-08-19 17:23:44 +04:00
|
|
|
* \return true iff redraw required
|
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static bool
|
|
|
|
treeview_edit_node_at_point(treeview *tree,
|
|
|
|
treeview_node *n,
|
|
|
|
int node_y,
|
|
|
|
int mouse_x,
|
|
|
|
int mouse_y,
|
|
|
|
struct rect *rect)
|
2013-08-19 17:23:44 +04:00
|
|
|
{
|
|
|
|
struct treeview_text *field_data = NULL;
|
2013-09-08 22:34:43 +04:00
|
|
|
struct treeview_field *ef, *field_desc = NULL;
|
2013-08-19 17:23:44 +04:00
|
|
|
int pos = node_y + tree_g.line_height;
|
|
|
|
int field_y = node_y;
|
|
|
|
int field_x;
|
|
|
|
int width, height;
|
|
|
|
bool success;
|
|
|
|
|
|
|
|
/* If the main field is editable, make field_data point to it */
|
2013-08-20 22:44:14 +04:00
|
|
|
if (n->type == TREE_NODE_ENTRY)
|
|
|
|
ef = &(tree->fields[0]);
|
|
|
|
else
|
|
|
|
ef = &(tree->fields[tree->n_fields]);
|
2013-08-19 17:23:44 +04:00
|
|
|
if (ef->flags & TREE_FLAG_ALLOW_EDIT) {
|
|
|
|
field_data = &n->text;
|
|
|
|
field_desc = ef;
|
|
|
|
field_y = node_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for editable entry fields */
|
|
|
|
if (n->type == TREE_NODE_ENTRY && n->height != tree_g.line_height) {
|
|
|
|
struct treeview_node_entry *e = (struct treeview_node_entry *)n;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < tree->n_fields - 1; i++) {
|
|
|
|
if (mouse_y <= pos)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ef = &(tree->fields[i + 1]);
|
|
|
|
pos += tree_g.line_height;
|
|
|
|
if (mouse_y <= pos && (ef->flags &
|
2017-03-01 02:41:48 +03:00
|
|
|
TREE_FLAG_ALLOW_EDIT)) {
|
2013-08-19 17:23:44 +04:00
|
|
|
field_data = &e->fields[i].value;
|
|
|
|
field_desc = ef;
|
|
|
|
field_y = pos - tree_g.line_height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-08 22:34:43 +04:00
|
|
|
if (field_data == NULL || field_desc == NULL) {
|
2013-08-19 17:23:44 +04:00
|
|
|
/* No editable field */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get window width/height */
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_get_window_dimensions(tree, &width, &height);
|
2013-08-19 17:23:44 +04:00
|
|
|
|
2017-09-09 16:38:13 +03:00
|
|
|
/* Calculate textarea width/height */
|
2013-08-19 17:23:44 +04:00
|
|
|
field_x = n->inset + tree_g.step_width + tree_g.icon_step - 3;
|
|
|
|
width -= field_x;
|
|
|
|
height = tree_g.line_height;
|
|
|
|
|
|
|
|
/* Create text area */
|
2017-09-09 20:35:24 +03:00
|
|
|
tree->edit.textarea = treeview__create_textarea(tree, width, height,
|
|
|
|
0x000000, 0xffffff, 0x000000, plot_style_odd.text,
|
|
|
|
treeview_textarea_callback);
|
2013-08-19 17:23:44 +04:00
|
|
|
if (tree->edit.textarea == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
success = textarea_set_text(tree->edit.textarea, field_data->data);
|
|
|
|
if (!success) {
|
|
|
|
textarea_destroy(tree->edit.textarea);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
tree->edit.node = n;
|
2013-09-05 14:26:05 +04:00
|
|
|
tree->edit.field = field_desc->field;
|
2013-08-19 17:23:44 +04:00
|
|
|
|
|
|
|
/* Position the caret */
|
|
|
|
mouse_x -= field_x;
|
|
|
|
if (mouse_x < 0)
|
|
|
|
mouse_x = 0;
|
|
|
|
else if (mouse_x >= width)
|
|
|
|
mouse_x = width - 1;
|
|
|
|
|
|
|
|
textarea_mouse_action(tree->edit.textarea,
|
2017-03-01 02:41:48 +03:00
|
|
|
BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1,
|
|
|
|
mouse_x, tree_g.line_height / 2);
|
2013-08-19 17:23:44 +04:00
|
|
|
|
|
|
|
/* Position the textarea */
|
|
|
|
tree->edit.x = field_x;
|
|
|
|
tree->edit.y = field_y;
|
|
|
|
tree->edit.w = width;
|
|
|
|
tree->edit.h = height;
|
|
|
|
|
|
|
|
/* Setup redraw rectangle */
|
|
|
|
if (rect->x0 > field_x)
|
|
|
|
rect->x0 = field_x;
|
|
|
|
if (rect->y0 > field_y)
|
|
|
|
rect->y0 = field_y;
|
|
|
|
if (rect->x1 < field_x + width)
|
|
|
|
rect->x1 = field_x + width;
|
|
|
|
if (rect->y1 < field_y + height)
|
|
|
|
rect->y1 = field_y + height;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-28 15:58:41 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
void treeview_edit_selection(treeview *tree)
|
|
|
|
{
|
|
|
|
struct rect rect;
|
|
|
|
treeview_node *n;
|
|
|
|
bool redraw;
|
|
|
|
int y;
|
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
|
|
|
/* Get first selected node */
|
|
|
|
n = treeview_get_first_selected(tree);
|
|
|
|
|
|
|
|
if (n == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Get node's y-position */
|
|
|
|
y = treeview_node_y(tree, n);
|
|
|
|
|
|
|
|
/* Edit node at y */
|
|
|
|
redraw = treeview_edit_node_at_point(tree, n, y,
|
2017-03-01 02:41:48 +03:00
|
|
|
0, y + tree_g.line_height / 2, &rect);
|
2013-08-28 15:58:41 +04:00
|
|
|
|
|
|
|
if (redraw == false)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Redraw */
|
|
|
|
rect.x0 = 0;
|
|
|
|
rect.y0 = y;
|
|
|
|
rect.x1 = REDRAW_MAX;
|
|
|
|
rect.y1 = y + tree_g.line_height;
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &rect);
|
2013-08-28 15:58:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 02:41:48 +03:00
|
|
|
/**
|
|
|
|
* context for treeview mouse handling
|
|
|
|
*/
|
2013-05-31 16:24:27 +04:00
|
|
|
struct treeview_mouse_action {
|
2013-07-03 16:48:53 +04:00
|
|
|
treeview *tree;
|
2013-05-31 16:24:27 +04:00
|
|
|
browser_mouse_state mouse;
|
|
|
|
int x;
|
|
|
|
int y;
|
2013-06-18 20:08:35 +04:00
|
|
|
int current_y; /* Y coordinate value of top of current node */
|
2013-05-31 16:24:27 +04:00
|
|
|
};
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treewalk node callback for handling mouse action.
|
|
|
|
*
|
2017-03-01 15:50:30 +03:00
|
|
|
* \param node current node
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param ctx node context
|
|
|
|
* \param skip_children flag to allow children to be skipped
|
|
|
|
* \param end flag to allow iteration to be finished early.
|
2017-03-01 15:50:30 +03:00
|
|
|
* \return NSERROR_OK on success else error code.
|
2017-03-01 02:41:48 +03:00
|
|
|
*/
|
|
|
|
static nserror
|
|
|
|
treeview_node_mouse_action_cb(treeview_node *node,
|
|
|
|
void *ctx,
|
|
|
|
bool *skip_children,
|
|
|
|
bool *end)
|
2013-05-31 16:24:27 +04:00
|
|
|
{
|
|
|
|
struct treeview_mouse_action *ma = ctx;
|
2013-05-31 20:56:22 +04:00
|
|
|
struct rect r;
|
2013-06-03 20:06:47 +04:00
|
|
|
bool redraw = false;
|
2013-06-04 17:33:02 +04:00
|
|
|
bool click;
|
2013-06-03 20:06:47 +04:00
|
|
|
int height;
|
|
|
|
enum {
|
|
|
|
TV_NODE_ACTION_NONE = 0,
|
|
|
|
TV_NODE_ACTION_SELECTION = (1 << 0)
|
|
|
|
} action = TV_NODE_ACTION_NONE;
|
2013-08-16 14:15:16 +04:00
|
|
|
enum treeview_node_part part = TV_NODE_PART_NONE;
|
2013-06-03 20:06:47 +04:00
|
|
|
nserror err;
|
|
|
|
|
|
|
|
r.x0 = 0;
|
2013-06-03 23:21:00 +04:00
|
|
|
r.x1 = REDRAW_MAX;
|
2013-06-03 20:06:47 +04:00
|
|
|
|
|
|
|
height = (node->type == TREE_NODE_ENTRY) ? node->height :
|
2017-03-01 02:41:48 +03:00
|
|
|
tree_g.line_height;
|
2013-05-31 16:24:27 +04:00
|
|
|
|
|
|
|
/* Skip line if we've not reached mouse y */
|
2013-06-03 20:06:47 +04:00
|
|
|
if (ma->y > ma->current_y + height) {
|
|
|
|
ma->current_y += height;
|
2013-07-03 17:18:33 +04:00
|
|
|
return NSERROR_OK; /* Don't want to abort tree walk */
|
2013-05-31 16:24:27 +04:00
|
|
|
}
|
|
|
|
|
2013-06-04 17:33:02 +04:00
|
|
|
/* Find where the mouse is */
|
2013-06-18 17:58:43 +04:00
|
|
|
if (ma->y <= ma->current_y + tree_g.line_height) {
|
2017-09-14 22:11:53 +03:00
|
|
|
int inset = node->inset;
|
|
|
|
if (ma->tree->search.search == true) {
|
|
|
|
inset = tree_g.window_padding;
|
|
|
|
}
|
|
|
|
if (ma->x >= inset - 1 &&
|
|
|
|
ma->x < inset + tree_g.step_width) {
|
2013-06-18 17:58:43 +04:00
|
|
|
/* Over expansion toggle */
|
2013-08-16 14:15:16 +04:00
|
|
|
part = TV_NODE_PART_TOGGLE;
|
2013-06-18 17:58:43 +04:00
|
|
|
|
2017-09-14 22:11:53 +03:00
|
|
|
} else if (ma->x >= inset + tree_g.step_width &&
|
|
|
|
ma->x < inset + tree_g.step_width +
|
|
|
|
tree_g.icon_step + node->text.width) {
|
2013-06-18 17:58:43 +04:00
|
|
|
/* On node */
|
2013-08-16 14:15:16 +04:00
|
|
|
part = TV_NODE_PART_ON_NODE;
|
2013-06-18 17:58:43 +04:00
|
|
|
}
|
|
|
|
} else if (node->type == TREE_NODE_ENTRY &&
|
2017-03-01 02:41:48 +03:00
|
|
|
height > tree_g.line_height) {
|
2013-06-18 17:58:43 +04:00
|
|
|
/* Expanded entries */
|
|
|
|
int x = node->inset + tree_g.step_width + tree_g.icon_step;
|
|
|
|
int y = ma->current_y + tree_g.line_height;
|
|
|
|
int i;
|
|
|
|
struct treeview_node_entry *entry =
|
2017-03-01 02:41:48 +03:00
|
|
|
(struct treeview_node_entry *)node;
|
2013-06-18 17:58:43 +04:00
|
|
|
for (i = 0; i < ma->tree->n_fields - 1; i++) {
|
|
|
|
struct treeview_field *ef = &(ma->tree->fields[i + 1]);
|
|
|
|
|
|
|
|
if (ma->y > y + tree_g.line_height) {
|
|
|
|
y += tree_g.line_height;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ef->flags & TREE_FLAG_SHOW_NAME) {
|
|
|
|
int max_width = ma->tree->field_width;
|
|
|
|
|
|
|
|
if (ma->x >= x + max_width - ef->value.width -
|
2017-03-01 02:41:48 +03:00
|
|
|
tree_g.step_width &&
|
|
|
|
ma->x < x + max_width -
|
|
|
|
tree_g.step_width) {
|
2013-06-18 17:58:43 +04:00
|
|
|
/* On a field name */
|
2013-08-16 14:15:16 +04:00
|
|
|
part = TV_NODE_PART_ON_NODE;
|
2013-06-18 17:58:43 +04:00
|
|
|
|
|
|
|
} else if (ma->x >= x + max_width &&
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->x < x + max_width +
|
|
|
|
entry->fields[i].value.width) {
|
2013-06-18 17:58:43 +04:00
|
|
|
/* On a field value */
|
2013-08-16 14:15:16 +04:00
|
|
|
part = TV_NODE_PART_ON_NODE;
|
2013-06-18 17:58:43 +04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ma->x >= x && ma->x < x +
|
2017-03-01 02:41:48 +03:00
|
|
|
entry->fields[i].value.width) {
|
2013-06-18 17:58:43 +04:00
|
|
|
/* On a field value */
|
2013-08-16 14:15:16 +04:00
|
|
|
part = TV_NODE_PART_ON_NODE;
|
2013-06-18 17:58:43 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-16 14:15:16 +04:00
|
|
|
/* Record what position / part a drag started on */
|
2013-06-18 17:58:43 +04:00
|
|
|
if (ma->mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) &&
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->tree->drag.type == TV_DRAG_NONE) {
|
2014-05-24 18:09:20 +04:00
|
|
|
ma->tree->drag.selected = node->flags & TV_NFLAGS_SELECTED;
|
2013-06-18 17:58:43 +04:00
|
|
|
ma->tree->drag.start_node = node;
|
2013-08-16 14:15:16 +04:00
|
|
|
ma->tree->drag.part = part;
|
2013-06-18 17:58:43 +04:00
|
|
|
ma->tree->drag.start.x = ma->x;
|
|
|
|
ma->tree->drag.start.y = ma->y;
|
|
|
|
ma->tree->drag.start.node_y = ma->current_y;
|
|
|
|
ma->tree->drag.start.node_h = height;
|
|
|
|
|
|
|
|
ma->tree->drag.prev.x = ma->x;
|
|
|
|
ma->tree->drag.prev.y = ma->y;
|
|
|
|
ma->tree->drag.prev.node_y = ma->current_y;
|
|
|
|
ma->tree->drag.prev.node_h = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle drag start */
|
|
|
|
if (ma->tree->drag.type == TV_DRAG_NONE) {
|
|
|
|
if (ma->mouse & BROWSER_MOUSE_DRAG_1 &&
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->tree->drag.selected == false &&
|
|
|
|
ma->tree->drag.part == TV_NODE_PART_NONE) {
|
2013-06-18 17:58:43 +04:00
|
|
|
ma->tree->drag.type = TV_DRAG_SELECTION;
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_drag_status(ma->tree,
|
2017-03-01 02:41:48 +03:00
|
|
|
CORE_WINDOW_DRAG_SELECTION);
|
2013-07-01 18:10:45 +04:00
|
|
|
|
2017-09-14 22:11:53 +03:00
|
|
|
} else if (ma->tree->search.search == false &&
|
|
|
|
!(ma->tree->flags & TREEVIEW_NO_MOVES) &&
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->mouse & BROWSER_MOUSE_DRAG_1 &&
|
|
|
|
(ma->tree->drag.selected == true ||
|
|
|
|
ma->tree->drag.part == TV_NODE_PART_ON_NODE)) {
|
2013-08-16 23:06:08 +04:00
|
|
|
ma->tree->drag.type = TV_DRAG_MOVE;
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_drag_status(ma->tree,
|
2017-03-01 02:41:48 +03:00
|
|
|
CORE_WINDOW_DRAG_MOVE);
|
2013-08-17 15:51:08 +04:00
|
|
|
redraw |= treeview_propagate_selection(ma->tree, &r);
|
2013-08-16 23:06:08 +04:00
|
|
|
|
2013-06-18 17:58:43 +04:00
|
|
|
} else if (ma->mouse & BROWSER_MOUSE_DRAG_2) {
|
|
|
|
ma->tree->drag.type = TV_DRAG_SELECTION;
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_drag_status(ma->tree,
|
2017-03-01 02:41:48 +03:00
|
|
|
CORE_WINDOW_DRAG_SELECTION);
|
2013-06-18 17:58:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ma->tree->drag.start_node != NULL &&
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->tree->drag.type == TV_DRAG_SELECTION) {
|
2014-05-24 18:09:20 +04:00
|
|
|
ma->tree->drag.start_node->flags ^= TV_NFLAGS_SELECTED;
|
2013-06-18 17:58:43 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-16 23:06:08 +04:00
|
|
|
/* Handle active drags */
|
|
|
|
switch (ma->tree->drag.type) {
|
|
|
|
case TV_DRAG_SELECTION:
|
|
|
|
{
|
2013-06-18 17:58:43 +04:00
|
|
|
int curr_y1 = ma->current_y + height;
|
|
|
|
int prev_y1 = ma->tree->drag.prev.node_y +
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->tree->drag.prev.node_h;
|
2013-06-18 17:58:43 +04:00
|
|
|
|
|
|
|
r.y0 = (ma->current_y < ma->tree->drag.prev.node_y) ?
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->current_y : ma->tree->drag.prev.node_y;
|
2013-06-18 17:58:43 +04:00
|
|
|
r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1;
|
|
|
|
|
|
|
|
redraw = true;
|
|
|
|
|
|
|
|
ma->tree->drag.prev.x = ma->x;
|
|
|
|
ma->tree->drag.prev.y = ma->y;
|
|
|
|
ma->tree->drag.prev.node_y = ma->current_y;
|
|
|
|
ma->tree->drag.prev.node_h = height;
|
2013-06-04 17:33:02 +04:00
|
|
|
}
|
2017-09-14 22:11:53 +03:00
|
|
|
break;
|
2013-08-16 23:06:08 +04:00
|
|
|
|
|
|
|
case TV_DRAG_MOVE:
|
2013-08-17 15:51:08 +04:00
|
|
|
redraw |= treeview_set_move_indicator(ma->tree, redraw,
|
2017-03-01 02:41:48 +03:00
|
|
|
node, height,
|
|
|
|
ma->current_y, ma->y, &r);
|
2013-08-16 23:06:08 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-06-04 17:33:02 +04:00
|
|
|
|
|
|
|
click = ma->mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2);
|
|
|
|
|
|
|
|
if (((node->type == TREE_NODE_FOLDER) &&
|
2017-03-01 02:41:48 +03:00
|
|
|
(ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) ||
|
|
|
|
(part == TV_NODE_PART_TOGGLE && click)) {
|
2013-08-29 22:12:49 +04:00
|
|
|
int h = ma->tree->root->height;
|
|
|
|
|
2013-06-03 20:06:47 +04:00
|
|
|
/* Clear any existing selection */
|
|
|
|
redraw |= treeview_clear_selection(ma->tree, &r);
|
2013-05-31 16:24:27 +04:00
|
|
|
|
2013-06-04 18:03:07 +04:00
|
|
|
/* Toggle node expansion */
|
2014-05-24 18:09:20 +04:00
|
|
|
if (node->flags & TV_NFLAGS_EXPANDED) {
|
2013-09-03 17:08:44 +04:00
|
|
|
err = treeview_node_contract_internal(ma->tree, node);
|
2013-06-03 20:06:47 +04:00
|
|
|
} else {
|
2013-09-03 17:08:44 +04:00
|
|
|
err = treeview_node_expand_internal(ma->tree, node);
|
2013-06-03 20:06:47 +04:00
|
|
|
}
|
2013-07-03 17:18:33 +04:00
|
|
|
if (err != NSERROR_OK) {
|
|
|
|
return err;
|
|
|
|
}
|
2013-06-04 18:03:07 +04:00
|
|
|
|
|
|
|
/* Set up redraw */
|
2013-06-04 18:43:52 +04:00
|
|
|
if (!redraw || r.y0 > ma->current_y)
|
2013-06-04 17:33:02 +04:00
|
|
|
r.y0 = ma->current_y;
|
2013-08-29 22:12:49 +04:00
|
|
|
r.y1 = h > ma->tree->root->height ? h : ma->tree->root->height;
|
2013-06-04 18:43:52 +04:00
|
|
|
redraw = true;
|
2013-06-03 20:06:47 +04:00
|
|
|
|
2013-06-04 17:33:02 +04:00
|
|
|
} else if ((node->type == TREE_NODE_ENTRY) &&
|
2017-03-01 02:41:48 +03:00
|
|
|
(ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) {
|
2013-06-04 18:03:07 +04:00
|
|
|
struct treeview_node_msg msg;
|
|
|
|
msg.msg = TREE_MSG_NODE_LAUNCH;
|
|
|
|
msg.data.node_launch.mouse = ma->mouse;
|
|
|
|
|
2013-06-04 17:33:02 +04:00
|
|
|
/* Clear any existing selection */
|
|
|
|
redraw |= treeview_clear_selection(ma->tree, &r);
|
|
|
|
|
|
|
|
/* Tell client an entry was launched */
|
2013-06-04 18:11:05 +04:00
|
|
|
ma->tree->callbacks->entry(msg, node->client_data);
|
2013-06-04 17:33:02 +04:00
|
|
|
|
2013-08-18 16:22:57 +04:00
|
|
|
} else if (ma->mouse & BROWSER_MOUSE_PRESS_2 ||
|
2017-03-01 02:41:48 +03:00
|
|
|
(ma->mouse & BROWSER_MOUSE_PRESS_1 &&
|
|
|
|
ma->mouse & BROWSER_MOUSE_MOD_2)) {
|
2013-08-18 16:22:57 +04:00
|
|
|
/* Toggle selection of node */
|
|
|
|
action |= TV_NODE_ACTION_SELECTION;
|
|
|
|
|
2013-08-19 17:41:12 +04:00
|
|
|
} else if (ma->mouse & BROWSER_MOUSE_CLICK_1 &&
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->mouse &
|
|
|
|
(BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3) &&
|
|
|
|
part != TV_NODE_PART_TOGGLE) {
|
2013-08-19 17:23:44 +04:00
|
|
|
|
|
|
|
/* Clear any existing selection */
|
|
|
|
redraw |= treeview_clear_selection(ma->tree, &r);
|
|
|
|
|
|
|
|
/* Edit node */
|
|
|
|
redraw |= treeview_edit_node_at_point(ma->tree, node,
|
2017-03-01 02:41:48 +03:00
|
|
|
ma->current_y, ma->x,
|
|
|
|
ma->y, &r);
|
2013-08-19 17:23:44 +04:00
|
|
|
|
2013-06-03 20:06:47 +04:00
|
|
|
} else if (ma->mouse & BROWSER_MOUSE_PRESS_1 &&
|
2017-03-01 02:41:48 +03:00
|
|
|
!(ma->mouse &
|
|
|
|
(BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_3)) &&
|
|
|
|
!(node->flags & TV_NFLAGS_SELECTED) &&
|
|
|
|
part != TV_NODE_PART_TOGGLE) {
|
2013-06-03 20:06:47 +04:00
|
|
|
/* Clear any existing selection */
|
|
|
|
redraw |= treeview_clear_selection(ma->tree, &r);
|
|
|
|
|
|
|
|
/* Select node */
|
|
|
|
action |= TV_NODE_ACTION_SELECTION;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action & TV_NODE_ACTION_SELECTION) {
|
|
|
|
/* Handle change in selection */
|
2014-05-24 18:09:20 +04:00
|
|
|
node->flags ^= TV_NFLAGS_SELECTED;
|
2013-06-03 20:06:47 +04:00
|
|
|
|
|
|
|
/* Redraw */
|
|
|
|
if (!redraw) {
|
|
|
|
r.y0 = ma->current_y;
|
|
|
|
r.y1 = ma->current_y + height;
|
|
|
|
redraw = true;
|
|
|
|
} else {
|
2017-03-01 02:41:48 +03:00
|
|
|
if (r.y0 > ma->current_y) {
|
2013-06-03 20:06:47 +04:00
|
|
|
r.y0 = ma->current_y;
|
2017-03-01 02:41:48 +03:00
|
|
|
}
|
|
|
|
if (r.y1 < ma->current_y + height) {
|
2013-06-03 20:06:47 +04:00
|
|
|
r.y1 = ma->current_y + height;
|
2017-03-01 02:41:48 +03:00
|
|
|
}
|
2013-06-03 20:06:47 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redraw) {
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(ma->tree, &r);
|
2013-05-31 20:56:22 +04:00
|
|
|
}
|
|
|
|
|
2013-07-03 17:18:33 +04:00
|
|
|
*end = true; /* Reached line with click; stop walking tree */
|
|
|
|
return NSERROR_OK;
|
2013-05-31 16:24:27 +04:00
|
|
|
}
|
2017-03-01 02:41:48 +03:00
|
|
|
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2017-03-01 02:41:48 +03:00
|
|
|
void
|
|
|
|
treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y)
|
2013-05-31 16:24:27 +04:00
|
|
|
{
|
2013-08-17 22:39:17 +04:00
|
|
|
struct rect r;
|
2013-07-01 13:37:40 +04:00
|
|
|
bool redraw = false;
|
2017-09-09 20:45:22 +03:00
|
|
|
int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ?
|
|
|
|
tree_g.line_height : 0;
|
2013-07-01 13:37:40 +04:00
|
|
|
|
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
2013-05-31 16:24:27 +04:00
|
|
|
|
2017-09-27 19:47:34 +03:00
|
|
|
/* Not interested in whether mouse leaves window. */
|
|
|
|
if (mouse == BROWSER_MOUSE_LEAVE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-19 17:23:44 +04:00
|
|
|
/* Handle mouse drag captured by textarea */
|
|
|
|
if (tree->drag.type == TV_DRAG_TEXTAREA) {
|
|
|
|
textarea_mouse_action(tree->edit.textarea, mouse,
|
2017-03-01 02:41:48 +03:00
|
|
|
x - tree->edit.x, y - tree->edit.y);
|
2013-08-19 17:23:44 +04:00
|
|
|
return;
|
2017-09-14 22:13:44 +03:00
|
|
|
} else if (tree->drag.type == TV_DRAG_SEARCH ||
|
|
|
|
(y < search_height &&
|
|
|
|
tree->drag.type == TV_DRAG_NONE)) {
|
2017-09-09 23:24:29 +03:00
|
|
|
if (tree->search.active == false) {
|
|
|
|
tree->search.active = true;
|
|
|
|
if (treeview_clear_selection(tree, &r)) {
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2017-09-09 23:24:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
textarea_mouse_action(tree->search.textarea, mouse,
|
|
|
|
x - tree_g.window_padding - tree_g.icon_size,
|
|
|
|
y);
|
|
|
|
return;
|
|
|
|
} else if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) &&
|
|
|
|
tree->search.active == true) {
|
|
|
|
|
|
|
|
tree->search.active = false;
|
|
|
|
textarea_set_caret(tree->search.textarea, -1);
|
2017-09-14 22:14:28 +03:00
|
|
|
r.x0 = 0;
|
|
|
|
r.y0 = 0;
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
r.y1 = tree_g.line_height;
|
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-19 17:23:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle textarea related mouse action */
|
|
|
|
if (tree->edit.textarea != NULL) {
|
|
|
|
int ta_x = x - tree->edit.x;
|
|
|
|
int ta_y = y - tree->edit.y;
|
|
|
|
|
|
|
|
if (ta_x > 0 && ta_x < tree->edit.w &&
|
2017-03-01 02:41:48 +03:00
|
|
|
ta_y > 0 && ta_y < tree->edit.h) {
|
2013-08-19 17:23:44 +04:00
|
|
|
/* Inside textarea */
|
|
|
|
textarea_mouse_action(tree->edit.textarea, mouse,
|
2017-03-01 02:41:48 +03:00
|
|
|
ta_x, ta_y);
|
2013-08-19 17:23:44 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
} else if (mouse != BROWSER_MOUSE_HOVER) {
|
|
|
|
/* Action outside textarea */
|
|
|
|
treeview_edit_cancel(tree, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-16 15:23:37 +04:00
|
|
|
/* Handle drag ends */
|
|
|
|
if (mouse == BROWSER_MOUSE_HOVER) {
|
|
|
|
switch (tree->drag.type) {
|
|
|
|
case TV_DRAG_SELECTION:
|
|
|
|
treeview_commit_selection_drag(tree);
|
|
|
|
tree->drag.type = TV_DRAG_NONE;
|
|
|
|
tree->drag.start_node = NULL;
|
|
|
|
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_drag_status(tree, CORE_WINDOW_DRAG_NONE);
|
2013-08-16 15:23:37 +04:00
|
|
|
return;
|
|
|
|
case TV_DRAG_MOVE:
|
2013-08-17 22:39:17 +04:00
|
|
|
treeview_move_selection(tree, &r);
|
2013-08-16 23:06:08 +04:00
|
|
|
tree->drag.type = TV_DRAG_NONE;
|
|
|
|
tree->drag.start_node = NULL;
|
|
|
|
|
2013-08-17 00:40:45 +04:00
|
|
|
tree->move.target = NULL;
|
|
|
|
tree->move.target_pos = TV_TARGET_NONE;
|
2013-08-16 23:06:08 +04:00
|
|
|
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_drag_status(tree, CORE_WINDOW_DRAG_NONE);
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-08-16 23:06:08 +04:00
|
|
|
return;
|
2013-08-16 15:23:37 +04:00
|
|
|
default:
|
|
|
|
/* No drag to end */
|
|
|
|
break;
|
|
|
|
}
|
2013-06-18 17:58:43 +04:00
|
|
|
}
|
|
|
|
|
2017-09-14 22:07:42 +03:00
|
|
|
if (y > treeview__get_display_height(tree) + search_height) {
|
2013-07-01 13:37:40 +04:00
|
|
|
/* Below tree */
|
|
|
|
|
|
|
|
r.x0 = 0;
|
|
|
|
r.x1 = REDRAW_MAX;
|
|
|
|
|
2013-08-16 14:15:16 +04:00
|
|
|
/* Record what position / part a drag started on */
|
2013-07-01 13:37:40 +04:00
|
|
|
if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) &&
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->drag.type == TV_DRAG_NONE) {
|
2013-07-01 13:37:40 +04:00
|
|
|
tree->drag.selected = false;
|
|
|
|
tree->drag.start_node = NULL;
|
2013-08-16 14:15:16 +04:00
|
|
|
tree->drag.part = TV_NODE_PART_NONE;
|
2013-07-01 13:37:40 +04:00
|
|
|
tree->drag.start.x = x;
|
|
|
|
tree->drag.start.y = y;
|
|
|
|
tree->drag.start.node_y = tree->root->height;
|
|
|
|
tree->drag.start.node_h = 0;
|
|
|
|
|
|
|
|
tree->drag.prev.x = x;
|
|
|
|
tree->drag.prev.y = y;
|
|
|
|
tree->drag.prev.node_y = tree->root->height;
|
|
|
|
tree->drag.prev.node_h = 0;
|
|
|
|
}
|
2013-05-31 16:24:27 +04:00
|
|
|
|
2013-07-01 13:37:40 +04:00
|
|
|
/* Handle drag start */
|
|
|
|
if (tree->drag.type == TV_DRAG_NONE) {
|
|
|
|
if (mouse & BROWSER_MOUSE_DRAG_1 &&
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->drag.selected == false &&
|
|
|
|
tree->drag.part == TV_NODE_PART_NONE) {
|
2013-07-01 13:37:40 +04:00
|
|
|
tree->drag.type = TV_DRAG_SELECTION;
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_drag_status(tree,
|
2013-08-16 11:24:15 +04:00
|
|
|
CORE_WINDOW_DRAG_SELECTION);
|
2013-07-01 13:37:40 +04:00
|
|
|
} else if (mouse & BROWSER_MOUSE_DRAG_2) {
|
|
|
|
tree->drag.type = TV_DRAG_SELECTION;
|
2016-12-29 17:39:44 +03:00
|
|
|
treeview__cw_drag_status(tree,
|
2013-08-16 11:24:15 +04:00
|
|
|
CORE_WINDOW_DRAG_SELECTION);
|
2013-07-01 13:37:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tree->drag.start_node != NULL &&
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->drag.type == TV_DRAG_SELECTION) {
|
2013-07-01 13:37:40 +04:00
|
|
|
tree->drag.start_node->flags ^=
|
2017-03-01 02:41:48 +03:00
|
|
|
TV_NFLAGS_SELECTED;
|
2013-07-01 13:37:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle selection drags */
|
|
|
|
if (tree->drag.type == TV_DRAG_SELECTION) {
|
|
|
|
int curr_y1 = tree->root->height;
|
|
|
|
int prev_y1 = tree->drag.prev.node_y +
|
2017-03-01 02:41:48 +03:00
|
|
|
tree->drag.prev.node_h;
|
2013-07-01 13:37:40 +04:00
|
|
|
|
|
|
|
r.y0 = tree->drag.prev.node_y;
|
|
|
|
r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1;
|
|
|
|
|
|
|
|
redraw = true;
|
|
|
|
|
|
|
|
tree->drag.prev.x = x;
|
|
|
|
tree->drag.prev.y = y;
|
|
|
|
tree->drag.prev.node_y = curr_y1;
|
|
|
|
tree->drag.prev.node_h = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mouse & BROWSER_MOUSE_PRESS_1) {
|
|
|
|
/* Clear any existing selection */
|
|
|
|
redraw |= treeview_clear_selection(tree, &r);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redraw) {
|
2017-09-11 20:29:54 +03:00
|
|
|
treeview__cw_invalidate_area(tree, &r);
|
2013-07-01 13:37:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* On tree */
|
|
|
|
struct treeview_mouse_action ma;
|
|
|
|
|
|
|
|
ma.tree = tree;
|
|
|
|
ma.mouse = mouse;
|
|
|
|
ma.x = x;
|
|
|
|
ma.y = y;
|
2017-09-09 20:45:22 +03:00
|
|
|
ma.current_y = search_height;
|
2013-07-01 13:37:40 +04:00
|
|
|
|
2017-09-14 22:10:30 +03:00
|
|
|
treeview_walk_internal(tree, tree->root,
|
|
|
|
TREEVIEW_WALK_MODE_DISPLAY, NULL,
|
|
|
|
treeview_node_mouse_action_cb, &ma);
|
2013-07-01 13:37:40 +04:00
|
|
|
}
|
2013-05-31 16:24:27 +04:00
|
|
|
}
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-08-28 17:12:30 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
|
|
|
int treeview_get_height(treeview *tree)
|
|
|
|
{
|
2017-09-10 13:55:25 +03:00
|
|
|
int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ?
|
|
|
|
tree_g.line_height : 0;
|
2017-09-14 22:07:42 +03:00
|
|
|
int height = treeview__get_display_height(tree);
|
2017-09-10 13:55:25 +03:00
|
|
|
|
2013-08-28 17:12:30 +04:00
|
|
|
assert(tree != NULL);
|
|
|
|
assert(tree->root != NULL);
|
|
|
|
|
2017-09-14 22:07:42 +03:00
|
|
|
treeview__cw_update_size(tree, -1, height);
|
2013-09-03 17:38:01 +04:00
|
|
|
|
2017-09-14 22:07:42 +03:00
|
|
|
return height + search_height;
|
2013-08-28 17:12:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 20:08:35 +04:00
|
|
|
/**
|
|
|
|
* Initialise the plot styles from CSS system colour values.
|
2017-03-01 02:41:48 +03:00
|
|
|
*
|
|
|
|
* \param font_pt_size font size to use
|
2017-03-01 15:50:30 +03:00
|
|
|
* \return NSERROR_OK on success else appropriate error code
|
2013-06-18 20:08:35 +04:00
|
|
|
*/
|
2017-03-01 15:50:30 +03:00
|
|
|
static nserror treeview_init_plot_styles(int font_pt_size)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
2017-03-01 15:50:30 +03:00
|
|
|
nserror res;
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Background colour */
|
|
|
|
plot_style_even.bg.stroke_type = PLOT_OP_TYPE_NONE;
|
|
|
|
plot_style_even.bg.stroke_width = 0;
|
|
|
|
plot_style_even.bg.stroke_colour = 0;
|
|
|
|
plot_style_even.bg.fill_type = PLOT_OP_TYPE_SOLID;
|
2017-03-01 15:50:30 +03:00
|
|
|
res = ns_system_colour_char("Window", &plot_style_even.bg.fill_colour);
|
|
|
|
if (res != NSERROR_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
/* Text colour */
|
|
|
|
plot_style_even.text.family = PLOT_FONT_FAMILY_SANS_SERIF;
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
plot_style_even.text.size = font_pt_size;
|
2013-05-31 13:05:15 +04:00
|
|
|
plot_style_even.text.weight = 400;
|
|
|
|
plot_style_even.text.flags = FONTF_NONE;
|
2017-03-01 15:50:30 +03:00
|
|
|
res = ns_system_colour_char("WindowText", &plot_style_even.text.foreground);
|
|
|
|
if (res != NSERROR_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
res = ns_system_colour_char("Window", &plot_style_even.text.background);
|
|
|
|
if (res != NSERROR_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-04 14:15:44 +04:00
|
|
|
/* Entry field text colour */
|
|
|
|
plot_style_even.itext = plot_style_even.text;
|
|
|
|
plot_style_even.itext.foreground = mix_colour(
|
2017-03-01 02:41:48 +03:00
|
|
|
plot_style_even.text.foreground,
|
2017-03-01 15:50:30 +03:00
|
|
|
plot_style_even.text.background,
|
|
|
|
255 * 10 / 16);
|
2013-06-04 14:15:44 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
/* Selected background colour */
|
|
|
|
plot_style_even.sbg = plot_style_even.bg;
|
2017-03-01 15:50:30 +03:00
|
|
|
res = ns_system_colour_char("Highlight", &plot_style_even.sbg.fill_colour);
|
|
|
|
if (res != NSERROR_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
/* Selected text colour */
|
|
|
|
plot_style_even.stext = plot_style_even.text;
|
2017-03-01 15:50:30 +03:00
|
|
|
res = ns_system_colour_char("HighlightText", &plot_style_even.stext.foreground);
|
|
|
|
if (res != NSERROR_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
res = ns_system_colour_char("Highlight", &plot_style_even.stext.background);
|
|
|
|
if (res != NSERROR_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-06-04 14:15:44 +04:00
|
|
|
/* Selected entry field text colour */
|
|
|
|
plot_style_even.sitext = plot_style_even.stext;
|
|
|
|
plot_style_even.sitext.foreground = mix_colour(
|
2017-03-01 02:41:48 +03:00
|
|
|
plot_style_even.stext.foreground,
|
2017-03-01 15:50:30 +03:00
|
|
|
plot_style_even.stext.background,
|
|
|
|
255 * 25 / 32);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
/* Odd numbered node styles */
|
|
|
|
plot_style_odd.bg = plot_style_even.bg;
|
|
|
|
plot_style_odd.bg.fill_colour = mix_colour(
|
2017-03-01 02:41:48 +03:00
|
|
|
plot_style_even.bg.fill_colour,
|
|
|
|
plot_style_even.text.foreground, 255 * 15 / 16);
|
2013-05-31 13:05:15 +04:00
|
|
|
plot_style_odd.text = plot_style_even.text;
|
|
|
|
plot_style_odd.text.background = plot_style_odd.bg.fill_colour;
|
2013-06-04 14:15:44 +04:00
|
|
|
plot_style_odd.itext = plot_style_odd.text;
|
|
|
|
plot_style_odd.itext.foreground = mix_colour(
|
2017-03-01 02:41:48 +03:00
|
|
|
plot_style_odd.text.foreground,
|
|
|
|
plot_style_odd.text.background, 255 * 10 / 16);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
plot_style_odd.sbg = plot_style_even.sbg;
|
|
|
|
plot_style_odd.stext = plot_style_even.stext;
|
2013-06-04 14:15:44 +04:00
|
|
|
plot_style_odd.sitext = plot_style_even.sitext;
|
2017-03-01 15:50:30 +03:00
|
|
|
|
|
|
|
return NSERROR_OK;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-01 15:50:30 +03:00
|
|
|
* Callback for hlcache retrieving resources.
|
2017-03-01 02:41:48 +03:00
|
|
|
*
|
|
|
|
* \param handle content hlcache handle
|
2017-03-01 15:50:30 +03:00
|
|
|
* \param event The event that occurred on the content
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param pw treeview resource context
|
2013-05-31 13:05:15 +04:00
|
|
|
*/
|
2016-06-06 10:59:23 +03:00
|
|
|
static nserror
|
|
|
|
treeview_res_cb(struct hlcache_handle *handle,
|
|
|
|
const hlcache_event *event,
|
|
|
|
void *pw)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
|
|
|
struct treeview_resource *r = pw;
|
|
|
|
|
|
|
|
switch (event->type) {
|
|
|
|
case CONTENT_MSG_READY:
|
|
|
|
case CONTENT_MSG_DONE:
|
|
|
|
r->ready = true;
|
|
|
|
r->height = content_get_height(handle);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 20:08:35 +04:00
|
|
|
/**
|
|
|
|
* Fetch content resources used by treeview.
|
|
|
|
*/
|
2013-05-31 13:05:15 +04:00
|
|
|
static void treeview_init_resources(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < TREE_RES_LAST; i++) {
|
|
|
|
nsurl *url;
|
2016-12-28 18:00:58 +03:00
|
|
|
treeview_res[i].ready = false;
|
|
|
|
treeview_res[i].height = 0;
|
2013-05-31 13:05:15 +04:00
|
|
|
if (nsurl_create(treeview_res[i].url, &url) == NSERROR_OK) {
|
|
|
|
hlcache_handle_retrieve(url, 0, NULL, NULL,
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_res_cb,
|
|
|
|
&(treeview_res[i]), NULL,
|
|
|
|
CONTENT_IMAGE,
|
|
|
|
&(treeview_res[i].c));
|
2013-05-31 13:05:15 +04:00
|
|
|
nsurl_unref(url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 20:08:35 +04:00
|
|
|
/**
|
2013-10-03 02:34:34 +04:00
|
|
|
* Create a right-pointing anti-aliased triangle bitmap
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param bg background colour
|
|
|
|
* \param fg foreground colour
|
|
|
|
* \param size required bitmap size
|
2013-06-18 20:08:35 +04:00
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static struct bitmap *
|
|
|
|
treeview_generate_triangle_bitmap(colour bg, colour fg, int size)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
2013-10-03 02:34:34 +04:00
|
|
|
struct bitmap *b = NULL;
|
|
|
|
int x, y;
|
|
|
|
unsigned char *rpos;
|
|
|
|
unsigned char *pos;
|
|
|
|
size_t stride;
|
|
|
|
|
|
|
|
/* Set up required colour graduations. Ignores screen gamma. */
|
|
|
|
colour colour0 = bg;
|
|
|
|
colour colour1 = mix_colour(bg, fg, 255 * 3 / 4);
|
|
|
|
colour colour2 = blend_colour(bg, fg);
|
|
|
|
colour colour3 = mix_colour(bg, fg, 255 * 1 / 4);
|
|
|
|
colour colour4 = fg;
|
|
|
|
|
|
|
|
/* Create the bitmap */
|
2015-04-14 01:19:04 +03:00
|
|
|
b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
|
2013-10-03 02:34:34 +04:00
|
|
|
if (b == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
rpos = guit->bitmap->get_buffer(b);
|
|
|
|
stride = guit->bitmap->get_rowstride(b);
|
2013-10-03 02:34:34 +04:00
|
|
|
|
|
|
|
/* Draw the triangle */
|
|
|
|
for (y = 0; y < size; y++) {
|
|
|
|
pos = rpos;
|
|
|
|
|
|
|
|
if (y < size / 2) {
|
|
|
|
/* Top half */
|
|
|
|
for (x = 0; x < y * 2; x++) {
|
|
|
|
*(pos++) = red_from_colour(colour4);
|
|
|
|
*(pos++) = green_from_colour(colour4);
|
|
|
|
*(pos++) = blue_from_colour(colour4);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
}
|
|
|
|
*(pos++) = red_from_colour(colour3);
|
|
|
|
*(pos++) = green_from_colour(colour3);
|
|
|
|
*(pos++) = blue_from_colour(colour3);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
*(pos++) = red_from_colour(colour1);
|
|
|
|
*(pos++) = green_from_colour(colour1);
|
|
|
|
*(pos++) = blue_from_colour(colour1);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
for (x = y * 2 + 2; x < size ; x++) {
|
|
|
|
*(pos++) = red_from_colour(colour0);
|
|
|
|
*(pos++) = green_from_colour(colour0);
|
|
|
|
*(pos++) = blue_from_colour(colour0);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
}
|
|
|
|
} else if ((y == size / 2) && (size & 0x1)) {
|
|
|
|
/* Middle row */
|
|
|
|
for (x = 0; x < size - 1; x++) {
|
|
|
|
*(pos++) = red_from_colour(colour4);
|
|
|
|
*(pos++) = green_from_colour(colour4);
|
|
|
|
*(pos++) = blue_from_colour(colour4);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
}
|
|
|
|
*(pos++) = red_from_colour(colour2);
|
|
|
|
*(pos++) = green_from_colour(colour2);
|
|
|
|
*(pos++) = blue_from_colour(colour2);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
} else {
|
|
|
|
/* Bottom half */
|
|
|
|
for (x = 0; x < (size - y - 1) * 2; x++) {
|
|
|
|
*(pos++) = red_from_colour(colour4);
|
|
|
|
*(pos++) = green_from_colour(colour4);
|
|
|
|
*(pos++) = blue_from_colour(colour4);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
}
|
|
|
|
*(pos++) = red_from_colour(colour3);
|
|
|
|
*(pos++) = green_from_colour(colour3);
|
|
|
|
*(pos++) = blue_from_colour(colour3);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
*(pos++) = red_from_colour(colour1);
|
|
|
|
*(pos++) = green_from_colour(colour1);
|
|
|
|
*(pos++) = blue_from_colour(colour1);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
for (x = (size - y) * 2; x < size ; x++) {
|
|
|
|
*(pos++) = red_from_colour(colour0);
|
|
|
|
*(pos++) = green_from_colour(colour0);
|
|
|
|
*(pos++) = blue_from_colour(colour0);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2013-10-03 02:34:34 +04:00
|
|
|
}
|
|
|
|
}
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
rpos += stride;
|
|
|
|
}
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
guit->bitmap->modified(b);
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
return b;
|
|
|
|
}
|
2013-10-02 16:34:51 +04:00
|
|
|
|
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
/**
|
|
|
|
* Create bitmap copy of another bitmap
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param orig bitmap to copy
|
|
|
|
* \param size required bitmap size
|
2013-10-03 02:34:34 +04:00
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static struct bitmap *
|
|
|
|
treeview_generate_copy_bitmap(struct bitmap *orig, int size)
|
2013-10-03 02:34:34 +04:00
|
|
|
{
|
|
|
|
struct bitmap *b = NULL;
|
|
|
|
unsigned char *data;
|
|
|
|
unsigned char *orig_data;
|
|
|
|
size_t stride;
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
if (orig == NULL)
|
|
|
|
return NULL;
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
assert(size == guit->bitmap->get_width(orig));
|
|
|
|
assert(size == guit->bitmap->get_height(orig));
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
/* Create the bitmap */
|
2015-04-14 01:19:04 +03:00
|
|
|
b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
|
2013-10-03 02:34:34 +04:00
|
|
|
if (b == NULL)
|
|
|
|
return NULL;
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
stride = guit->bitmap->get_rowstride(b);
|
|
|
|
assert(stride == guit->bitmap->get_rowstride(orig));
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
data = guit->bitmap->get_buffer(b);
|
|
|
|
orig_data = guit->bitmap->get_buffer(orig);
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
/* Copy the bitmap */
|
|
|
|
memcpy(data, orig_data, stride * size);
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
guit->bitmap->modified(b);
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-10-03 14:45:55 +04:00
|
|
|
/* We've not modified the original image, but we called
|
|
|
|
* bitmap_get_buffer(), so we need to pair that with a
|
|
|
|
* bitmap_modified() call to appease certain front ends. */
|
2015-04-14 01:19:04 +03:00
|
|
|
guit->bitmap->modified(orig);
|
2013-10-03 14:45:55 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
return b;
|
|
|
|
}
|
2013-10-02 16:34:51 +04:00
|
|
|
|
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
/**
|
|
|
|
* Create bitmap from rotation of another bitmap
|
|
|
|
*
|
2017-03-01 02:41:48 +03:00
|
|
|
* \param orig bitmap to create rotation of
|
|
|
|
* \param size required bitmap size
|
2013-10-03 02:34:34 +04:00
|
|
|
*/
|
2017-03-01 02:41:48 +03:00
|
|
|
static struct bitmap *
|
|
|
|
treeview_generate_rotate_bitmap(struct bitmap *orig, int size)
|
2013-10-03 02:34:34 +04:00
|
|
|
{
|
|
|
|
struct bitmap *b = NULL;
|
|
|
|
int x, y;
|
|
|
|
unsigned char *rpos;
|
|
|
|
unsigned char *pos;
|
|
|
|
unsigned char *orig_data;
|
|
|
|
unsigned char *orig_pos;
|
|
|
|
size_t stride;
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
if (orig == NULL)
|
|
|
|
return NULL;
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
assert(size == guit->bitmap->get_width(orig));
|
|
|
|
assert(size == guit->bitmap->get_height(orig));
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
/* Create the bitmap */
|
2015-04-14 01:19:04 +03:00
|
|
|
b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
|
2013-10-03 02:34:34 +04:00
|
|
|
if (b == NULL)
|
|
|
|
return NULL;
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
stride = guit->bitmap->get_rowstride(b);
|
|
|
|
assert(stride == guit->bitmap->get_rowstride(orig));
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
rpos = guit->bitmap->get_buffer(b);
|
|
|
|
orig_data = guit->bitmap->get_buffer(orig);
|
2013-10-02 16:34:51 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
/* Copy the rotated bitmap */
|
|
|
|
for (y = 0; y < size; y++) {
|
|
|
|
pos = rpos;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
for (x = 0; x < size; x++) {
|
|
|
|
orig_pos = orig_data + x * stride + y * 4;
|
|
|
|
*(pos++) = *(orig_pos++);
|
|
|
|
*(pos++) = *(orig_pos++);
|
|
|
|
*(pos++) = *(orig_pos);
|
2013-10-05 14:44:15 +04:00
|
|
|
*(pos++) = 0xff;
|
2017-03-01 02:41:48 +03:00
|
|
|
|
2013-10-02 16:34:51 +04:00
|
|
|
}
|
2013-10-03 02:34:34 +04:00
|
|
|
|
|
|
|
rpos += stride;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
guit->bitmap->modified(b);
|
2013-10-03 02:34:34 +04:00
|
|
|
|
2013-10-03 14:45:55 +04:00
|
|
|
/* We've not modified the original image, but we called
|
|
|
|
* bitmap_get_buffer(), so we need to pair that with a
|
2015-04-14 01:19:04 +03:00
|
|
|
* bitmap_modified() call to appease certain front ends.
|
|
|
|
*/
|
|
|
|
guit->bitmap->modified(orig);
|
2013-10-03 14:45:55 +04:00
|
|
|
|
2013-10-03 02:34:34 +04:00
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Measures width of characters used to represent treeview furniture.
|
2017-03-01 02:41:48 +03:00
|
|
|
*
|
|
|
|
* \return NSERROR_OK on success else error code
|
2013-10-03 02:34:34 +04:00
|
|
|
*/
|
|
|
|
static nserror treeview_init_furniture(void)
|
|
|
|
{
|
|
|
|
int size = tree_g.line_height / 2;
|
|
|
|
|
|
|
|
plot_style_odd.furn[TREE_FURN_EXPAND].size = size;
|
|
|
|
plot_style_odd.furn[TREE_FURN_EXPAND].bmp =
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_generate_triangle_bitmap(
|
2013-10-03 02:34:34 +04:00
|
|
|
plot_style_odd.bg.fill_colour,
|
|
|
|
plot_style_odd.itext.foreground, size);
|
|
|
|
plot_style_odd.furn[TREE_FURN_EXPAND].sel =
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_generate_triangle_bitmap(
|
2013-10-03 02:34:34 +04:00
|
|
|
plot_style_odd.sbg.fill_colour,
|
|
|
|
plot_style_odd.sitext.foreground, size);
|
|
|
|
|
|
|
|
plot_style_even.furn[TREE_FURN_EXPAND].size = size;
|
|
|
|
plot_style_even.furn[TREE_FURN_EXPAND].bmp =
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_generate_triangle_bitmap(
|
2013-10-03 02:34:34 +04:00
|
|
|
plot_style_even.bg.fill_colour,
|
|
|
|
plot_style_even.itext.foreground, size);
|
|
|
|
plot_style_even.furn[TREE_FURN_EXPAND].sel =
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_generate_copy_bitmap(
|
2013-10-03 02:34:34 +04:00
|
|
|
plot_style_odd.furn[TREE_FURN_EXPAND].sel, size);
|
|
|
|
|
|
|
|
plot_style_odd.furn[TREE_FURN_CONTRACT].size = size;
|
|
|
|
plot_style_odd.furn[TREE_FURN_CONTRACT].bmp =
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_generate_rotate_bitmap(
|
2013-10-03 02:34:34 +04:00
|
|
|
plot_style_odd.furn[TREE_FURN_EXPAND].bmp, size);
|
|
|
|
plot_style_odd.furn[TREE_FURN_CONTRACT].sel =
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_generate_rotate_bitmap(
|
2013-10-03 02:34:34 +04:00
|
|
|
plot_style_odd.furn[TREE_FURN_EXPAND].sel, size);
|
|
|
|
|
|
|
|
plot_style_even.furn[TREE_FURN_CONTRACT].size = size;
|
|
|
|
plot_style_even.furn[TREE_FURN_CONTRACT].bmp =
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_generate_rotate_bitmap(
|
2013-10-03 02:34:34 +04:00
|
|
|
plot_style_even.furn[TREE_FURN_EXPAND].bmp, size);
|
|
|
|
plot_style_even.furn[TREE_FURN_CONTRACT].sel =
|
2017-03-01 02:41:48 +03:00
|
|
|
treeview_generate_rotate_bitmap(
|
2013-10-03 02:34:34 +04:00
|
|
|
plot_style_even.furn[TREE_FURN_EXPAND].sel, size);
|
|
|
|
|
|
|
|
if (plot_style_odd.furn[TREE_FURN_EXPAND].bmp == NULL ||
|
2017-03-01 02:41:48 +03:00
|
|
|
plot_style_odd.furn[TREE_FURN_EXPAND].sel == NULL ||
|
|
|
|
plot_style_even.furn[TREE_FURN_EXPAND].bmp == NULL ||
|
|
|
|
plot_style_even.furn[TREE_FURN_EXPAND].sel == NULL ||
|
|
|
|
plot_style_odd.furn[TREE_FURN_CONTRACT].bmp == NULL ||
|
|
|
|
plot_style_odd.furn[TREE_FURN_CONTRACT].sel == NULL ||
|
|
|
|
plot_style_even.furn[TREE_FURN_CONTRACT].bmp == NULL ||
|
|
|
|
plot_style_even.furn[TREE_FURN_CONTRACT].sel == NULL)
|
2013-10-03 02:34:34 +04:00
|
|
|
return NSERROR_NOMEM;
|
|
|
|
|
2013-10-02 16:34:51 +04:00
|
|
|
tree_g.furniture_width = size + tree_g.line_height / 4;
|
2013-10-03 02:34:34 +04:00
|
|
|
|
|
|
|
return NSERROR_OK;
|
2013-05-31 13:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
nserror treeview_init(void)
|
2013-05-31 13:05:15 +04:00
|
|
|
{
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
long long font_px_size;
|
|
|
|
long long font_pt_size;
|
2017-03-01 15:50:30 +03:00
|
|
|
nserror res;
|
2013-06-04 00:56:18 +04:00
|
|
|
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
if (tree_g.initialised > 0) {
|
|
|
|
tree_g.initialised++;
|
2013-09-09 16:00:24 +04:00
|
|
|
return NSERROR_OK;
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
}
|
2013-09-09 16:00:24 +04:00
|
|
|
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Initialising treeview module");
|
2013-10-12 01:41:09 +04:00
|
|
|
|
2016-08-11 10:13:51 +03:00
|
|
|
font_pt_size = nsoption_int(treeview_font_size);
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
if (font_pt_size <= 0) {
|
|
|
|
font_pt_size = 11 * 10;
|
|
|
|
}
|
2013-09-09 16:11:36 +04:00
|
|
|
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
font_px_size = (font_pt_size * FIXTOINT(nscss_screen_dpi) /
|
|
|
|
10 + 36) / 72;
|
2013-10-02 16:34:51 +04:00
|
|
|
tree_g.line_height = (font_px_size * 8 + 3) / 6;
|
|
|
|
|
2018-05-23 13:48:35 +03:00
|
|
|
res = treeview_init_plot_styles(font_pt_size * PLOT_STYLE_SCALE / 10);
|
2017-03-01 15:50:30 +03:00
|
|
|
if (res != NSERROR_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
treeview_init_resources();
|
2017-03-01 15:50:30 +03:00
|
|
|
|
|
|
|
res = treeview_init_furniture();
|
|
|
|
if (res != NSERROR_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
2013-05-31 13:05:15 +04:00
|
|
|
|
|
|
|
tree_g.step_width = tree_g.furniture_width;
|
|
|
|
tree_g.window_padding = 6;
|
2013-08-17 14:48:16 +04:00
|
|
|
tree_g.icon_size = 17;
|
2013-05-31 13:05:15 +04:00
|
|
|
tree_g.icon_step = 23;
|
2013-08-16 23:06:08 +04:00
|
|
|
tree_g.move_offset = 18;
|
2013-05-31 13:05:15 +04:00
|
|
|
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
tree_g.initialised++;
|
2013-09-09 16:00:24 +04:00
|
|
|
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Initialised treeview module");
|
2013-10-12 01:41:09 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-18 19:08:21 +04:00
|
|
|
/* Exported interface, documented in treeview.h */
|
2013-05-31 13:05:15 +04:00
|
|
|
nserror treeview_fini(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
if (tree_g.initialised > 1) {
|
|
|
|
tree_g.initialised--;
|
|
|
|
return NSERROR_OK;
|
|
|
|
|
|
|
|
} else if (tree_g.initialised == 0) {
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO,
|
|
|
|
"Warning: tried to finalise uninitialised treeview module");
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|
|
|
|
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Finalising treeview module");
|
2013-10-12 01:41:09 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
for (i = 0; i < TREE_RES_LAST; i++) {
|
|
|
|
hlcache_handle_release(treeview_res[i].c);
|
|
|
|
}
|
|
|
|
|
2015-04-14 01:19:04 +03:00
|
|
|
guit->bitmap->destroy(plot_style_odd.furn[TREE_FURN_EXPAND].bmp);
|
|
|
|
guit->bitmap->destroy(plot_style_odd.furn[TREE_FURN_EXPAND].sel);
|
|
|
|
guit->bitmap->destroy(plot_style_even.furn[TREE_FURN_EXPAND].bmp);
|
|
|
|
guit->bitmap->destroy(plot_style_even.furn[TREE_FURN_EXPAND].sel);
|
|
|
|
guit->bitmap->destroy(plot_style_odd.furn[TREE_FURN_CONTRACT].bmp);
|
|
|
|
guit->bitmap->destroy(plot_style_odd.furn[TREE_FURN_CONTRACT].sel);
|
|
|
|
guit->bitmap->destroy(plot_style_even.furn[TREE_FURN_CONTRACT].bmp);
|
|
|
|
guit->bitmap->destroy(plot_style_even.furn[TREE_FURN_CONTRACT].sel);
|
2013-10-03 02:34:34 +04:00
|
|
|
|
Treeview: Rationalise initialisation and finalisation.
Previously the expected behaviour for front ends using the correct
API for hotlist, global history, cookie manager, and ssl cert
viewer was that the front end would initialise the treeview module
on startup and finalise it on application exit.
However, this meant that the front ends had to include the core
treeview header, which they didn't otherwise need.
Since the tree module provided access to the new treeview utilities
through the old tree API, and was used by front ends with no changes
for the new treeview API, the tree layer refcounted initialisations
of treeview-based widgets, and only called the underlying treeview
init/fini functions when needed.
This change moves that refcounting into the treeview module. Now
the hotlist, global history, cookie manager, and ssl cert viewer
widgets call call treeview init/fini as part of their own
initialisation and finalisation. This means that front ends
using the correct APIs for treeview-based widgets don't need to
know anything about the underlying treeview, and the tree module
compatibility layer has had its treeview refcounting removed.
Finally, the treeview_init function took a font size parameter.
Now it does not and lit gets font size from config. We probably
want to add a new `treeview_font_size` option to nsoptions, and
have differnent defaults on different platforms. 12pt on RISC OS,
and 11pt elsewhere, most likely.
2016-08-10 20:36:41 +03:00
|
|
|
tree_g.initialised--;
|
2013-09-09 16:00:24 +04:00
|
|
|
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Finalised treeview module");
|
2013-10-12 01:41:09 +04:00
|
|
|
|
2013-05-31 13:05:15 +04:00
|
|
|
return NSERROR_OK;
|
|
|
|
}
|