mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-09-30 06:23:22 +08:00
2552 lines
80 KiB
C
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 */
|
|
|