#include #include #include #include #include "gmemory.h" #include "ggridview.h" #include "tablerender.h" //*********************************** // gTableData //*********************************** gTableData::gTableData() { text = NULL; richText = NULL; markup = NULL; picture = NULL; font = NULL; bg = COLOR_DEFAULT; fg = COLOR_DEFAULT; padding = 0; alignment = ALIGN_NORMAL; } gTableData::~gTableData() { clear(); } void gTableData::setText(const char *vl) { if (text) g_free(text); if (vl) text = g_strdup(vl); else text = NULL; } void gTableData::setRichText(const char *vl) { if (richText) { g_free(richText); g_free(markup); } if (vl) { richText = g_strdup(vl); markup = gt_html_to_pango_string(richText, -1, true); } else { richText = NULL; markup = NULL; } } void gTableData::clear() { if (text) g_free(text); if (richText) g_free(richText); if (markup) g_free(markup); gFont::assign(&font); gPicture::assign(&picture); text = NULL; richText = NULL; markup = NULL; picture=NULL; font=NULL; bg = COLOR_DEFAULT; fg = COLOR_DEFAULT; padding = 0; alignment = ALIGN_NORMAL; } //*********************************** // gTable //*********************************** gboolean gTable_ecol(gTablePair *key,gTableData *data,int number) { if ( (key->row == number) && (key->col!=-1) ) return true; return false; } gboolean gTable_remove_row(gTablePair *key,gTableData *data,int number) { if (key->row >= number) return true; return false; } gboolean gTable_remove_col(gTablePair *key,gTableData *data,int number) { if (key->col >= number) return true; return false; } gboolean gTable_equal (gTablePair *a, gTablePair *b) { if ( a->col != b->col) return false; if ( a->row != b->row) return false; return true; } void gTable_ekey(gTablePair *pair) { if (pair) g_free(pair); } void gTable_edata(gTableData *data) { if (data) delete data; } gTable::gTable() { colpos = colsize = NULL; rowpos = rowsize = NULL; columns=0; rows=0; doNotInvalidate = false; seldata=g_hash_table_new_full((GHashFunc)g_int_hash,(GEqualFunc)gTable_equal, (GDestroyNotify)gTable_ekey,NULL); data=g_hash_table_new_full((GHashFunc)g_int_hash,(GEqualFunc)gTable_equal, (GDestroyNotify)gTable_ekey,(GDestroyNotify)gTable_edata); } gTable::~gTable() { g_hash_table_destroy(data); g_hash_table_destroy(seldata); g_free(rowsize); g_free(rowpos); g_free(colsize); g_free(colpos); } int gTable::columnCount() { return columns; } int gTable::rowCount() { return rows; } void gTable::setRowCount(int number) { int bc; if (number<0) number=0; if (number==rows) return; if (number>rows) { if (!rows) { rowsize = g_new(int, number); rowpos = g_new(int, number); } else { rowsize = g_renew(int, rowsize, number); rowpos = g_renew(int, rowpos, number); } for (bc=rows; bccolumns) { if (!columns) { colsize = g_new(int, number); colpos = g_new(int, number); } else { colsize = g_renew(int, colsize, number); colpos = g_renew(int, colpos, number); } for (bc=columns; bc= columns || row < 0 || row >= rows) return NULL; d = (gTableData*)g_hash_table_lookup(data, (gpointer)&pair); if (!d) { if (create) { key = (gTablePair *)g_malloc(sizeof(gTablePair)); key->row = row; key->col = col; d = new gTableData(); g_hash_table_insert(data, (gpointer)key, (gpointer)d); } else { cell.clear(); if (voidCell) voidCell(&cell, row, col, userData); d = &cell; } } return d; } void gTable::moveCell(int srow, int scol, int drow, int dcol) { gTableData *d; gTablePair pair; gTablePair *key; pair.row = drow; pair.col = dcol; g_hash_table_remove(data, (gpointer)&pair); pair.row = srow; pair.col = scol; if (!g_hash_table_lookup_extended(data, (gconstpointer)&pair, POINTER(&key), POINTER(&d))) return; g_hash_table_steal(data, &pair); key->row = drow; key->col = dcol; g_hash_table_insert(data, (gpointer)key, (gpointer)d); } void gTable::clearField (int col,int row) { gTableData *ptr; gTablePair pair={row,col}; if ( (col<0) || (col>=columns) ) return; if ( (row<0) || (row>=rows) ) return; ptr=(gTableData*)g_hash_table_lookup(data,(gpointer)&pair); if (!ptr) return; g_hash_table_remove(data,(gpointer)&pair); } char* gTable::getFieldText(int col,int row) { gTableData *d = getData(row, col); return d ? d->text : NULL; } void gTable::setFieldText(int col,int row, const char* value) { gTableData *d = getData(row, col, true); d->setText(value); } char* gTable::getFieldRichText(int col,int row) { gTableData *d = getData(row, col); return d ? d->richText : NULL; } void gTable::setFieldRichText(int col,int row, const char* value) { gTableData *d = getData(row, col, true); d->setRichText(value); } gColor gTable::getFieldFg (int col,int row) { gTableData *d = getData(row, col); return d ? d->fg : COLOR_DEFAULT; } void gTable::setFieldFg (int col,int row, gColor value) { gTableData *d = getData(row, col, true); d->fg = value; } gColor gTable::getFieldBg (int col,int row) { gTableData *d = getData(row, col); return d ? d->bg : COLOR_DEFAULT; } void gTable::setFieldBg (int col,int row, gColor value) { gTableData *d = getData(row, col, true); d->bg = value; } int gTable::getFieldPadding(int col, int row) { gTableData *d = getData(row, col); return d ? d->padding : 0; } void gTable::setFieldPadding(int col, int row, int value) { gTableData *d = getData(row, col, true); d->padding = value; } int gTable::getFieldAlignment(int col, int row) { gTableData *d = getData(row, col); return d ? d->alignment : 0; } void gTable::setFieldAlignment(int col, int row, int value) { gTableData *d = getData(row, col, true); d->alignment = value; } gPicture *gTable::getFieldPicture(int col, int row) { gTableData *d = getData(row, col); return d ? d->picture : 0; } void gTable::setFieldPicture(int col, int row, gPicture *value) { gTableData *d = getData(row, col, true); d->setPicture(value); } gFont *gTable::getFieldFont(int col, int row) { gTableData *d = getData(row, col); return d ? d->font : 0; } void gTable::setFieldFont(int col, int row, gFont *value) { gTableData *d = getData(row, col, true); d->setFont(value); } bool gTable::getRowSelected(int row) { gTablePair pair={row,-1}; if ( (row<0) || (row>=rows) ) return false; if (g_hash_table_lookup(seldata,(gpointer)&pair)) return true; return false; } void gTable::setRowSelected(int row,bool value) { gTablePair pair={row,-1}; gTablePair *key; if ( (row<0) || (row>=rows) ) return; if (g_hash_table_lookup(seldata,(gpointer)&pair)) { if (value) return; g_hash_table_remove(seldata,(gpointer)&pair); } if (value) { key=(gTablePair*)g_malloc(sizeof(gTablePair)); key->row=row; key->col=-1; g_hash_table_insert(seldata,(gpointer)key,(gpointer)true); } if (!value) g_hash_table_foreach_remove(seldata,(GHRFunc)gTable_ecol,(gpointer)row); } bool gTable::getFieldSelected (int col,int row) { gTablePair pair={row,col}; if ( (col<0) || (col>=columns) ) return false; if ( (row<0) || (row>=rows) ) return false; if (g_hash_table_lookup(seldata,(gpointer)&pair)) return true; pair.col=-1; if (g_hash_table_lookup(seldata,(gpointer)&pair)) return true; return false; } void gTable::setFieldSelected (int col,int row,bool value) { gTablePair pair={row,col}; gTablePair *key; int bc; if ( (col<0) || (col>=columns) ) return; if ( (row<0) || (row>=rows) ) return; if (g_hash_table_lookup(seldata,(gpointer)&pair)) { if (value) return; g_hash_table_remove(seldata,(gpointer)&pair); pair.col=-1; if (g_hash_table_lookup(seldata,(gpointer)&pair)) g_hash_table_remove(seldata,(gpointer)&pair); return; } if (!value) { pair.col=-1; if (g_hash_table_lookup(seldata,(gpointer)&pair)) { g_hash_table_remove(seldata,(gpointer)&pair); for (bc=0; bcrow=row; key->col=col; g_hash_table_insert(seldata,(gpointer)key,(gpointer)value); } int gTable::getColumnPos(int index) { int pos, i; if (index < 0 || index >= columns) return -1; pos = colpos[index]; if (pos < 0) { i = index; for(;;) { i--; if (colpos[i] >= 0) break; } pos = colpos[i]; for(;;) { pos += colsize[i]; i++; if (i > index) break; colpos[i] = pos; } pos = colpos[index]; } return pos; } int gTable::getRowPos(int index) { int pos, i; if (index < 0 || index >= rows) return -1; pos = rowpos[index]; if (pos < 0) { i = index; for(;;) { i--; if (rowpos[i] >= 0) break; } pos = rowpos[i]; for(;;) { pos += rowsize[i]; i++; if (i > index) break; rowpos[i] = pos; } pos = rowpos[index]; } return pos; } int gTable::getColumnSize (int position) { if (position>=columns) return -1; if (position<0) return -1; return colsize[position]; } void gTable::setColumnSize(int position, int value) { int i; if (position < 0 || position >= columns) return; if (value<0) value=0; colsize[position]=value; if (!doNotInvalidate) { for (i = position + 1; i < columns; i++) colpos[i] = -1; } } int gTable::getRowSize (int position) { if (position>=rows) return -1; if (position<0) return -1; return rowsize[position]; } void gTable::setRowSize (int position,int value) { int i; if (position < 0 || position >= rows) return; if (value < 0) value = 0; rowsize[position] = value; if (!doNotInvalidate) { for (i = position + 1; i < rows; i++) rowpos[i] = -1; } } //*********************************** // gTableRender //*********************************** static gboolean tbrender_expose(GtkWidget *wid,GdkEventExpose *e,gTableRender *data) { data->render(&e->area); return FALSE; } gTableRender::gTableRender(gGridView *v) { voidCell=NULL; userData=NULL; grid=true; txt=(GtkCellRendererText*)gtk_cell_renderer_text_new(); g_object_ref_sink(txt); pix=(GtkCellRendererPixbuf*)gtk_cell_renderer_pixbuf_new(); g_object_ref_sink(pix); view = v; sf = view->contents; offX=0; offY=0; firstRow = offRow = 0; firstCol = offCol = 0; g_object_ref(G_OBJECT(sf)); g_signal_connect(G_OBJECT(sf),"expose-event",G_CALLBACK(tbrender_expose),this); //g_signal_connect(G_OBJECT(sf),"size-allocate",G_CALLBACK(cb_render_size_allocate),this); } void gTableRender::queryUpdate(int row,int col) { GdkRectangle rect={-getOffsetX(),-getOffsetY(),0,0}; int bc; if (row>=rowCount()) return; if (col>=columnCount()) return; if (!sf->window) return; for (bc=0; bcwindow,&rect.width,NULL); } if (row < 0) { rect.y = 0; gdk_drawable_get_size(sf->window,NULL,&rect.height); } gdk_window_invalidate_rect(sf->window,&rect,TRUE); } gTableRender::~gTableRender() { g_object_unref(G_OBJECT(pix)); g_object_unref(G_OBJECT(txt)); if (sf) g_object_unref(G_OBJECT(sf)); } bool gTableRender::drawGrid() { return grid; } void gTableRender::setDrawGrid(bool vl) { if (grid == vl) return; grid=vl; gtk_widget_queue_draw(sf); } int gTableRender::visibleWidth() { int vl; if (!sf->window) return 0; gdk_drawable_get_size (sf->window,&vl,NULL); return vl; } int gTableRender::visibleHeight() { int vl; if (!sf->window) return 0; gdk_drawable_get_size (sf->window,NULL,&vl); return vl; } int gTableRender::width() { if (columnCount() <= 0) return 0; else return getColumnPos(columnCount() - 1) + getColumnSize(columnCount() - 1); } int gTableRender::height() { if (rowCount() <= 0) return 0; else return getRowPos(rowCount() - 1) + getRowSize(rowCount() - 1); } int gTableRender::getOffsetX() { return offX; } int gTableRender::getOffsetY() { return offY; } void gTableRender::setOffsetX(int vl) { int diff; if (offX != vl) { diff=offX-vl; offX=vl; /*for (offCol = 0, firstCol = 0; firstCol < columnCount(); firstCol++) { w = getColumnSize(firstCol); if ((offCol + w) > offX) break; offCol += w; }*/ firstCol = findColumn(offX); offCol = getColumnPos(firstCol); if (!sf->window) return; gdk_window_scroll(sf->window,diff,0); } } void gTableRender::setOffsetY(int vl) { int diff; if (offY != vl) { diff=offY-vl; offY=vl; /*for (offRow = 0, firstRow = 0; firstRow < rowCount(); firstRow++) { h = getRowSize(firstRow); if ((offRow + h) > offY) break; offRow += h; }*/ firstRow = findRow(offY); offRow = getRowPos(firstRow); if (!sf->window) return; gdk_window_scroll(sf->window,0,diff); } } void gTableRender::renderCell(gTableData *data, GdkGC *gc, GdkRectangle *rect, bool sel) { GdkColor color; GtkStyle *st; char *markup = data->markup; char *buf = data->text; int padding = data->padding; double xa, ya; if (padding < 1) padding = 1; rect->x += padding; rect->y += padding; rect->width -= padding * 2; rect->height -= padding * 2; if (rect->width < 1 || rect->height < 1) return; xa = gt_from_alignment(data->alignment, false); ya = gt_from_alignment(data->alignment, true); g_object_set(G_OBJECT(txt), /*"font-desc", data->font ? data->font->desc() : sf->style->font_desc,*/ "xalign", xa, "yalign", ya, (void *)NULL); gt_set_cell_renderer_text_from_font(txt, data->font ? data->font : view->font()); if (sel) { st = gt_get_style("GtkEntry", GTK_TYPE_ENTRY); g_object_set(G_OBJECT(txt),"foreground-gdk", &st->text[GTK_STATE_SELECTED],(void *)NULL); g_object_set(G_OBJECT(txt),"background-gdk", &st->base[GTK_STATE_SELECTED],(void *)NULL); } else { g_object_set(G_OBJECT(txt), "foreground-set", data->fg != COLOR_DEFAULT, "background-set", data->bg != COLOR_DEFAULT, (void *)NULL); if (data->fg != COLOR_DEFAULT) { fill_gdk_color(&color, data->fg); g_object_set(G_OBJECT(txt),"foreground-gdk", &color, (void *)NULL); } if (data->bg != COLOR_DEFAULT) { fill_gdk_color(&color, data->bg); g_object_set(G_OBJECT(txt),"background-gdk", &color, (void *)NULL); } } if (markup) g_object_set(G_OBJECT(txt), "text", NULL, "markup", markup, (void *)NULL); else g_object_set(G_OBJECT(txt), "markup", NULL, "text", buf, (void *)NULL); gtk_cell_renderer_render(GTK_CELL_RENDERER(txt),sf->window,sf,rect,rect,rect,(GtkCellRendererState)0); if (data->picture) { if ((markup && *markup) || (buf && *buf)) { xa = 0; ya = 0.5; } g_object_set(G_OBJECT(pix), "pixbuf", data->picture->getPixbuf(), "xalign", xa, "yalign", ya, (void *)NULL); gtk_cell_renderer_render(GTK_CELL_RENDERER(pix),sf->window,sf,rect,rect,rect,(GtkCellRendererState)0); } } void gTableRender::render(GdkRectangle *ar) { bool sel; int pos; int bx,by; int bpos; int cpos; int pmaxX; int pmaxY; int bufW = 0; GdkGC *gc; GdkRectangle rect; gTableData *cell; gTableData *caux; if (!sf->window) return; gdk_window_clear_area(sf->window,ar->x,ar->y,ar->width,ar->height); //fprintf(stderr, "render: %d %d %d %d\n", ar->x,ar->y,ar->width,ar->height); if ( (!rowCount()) || (!columnCount()) ) return; caux=new gTableData(); // Prepare data pmaxX=width(); pmaxY=height(); if (pmaxX>visibleWidth()) pmaxX=visibleWidth(); if (pmaxY>visibleHeight()) pmaxY=visibleHeight(); gc=gdk_gc_new(sf->window); gdk_gc_set_background(gc,&sf->style->base[GTK_STATE_NORMAL]); gdk_gc_set_clip_origin(gc,0,0); gdk_gc_set_clip_rectangle(gc,ar); // Rendering texts and pixbufs bpos = offCol; for (bx=firstCol; bxx) { bpos += getColumnSize(bx); continue; } if ((bpos - offX) >= visibleWidth()) break; if ((bpos - offX) >= (ar->x + ar->width)) break; cpos = offRow; for (by = firstRow; by < rowCount(); by++) { if ((cpos + getRowSize(by) - offY) < ar->y ) { cpos += getRowSize(by); continue; } if ((cpos - offY) >= visibleHeight()) break; if ((cpos - offY) >= (ar->y + ar->height)) break; cell = getData(by, bx); rect.x=bpos-offX+bufW; rect.y=cpos-offY; rect.width=getColumnSize(bx)-bufW; rect.height=getRowSize(by); sel=getFieldSelected(bx,by); gdk_gc_set_clip_rectangle(gc, &rect); renderCell(cell, gc, &rect, sel); cpos += getRowSize(by); } bpos += getColumnSize(bx); } delete caux; if (!grid) { g_object_unref(G_OBJECT(gc)); return; } gdk_gc_set_clip_origin(gc,0,0); gdk_gc_set_clip_rectangle(gc,ar); //gint8 dashes[2] = { 1, 1 }; //gdk_gc_set_line_attributes(gc, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_NOT_LAST, GDK_JOIN_MITER); gdk_gc_set_foreground(gc, &sf->style->mid[GTK_STATE_NORMAL]); // Horizontal lines //gdk_gc_set_dashes(gc, offY & 1, dashes, 2); bpos = offRow; for (bx = firstRow; bx < rowCount(); bx++) { bpos += getRowSize(bx); pos = bpos - offY - 1; if (pos > visibleHeight()) break; if (pos >= 0) gdk_draw_line(sf->window, gc, 0, pos, pmaxX - 1, pos); } // Vertical lines //gdk_gc_set_dashes(gc, offX & 1, dashes, 2); bpos = offCol; for (bx = firstCol; bx < columnCount(); bx++) { bpos += getColumnSize(bx); pos = bpos - offX - 1; if (pos >= visibleWidth()) break; if (pos >= 0) gdk_draw_line(sf->window, gc, pos, 0, pos, pmaxY - 1); } g_object_unref(G_OBJECT(gc)); } void gTableRender::setRowSize(int position,int value) { GdkRectangle rect={0,0,0,0}; int pos; if (position<0) return; if (position>=rowCount()) return; gTable::setRowSize(position,value); if (view->locked() || !view->isVisible()) return; if (!sf->window) return; gdk_drawable_get_size (sf->window,&rect.width,&rect.height); pos = getRowPos(position) - offY; if ((pos + getRowSize(position))<0) return; if (pos > rect.height) return; gdk_window_invalidate_rect(sf->window, &rect, TRUE); } void gTableRender::setColumnSize(int position,int value) { GdkRectangle rect={0,0,0,0}; int pos; if (position<0) return; if (position>=columnCount()) return; gTable::setColumnSize(position,value); if (view->locked() || !view->isVisible()) return; if (!sf->window) return; gdk_drawable_get_size (sf->window,&rect.width,&rect.height); pos = getColumnPos(position) - offX; if ((pos + getColumnSize(position)) < 0) return; if (pos > rect.width) return; gdk_window_invalidate_rect(sf->window, &rect, TRUE); } #define CHECK_COORD(_col, _row) if ((_col) < 0 || (_row) < 0 || (_col) >= columnCount() || (_row) > rowCount()) return void gTableRender::clearField (int col,int row) { CHECK_COORD(col, row); gTable::clearField(col,row); queryUpdate(row,col); } void gTableRender::setFieldText (int col,int row, const char* value) { CHECK_COORD(col, row); gTable::setFieldText(col,row,value); queryUpdate(row,col); } void gTableRender::setFieldRichText (int col,int row, const char* value) { CHECK_COORD(col, row); gTable::setFieldRichText(col,row,value); queryUpdate(row,col); } void gTableRender::setFieldFg (int col,int row,gColor value) { CHECK_COORD(col, row); gTable::setFieldFg(col,row,value); queryUpdate(row,col); } void gTableRender::setFieldBg (int col,int row,gColor value) { CHECK_COORD(col, row); gTable::setFieldBg(col,row,value); queryUpdate(row,col); } void gTableRender::setFieldPadding(int col,int row,int value) { CHECK_COORD(col, row); gTable::setFieldPadding(col,row,value); queryUpdate(row,col); } void gTableRender::setFieldPicture(int col, int row, gPicture *value) { CHECK_COORD(col, row); gTable::setFieldPicture(col, row, value); queryUpdate(row, col); } void gTableRender::setFieldFont(int col, int row, gFont *value) { CHECK_COORD(col, row); gTable::setFieldFont(col, row, value); queryUpdate(row, col); } void gTableRender::setFieldSelected(int col,int row,bool value) { gTable::setFieldSelected(col,row,value); queryUpdate(row,-1); } void gTableRender::setRowSelected(int row,bool value) { if (row<0) return; if (row>=rowCount()) return; if (value == getRowSelected(row)) return; gTable::setRowSelected(row,value); queryUpdate(row,-1); //view->emit(SIGNAL(view->onSelect)); } void gTableRender::clearSelection() { g_hash_table_destroy(seldata); seldata=g_hash_table_new_full((GHashFunc)g_int_hash,(GEqualFunc)gTable_equal, (GDestroyNotify)gTable_ekey,NULL); gtk_widget_queue_draw(sf); //view->emit(SIGNAL(view->onSelect)); } void gTableRender::selectRows(int start, int length, bool value) { int end, i; if (length < 0) length = rowCount(); end = start + length - 1; if (end < start) return; if (start < 0) start = 0; if (end >= rowCount()) end = rowCount() - 1; view->lock(); for (i = start; i <= end; i++) setRowSelected(i, value); view->unlock(); } int gTableRender::findVisibleRow(int y) { int pos, row, h; pos = offRow; y += offY; for (row = firstRow; row < rowCount(); row++) { h = getRowSize(row); if (y < (pos + h)) return row; pos += h; } return -1; } int gTableRender::findVisibleColumn(int x) { int pos, col, w; pos = offCol; x += offX; for (col = firstCol; col < columnCount(); col++) { w = getColumnSize(col); if (x < (pos + w)) return col; pos += w; } return -1; } int gTableRender::findRow(int pos) { int d, f, row; d = 0; f = rowCount(); while (f > d) { row = (d + f) / 2; if (pos < getRowPos(row)) { f = row; continue; } if (pos >= (getRowPos(row) + getRowSize(row))) { d = row + 1; continue; } return row; } return -1; } int gTableRender::findColumn(int pos) { int d, f, col; d = 0; f = columnCount(); while (f > d) { col = (d + f) / 2; if (pos < getColumnPos(col)) { f = col; continue; } if (pos >= (getColumnPos(col) + getColumnSize(col))) { d = col + 1; continue; } return col; } return -1; } void gTableRender::insertRows(int start, int length) { int i, j, c; if (start < 0 || length <= 0 || start > rowCount()) return; c = rowCount(); setRowCount(rowCount() + length); for (i = c - 1; i >= start; i--) { for (j = 0; j < columnCount(); j++) moveCell(i, j, i + length, j); queryUpdate(i, -1); queryUpdate(i + length, -1); } } void gTableRender::removeRows(int start, int length) { int i, j, dst; dst = start; for (i = start + length; i < rowCount(); i++) { for (j = 0; j < columnCount(); j++) moveCell(i, j, dst, j); queryUpdate(i, -1); queryUpdate(dst, -1); dst++; } setRowCount(rowCount() - length); }