/////////////////////////////////////////////////////////////////////////////// // // 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 . * * 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 * . */ /* ** gridview.c: an grid control ** ** Created by Peng Ke at 2004/11/29 */ #include #include #include #include #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; iheight; i++) { for (j = 0; jwidth; 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 */