Files
MiniGUI/src/ex_ctrl/gridview.c
T

2552 lines
80 KiB
C

///////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT NOTICE
//
// The following open source license statement does not apply to any
// entity in the Exception List published by FMSoft.
//
// For more information, please visit:
//
// https://www.fmsoft.cn/exception-list
//
//////////////////////////////////////////////////////////////////////////////
/*
* This file is part of MiniGUI, a mature cross-platform windowing
* and Graphics User Interface (GUI) support system for embedded systems
* and smart IoT devices.
*
* Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd.
* Copyright (C) 1998~2002, WEI Yongming
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Or,
*
* As this program is a library, any link to this program must follow
* GNU General Public License version 3 (GPLv3). If you cannot accept
* GPLv3, you need to be licensed from FMSoft.
*
* If you have got a commercial license of this program, please use it
* under the terms and conditions of the commercial license.
*
* For more information about the commercial license, please refer to
* <http://www.minigui.com/blog/minigui-licensing-policy/>.
*/
/*
** gridview.c: an grid control
**
** Created by Peng Ke at 2004/11/29
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "common.h"
#ifdef _MGCTRL_GRIDVIEW
#include "minigui.h"
#include "gdi.h"
#include "window.h"
#include "control.h"
#include "cliprect.h"
#include "internals.h"
#include "ctrlclass.h"
#include "ctrl/gridview.h"
#include "scrolled.h"
#include "gridview_impl.h"
static gvGridDataModal* GridDataModalArray_new_modal(int row, int col);
static void GridDataModalArray_delete_modal(gvGridDataModal* modal);
static int gvGridCellDep_add_dep(gvGridViewData* view, gvGridCellDepedence* dep);
static int gvGridCellDep_delete_dep(gvGridViewData* view, int id);
static int gvGridCellDep_update_dep(gvGridViewData* view, GRIDCELLS* cells);
static void update_viewport (gvGridViewData *view, int from, int to, int is_row)
{
RECT r;
GetClientRect(view->hCtrl, &r);
if (is_row) {
r.top = from;
ScrollWindow(view->hCtrl, 0, to, &r, NULL);
}
else {
r.left = from;
ScrollWindow(view->hCtrl, to, 0, &r, NULL);
}
UpdateInvalidClient(view->hCtrl, FALSE);
}
// scroll function {{{
static BOOL gridview_make_rect_visible(gvGridViewData* view, RECT* rect)
{
RECT r;
RECT r_client;
gvGridCellData* table;
int head_w, head_h;
table = view->modal->get_value_at(view, 0, 0);
head_w = table->data.table.width;
head_h = table->data.table.height;
GetClientRect(view->hCtrl, &r_client);
r = *rect;
scrolled_window_to_content(&view->scrdata, &r.right, &r.bottom);
scrolled_window_to_content(&view->scrdata, &r.left, &r.top);
r.left -= head_w;
r.top -= head_h;
if(RECTW(r) > RECTW(r_client))
r.right = r.left + RECTW(r_client);
if(RECTH(r) > RECTH(r_client))
r.bottom = r.top + RECTH(r_client);
scrolled_make_pos_visible(view->hCtrl, &view->scrdata, r.right-1, r.bottom-1);
scrolled_make_pos_visible(view->hCtrl, &view->scrdata, r.left, r.top);
return TRUE;
}
static void gridview_set_content(HWND hWnd, PSCRDATA pscrdata, BOOL visChanged)
{
gvGridViewData* view = NULL;
int w, h;
view = (gvGridViewData*) GetWindowAdditionalData2(hWnd);
w = view->ContX - pscrdata->nContX;
h = view->ContY - pscrdata->nContY;
view->ContX = pscrdata->nContX;
view->ContY = pscrdata->nContY;
if (w != 0)
update_viewport (view, view->cells_rect.left, w, 0);
if (h != 0)
update_viewport (view, view->cells_rect.top, h, 1);
}
// }}}
// static functions for row/column and xy {{{
static void update_row_col_end_xy(gvGridViewData* view, int count, int header_size, int is_row)
{
gvGridCellData* cell;
int cur_end_xy = header_size;
int i;
for(i=1; i<=count; i++)
{
if (is_row)
cell = view->modal->get_value_at(view, i, 0);
else
cell = view->modal->get_value_at(view, 0, i);
//MGU_Assert(cell != NULL);
if (cell->data.header.size < 0) {
cell->data.header.size = header_size;
cell->data.header.end_xy = cur_end_xy + header_size;
}
else
cell->data.header.end_xy = cur_end_xy + cell->data.header.size;
cur_end_xy = cell->data.header.end_xy;
}
if (is_row)
scrolled_set_cont_height(view->hCtrl, &view->scrdata, cur_end_xy);
else
scrolled_set_cont_width(view->hCtrl, &view->scrdata, cur_end_xy);
}
static void update_row_col_size(gvGridViewData* view)
{
gvGridCellData* cell;
int row, col;
row = view->modal->get_row_count(view);
col = view->modal->get_col_count(view);
cell = view->modal->get_value_at(view, 0, 0);
//MGU_Assert(cell != NULL);
if(cell->data.table.width <= 0)
cell->data.table.width = 40;
if(cell->data.table.height <= 0)
cell->data.table.height = 20;
GetClientRect(view->hCtrl, &view->cells_rect);
view->cells_rect.left += cell->data.table.width;
view->cells_rect.top += cell->data.table.height;
if(view->cells_rect.left > view->cells_rect.right)
view->cells_rect.right = view->cells_rect.left;
if(view->cells_rect.top > view->cells_rect.bottom)
view->cells_rect.bottom = view->cells_rect.top;
scrolled_set_visible (view->hCtrl, &view->scrdata);
update_row_col_end_xy(view, row, cell->data.table.height, 1);
update_row_col_end_xy(view, col, cell->data.table.width, 0);
}
static void get_cell_rect(gvGridViewData* view, int row, int col, RECT* r)
{
int row_count, col_count;
gvGridCellData* cell;
if(row<0 || col<0)
return;
row_count = view->modal->get_row_count(view);
col_count = view->modal->get_col_count(view);
if(row>row_count || col>col_count)
return;
switch(row)
{
case 0:
r->top = 0;
cell = view->modal->get_value_at(view, 0, 0);
r->bottom = cell->data.table.height;
break;
case 1:
cell = view->modal->get_value_at(view, 0, 0);
r->top = cell->data.table.height;
cell = view->modal->get_value_at(view, row, 0);
r->bottom = cell->data.header.end_xy;
break;
default:
cell = view->modal->get_value_at(view, row-1, 0);
r->top = cell->data.header.end_xy;
cell = view->modal->get_value_at(view, row, 0);
r->bottom = cell->data.header.end_xy;
break;
}
switch(col)
{
case 0:
r->left = 0;
cell = view->modal->get_value_at(view, 0, 0);
r->right = cell->data.table.width;
break;
case 1:
cell = view->modal->get_value_at(view, 0, 0);
r->left = cell->data.table.width;
cell = view->modal->get_value_at(view, 0, col);
r->right = cell->data.header.end_xy;
break;
default:
cell = view->modal->get_value_at(view, 0, col-1);
r->left = cell->data.header.end_xy;
cell = view->modal->get_value_at(view, 0, col);
r->right = cell->data.header.end_xy;
break;
}
// scroll rect
{
RECT r_tmp;
memcpy(&r_tmp, r, sizeof(RECT));
scrolled_content_to_window(&view->scrdata, &r_tmp.left, &r_tmp.top);
scrolled_content_to_window(&view->scrdata, &r_tmp.right, &r_tmp.bottom);
if(row!=0)
{
r->top = r_tmp.top;
r->bottom = r_tmp.bottom;
}
if(col!=0)
{
r->left = r_tmp.left;
r->right = r_tmp.right;
}
}
}
//TODO add scroll support
static void get_cell_from_xy(gvGridViewData* view, int x, int y, int* row, int* col)
{
int i;
int row_count, col_count;
gvGridCellData* table;
gvGridCellData* cell;
row_count = view->modal->get_row_count(view);
col_count = view->modal->get_col_count(view);
table = view->modal->get_value_at(view, 0, 0);
*row = -1;
*col = -1;
if(y < table->data.table.height)
*row = 0;
else
{
for(i=1; i<=row_count; i++)
{
cell = view->modal->get_value_at(view, i, 0);
//if(y < cell->data.header.end_xy)
if(y < (cell->data.header.end_xy - psvscr->nContY))
{
*row = i;
break;
}
}
}
if(x < table->data.table.width)
*col = 0;
else
{
for(i=1; i<=col_count; i++)
{
cell = view->modal->get_value_at(view, 0, i);
//if(x < cell->data.header.end_xy)
if(x < (cell->data.header.end_xy - psvscr->nContX))
{
*col = i;
break;
}
}
}
}
// }}}
// grid cells functions {{{
static int gvGridCells_get_start_stop(int begin, int width, int* start, int* stop)
{
if(width < -1)
{
begin = begin + width + 1;
width = - width;
}
*start = begin;
*stop = begin + width;
return 1;
}
int gvGridCells_is_in_cells(GRIDCELLS* cells, int row, int col)
{
int start, end;
/* header not select*/
if(row == 0 || col == 0)
return FALSE;
/* null select*/
if(cells->width == 0 || cells->height == 0)
return FALSE;
/* row region*/
gvGridCells_get_start_stop(cells->row, cells->height, &start, &end);
if(start <= end && !(row >= start && row < end))
return FALSE;
/* col region*/
gvGridCells_get_start_stop(cells->column, cells->width, &start, &end);
if(start <= end && !(col >= start && col < end))
return FALSE;
return TRUE;
}
static int gvGridCells_lineseg_intersect(int begin, int width, int begin2, int width2)
{
int start, stop;
int start2, stop2;
gvGridCells_get_start_stop(begin, width, &start, &stop);
gvGridCells_get_start_stop(begin2, width2, &start2, &stop2);
/* has null select*/
if(start == stop || start2 == stop2)
return FALSE;
/* has a all select*/
if(start > stop || start2 > stop2)
return TRUE;
if(start >= stop2 || stop <= start2)
return FALSE;
return TRUE;
}
int gvGridCells_intersect(GRIDCELLS* first, GRIDCELLS* second)
{
if(!gvGridCells_lineseg_intersect(first->row, first->height, second->row, second->height))
return FALSE;
if(!gvGridCells_lineseg_intersect(first->column, first->width, second->column, second->width))
return FALSE;
return TRUE;
}
static void gvGridCells_get_cells_rect(gvGridViewData* view, GRIDCELLS* cells, RECT* rect)
{
RECT rc_client;
RECT r;
int row, col, row_end, col_end;
if(cells->height == 0 || cells->width == 0)
{
SetRectEmpty(rect);
return;
}
GetClientRect(view->hCtrl, &rc_client);
get_cell_rect(view, 0, 0, &r);
rc_client.left = r.right;
rc_client.top = r.bottom;
gvGridCells_get_start_stop(cells->row, cells->height, &row, &row_end);
gvGridCells_get_start_stop(cells->column, cells->width, &col, &col_end);
if(row > row_end) /* select col*/
{
rect->top = rc_client.top;
rect->bottom = rc_client.bottom;
}
else
{
get_cell_rect(view, row, 0, &r);
rect->top = r.top;
if(row != row_end-1)
get_cell_rect(view, row_end-1, 0, &r);
rect->bottom = r.bottom;
}
if(col > col_end)
{
rect->left = rc_client.left;
rect->right = rc_client.right;
}
else
{
get_cell_rect(view, 0, col, &r);
rect->left = r.left;
if(col != col_end-1)
get_cell_rect(view, 0, col_end-1, &r);
rect->right = r.right;
}
}
// }}}
// set_cell_value {{{
static int set_cell_value(gvGridViewData* view, GRIDCELLDATA* data, int row, int col)
{
int need_set_value = FALSE;
gvGridCellData* cell = view->modal->get_value_at(view, row, col);
gvGridCellData newcell;
gvGridCellData celldata;
if(cell == NULL)
{
cell = &newcell;
need_set_value = TRUE;
}
switch (data->style & GV_TYPE_MASK)
{
case GV_TYPE_HEADER:
{
char *buff = NULL;
GRIDCELLDATAHEADER* header;
if(need_set_value)
gvGridCellTypeHeader->init(cell);
gvGridCellTypeHeader->init(&celldata);
celldata.mask = data->mask;
celldata.style |= data->style;
celldata.data.header.end_xy = -1;
celldata.data.header.color_bg = data->color_bg;
celldata.data.header.color_fg = data->color_fg;
celldata.data.header.image = data->image;
celldata.data.header.font = data->font;
header = (GRIDCELLDATAHEADER*)data->content;
celldata.data.header.size = header->size;
if (header->buff != NULL) {
int len;
len = MIN(strlen(header->buff), header->len_buff);
buff = (char *) malloc (sizeof(char)*(len + 1));
strncpy(buff, header->buff, len);
buff[len] = '\0';
}
celldata.data.header.text = buff;
cell->func->merge(cell, &celldata);
if (buff != NULL)
free (buff);
break;
}
case GV_TYPE_TEXT:
{
char *buff = NULL;
GRIDCELLDATATEXT* text;
if(need_set_value)
gvGridCellTypeText->init(cell);
gvGridCellTypeText->init(&celldata);
celldata.mask = data->mask;
celldata.style |= data->style;
celldata.data.text.color_bg = data->color_bg;
celldata.data.text.color_fg = data->color_fg;
celldata.data.text.image = data->image;
celldata.data.text.font = data->font;
text = (GRIDCELLDATATEXT*)data->content;
if (text->buff != NULL) {
int len;
len = MIN(strlen(text->buff), text->len_buff);
buff = (char *) malloc (sizeof(char)*(len + 1));
strncpy (buff, text->buff, len);
buff[len] = '\0';
}
celldata.data.text.text = buff;
cell->func->merge(cell, &celldata);
if (buff != NULL)
free (buff);
break;
}
case GV_TYPE_NUMBER:
{
char *buff = NULL;
GRIDCELLDATANUMBER* number;
if(need_set_value)
gvGridCellTypeNumber->init(cell);
gvGridCellTypeNumber->init(&celldata);
celldata.mask = data->mask;
celldata.style |= data->style;
celldata.data.number.color_bg = data->color_bg;
celldata.data.number.color_fg = data->color_fg;
celldata.data.number.image = data->image;
celldata.data.number.font = data->font;
number = (GRIDCELLDATANUMBER*)data->content;
celldata.data.number.num = number->number;
if (number->format != NULL) {
int len;
len = MIN(strlen(number->format), number->len_format);
buff = (char *) malloc (sizeof(char)*(len + 1));
strncpy (buff, number->format, len);
buff[len] = '\0';
}
celldata.data.number.format = buff;
cell->func->merge(cell, &celldata);
if (buff != NULL)
free (buff);
break;
}
case GV_TYPE_CHECKBOX:
{
char *buff = NULL;
GRIDCELLDATACHECKBOX* checkbox;
if(need_set_value)
gvGridCellTypeCheckBox->init(cell);
gvGridCellTypeCheckBox->init(&celldata);
celldata.mask = data->mask;
celldata.style |= data->style;
celldata.data.checkbox.color_bg = data->color_bg;
celldata.data.checkbox.color_fg = data->color_fg;
celldata.data.checkbox.image = data->image;
celldata.data.checkbox.font = data->font;
checkbox = (GRIDCELLDATACHECKBOX*) data->content;
celldata.data.checkbox.checked = checkbox->checked;
if (checkbox->text != NULL) {
int len;
len = MIN(strlen(checkbox->text), checkbox->len_text);
buff = (char *) malloc (sizeof(char)*(len + 1));
strncpy (buff, checkbox->text, len);
buff[len] = '\0';
}
celldata.data.checkbox.text = buff;
cell->func->merge(cell, &celldata);
if (buff != NULL)
free (buff);
break;
}
case GV_TYPE_SELECTION:
{
char *buff = NULL;
GRIDCELLDATASELECTION* selection;
if(need_set_value)
gvGridCellTypeSelection->init(cell);
gvGridCellTypeSelection->init(&celldata);
celldata.mask = data->mask;
celldata.style |= data->style;
celldata.data.selection.color_bg = data->color_bg;
celldata.data.selection.color_fg = data->color_fg;
celldata.data.selection.image = data->image;
celldata.data.selection.font = data->font;
selection = (GRIDCELLDATASELECTION*) data->content;
celldata.data.selection.cur_index = selection->cur_index;
if (selection->selections != NULL) {
int len;
len = MIN(strlen(selection->selections), selection->len_sel);
buff = (char *) malloc (sizeof(char)*(len + 1));
strncpy (buff, selection->selections, len);
buff[len] = '\0';
}
celldata.data.selection.text = buff;
cell->func->merge(cell, &celldata);
if (buff != NULL)
free (buff);
break;
}
default:
return GRID_ERR;
}
// cell->func->merge(cell, &celldata);
if(need_set_value)
view->modal->set_value_at(view, row, col, cell);
return GRID_OKAY;
}
// }}}
// fetch_cell_value {{{
static int fetch_cell_value(gvGridViewData* view, GRIDCELLDATA* data, int row, int col)
{
gvGridCellData* cell;
GRIDCELLDATAHEADER* header;
GRIDCELLDATATEXT* text;
GRIDCELLDATANUMBER* number;
GRIDCELLDATASELECTION* selection;
GRIDCELLDATACHECKBOX* checkbox;
cell = view->modal->get_value_at(view, row, col);
if ((data->style & GV_TYPE_MASK) == GV_TYPE_HEADER)
{
if (cell->celltype != GV_TYPE_HEADER)
return GRID_ERR;
header = (GRIDCELLDATAHEADER*)data->content;
if (header == NULL)
{
data->style = cell->style;
data->mask = cell->mask;
return GRID_OKAY;
}
if ((data->style & GV_TYPE_MASK) != GV_TYPE_HEADER)
return GRID_ERR;
if (header->buff == NULL)
{
if (cell->data.header.text != NULL)
header->len_buff = strlen(cell->data.header.text);
else
header->len_buff = 0;
return GRID_OKAY;
}
if (header->len_buff != 0)
{
data->style = cell->style;
data->mask = cell->mask;
strncpy(header->buff ,
cell->data.header.text,
header->len_buff);
header->buff[header->len_buff] = '\0';
header->size = cell->data.header.size;
data->color_bg = cell->data.header.color_bg;
data->color_fg = cell->data.header.color_fg;
header->len_buff = strlen(cell->data.header.text);
return GRID_OKAY;
}
}
else if ((data->style & GV_TYPE_MASK) == GV_TYPE_TEXT)
{
if (cell->celltype != GV_TYPE_TEXT)
return GRID_ERR;
text = (GRIDCELLDATATEXT*)data->content;
if (text == NULL)
{
data->style = cell->style;
data->mask = cell->mask;
return GRID_OKAY;
}
if (text->buff == NULL)
{
if (cell->data.text.text != NULL)
text->len_buff = strlen(cell->data.text.text);
else
text->len_buff = 0;
return GRID_OKAY;
}
if (text->len_buff != 0)
{
strncpy(text->buff ,
cell->data.text.text,
text->len_buff);
text->buff[text->len_buff] = '\0';
data->style = cell->style;
data->mask = cell->mask;
data->color_bg = cell->data.text.color_bg;
data->color_fg = cell->data.text.color_fg;
text->len_buff = strlen(cell->data.text.text);
return GRID_OKAY;
}
}
else if((data->style & GV_TYPE_MASK) == GV_TYPE_NUMBER)
{
if (cell->celltype != GV_TYPE_NUMBER)
return GRID_ERR;
number = (GRIDCELLDATANUMBER*)data->content;
if (number == NULL)
{
data->style = cell->style;
data->mask = cell->mask;
return GRID_OKAY;
}
number->number = cell->data.number.num;
if (number->format == NULL)
{
if (cell->data.number.format != NULL)
number->len_format = strlen(cell->data.number.format);
else
number->len_format = 0;
return GRID_OKAY;
}
if (number->len_format > 0)
{
strncpy(number->format,
cell->data.number.format,
number->len_format);
number->format[number->len_format] = '\0';
data->style = cell->style;
data->mask = cell->mask;
number->len_format = strlen(cell->data.number.format);
return GRID_OKAY;
}
}
else if((data->style & GV_TYPE_MASK) == GV_TYPE_SELECTION)
{
if (cell->celltype != GV_TYPE_SELECTION)
return GRID_ERR;
selection = (GRIDCELLDATASELECTION*)data->content;
if (selection == NULL)
{
data->style = cell->style;
data->mask = cell->mask;
return GRID_OKAY;
}
if (selection->selections == NULL)
{
if (cell->data.selection.text != NULL)
selection->len_sel = strlen(cell->data.selection.text);
else
selection->len_sel = 0;
return GRID_OKAY;
}
if (selection->len_sel != 0)
{
strncpy(selection->selections,
cell->data.selection.text,
selection->len_sel);
selection->selections[selection->len_sel] = '\0';
data->style = cell->style;
data->mask = cell->mask;
data->color_bg = cell->data.selection.color_bg;
data->color_fg = cell->data.selection.color_fg;
selection->cur_index = cell->data.selection.cur_index;
selection->len_sel = strlen(cell->data.selection.text);
return GRID_OKAY;
}
}
else if((data->style & GV_TYPE_MASK) == GV_TYPE_CHECKBOX)
{
if (cell->celltype != GV_TYPE_CHECKBOX)
return GRID_ERR;
checkbox = (GRIDCELLDATACHECKBOX*)data->content;
if (checkbox == NULL)
{
data->style = cell->style;
data->mask = cell->mask;
return GRID_OKAY;
}
if (checkbox->text == NULL)
{
if (cell->data.checkbox.text != NULL)
checkbox->len_text = strlen(cell->data.checkbox.text);
else
checkbox->len_text = 0;
return GRID_OKAY;
}
if (checkbox->len_text != 0)
{
strncpy(checkbox->text,
cell->data.checkbox.text,
checkbox->len_text);
checkbox->text[checkbox->len_text] = '\0';
checkbox->checked = cell->data.checkbox.checked;
data->style = cell->style;
data->mask = cell->mask;
checkbox->len_text = strlen(cell->data.checkbox.text);
return GRID_OKAY;
}
}
return GRID_ERR;
}
// }}}
// set highlight & current cells {{{
static void set_highlight_cells(gvGridViewData* view, int row, int col, int width, int height)
{
RECT rect;
gvGridCells_get_cells_rect(view, &view->highlight_cells, &rect);
InvalidateRect(view->hCtrl, &rect, FALSE);
view->highlight_cells.row = row;
view->highlight_cells.column = col;
view->highlight_cells.width = width;
view->highlight_cells.height = height;
gvGridCells_get_cells_rect(view, &view->highlight_cells, &rect);
InvalidateRect(view->hCtrl, &rect, FALSE);
// gridview_make_rect_visible(view, &rect);
}
static void set_current_cell(gvGridViewData* view, int row, int col)
{
RECT rect;
int count;
if(row < 1) row = 1;
if(col < 1) col = 1;
count = view->modal->get_row_count(view);
if(row >= count) row = count;
count = view->modal->get_col_count(view);
if(col >= count) col = count;
if(view->current_col != col || view->current_row != row)
{
get_cell_rect(view, view->current_row, view->current_col, &rect);
InvalidateRect (view->hCtrl, &rect, FALSE);
get_cell_rect(view, row, col, &rect);
InvalidateRect (view->hCtrl, &rect, FALSE);
view->current_col = col;
view->current_row = row;
gridview_make_rect_visible(view, &rect);
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_FOCUSCHANGED);
}
}
static void set_highlight_cells_to(gvGridViewData* view, int row, int col)
{
int x;
int r, c, w, h;
int row_count, col_count;
row_count = view->modal->get_row_count(view);
col_count = view->modal->get_col_count(view);
if(row <= 0 || col <= 0 || row > row_count || col > col_count)
return;
r = view->highlight_cells.row;
c = view->highlight_cells.column;
x = (row>=r)?1:-1;
h = row - r + x;
x = (col>=c)?1:-1;
w = col - c + x;
set_highlight_cells(view, r, c, w, h);
set_current_cell(view, row, col);
}
// }}}
// dependence operation {{{
static int gvGridCellDep_add_dep_to_list(gvGridViewData* view, gvGridCellDepedence* dep)
{
gvGridCellDepedence *new_dep;
new_dep = malloc(sizeof(gvGridCellDepedence));
memcpy(new_dep, dep, sizeof(gvGridCellDepedence));
new_dep->next = NULL;
view->depend_cur_id++;
new_dep->id = view->depend_cur_id;
if(view->depend_list == NULL)
view->depend_list = new_dep;
else
{
gvGridCellDepedence* list = view->depend_list;
while(list->next != NULL)
list = list->next;
list->next = new_dep;
}
return new_dep->id;
}
static int gvGridCellDep_add_dep(gvGridViewData* view, gvGridCellDepedence* dep)
{
int ret;
gvGridCellDepedence* head = view->depend_list;
if (gvGridCells_intersect(&dep->source, &dep->target))
return -1;
while(head != NULL)
{
if (gvGridCells_intersect(&head->target, &dep->target))
return -1;
head = head->next;
}
ret = gvGridCellDep_add_dep_to_list(view, dep);
gvGridCellDep_update_dep(view, &dep->source);
return ret;
}
static int gvGridCellDep_delete_dep(gvGridViewData* view, int id)
{
int ret = -1;
gvGridCellDepedence *prev_dep;
gvGridCellDepedence *cur_dep;
if(view->depend_list == NULL)
return -1;
cur_dep = view->depend_list;
if (cur_dep->id == id)
{
view->depend_list = cur_dep->next;
free(cur_dep);
return id;
}
prev_dep = view->depend_list;
while(prev_dep != NULL)
{
cur_dep = prev_dep->next;
if (cur_dep != NULL && cur_dep->id == id)
{
prev_dep->next = cur_dep->next;
free(cur_dep);
ret = id;
break;
}
prev_dep = cur_dep;
}
return ret;
}
static int gvGridCellDep_update_dep(gvGridViewData* view, GRIDCELLS* cells)
{
gvGridCellDepedence* dep = view->depend_list;
while (dep)
{
if (gvGridCells_intersect(&dep->source, cells))
dep->callback(&dep->target, &dep->source, dep->dwAddData);
dep = dep->next;
}
return 0;
}
// }}}
// draw cell {{{
static BOOL gridview_is_pt_in_hwnd_rect(HWND hParent, HWND hWnd, int x, int y)
{
RECT r;
HWND hChild = 0;
GetWindowRect(hWnd, &r);
if(PtInRect(&r, x, y))
return TRUE;
ClientToScreen(hParent, &x, &y);
ScreenToClient(hWnd, &x, &y);
while ((hChild = GetNextChild(hWnd, hChild)) != 0)
{
GetWindowRect(hChild, &r);
if(PtInRect(&r, x, y))
return TRUE;
}
return FALSE;
}
static BOOL gridview_is_pt_in_control(gvGridViewData* view, int x, int y)
{
int row, col;
gvGridCellData* cell;
get_cell_from_xy(view, x, y, &row, &col);
cell = view->modal->get_value_at(view, row, col);
if(cell != NULL)
{
HWND ctrl = cell->func->get_control(cell, view, row, col);
if(ctrl != HWND_INVALID && gridview_is_pt_in_hwnd_rect(view->hCtrl, ctrl, x, y))
return TRUE;
}
return FALSE;
}
static BOOL is_cell_visible(gvGridViewData* view, gvGridCellData* cell, HDC hdc, RECT* r)
{
if(cell == NULL || cell->func->invisible_draw == NULL)
return RectVisible(hdc, r);
return DoesIntersect(r, &view->cells_rect);
}
static void draw_cell(gvGridViewData* view, HWND hWnd, HDC hdc, int row, int col)
{
RECT r;
gvGridCellData* cell;
cell = view->modal->get_value_or_default_at(view, row, col);
get_cell_rect(view, row, col, &r);
if(cell == NULL)
return;
if(is_cell_visible(view, cell, hdc, &r))
{
cell->func->draw(cell, view, hdc, row, col, &r);
if(row==0 || col==0)
ExcludeClipRect(hdc, &r);
}
else if(cell->func->invisible_draw != NULL)
cell->func->invisible_draw(cell, view, hdc, row, col, &r);
}
// }}}
// mouse status event {{{
static int change_mouse_status(gvGridViewData* view, int x, int y)
{
int row, col;
RECT r;
get_cell_from_xy(view, x, y, &row, &col);
get_cell_rect(view, row, col, &r);
if (row == 0)
{
view->mouse_status = MOUSE_COLUMN_SELECT;
if (col < 1)
return 0;
if (x >= r.right -2)
{
view->mouse_status = MOUSE_COLUMN_RESIZE;
view->index_drag = col;
}
}
else if (col == 0)
{
view->mouse_status = MOUSE_ROW_SELECT;
if (row < 1)
return 0;
if (y >= r.bottom -2)
{
view->mouse_status = MOUSE_ROW_RESIZE;
view->index_drag = row;
}
}
else
view->mouse_status = MOUSE_CELL_SELECT;
return 0;
}
static int select_cell_mouse_down(gvGridViewData* view, int x, int y)
{
int row, col;
get_cell_from_xy(view, x, y, &row, &col);
set_highlight_cells(view, row, col, 1, 1);
set_current_cell(view, row, col);
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_CELLCLK);
return 0;
}
static int select_head_mouse_down(gvGridViewData* view, int x, int y, int isrow)
{
int row, col;
get_cell_from_xy(view, x, y, &row, &col);
if (isrow) {
set_highlight_cells(view, row, col, -1, 1);
set_current_cell(view, row, 1);
}
else {
set_highlight_cells(view, row, col, 1, -1);
set_current_cell(view, 1, col);
}
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_HEADLDOWN);
return 0;
}
static int select_cell_mouse_up(gvGridViewData* view, int x, int y)
{
return 0;
}
static int select_row_mouse_up(gvGridViewData* view, int x, int y)
{
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_HEADLUP);
return 0;
}
static int select_col_mouse_up(gvGridViewData* view, int x, int y)
{
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_HEADLUP);
return 0;
}
static int select_cell_mouse_move(gvGridViewData* view, int x, int y)
{
int row, col, r1, c1;
RECT rect;
get_cell_from_xy(view, x, y, &row, &col);
get_cell_rect (view, 0, 0, &rect);
if (row == 0) {
get_cell_from_xy(view, x, y + RECTH(rect), &r1, &c1);
if (r1 != 1)
row = r1 -1;
}
if (col == 0) {
get_cell_from_xy(view, x + RECTW(rect), y, &r1, &c1);
if (c1 != 1)
col = c1 -1;
}
set_highlight_cells_to(view, row, col);
return 0;
}
static int select_row_mouse_move(gvGridViewData* view, int x, int y)
{
int row, col;
int begin_row, begin_col;
int height;
RECT rect;
get_cell_from_xy(view, x, y, &row, &col);
if (row < 0 || col < 0)
return -1;
get_cell_rect (view, 0, 0, &rect);
if(row == 0) {
row = 1;
}
begin_row = view->highlight_cells.row;
begin_col = view->highlight_cells.column;
y = (row >= begin_row)?1:-1;
height = row - begin_row + y;
set_highlight_cells(view, begin_row, begin_col, -1, height);
set_current_cell(view, row, 1);
return 0;
}
static int select_col_mouse_move(gvGridViewData* view, int x, int y)
{
int row, col;
int begin_row, begin_col;
int width;
RECT rect;
get_cell_from_xy(view, x, y, &row, &col);
if (row < 0 || col < 0)
return -1;
get_cell_rect (view, 0, 0, &rect);
if(col == 0)
col = 1;
begin_row = view->highlight_cells.row;
begin_col = view->highlight_cells.column;
x = (col >= begin_col)?1:-1;
width = col - begin_col + x;
set_highlight_cells(view, begin_row, begin_col, width, -1);
set_current_cell (view, 1, col);
return 0;
}
static int resize_row_mouse_down(gvGridViewData* view, int x, int y)
{
HDC hdc;
RECT r;
int oldrop;
hdc = GetClientDC (view->hCtrl);
oldrop = SetRasterOperation (hdc, ROP_XOR);
GetClientRect (view->hCtrl, &r);
SetPenColor (hdc, PIXEL_lightwhite);
LineEx (hdc, 0, y, RECTW(r), y);
SetRasterOperation (hdc, oldrop);
ReleaseDC (hdc);
view->drag_y = y;
return 0;
}
static int resize_col_mouse_down(gvGridViewData* view, int x, int y)
{
HDC hdc;
RECT r;
int oldrop;
hdc = GetClientDC (view->hCtrl);
oldrop = SetRasterOperation (hdc, ROP_XOR);
GetClientRect (view->hCtrl, &r);
SetPenColor (hdc, PIXEL_lightwhite);
LineEx (hdc, x, 0, x, RECTW(r));
SetRasterOperation (hdc, oldrop);
ReleaseDC (hdc);
view->drag_x = x;
return 0;
}
static int resize_row_mouse_up(gvGridViewData* view, int x, int y)
{
HDC hdc;
RECT r;
RECT rect;
int oldrop;
hdc = GetClientDC (view->hCtrl);
oldrop = SetRasterOperation (hdc, ROP_XOR);
GetClientRect (view->hCtrl, &r);
SetPenColor (hdc, PIXEL_lightwhite);
LineEx (hdc, 0, view->drag_y, RECTW(r), view->drag_y);
SetRasterOperation (hdc, oldrop);
ReleaseDC (hdc);
if (y < view->drag_y) y = view->drag_y;
get_cell_rect (view, view->index_drag, 0, &rect);
SendMessage (view->hCtrl, GRIDM_SETROWHEIGHT, view->index_drag, y-rect.top+2);
return 0;
}
/*
static BOOL find_checkbox(gvGridViewData* view)
{
gvGridCellData* cell;
int row_count;
row_count = view->modal->get_row_count(view);
while (row_count > 0)
{
cell = view->modal->get_value_at(view, row_count, view->index_drag);
if (cell != NULL && (cell->celltype == GV_TYPE_CHECKBOX))
return TRUE;
row_count --;
}
return FALSE;
}
*/
static int resize_col_mouse_up(gvGridViewData* view, int x, int y)
{
HDC hdc;
RECT r;
RECT rect;
int oldrop;
hdc = GetClientDC (view->hCtrl);
oldrop = SetRasterOperation (hdc, ROP_XOR);
GetClientRect (view->hCtrl, &r);
SetPenColor (hdc, PIXEL_lightwhite);
LineEx (hdc, view->drag_x, 0, view->drag_x, RECTH(r));
SetRasterOperation (hdc, oldrop);
ReleaseDC (hdc);
if (x < view->drag_x) x = view->drag_x;
get_cell_rect (view, 0, view->index_drag, &rect);
SendMessage (view->hCtrl, GRIDM_SETCOLWIDTH, view->index_drag, x-rect.left+2);
/*
if (view->index_drag) {
cell->data.header.size = (view->drag_x-rect.left);
if (cell->data.header.size < 50 && find_checkbox(view))
cell->data.header.size = 50;
} else
cell->data.table.height = (view->drag_x-rect.left);
*/
return 0;
}
static int resize_row_mouse_move(gvGridViewData* view, int x, int y)
{
HDC hdc;
RECT r1;
RECT r2;
int oldrop;
get_cell_rect (view, view->index_drag, 0, &r1);
if (y <= r1.top)
return 0;
hdc = GetClientDC (view->hCtrl);
GetClientRect (view->hCtrl, &r2);
oldrop = SetRasterOperation (hdc, ROP_XOR);
SetPenColor (hdc, PIXEL_lightwhite);
LineEx (hdc, 0, view->drag_y, RECTW(r2), view->drag_y);
LineEx (hdc, 0, y, RECTW(r2), y);
SetRasterOperation (hdc, oldrop);
ReleaseDC (hdc);
view->drag_y = y;
return 0;
}
static int resize_col_mouse_move(gvGridViewData* view, int x, int y)
{
HDC hdc;
RECT r1;
RECT r2;
int oldrop;
get_cell_rect (view, 0, view->index_drag, &r1);
if (x <= r1.left)
return 0;
hdc = GetClientDC (view->hCtrl);
GetClientRect (view->hCtrl, &r2);
oldrop = SetRasterOperation (hdc, ROP_XOR);
SetPenColor (hdc, PIXEL_lightwhite);
LineEx (hdc, view->drag_x, 0, view->drag_x, RECTW(r2));
LineEx (hdc, x, 0, x, RECTW(r2));
SetRasterOperation (hdc, oldrop);
ReleaseDC (hdc);
view->drag_x = x;
return 0;
}
static int default_mouse_down(gvGridViewData* view, int x, int y)
{
int row, col;
RECT rect;
get_cell_from_xy(view, x, y, &row, &col);
get_cell_rect(view, row, col, &rect);
gridview_make_rect_visible(view, &rect);
//set_current_cell(view, row, col);
return 0;
}
static int mouse_default_move(gvGridViewData* view, int x, int y)
{
int row, col;
int cursor_id = 0;
RECT r;
get_cell_from_xy(view, x, y, &row, &col);
get_cell_rect(view, row, col, &r);
if ((row == 0) && (col == 0))
return 0;
if (row == 0 && (x >= r.right -2 && x <= r.right))
cursor_id = IDC_SPLIT_VERT;
else if (col == 0 && (y >= r.bottom-2 && y <= r.bottom))
cursor_id = IDC_SPLIT_HORZ;
SetCursor(GetSystemCursor(cursor_id));
return 0;
}
// }}}
// gridview_init {{{
static int gridview_init(HWND hWnd, GRIDVIEWDATA* data)
{
gvGridViewData* view;
gvGridCellData cell;
RECT rcWnd;
int i;
view = (gvGridViewData*)calloc(1, sizeof(gvGridViewData));
if(view == NULL)
return -1;
// set window edit rows & edit cols
view->current_row = 1;
view->current_col = 1;
// set window handles
view->ctrl_id = GetDlgCtrlID(hWnd);
view->hCtrl = hWnd;
view->hEdit = HWND_INVALID;
//initialize modal
view->modal = GridDataModalArray_new_modal(data->nr_rows, data->nr_cols);
//set table properties
gvGridCellTypeTableHeader->init(&cell);
cell.data.table.width = data->col_width;
cell.data.table.height = data->row_height;
cell.data.table.def_cell = malloc(sizeof(gvGridCellData));
gvGridCellTypeText->init(cell.data.table.def_cell);
view->modal->set_value_at(view, 0, 0, &cell);
//set all headers
gvGridCellTypeHeader->init(&cell);
cell.data.header.size = data->row_height;
for(i=1; i<=data->nr_rows; i++)
view->modal->set_value_at(view, i, 0, &cell);
cell.data.header.size = data->col_width;
for(i=1; i<=data->nr_cols; i++)
view->modal->set_value_at(view, 0, i, &cell);
//initialize scroll
GetClientRect (hWnd, &rcWnd);
scrolled_init (hWnd, &view->scrdata,
RECTW(rcWnd), RECTH(rcWnd));
scrolled_init_margins (&view->scrdata, 0, 0, 0, 0);
view->scrdata.move_content = gridview_set_content;
//set add data
SetWindowAdditionalData2(hWnd, (DWORD)view);
SetFocusChild(hWnd);
return 0;
}
// }}}
// Grid Array Modal {{{
typedef struct _GridDataModalArray GridDataModalArray;
struct _GridDataModalArray
{
int row;
int col;
gvGridCellData** cells;
};
static inline int array_xy_of(GridDataModalArray* array, int row, int col)
{
return (array->col+1)*row+col;
}
static int GridDataModalArray_get_col_count(gvGridViewData* view)
{
GridDataModalArray* array = (GridDataModalArray*)view->modal->data;
return array->col;
}
static int GridDataModalArray_get_row_count(gvGridViewData* view)
{
GridDataModalArray* array = (GridDataModalArray*)view->modal->data;
return array->row;
}
static gvGridCellData* GridDataModalArray_get_value_at(gvGridViewData* view, int row, int col)
{
GridDataModalArray* array = (GridDataModalArray*)view->modal->data;
gvGridCellData* cell;
if(row < 0 || col < 0 || row > array->row || col > array->col)
return NULL;
cell = array->cells[array_xy_of(array, row, col)];
return cell;
}
static int GridDataModalArray_set_value_at(gvGridViewData* view, int row, int col, gvGridCellData* data)
{
GridDataModalArray* array = (GridDataModalArray*)view->modal->data;
gvGridCellData* cell = NULL;
if(row < 0 || col < 0 || row > array->row || col > array->col)
return -1;
cell = array->cells[array_xy_of(array, row, col)];
if(cell != NULL && cell->func->on_destroy != NULL)
cell->func->on_destroy(cell);
if(data != NULL)
{
if(cell == NULL)
cell = malloc(sizeof(gvGridCellData));
memcpy(cell, data, sizeof(gvGridCellData));
}
else if(cell != NULL)
{
free(cell);
cell = NULL;
}
array->cells[array_xy_of(array, row, col)] = cell;
return 0;
}
static gvGridCellData* GridDataModalArray_get_value_or_default_at(gvGridViewData* view, int row, int col)
{
gvGridCellData* cell;
cell = view->modal->get_value_at(view, row, col);
if(cell != NULL || row == 0 || col == 0)
return cell;
do
{
BOOL col_first;
gvGridCellData* table;
int r1, r2, c1, c2;
table = view->modal->get_value_at(view, 0, 0);
col_first = table->data.table.col_first;
if(col_first)
{ r1 = 0; c1 = col; r2 = row; c2 = 0; }
else
{ r1 = row; c1 = 0; r2 = 0; c2 = col; }
cell = view->modal->get_value_at(view, r1, c1);
cell = cell->data.header.def_cell;
if(cell != NULL)
break;
cell = view->modal->get_value_at(view, r2, c2);
cell = cell->data.header.def_cell;
if(cell != NULL)
break;
cell = table->data.table.def_cell;
}
while(0);
return cell;
}
static int GridDataModalArray_add_new_row(gvGridViewData* view, int index)
{
GridDataModalArray* array = (GridDataModalArray*)view->modal->data;
int row_length = array->col+1;
int row_byte_length = row_length*sizeof(gvGridCellData*);
gvGridCellData** new_line_begining;
if (view->current_row >= index)
view->current_row++;
if(index < 0 || index > array->row)
index = array->row;
array->cells = realloc(array->cells, (array->row+2)*row_byte_length);
new_line_begining = &array->cells[row_length*(index+1)];
if(index < array->row)
{
gvGridCellData** move_line_begining = &array->cells[row_length*array->row];
for(; move_line_begining >= new_line_begining;
move_line_begining -= row_length)
memcpy(move_line_begining+row_length, move_line_begining, row_byte_length);
}
memset(new_line_begining, 0, row_byte_length);
array->row++;
return index+1;
}
static int GridDataModalArray_delete_row(gvGridViewData* view, int index)
{
GridDataModalArray* array = (GridDataModalArray*)view->modal->data;
int row_length = array->col+1;
int row_byte_length = row_length*sizeof(gvGridCellData*);
int i;
gvGridCellData* cell;
if (view->current_row > index && view->current_row > 1)
view->current_row--;
if(index < 0 || index >= array->row)
index = array->row-1;
index++;
for(i=0; i<=array->col; i++)
{
cell = array->cells[index*row_length+i];
if(cell != NULL)
cell->func->on_destroy(cell);
}
if(index < array->row)
{
gvGridCellData** end_line_begining = &array->cells[row_length*array->row];
gvGridCellData** del_line_begining = &array->cells[row_length*index];
i = index;
for(; del_line_begining < end_line_begining;
del_line_begining += row_length)
memcpy(del_line_begining, del_line_begining+row_length, row_byte_length);
}
array->cells = realloc(array->cells, (array->row)*row_byte_length);
array->row--;
return 0;
}
static int GridDataModalArray_add_new_col(gvGridViewData* view, int index)
{
GridDataModalArray* array = (GridDataModalArray*)view->modal->data;
gvGridCellData** new_cells;
int i, j;
if (view->current_col >= index)
view->current_col++;
if(index < 0 || index > array->col)
index = array->col;
new_cells = malloc((array->row+1)*(array->col+2)*sizeof(gvGridCellData*));
for(i=0; i<=array->row; i++)
{
for(j=0; j<=array->col+1; j++)
{
if(j==index+1)
new_cells[i*(array->col+2)+j] = NULL;
else if(j < index+1)
new_cells[i*(array->col+2)+j] = array->cells[i*(array->col+1)+j];
else
new_cells[i*(array->col+2)+j] = array->cells[i*(array->col+1)+j-1];
}
}
array->col++;
free(array->cells);
array->cells = new_cells;
return index+1;
}
static int GridDataModalArray_delete_col(gvGridViewData* view, int index)
{
GridDataModalArray* array = (GridDataModalArray*)view->modal->data;
gvGridCellData** new_cells;
int i, j;
gvGridCellData* cell;
if (view->current_col > index && view->current_col > 1)
view->current_col--;
if(index < 0 || index >= array->col)
index = array->col-1;
new_cells = malloc((array->row+1)*(array->col)*sizeof(gvGridCellData*));
for(i=0; i<=array->row; i++)
{
for(j=0; j<=array->col; j++)
{
if(j == index+1)
{
cell = array->cells[i*(array->col+1)+j];
if(cell != NULL)
cell->func->on_destroy(cell);
free(cell);
}
else if(j < index+1)
new_cells[i*(array->col)+j] = array->cells[i*(array->col+1)+j];
else
new_cells[i*(array->col)+j-1] = array->cells[i*(array->col+1)+j];
}
}
array->col--;
free(array->cells);
array->cells = new_cells;
return 0;
}
static gvGridDataModal* GridDataModalArray_new_modal(int row, int col)
{
gvGridDataModal* modal;
GridDataModalArray* array;
modal = malloc(sizeof(gvGridDataModal));
modal->get_col_count = GridDataModalArray_get_col_count;
modal->get_row_count = GridDataModalArray_get_row_count;
modal->get_value_at = GridDataModalArray_get_value_at;
modal->set_value_at = GridDataModalArray_set_value_at;
modal->get_value_or_default_at = GridDataModalArray_get_value_or_default_at;
modal->add_new_row = GridDataModalArray_add_new_row;
modal->delete_row = GridDataModalArray_delete_row;
modal->add_new_col = GridDataModalArray_add_new_col;
modal->delete_col = GridDataModalArray_delete_col;
array = malloc(sizeof(GridDataModalArray));
array->row = row;
array->col = col;
array->cells = calloc((row+1)*(col+1), sizeof(gvGridCellData*));
modal->data = array;
return modal;
}
static void GridDataModalArray_delete_modal(gvGridDataModal* modal)
{
int i;
GridDataModalArray* array = (GridDataModalArray*)modal->data;
for(i=(array->row+1)*(array->col+1)-1; i>=0; i--)
{
gvGridCellData* cell = array->cells[i];
if(cell != NULL)
{
cell->func->on_destroy(cell);
free(cell);
}
}
free(array->cells);
free(array);
free(modal);
}
// }}}
static LRESULT GridViewCtrlProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
gvGridViewData* view = NULL;
if (message != MSG_CREATE)
view = (gvGridViewData*) GetWindowAdditionalData2(hWnd);
switch (message)
{
// MiniGUI Window Messages {{{
case MSG_CREATE:
{
return gridview_init(hWnd, (GRIDVIEWDATA*)lParam);
}
case MSG_SIZECHANGED:
{
RECT rect;
update_row_col_size(view);
GetClientRect(hWnd, &rect);
view->scrdata.visibleWidth = RECTW(rect);
view->scrdata.visibleHeight = RECTH(rect);
view->scrdata.hScrollPageVal = RECTW(view->cells_rect);
view->scrdata.vScrollPageVal = RECTH(view->cells_rect);
break;
}
case MSG_PAINT:
{
int i, j;
int row, col;
HDC hdc = BeginPaint(hWnd);
if(view->hEdit != HWND_INVALID)
{
RECT r;
GetWindowRect(view->hEdit, &r);
ExcludeClipRect(hdc, &r);
}
row = view->modal->get_row_count(view);
col = view->modal->get_col_count(view);
//draw cells
for(i=1; i<=row; i++)
{
for(j=1; j<=col; j++)
draw_cell(view, hWnd, hdc, i, j);
}
//draw table header & row header
j=0;
for(i=0; i<=row; i++)
draw_cell(view, hWnd, hdc, i, j);
//draw col header
i=0;
for(j=1; j<=col; j++)
draw_cell(view, hWnd, hdc, i, j);
EndPaint(hWnd, hdc);
return 0;
}
case MSG_DESTROY:
{
if(view->depend_list != NULL)
{
gvGridCellDepedence* list = view->depend_list;
while(list != NULL)
{
view->depend_list = list->next;
free(list);
list = view->depend_list;
}
}
GridDataModalArray_delete_modal(view->modal);
free (view);
break;
}
// }}}
// MiniGUI mouse Messages {{{
case MSG_LBUTTONDBLCLK:
{
int x = LOSWORD (lParam);
int y = HISWORD (lParam);
int row, col;
//view->is_mouse_down = FALSE;
get_cell_from_xy(view, x, y, &row, &col);
if(row > 0 && col > 0)
{
RECT rect;
gvGridCellData* cell;
set_current_cell(view, row, col);
set_highlight_cells(view, row, col, 1, 1);
get_cell_rect(view, row, col, &rect);
cell = view->modal->get_value_or_default_at(view, row, col);
cell->func->begin_edit(cell, view, row, col, &rect);
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_CELLDBCLK);
}
break;
}
case MSG_LBUTTONDOWN:
{
BOOL is_capture;
int x = LOSWORD (lParam);
int y = HISWORD (lParam);
if(view->hEdit != HWND_INVALID)
{
int row, col;
if(!gridview_is_pt_in_hwnd_rect(view->hCtrl, view->hEdit, x, y))
{
SendMessage(view->hEdit, MSG_KEYDOWN, SCANCODE_ENTER, 0);
get_cell_from_xy(view, x, y, &row, &col);
set_current_cell(view, row, col);
set_highlight_cells(view, row, col, 1, 1);
}
break;
}
is_capture = (GetCapture() == hWnd);
if (is_capture)
ScreenToClient(view->hCtrl, &x, &y);
else if(gridview_is_pt_in_control(view, x, y))
break;
else
SetCapture(hWnd);
change_mouse_status(view, x, y);
switch(view->mouse_status)
{
case MOUSE_CELL_SELECT:
select_cell_mouse_down(view, x, y);
break;
case MOUSE_ROW_SELECT:
select_head_mouse_down(view, x, y, 1);
break;
case MOUSE_COLUMN_SELECT:
select_head_mouse_down(view, x, y, 0);
break;
case MOUSE_ROW_RESIZE:
resize_row_mouse_down(view, x, y);
break;
case MOUSE_COLUMN_RESIZE:
resize_col_mouse_down(view, x, y);
break;
case MOUSE_DEFAULT:
default:
default_mouse_down(view, x, y);
break;
}
if(is_capture)
return 0;
break;
}
case MSG_LBUTTONUP:
{
BOOL is_capture;
int x = LOSWORD (lParam);
int y = HISWORD (lParam);
is_capture = (GetCapture() == hWnd);
if (is_capture)
{
ScreenToClient(hWnd, &x, &y);
ReleaseCapture();
}
else if(view->hEdit != HWND_INVALID)
break;
switch(view->mouse_status)
{
case MOUSE_CELL_SELECT:
select_cell_mouse_up(view, x, y);
break;
case MOUSE_ROW_SELECT:
select_row_mouse_up(view, x, y);
break;
case MOUSE_COLUMN_SELECT:
select_col_mouse_up(view, x, y);
break;
case MOUSE_ROW_RESIZE:
resize_row_mouse_up(view, x, y);
break;
case MOUSE_COLUMN_RESIZE:
resize_col_mouse_up(view, x, y);
break;
case MOUSE_DEFAULT:
default:
break;
}
view->mouse_status = MOUSE_DEFAULT;
if(is_capture)
return 0;
break;
}
case MSG_MOUSEMOVE:
{
BOOL is_capture;
RECT rect;
int x = LOSWORD (lParam);
int y = HISWORD (lParam);
GetClientRect(hWnd, &rect);
if(view->hEdit != HWND_INVALID)
break;
is_capture = (GetCapture() == hWnd);
if (is_capture) {
ScreenToClient(view->hCtrl, &x, &y);
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x > RECTW(rect)) x = RECTW(rect);
if (y > RECTH(rect)) y = RECTH(rect);
}
switch(view->mouse_status)
{
case MOUSE_CELL_SELECT:
select_cell_mouse_move(view, x, y);
break;
case MOUSE_ROW_SELECT:
select_row_mouse_move(view, x, y);
break;
case MOUSE_COLUMN_SELECT:
select_col_mouse_move(view, x, y);
break;
case MOUSE_ROW_RESIZE:
resize_row_mouse_move(view, x, y);
break;
case MOUSE_COLUMN_RESIZE:
resize_col_mouse_move(view, x, y);
break;
case MOUSE_DEFAULT:
default:
mouse_default_move(view, x, y);
break;
}
if(is_capture)
return 0;
break;
}
// }}}
// MiniGUI key Messages {{{
case MSG_KEYDOWN:
{
if(view->hEdit != HWND_INVALID)
return DefaultControlProc(hWnd, message, wParam, lParam);
else
{
BOOL need_break = FALSE;
switch (wParam)
{
case SCANCODE_TAB:
set_current_cell(view, view->current_row, view->current_col+1);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
break;
case SCANCODE_ENTER:
set_current_cell(view, view->current_row+1, view->current_col);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
break;
case SCANCODE_CURSORBLOCKUP:
if (lParam & KS_SHIFT)
set_highlight_cells_to(view, view->current_row-1, view->current_col);
else {
set_current_cell(view, view->current_row-1, view->current_col);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
break;
case SCANCODE_CURSORBLOCKDOWN:
if (lParam & KS_SHIFT) {
set_highlight_cells_to(view, view->current_row+1, view->current_col);
}
else {
set_current_cell(view, view->current_row+1, view->current_col);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
break;
case SCANCODE_CURSORBLOCKLEFT:
if (lParam & KS_SHIFT)
set_highlight_cells_to(view, view->current_row, view->current_col-1);
else {
set_current_cell(view, view->current_row, view->current_col-1);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
break;
case SCANCODE_CURSORBLOCKRIGHT:
if (lParam & KS_SHIFT)
set_highlight_cells_to(view, view->current_row, view->current_col + 1);
else {
set_current_cell(view, view->current_row, view->current_col + 1);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
break;
case SCANCODE_HOME:
{
if (view->current_col == 0)
return 0;
if (lParam & KS_SHIFT)
set_highlight_cells_to(view, view->current_row, 1);
else {
set_current_cell(view, view->current_row, 1);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
}
break;
case SCANCODE_END:
{
int col = view->current_col;
int ncols = view->modal->get_col_count(view);
if (col == 0 || col > ncols)
return 0;
if (lParam & KS_SHIFT)
set_highlight_cells_to(view, view->current_row, ncols);
else {
set_current_cell(view, view->current_row, ncols);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
}
break;
case SCANCODE_PAGEDOWN:
{
int row, col;
int count;
RECT rect;
RECT cell_rect;
count = view->modal->get_row_count(view);
GetClientRect(view->hCtrl, &rect);
get_cell_rect (view, count, 0, &cell_rect);
if (rect.bottom > cell_rect.bottom)
get_cell_from_xy(view, cell_rect.left + 1, cell_rect.bottom - 1, &row, &col);
else
get_cell_from_xy(view, rect.left -1, rect.bottom - 1, &row, &col);
SendMessage (view->hCtrl, MSG_VSCROLL, SB_PAGEDOWN, 0);
if (lParam & KS_SHIFT)
set_highlight_cells_to(view, row, view->current_col);
else {
set_current_cell(view, row, view->current_col);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
}
break;
case SCANCODE_PAGEUP:
{
int row, col;
gvGridCellData* table;
table = view->modal->get_value_at(view, 0, 0);
get_cell_from_xy(view, table->data.table.width,
table->data.table.height,
&row, &col);
SendMessage (view->hCtrl, MSG_VSCROLL, SB_PAGEUP, 0);
if (lParam & KS_SHIFT)
set_highlight_cells_to(view, row, view->current_col);
else {
set_current_cell(view, row, view->current_col);
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
}
break;
case SCANCODE_ESCAPE:
if (view->highlight_cells.width != 0 || view->highlight_cells.height != 0)
{
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
break;
default:
need_break = TRUE;
break;
}
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_KEYDOWN);
if(need_break)
break;
else
return DefaultControlProc(hWnd, message, wParam, lParam);
}
break;
}
case MSG_CHAR:
{
gvGridCellData* cell;
RECT rect;
if(view->current_row > 0 && view->current_col > 0)
{
if (view->hEdit == HWND_INVALID && isalnum(wParam))
{
get_cell_rect(view, view->current_row, view->current_col, &rect);
gridview_make_rect_visible(view, &rect);
cell = view->modal->get_value_or_default_at(
view, view->current_row, view->current_col);
get_cell_rect(view, view->current_row, view->current_col, &rect);
cell->func->begin_edit(cell, view, view->current_row, view->current_col, &rect);
}
}
return DefaultControlProc(hWnd, message, wParam, lParam);
}
// }}}
// MiniGUI scroll Messages {{{
case MSG_HSCROLL:
{
if (view->hEdit != HWND_INVALID)
SendMessage(view->hEdit, MSG_KEYDOWN, SCANCODE_ENTER, 0);
break;
}
case MSG_VSCROLL:
{
if (view->hEdit != HWND_INVALID)
SendMessage(view->hEdit, MSG_KEYDOWN, SCANCODE_ENTER, 0);
break;
}
// }}}
// Grid View Messages {{{
case GRIDM_SETCELLPROPERTY:
{
int i, j;
GRIDCELLS* cells = (GRIDCELLS*)wParam;
GRIDCELLDATA* data = (GRIDCELLDATA*)lParam;
if (cells == NULL || data == NULL || data->content == NULL)
return GRID_ERR;
for (i = 0; i<cells->height; i++)
{
for (j = 0; j<cells->width; j++)
set_cell_value(view, data, cells->row + i, cells->column +j);
}
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_CELLTEXTCHANGED);
//gvGridCells_get_cells_rect(view, &cells, &r);
//InvalidateRect(hWnd, &r, TRUE);
InvalidateRect(hWnd, NULL, FALSE);
if(cells->row == 0 || cells->column == 0)
update_row_col_size(view);
gvGridCellDep_update_dep(view, cells);
return 0;
}
case GRIDM_GETCELLPROPERTY:
{
GRIDCELLS* cells = (GRIDCELLS*)wParam;
GRIDCELLDATA* data = (GRIDCELLDATA*)lParam;
if (data == NULL)
return GRID_ERR;
return fetch_cell_value(view, data, cells->row, cells->column);
}
case GRIDM_ADDCOLUMN:
{
int index = wParam;
GRIDCELLDATA* data = (GRIDCELLDATA*)lParam;
GRIDCELLDATAHEADER* header;
gvGridCellData cell;
RECT rect;
RECT header_rect;
if(data != NULL
&& ( (data->style & GV_TYPE_MASK) != GV_TYPE_HEADER
|| data->content == NULL))
return GRID_ERR;
index = view->modal->add_new_col(view, index);
if(index == -1)
return GRID_ERR;
gvGridCellTypeHeader->init(&cell);
if(data != NULL)
{
header = (GRIDCELLDATAHEADER*)data->content;
cell.data.header.size = header->size;
cell.data.header.text = strdup(header->buff);
}
else
{
cell.data.header.size = -1;
cell.data.header.text = NULL;
}
view->modal->set_value_at(view, 0, index, &cell);
update_row_col_size(view);
get_cell_rect (view, 0, index, &rect);
// Update column header default content,
header_rect.bottom = rect.bottom;
header_rect.top = rect.top;
header_rect.left = view->cells_rect.left;
header_rect.right = view->cells_rect.right;
// Change cols position
if (rect.left <= view->cells_rect.left) {
InvalidateRect (hWnd, &header_rect, FALSE);
update_viewport (view, view->cells_rect.left, RECTW(rect), 0);
}
if (rect.left > view->cells_rect.left &&
rect.left < view->cells_rect.right) {
InvalidateRect (hWnd, &header_rect, FALSE);
update_viewport (view, rect.left, RECTW(rect), 0);
}
// Change current col
if (view->current_col >= index) {
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
return GRID_OKAY;
}
case GRIDM_DELCOLUMN:
{
int index = lParam;
int count;
RECT rect;
RECT header_rect;
count = view->modal->get_col_count(view);
if(index >= count)
return GRID_ERR;
if (count == 1) {
gvGridCellData *cell;
int row_count = view->modal->get_row_count(view);
if (row_count > 1) {
int i;
for (i = 1; i < row_count; i++){
cell = view->modal->get_value_at(view, i, 1);
if (cell != NULL && cell->func->on_destroy != NULL)
cell->func->on_destroy(cell);
}
InvalidateRect (hWnd, NULL, TRUE);
}
set_current_cell (view, 1, 1);
return GRID_OKAY;
}
get_cell_rect (view, 0, index, &rect);
view->modal->delete_col(view, index);
update_row_col_size(view);
// Update column header default content,
header_rect.bottom = rect.bottom;
header_rect.top = rect.top;
header_rect.left = view->cells_rect.left;
header_rect.right = view->cells_rect.right;
// Change cols position
if (rect.left <= view->cells_rect.left) {
InvalidateRect (hWnd, &header_rect, TRUE);
update_viewport (view, view->cells_rect.left, -RECTW(rect), 0);
}
if (rect.left > view->cells_rect.left &&
rect.left <= view->cells_rect.right) {
InvalidateRect (hWnd, &header_rect, TRUE);
update_viewport (view, rect.left, -RECTW(rect), 0);
}
// Change current col
if (view->current_col >= index)
set_highlight_cells (view, view->current_row, view->current_col, 1, 1);
return GRID_OKAY;
}
case GRIDM_ADDROW:
{
int index = wParam;
GRIDCELLDATA* data = (GRIDCELLDATA*)lParam;
GRIDCELLDATAHEADER* header;
gvGridCellData cell;
RECT rect;
RECT header_rect;
if(data != NULL
&& ( (data->style & GV_TYPE_MASK) != GV_TYPE_HEADER
|| data->content == NULL))
return GRID_ERR;
index = view->modal->add_new_row(view, index);
if(index == -1)
return GRID_ERR;
gvGridCellTypeHeader->init(&cell);
if(data != NULL) {
header = (GRIDCELLDATAHEADER*)data->content;
cell.data.header.size = header->size;
cell.data.header.text = strdup(header->buff);
}
else {
cell.data.header.size = -1;
cell.data.header.text = NULL;
}
view->modal->set_value_at(view, index, 0, &cell);
update_row_col_size(view);
get_cell_rect (view, index, 0, &rect);
header_rect.left = rect.left;
header_rect.right = rect.right;
header_rect.top = view->cells_rect.top;
header_rect.bottom = view->cells_rect.bottom;
if (rect.top <= view->cells_rect.top) {
InvalidateRect (hWnd, &header_rect, TRUE);
update_viewport (view, view->cells_rect.top, RECTH(rect), 1);
}
if (rect.top > view->cells_rect.top &&
rect.top < view->cells_rect.bottom) {
InvalidateRect (hWnd, &header_rect, TRUE);
update_viewport (view, rect.top, RECTH(rect), 1);
}
if (view->current_row >= index) {
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
}
return GRID_OKAY;
}
case GRIDM_DELROW:
{
int index = lParam;
int count;
RECT rect;
RECT header_rect;
count = view->modal->get_row_count(view);
if(index >= count)
return GRID_ERR;
if (count == 1) {
gvGridCellData *cell;
int col_count = view->modal->get_col_count(view);
if (col_count > 1) {
int i;
for (i = 1; i < col_count; i++){
cell = view->modal->get_value_at(view, 1, i);
if (cell != NULL && cell->func->on_destroy != NULL)
cell->func->on_destroy(cell);
}
InvalidateRect (hWnd, NULL, TRUE);
}
set_current_cell (view, 1, 1);
return GRID_OKAY;
}
get_cell_rect (view, index, 0, &rect);
view->modal->delete_row(view, index);
update_row_col_size(view);
// Updata row header default content
header_rect.left = rect.left;
header_rect.right = rect.right;
header_rect.top = view->cells_rect.top;
header_rect.bottom = view->cells_rect.bottom;
// Change rows position
if (rect.top <= view->cells_rect.top) {
InvalidateRect (hWnd, &header_rect, TRUE);
update_viewport (view, view->cells_rect.top, -RECTH(rect), 1);
}
if (rect.top > view->cells_rect.top &&
rect.top < view->cells_rect.bottom) {
InvalidateRect (hWnd, &header_rect, TRUE);
update_viewport (view, rect.bottom, -RECTH(rect), 1);
}
// Change current row
if (view->current_row >= index)
set_highlight_cells(view, view->current_row, view->current_col, 1, 1);
return GRID_OKAY;
}
case GRIDM_GETROWCOUNT:
{
return view->modal->get_row_count(view);
}
case GRIDM_GETCOLCOUNT:
{
return view->modal->get_col_count(view);
}
case GRIDM_SETCOLWIDTH:
{
gvGridCellData* col_head;
int size, col, width;
RECT up_rect;
RECT re_rect;
col = (int) wParam;
width = (int) lParam;
col_head = view->modal->get_value_at(view, 0, col);
if (col_head == NULL)
return -1;
if (col == 0) {
size = width - col_head->data.table.width;
col_head->data.table.width = width;
}
else {
size = width - col_head->data.header.size;
col_head->data.header.size = width;
col_head->data.header.end_xy += size;
}
get_cell_rect (view, 0, col, &up_rect);
update_row_col_size(view);
if (up_rect.right >= view->cells_rect.left &&
up_rect.left <= view->cells_rect.right) {
update_viewport (view, up_rect.right, size, 0);
GetClientRect (hWnd, &re_rect);
re_rect.left = up_rect.left;
re_rect.right = up_rect.right;
InvalidateRect (view->hCtrl, &re_rect, TRUE);
}
if (up_rect.left < view->cells_rect.left)
update_viewport (view, view->cells_rect.left, size, 0);
break;
}
case GRIDM_GETCOLWIDTH:
{
int col = (int) lParam;
gvGridCellData* col_head;
col_head = view->modal->get_value_at(view, 0, col);
if (col_head == NULL)
return GRID_ERR;
if (col == 0)
return col_head->data.table.width;
else
return col_head->data.header.size;
}
case GRIDM_SETROWHEIGHT:
{
gvGridCellData* row_head;
int size, row, height;
RECT up_rect;
RECT re_rect;
row = (int) wParam;
height = (int) lParam;
row_head = view->modal->get_value_at(view, wParam, 0);
if (row_head == NULL)
return -1;
if (row == 0) {
size = height - row_head->data.table.height;
row_head->data.table.height = height;
}
else {
size = height - row_head->data.header.size;
row_head->data.header.size = height;
row_head->data.header.end_xy += size;
}
get_cell_rect (view, row, 0, &up_rect);
update_row_col_size(view);
if (up_rect.top >= view->cells_rect.top &&
up_rect.top <= view->cells_rect.bottom) {
update_viewport (view, up_rect.bottom, size, 1);
GetClientRect (hWnd, &re_rect);
re_rect.top = up_rect.top;
re_rect.bottom = up_rect.bottom;
InvalidateRect (view->hCtrl, &re_rect, TRUE);
}
if (up_rect.top < view->cells_rect.top)
update_viewport (view, view->cells_rect.top, size, 1);
break;
}
case GRIDM_GETROWHEIGHT:
{
int row = (int) lParam;
gvGridCellData* row_head;
row_head = view->modal->get_value_at(view, row, 0);
if (row_head == NULL)
return GRID_ERR;
if (row == 0)
return row_head->data.table.height;
else
return row_head->data.header.size;
}
case GRIDM_SETNUMFORMAT:
{
GRIDCELLS* cells = (GRIDCELLS*)wParam;
char *format = (char *)lParam;
gvGridCellData* set_cell = NULL;
int i, j;
RECT rect;
if (format == NULL || cells == NULL)
break;
for (i = cells->row; i < cells->row + cells->height; ++i)
{
for (j = cells->column; j < cells->column + cells->width; ++j)
{
set_cell = view->modal->get_value_at(view, i, j);
if (set_cell == NULL) {
continue;
}
if (set_cell->celltype != GV_TYPE_NUMBER)
continue;
if (set_cell->data.number.format != NULL)
free(set_cell->data.number.format);
set_cell->data.number.format = strdup(format);
}
}
gvGridCells_get_cells_rect(view, cells, &rect);
InvalidateRect(view->hCtrl, &rect, FALSE);
break;
}
case GRIDM_SETSELECTED:
{
GRIDCELLS* cells = (GRIDCELLS*)lParam;
set_highlight_cells(view, cells->row, cells->column, cells->width, cells->height);
return 0;
}
case GRIDM_GETSELECTED:
{
GRIDCELLS* cells = (GRIDCELLS*)lParam;
*cells = view->highlight_cells;
break;
}
case GRIDM_ADDDEPENDENCE:
{
GRIDCELLDEPENDENCE* dep = (GRIDCELLDEPENDENCE*)lParam;
gvGridCellDepedence depedence;
depedence.source.row = dep->source.row;
depedence.source.column = dep->source.column;
depedence.source.width = dep->source.width;
depedence.source.height = dep->source.height;
depedence.target.row = dep->target.row;
depedence.target.column = dep->target.column;
depedence.target.width = dep->target.width;
depedence.target.height = dep->target.height;
depedence.callback = dep->callback;
depedence.dwAddData = dep->dwAddData;
return gvGridCellDep_add_dep(view, &depedence);
}
case GRIDM_DELDEPENDENCE:
{
if (gvGridCellDep_delete_dep(view, wParam) == -1)
return GRID_ERR;
return GRID_OKAY;
}
case GRIDM_ENDCELLEDIT:
{
GRIDCELLS cells;
GridCellEditData* editdata = (GridCellEditData*)lParam;
editdata->commit = (int)wParam;
editdata->cell->func->end_edit(editdata->cell, editdata->view, editdata->row, editdata->col);
cells.width = cells.height = 1;
cells.row = editdata->row;
cells.column = editdata->col;
gvGridCellDep_update_dep(view, &cells);
NotifyParent(view->hCtrl, view->ctrl_id, GRIDN_CELLTEXTCHANGED);
return 0;
}
// }}}
}
return DefaultScrolledProc (hWnd, message, wParam, lParam);
}
BOOL RegisterGridViewControl (void)
{
WNDCLASS WndClass;
WndClass.spClassName = CTRL_GRIDVIEW;
WndClass.dwStyle = WS_NONE;
WndClass.dwExStyle = WS_EX_NONE;
WndClass.hCursor = GetSystemCursor (0);
#ifdef _MGSCHEMA_COMPOSITING
WndClass.dwBkColor = GetWindowElementAttr (HWND_NULL, WE_BGC_WINDOW);
#else
WndClass.iBkColor =
GetWindowElementPixelEx (HWND_NULL, HDC_SCREEN, WE_BGC_WINDOW);
#endif
WndClass.WinProc = GridViewCtrlProc;
return gui_AddNewControlClass (&WndClass) == ERR_OK;
}
#endif /* _MGCTRL_GRIDVIEW */