Initial autofilter support.

This commit is contained in:
John McNamara 2015-04-17 23:54:05 +01:00
parent 8f764c632b
commit 269442101c
10 changed files with 360 additions and 2 deletions

BIN
docs/images/autofilter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View file

@ -177,6 +177,14 @@ typedef struct lxw_print_area {
lxw_col_t last_col;
} lxw_print_area;
typedef struct lxw_autofilter {
uint8_t in_use;
lxw_row_t first_row;
lxw_row_t last_row;
lxw_col_t first_col;
lxw_col_t last_col;
} lxw_autofilter;
/**
* @brief Header and footer options.
*
@ -269,6 +277,7 @@ typedef struct lxw_worksheet {
struct lxw_repeat_rows repeat_rows;
struct lxw_repeat_cols repeat_cols;
struct lxw_print_area print_area;
struct lxw_autofilter autofilter;
uint16_t merged_range_count;
@ -894,6 +903,42 @@ uint8_t worksheet_merge_range(lxw_worksheet *worksheet, lxw_row_t first_row,
lxw_col_t last_col, const char *string,
lxw_format *format);
/**
* @brief Set the autofilter area in the worksheet.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param first_row The first row of the range. (All zero indexed.)
* @param first_col The first column of the range.
* @param last_row The last row of the range.
* @param last_col The last col of the range.
*
* @return 0 for success, non-zero on error.
*
* The `%worksheet_autofilter()` method allows an autofilter to be added to a
* worksheet.
*
* An autofilter is a way of adding drop down lists to the headers of a 2D
* range of worksheet data. This allows users to filter the data based on
* simple criteria so that some data is shown and some is hidden.
*
* @image html autofilter.png
*
* To add an autofilter to a worksheet:
*
* @code
* worksheet_autofilter(worksheet, 0, 0, 50, 3);
*
* // Same as above using the RANGE() macro.
* worksheet_autofilter(worksheet, RANGE("A1:D51"));
* @endcode
*
* Note: it isn't currently possible to apply filter conditions to the
* autofilter.
*/
uint8_t worksheet_autofilter(lxw_worksheet *worksheet, lxw_row_t first_row,
lxw_col_t first_col, lxw_row_t last_row,
lxw_col_t last_col);
/**
* @brief Make a worksheet the active, i.e., visible worksheet.
*

View file

@ -187,7 +187,9 @@ _write_app_file(lxw_packager *self)
/* Add the Named Ranges parts. */
TAILQ_FOREACH(defined_name, workbook->defined_names, list_pointers) {
tmp_name = strstr(defined_name->name, "_xlnm.ZZZ");
/*Ignore autofilters. */
tmp_name = strstr(defined_name->name, "FilterDatabase");
if (!tmp_name) {
_add_part_name(app, defined_name->app_name);

View file

@ -526,10 +526,33 @@ _prepare_defined_names(lxw_workbook *self)
STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
/*
* Check for autofilter settings.
*/
if (worksheet->autofilter.in_use) {
/* Autofilters are different to other defined names. They are
* hidden and thus not stored in the App file. This entry
* will be filtered out in the Packager module. */
strncat(app_name, "FilterDatabase", LXW_DEFINED_NAME_LENGTH - 1);
lxw_range_abs(area,
worksheet->autofilter.first_row,
worksheet->autofilter.first_col,
worksheet->autofilter.last_row,
worksheet->autofilter.last_col);
__builtin_snprintf(range, LXW_DEFINED_NAME_LENGTH - 1, "%s!%s",
worksheet->quoted_name, area);
_store_defined_name(self, "_xlnm._FilterDatabase", app_name,
range, worksheet->index, LXW_TRUE);
}
/*
* Check for Print Area settings.
*/
if (worksheet->print_area.in_use) {
else if (worksheet->print_area.in_use) {
__builtin_snprintf(app_name, LXW_DEFINED_NAME_LENGTH - 1,
"%s!Print_Area", worksheet->quoted_name);

View file

@ -1558,6 +1558,32 @@ _worksheet_write_col_breaks(lxw_worksheet *self)
_FREE_ATTRIBUTES();
}
/*
* Write the <autoFilter> element.
*/
STATIC void
_worksheet_write_auto_filter(lxw_worksheet *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
char range[MAX_CELL_RANGE_LENGTH];
if (!self->autofilter.in_use)
return;
lxw_range(range,
self->autofilter.first_row,
self->autofilter.first_col,
self->autofilter.last_row, self->autofilter.last_col);
_INIT_ATTRIBUTES();
_PUSH_ATTRIBUTES_STR("ref", range);
_xml_empty_tag(self->file, "autoFilter", &attributes);
_FREE_ATTRIBUTES();
}
/*
* Assemble and write the XML file.
*/
@ -1591,6 +1617,9 @@ _worksheet_assemble_xml_file(lxw_worksheet *self)
else
_worksheet_write_optimized_sheet_data(self);
/* Write the autoFilter element. */
_worksheet_write_auto_filter(self);
/* Write the mergeCells element. */
_worksheet_write_merge_cells(self);
@ -2129,6 +2158,49 @@ worksheet_merge_range(lxw_worksheet *self, lxw_row_t first_row,
return 0;
}
/*
* Set the autofilter area in the worksheet.
*/
uint8_t
worksheet_autofilter(lxw_worksheet *self, lxw_row_t first_row,
lxw_col_t first_col, lxw_row_t last_row,
lxw_col_t last_col)
{
lxw_row_t tmp_row;
lxw_col_t tmp_col;
int8_t err;
/* Excel doesn't allow a single cell to be merged */
if (first_row == last_row && first_col == last_col)
return 1;
/* Swap last row/col with first row/col as necessary */
if (first_row > last_row) {
tmp_row = last_row;
last_row = first_row;
first_row = tmp_row;
}
if (first_col > last_col) {
tmp_col = last_col;
last_col = first_col;
first_col = tmp_col;
}
/* Check that column number is valid and store the max value */
err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE);
if (err)
return err;
self->autofilter.in_use = LXW_TRUE;
self->autofilter.first_row = first_row;
self->autofilter.first_col = first_col;
self->autofilter.last_row = last_row;
self->autofilter.last_col = last_col;
return 0;
}
/*
* Set this worksheet as a selected worksheet, i.e. the worksheet has its tab
* highlighted.

View file

@ -0,0 +1,95 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2015, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = new_workbook("test_autofilter00.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
uint16_t i;
struct row {
char region[16];
char item[16];
int volume;
char month[16];
};
struct row data[] = {
{"East", "Apple", 9000, "July" },
{"East", "Apple", 5000, "July" },
{"South", "Orange", 9000, "September" },
{"North", "Apple", 2000, "November" },
{"West", "Apple", 9000, "November" },
{"South", "Pear", 7000, "October" },
{"North", "Pear", 9000, "August" },
{"West", "Orange", 1000, "December" },
{"West", "Grape", 1000, "November" },
{"South", "Pear", 10000, "April" },
{"West", "Grape", 6000, "January" },
{"South", "Orange", 3000, "May" },
{"North", "Apple", 3000, "December" },
{"South", "Apple", 7000, "February" },
{"West", "Grape", 1000, "December" },
{"East", "Grape", 8000, "February" },
{"South", "Grape", 10000, "June" },
{"West", "Pear", 7000, "December" },
{"South", "Apple", 2000, "October" },
{"East", "Grape", 7000, "December" },
{"North", "Grape", 6000, "April" },
{"East", "Pear", 8000, "February" },
{"North", "Apple", 7000, "August" },
{"North", "Orange", 7000, "July" },
{"North", "Apple", 6000, "June" },
{"South", "Grape", 8000, "September" },
{"West", "Apple", 3000, "October" },
{"South", "Orange", 10000, "November" },
{"West", "Grape", 4000, "July" },
{"North", "Orange", 5000, "August" },
{"East", "Orange", 1000, "November" },
{"East", "Orange", 4000, "October" },
{"North", "Grape", 5000, "August" },
{"East", "Apple", 1000, "December" },
{"South", "Apple", 10000, "March" },
{"East", "Grape", 7000, "October" },
{"West", "Grape", 1000, "September" },
{"East", "Grape", 10000, "October" },
{"South", "Orange", 8000, "March" },
{"North", "Apple", 4000, "July" },
{"South", "Orange", 5000, "July" },
{"West", "Apple", 4000, "June" },
{"East", "Apple", 5000, "April" },
{"North", "Pear", 3000, "August" },
{"East", "Grape", 9000, "November" },
{"North", "Orange", 8000, "October" },
{"East", "Apple", 10000, "June" },
{"South", "Pear", 1000, "December" },
{"North", "Grape", 10000, "July" },
{"East", "Grape", 6000, "February" }
};
/* Write the column headers. */
worksheet_write_string(worksheet, 0, 0, "Region", NULL);
worksheet_write_string(worksheet, 0, 1, "Item", NULL);
worksheet_write_string(worksheet, 0, 2, "Volume" , NULL);
worksheet_write_string(worksheet, 0, 3, "Month", NULL);
/* Write the row data. */
for (i = 0; i < sizeof(data)/sizeof(struct row); i++) {
worksheet_write_string(worksheet, i + 1, 0, data[i].region, NULL);
worksheet_write_string(worksheet, i + 1, 1, data[i].item, NULL);
worksheet_write_number(worksheet, i + 1, 2, data[i].volume , NULL);
worksheet_write_string(worksheet, i + 1, 3, data[i].month, NULL);
}
return workbook_close(workbook);
}

View file

@ -0,0 +1,99 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2015, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = new_workbook("test_autofilter01.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
uint16_t i;
struct row {
char region[16];
char item[16];
int volume;
char month[16];
};
struct row data[] = {
{"East", "Apple", 9000, "July" },
{"East", "Apple", 5000, "July" },
{"South", "Orange", 9000, "September" },
{"North", "Apple", 2000, "November" },
{"West", "Apple", 9000, "November" },
{"South", "Pear", 7000, "October" },
{"North", "Pear", 9000, "August" },
{"West", "Orange", 1000, "December" },
{"West", "Grape", 1000, "November" },
{"South", "Pear", 10000, "April" },
{"West", "Grape", 6000, "January" },
{"South", "Orange", 3000, "May" },
{"North", "Apple", 3000, "December" },
{"South", "Apple", 7000, "February" },
{"West", "Grape", 1000, "December" },
{"East", "Grape", 8000, "February" },
{"South", "Grape", 10000, "June" },
{"West", "Pear", 7000, "December" },
{"South", "Apple", 2000, "October" },
{"East", "Grape", 7000, "December" },
{"North", "Grape", 6000, "April" },
{"East", "Pear", 8000, "February" },
{"North", "Apple", 7000, "August" },
{"North", "Orange", 7000, "July" },
{"North", "Apple", 6000, "June" },
{"South", "Grape", 8000, "September" },
{"West", "Apple", 3000, "October" },
{"South", "Orange", 10000, "November" },
{"West", "Grape", 4000, "July" },
{"North", "Orange", 5000, "August" },
{"East", "Orange", 1000, "November" },
{"East", "Orange", 4000, "October" },
{"North", "Grape", 5000, "August" },
{"East", "Apple", 1000, "December" },
{"South", "Apple", 10000, "March" },
{"East", "Grape", 7000, "October" },
{"West", "Grape", 1000, "September" },
{"East", "Grape", 10000, "October" },
{"South", "Orange", 8000, "March" },
{"North", "Apple", 4000, "July" },
{"South", "Orange", 5000, "July" },
{"West", "Apple", 4000, "June" },
{"East", "Apple", 5000, "April" },
{"North", "Pear", 3000, "August" },
{"East", "Grape", 9000, "November" },
{"North", "Orange", 8000, "October" },
{"East", "Apple", 10000, "June" },
{"South", "Pear", 1000, "December" },
{"North", "Grape", 10000, "July" },
{"East", "Grape", 6000, "February" }
};
/* Write the column headers. */
worksheet_write_string(worksheet, 0, 0, "Region", NULL);
worksheet_write_string(worksheet, 0, 1, "Item", NULL);
worksheet_write_string(worksheet, 0, 2, "Volume" , NULL);
worksheet_write_string(worksheet, 0, 3, "Month", NULL);
/* Write the row data. */
for (i = 0; i < sizeof(data)/sizeof(struct row); i++) {
worksheet_write_string(worksheet, i + 1, 0, data[i].region, NULL);
worksheet_write_string(worksheet, i + 1, 1, data[i].item, NULL);
worksheet_write_number(worksheet, i + 1, 2, data[i].volume , NULL);
worksheet_write_string(worksheet, i + 1, 3, data[i].month, NULL);
}
worksheet_autofilter(worksheet, 0, 0, 50, 3);
return workbook_close(workbook);
}

View file

@ -0,0 +1,22 @@
###############################################################################
#
# Tests for libxlsxwriter.
#
# Copyright 2014-2015, John McNamara, jmcnamara@cpan.org
#
import base_test_class
class TestCompareXLSXFiles(base_test_class.XLSXBaseTest):
"""
Test file created with libxlsxwriter against a file created by Excel.
"""
def test_autofilter00(self):
self.run_exe_test('test_autofilter00')
def test_autofilter01(self):
self.run_exe_test('test_autofilter01')

Binary file not shown.

Binary file not shown.