/*************************************************************************** ggridview.cpp (c) 2004-2006 - Daniel Campos Fernández Gtkmae "GTK+ made easy" classes Realizado para la Junta de Extremadura. Consejería de Educación Ciencia y Tecnología. Proyecto gnuLinEx 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 1, 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************/ #include "widgets.h" #include "widgets_private.h" #include "tablerender.h" #include "ggridview.h" class gGridHeader { public: char *header; char *footer; bool resizable; gGridHeader(); ~gGridHeader(); void setHeader(const char *vl); void setFooter(const char *vl); }; class gGridRow { public: char *text; bool resizable; gGridRow(); ~gGridRow(); void setText(const char *vl); }; static char void_row_buffer[16]; /************************************************************************** gGridHeader ***************************************************************************/ gGridHeader::gGridHeader() { resizable=true; header=NULL; footer=NULL; } gGridHeader::~gGridHeader() { if (header) g_free(header); if (footer) g_free(footer); } void gGridHeader::setHeader(const char *vl) { if (!vl) vl=""; if (header) g_free(header); header = g_strdup(vl); } void gGridHeader::setFooter(const char *vl) { if (!vl) vl=""; if (footer) g_free(footer); footer = g_strdup(vl); } /************************************************************************** gGridRow ***************************************************************************/ gGridRow::gGridRow() { resizable=false; text=NULL; } gGridRow::~gGridRow() { if (text) g_free(text); } void gGridRow::setText(const char *vl) { if (!vl) vl=""; if (text) g_free(text); text = g_strdup(text); } /************************************************************************** gGridView ***************************************************************************/ static void gGridView_hbar(GtkAdjustment *adj,gGridView *data) { gtk_widget_queue_draw(data->header); gtk_widget_queue_draw(data->footer); data->render->setOffsetX((int)gtk_adjustment_get_value (adj)); if (data->onScroll) data->onScroll(data); } static void gGridView_vbar(GtkAdjustment *adj,gGridView *data) { gtk_widget_queue_draw(data->lateral); data->render->setOffsetY((int)gtk_adjustment_get_value (adj)); if (data->onScroll) data->onScroll(data); } static gboolean gGridView_configure(GtkWidget *w,GdkEventConfigure *e,gGridView *data) { data->updateLastColumn(); data->calculateBars(); return false; } /*gboolean tbheader_leave(GtkWidget *wid,GdkEventCrossing *e,gGridView *data) { data->mouse_x=-1; gdk_window_set_cursor(wid->window,NULL); return false; } gboolean tblateral_leave(GtkWidget *wid,GdkEventCrossing *e,gGridView *data) { data->mouse_y=-1; gdk_window_set_cursor(wid->window,NULL); return false; }*/ static gboolean tbheader_move(GtkWidget *wid, GdkEventMotion *e, gGridView *data) { int pos; int width, min; pos = (int)e->x + data->scrollX(); if (e->state & GDK_BUTTON1_MASK) { if (data->_index >= 0) { min = data->minColumnWidth(data->_index); width = pos - data->columnPos(data->_index); width = MAX(width, min); if (data->_index == (data->columnCount() - 1)) data->_last_col_width = 0; data->setColumnWidth(data->_index, width); } } else { data->_index = data->findColumnSeparation(pos); if (data->_index < 0) gdk_window_set_cursor(wid->window, NULL); else gdk_window_set_cursor(wid->window, gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW)); } return true; } static void tblateral_select(gGridView *data, int bcurrent, bool move) { int bc, col; bool sel_changed; data->getCursor(NULL, &col); switch(data->selectionMode()) { case SELECT_NONE: return; case SELECT_SINGLE: if (!data->rowSelected(bcurrent)) { data->setCursor(bcurrent, col); data->emit(SIGNAL(data->onSelect)); } break; case SELECT_MULTIPLE: if (!move) { data->sel_row = bcurrent; data->setCursor(bcurrent, col); if (data->sel_current != bcurrent) { data->sel_current = bcurrent; data->clearSelection(); data->setRowSelected(bcurrent, true); data->emit(SIGNAL(data->onSelect)); } } else { //data->clearSelection(); if (bcurrent < data->sel_row) { if (data->sel_current > data->sel_row) { data->clearSelection(); data->sel_current = data->sel_row; } if (bcurrent < data->sel_current) { for (bc = bcurrent; bc <= data->sel_current; bc++) data->setRowSelected(bc, true); sel_changed = true; } else { for (bc = data->sel_current; bc < bcurrent; bc++) data->setRowSelected(bc, false); sel_changed = true; } } else { if (data->sel_current < data->sel_row) { data->clearSelection(); data->sel_current = data->sel_row; } if (bcurrent >= data->sel_current) { for (bc = data->sel_current; bc <= bcurrent; bc++) data->setRowSelected(bc, true); } else { for (bc = bcurrent + 1; bc <= data->sel_current; bc++) data->setRowSelected(bc, false); } } if (data->sel_current != bcurrent) { data->sel_current = bcurrent; data->emit(SIGNAL(data->onSelect)); } data->setCursor(bcurrent, col); //data->ensureVisible(bcurrent, -1); } break; } } static gboolean tblateral_press(GtkWidget *wid,GdkEventButton *e,gGridView *data) { //int realmode; int pos; int bcurrent = -1; bool is_lateral = (wid == data->lateral); pos = (int)e->y + data->scrollY(); if (e->button != 1 || (is_lateral && data->findRowSeparation(pos) >= 0)) return false; data->_index = -1; bcurrent = data->findRow(pos); if (bcurrent < 0) return false; tblateral_select(data, bcurrent, false); if (!is_lateral) { int row = bcurrent; int col = data->render->findVisibleColumn((int)e->x); data->setCursor(row, col); } return false; } static gboolean lateral_do_move(gGridView *data) { int min, height, pos; pos = data->mouse_pos + data->scrollY(); min = data->minRowHeight(data->_index); height = pos - data->rowPos(data->_index); height = MAX(height, min); data->setRowHeight(data->_index, height); return true; } static gboolean contents_do_move(gGridView *data) { int index, mode, pos; pos = data->mouse_pos + data->scrollY(); index = data->findRow(pos); mode = data->selectionMode(); if (index >= 0) tblateral_select(data, index, true); return true; } static gboolean tblateral_move(GtkWidget *wid, GdkEventMotion *e, gGridView *data) { int pos; bool is_lateral = (wid == data->lateral); data->mouse_pos = (int)e->y; pos = data->mouse_pos + data->scrollY(); if (e->state & GDK_BUTTON1_MASK) { if (is_lateral && data->_index >= 0) lateral_do_move(data); else data->startScrollTimer((GSourceFunc)contents_do_move); } else if (is_lateral) { data->_index = data->findRowSeparation(pos); if (data->_index < 0) gdk_window_set_cursor(wid->window, NULL); else gdk_window_set_cursor(wid->window, gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW)); } return false; } static gboolean tblateral_release(GtkWidget *wid,GdkEventButton *e,gGridView *data) { int pos; int bcurrent = -1; bool is_lateral = (wid == data->lateral); pos = (int)e->y + data->scrollY(); data->stopScrollTimer(); if (e->button != 1 || data->_index >= 0) return false; bcurrent = data->findRow(pos); if (bcurrent < 0) return false; tblateral_select(data, bcurrent, true); if (is_lateral) { data->emit(SIGNAL(data->onRowClick), bcurrent); data->getCursor(NULL, &pos); data->setCursor(bcurrent, pos); } else { int row = bcurrent; int col = data->render->findVisibleColumn((int)e->x); data->setCursor(row, col); if (data->onClick) data->onClick(data, row, col); } return false; } static gboolean cb_widget_expose(GtkWidget *wid, GdkEventExpose *e, gGridView *data) { int w, h, f; if (data->headersVisible() == 3) { GdkRectangle rect; f = data->getFrameWidth(); w = data->lateralWidth(); h = data->headerHeight(); rect.x = f; rect.y = f; rect.width = w; rect.height = h; gtk_paint_box(data->header->style,wid->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, &rect, wid->parent, NULL, // "buttondefault" 0, 0, w + f, h + f); } if (data->footersVisible() && data->headersVisible() & 2) { GdkRectangle rect; f = data->getFrameWidth(); w = data->lateralWidth(); h = data->footerHeight(); rect.x = f; rect.y = data->height() - h - f; rect.width = w; rect.height = h; gtk_paint_box(data->header->style,wid->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, &rect, wid->parent, NULL, // "buttondefault" 0, rect.y, w + f, h + f); } return false; } static gboolean tbheader_expose(GtkWidget *wid,GdkEventExpose *e,gGridView *data) { GtkCellRenderer *rend=NULL; GdkRectangle rect={0,0,0,0}; GdkGC *gc; char *buf; int bpos; int pw,ph,maxw; bool footer; int bc; footer = wid->window == data->footer->window; gc=gdk_gc_new(wid->window); gdk_gc_set_clip_origin(gc,0,0); gdk_gc_set_clip_rectangle(gc,&e->area); gdk_drawable_get_size(wid->window,&maxw,&ph); if (footer) { gtk_paint_box(wid->style, wid->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, wid->parent, "button", // "buttondefault" -4, 0, maxw + 8, ph + 4); } else { gtk_paint_box(wid->style, wid->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, wid->parent, "button", // "buttondefault" -4, -4, maxw + 8, ph + 4); } /* if (data->render->firstCol == 0) { gtk_paint_vline(data->header->style, wid->window, GTK_STATE_NORMAL, NULL, wid->parent, NULL, 0, ph - 1, 0); }*/ bpos = data->render->offCol - data->render->getOffsetX(); for (bc = data->render->firstCol; bc < data->columnCount(); bc++) { //if ((bpos+data->columnWidth(bc)) < 0) { bpos+=data->columnWidth(bc); continue; } if (bpos > maxw) break; pw=data->columnWidth(bc); if (footer) { gtk_paint_vline(wid->style, wid->window, GTK_STATE_NORMAL, NULL, wid->parent, NULL, 4, ph - 3, bpos + pw - 1); } else { gtk_paint_vline(wid->style, wid->window, GTK_STATE_NORMAL, NULL, wid->parent, NULL, 3, ph - 4, bpos + pw - 1); } if (footer) buf=data->footerText(bc); else buf=data->headerText(bc); if ( buf != NULL ) { if (!rend) rend=gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(rend), "text", buf, "xalign", 0.5, "yalign", 0.5, "font-desc", data->font()->desc(), (void *)NULL); rect.x=bpos; rect.y=0; rect.width=pw-1; rect.height=ph-1; gtk_cell_renderer_render(GTK_CELL_RENDERER(rend),wid->window,wid,&rect,&rect,NULL,(GtkCellRendererState)0); } bpos += data->columnWidth(bc); } g_object_unref(G_OBJECT(gc)); return false; } static gboolean tblateral_expose(GtkWidget *wid,GdkEventExpose *e,gGridView *data) { GtkCellRenderer *rend=NULL; GdkRectangle rect={0,0,0,0}; GdkGC *gc; GdkWindow *win=data->lateral->window; int pw,ph,maxh; int bc; int bpos; gc=gdk_gc_new(win); gdk_gc_set_clip_origin(gc,0,0); gdk_gc_set_clip_rectangle(gc,&e->area); gdk_drawable_get_size(win,&ph,&maxh); gtk_paint_box(wid->style, wid->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, wid->parent, "button", // "buttondefault" -4, -4, ph + 4, maxh + 8); /* if (data->render->firstRow == 0) { gtk_paint_hline(data->header->style, wid->window, GTK_STATE_NORMAL, NULL, wid->parent, NULL, 0, ph - 1, 0); }*/ bpos = data->render->offRow - data->render->getOffsetY(); for (bc=data->render->firstRow; bcrowCount(); bc++) { if (bpos > maxh) break; pw=data->rowHeight(bc); /* gtk_paint_box(data->lateral->style,data->lateral->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, data->lateral->parent, "buttondefault", 0,bpos, ph-1, pw-1); gtk_paint_shadow(data->lateral->style,data->lateral->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, data->lateral->parent, "buttondefault", 0,bpos, ph-1, pw-1);*/ gtk_paint_hline(wid->style, wid->window, GTK_STATE_NORMAL, NULL, wid->parent, NULL, 3, ph - 4, bpos + pw - 1); if (!rend) rend=gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(rend), "text", data->rowText(bc), "xalign", 0.5, "yalign", 0.5, "font-desc", data->font()->desc(), (void *)NULL); rect.x = 0; rect.y = bpos; rect.width = ph-1; rect.height = pw-1; gtk_cell_renderer_render(GTK_CELL_RENDERER(rend),wid->window,wid, &rect,&rect,NULL,(GtkCellRendererState)0); bpos+=data->rowHeight(bc); } g_object_unref(G_OBJECT(gc)); return false; } static gboolean tbheader_release(GtkWidget *wid,GdkEventButton *e,gGridView *data) { int bc,bcurrent=-1; bool bfooter=false; char *buf=NULL; if (e->button != 1 || data->_index >= 0) return false; bcurrent = data->findColumn((int)e->x + data->scrollX()); if (bcurrent<0) return false; g_object_get(G_OBJECT(wid),"name",&buf,(void *)NULL); if (buf) { if (!strcmp(buf,"gambas-grid-footer")) bfooter=true; g_free(buf); buf=NULL; } data->getCursor(&bc,NULL); data->setCursor(bc,bcurrent); if (bfooter) { if (data->onFooterClick) data->onFooterClick(data,bcurrent); } else { if (data->onColumnClick) data->onColumnClick(data,bcurrent); } return TRUE; } static gboolean cb_contents_button_press(GtkWidget *wid, GdkEventButton *e, gGridView *data) { int row, col; if (e->type != GDK_2BUTTON_PRESS) return false; if (e->button != 1) return false; data->getCursor(&row, &col); if (data->onActivate) data->onActivate(data, row, col); return false; } static gboolean cb_scroll(GtkWidget *wid, GdkEventScroll *e, gGridView *data) { GtkAdjustment *adj; gdouble step; if (e->direction == GDK_SCROLL_UP || e->direction == GDK_SCROLL_DOWN) adj = gtk_range_get_adjustment(GTK_RANGE(data->vbar)); else adj = gtk_range_get_adjustment(GTK_RANGE(data->hbar)); #if GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 14 step = gtk_adjustment_get_step_increment(adj); #else g_object_get(G_OBJECT(adj), "step-increment", &step, (void *)NULL); #endif switch (e->direction) { case GDK_SCROLL_UP: data->setScrollY(data->scrollY() - (int)step); break; case GDK_SCROLL_DOWN: data->setScrollY(data->scrollY() + (int)step); break; case GDK_SCROLL_LEFT: data->setScrollX(data->scrollX() - (int)step); break; case GDK_SCROLL_RIGHT: data->setScrollX(data->scrollX() + (int)step); break; } return TRUE; } static gboolean cb_keypress(GtkWidget *wid,GdkEventKey *e,gGridView *data) { int row,col; bool bchange=false; data->getCursor(&row,&col); switch(e->keyval) { case GDK_Up: if (row>0) { row--; bchange=true; } break; case GDK_Down: if (row<(data->rowCount()-1)) { row++; bchange=true; } break; case GDK_Left: if (col>0) { col--; bchange=true; } break; case GDK_Right: if (col<(data->columnCount()-1)) { col++; bchange=true; } break; } if (bchange) { data->ensureVisible(row,col); data->setCursor(row,col); return TRUE; } return false; } gGridView::gGridView(gContainer *parent) : gControl(parent) { GtkAdjustment *adj; g_typ = Type_gGridView; onChange = NULL; onActivate = NULL; onSelect = NULL; onScroll = NULL; onClick = NULL; onColumnClick = NULL; onFooterClick = NULL; onRowClick = NULL; onColumnResize = NULL; onRowResize = NULL; use_base = true; _index = -1; cursor_col=-1; cursor_row=-1; sel_row=-1; sel_mode=0; sel_current = -1; scroll=3; hdata=NULL; vdata=NULL; _last_col_width = 0; scroll_timer = 0; _updating_last_column = false; border=gtk_event_box_new(); widget=gtk_table_new(3,3,FALSE); //gtk_container_set_border_width (GTK_CONTAINER(widget),1); //gtk_container_add(GTK_CONTAINER(border),widget); hbar=gtk_hscrollbar_new(NULL); vbar=gtk_vscrollbar_new(NULL); contents = gtk_drawing_area_new(); gtk_widget_add_events(contents, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK); header=gtk_drawing_area_new(); footer=gtk_drawing_area_new(); lateral=gtk_drawing_area_new(); //gtk_widget_set_size_request(header,0,20); //gtk_widget_set_size_request(footer,0,20); //gtk_widget_set_size_request(lateral,16,0); gtk_table_attach_defaults(GTK_TABLE(widget),contents,1,2,1,2); gtk_table_attach(GTK_TABLE(widget),header,1,2,0,1,GTK_FILL,GTK_FILL,0,0); gtk_table_attach(GTK_TABLE(widget),footer,1,2,2,3,GTK_FILL,GTK_FILL,0,0); gtk_table_attach(GTK_TABLE(widget),lateral,0,1,1,2,GTK_FILL,GTK_FILL,0,0); gtk_table_attach(GTK_TABLE(widget),hbar,0,2,3,4,GTK_FILL,GTK_FILL,0,0); gtk_table_attach(GTK_TABLE(widget),vbar,2,3,0,3,GTK_FILL,GTK_FILL,0,0); render=new gTableRender(this); _no_default_mouse_event = true; realize(true); gtk_widget_add_events(border,GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(contents,GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); gtk_widget_add_events(header,GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_widget_add_events(footer,GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_widget_add_events(lateral,GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_widget_add_events(header,GDK_BUTTON_PRESS_MASK | GDK_BUTTON1_MOTION_MASK); gtk_widget_add_events(footer,GDK_BUTTON_PRESS_MASK | GDK_BUTTON1_MOTION_MASK); gtk_widget_add_events(lateral,GDK_BUTTON_PRESS_MASK | GDK_BUTTON1_MOTION_MASK); gtk_widget_add_events(header,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(footer,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(lateral,GDK_BUTTON_RELEASE_MASK); g_object_set(G_OBJECT(header),"name","gambas-grid-header",(void *)NULL); g_object_set(G_OBJECT(footer),"name","gambas-grid-footer",(void *)NULL); g_signal_connect_after(G_OBJECT(border),"size-allocate",G_CALLBACK(gGridView_configure),(void*)this); g_signal_connect(G_OBJECT(border), "key-press-event", G_CALLBACK(cb_keypress), this); g_signal_connect(G_OBJECT(border), "scroll-event", G_CALLBACK(cb_scroll), this); g_signal_connect(G_OBJECT(header),"expose-event",G_CALLBACK(tbheader_expose),this); g_signal_connect(G_OBJECT(header),"motion-notify-event",G_CALLBACK(tbheader_move),this); g_signal_connect(G_OBJECT(header),"button-release-event",G_CALLBACK(tbheader_release),this); g_signal_connect(G_OBJECT(footer),"expose-event",G_CALLBACK(tbheader_expose),this); g_signal_connect(G_OBJECT(footer),"motion-notify-event",G_CALLBACK(tbheader_move),this); g_signal_connect(G_OBJECT(footer),"button-release-event",G_CALLBACK(tbheader_release),this); g_signal_connect(G_OBJECT(lateral),"expose-event",G_CALLBACK(tblateral_expose),this); g_signal_connect(G_OBJECT(lateral),"motion-notify-event",G_CALLBACK(tblateral_move),this); g_signal_connect(G_OBJECT(lateral),"button-press-event",G_CALLBACK(tblateral_press),this); g_signal_connect(G_OBJECT(lateral),"button-release-event",G_CALLBACK(tblateral_release),this); g_signal_connect(G_OBJECT(widget),"expose-event",G_CALLBACK(cb_widget_expose),this); g_signal_connect_after(G_OBJECT(contents),"event",G_CALLBACK(cb_contents_button_press),this); g_signal_connect(G_OBJECT(contents),"motion-notify-event",G_CALLBACK(tblateral_move),this); g_signal_connect(G_OBJECT(contents),"button-release-event",G_CALLBACK(gcb_button_release),(gpointer)this); g_signal_connect(G_OBJECT(contents),"button-press-event",G_CALLBACK(gcb_button_press),(gpointer)this); g_signal_connect(G_OBJECT(contents),"button-press-event",G_CALLBACK(tblateral_press),this); g_signal_connect(G_OBJECT(contents),"button-release-event",G_CALLBACK(tblateral_release),this); //g_signal_connect(G_OBJECT(contents),"event",G_CALLBACK(gridview_release),this); g_object_set(G_OBJECT(vbar),"visible",FALSE,(void *)NULL); g_object_set(G_OBJECT(hbar),"visible",FALSE,(void *)NULL); g_object_set(G_OBJECT(footer),"visible",FALSE,(void *)NULL); g_object_set(G_OBJECT(widget),"can-focus",TRUE,(void *)NULL); gtk_widget_realize(footer); gtk_widget_realize(contents); adj=gtk_range_get_adjustment(GTK_RANGE(hbar)); g_signal_connect(G_OBJECT(adj),"value-changed",G_CALLBACK(gGridView_hbar),(void*)this); adj=gtk_range_get_adjustment(GTK_RANGE(vbar)); g_signal_connect(G_OBJECT(adj),"value-changed",G_CALLBACK(gGridView_vbar),(void*)this); // BM to see the result //GtkStyle *st = gt_get_style("GtkEntry", GTK_TYPE_ENTRY); //gtk_widget_set_style(contents, st); //gtk_widget_modify_base(contents, GTK_STATE_NORMAL, &st->bg[GTK_STATE_NORMAL]); //gtk_widget_modify_bg(contents, GTK_STATE_NORMAL, &st->base[GTK_STATE_NORMAL]); setBackground(); setForeground(); setHeadersVisible(0); setFootersVisible(false); setBorder(true); setDrawGrid(true); } gGridView::~gGridView() { setRowCount(0); setColumnCount(0); if (hdata) g_hash_table_destroy(hdata); if (vdata) g_hash_table_destroy(vdata); delete render; } int gGridView::selectionMode() { return sel_mode; } void gGridView::setSelectionMode(int vl) { if (vl<0) return; if (vl>2) return; sel_mode=vl; if (sel_mode == SELECT_NONE) clearSelection(); else if (sel_mode == SELECT_SINGLE) setRowSelected(cursor_row, true); } void gGridView::setDataFunc(void *func,void *data) { render->voidCell=(void(*)(gTableData *,int,int,void *))func; render->userData=data; } void gGridView::queryUpdate(int row,int col) { render->queryUpdate(row,col); } void gGridView::calculateBars() { GtkAdjustment *adj; int obh,obv,px,py,ovw,ovh,vw,vh,rw,rh; bool bh, bv; GtkRequisition req; gtk_widget_size_request(hbar, &req); py = req.height; gtk_widget_size_request(vbar, &req); px = req.width; obh = bh = GTK_WIDGET_VISIBLE(hbar); obv = bv = GTK_WIDGET_VISIBLE(vbar); vw = render->visibleWidth(); // + (obh ? px : 0); vh = render->visibleHeight(); // + (obv ? py : 0); rw = render->width(); rh = render->height(); if (vw > 1 && vh > 1) { ovw = vw + (obv ? px : 0); ovh = vh + (obh ? py : 0); //fprintf(stderr, "ovw = %d ovh = %d\n", ovw, ovh); obv = false; obh = false; for(;;) { vw = ovw - (obv ? px : 0); vh = ovh - (obh ? py : 0); //fprintf(stderr, "rw = %d rh = %d / vw = %d vh = %d / bh = %d bv = %d\n", rw, rh, vw, vh, bh, bv); bh = vw < rw && rw > 0 && (scroll & SCROLL_HORIZONTAL); if (bh != obh) { obh = bh; continue; } bv = vh < rh && rh > 0 && (scroll & SCROLL_VERTICAL); if (bv != obv) { obv = bv; continue; } break; } } else { bh = false; bv = false; } if (bh != GTK_WIDGET_VISIBLE(hbar)) { g_object_set(G_OBJECT(hbar),"visible",bh,(void *)NULL); if (!bh) setScrollX(0); } if (bv != GTK_WIDGET_VISIBLE(vbar)) { g_object_set(G_OBJECT(vbar),"visible",bv,(void *)NULL); if (!bv) setScrollY(0); } if (bh) { gtk_range_set_range(GTK_RANGE(hbar),0,render->width()); gtk_range_set_increments(GTK_RANGE(hbar),minColumnWidth(0) * 3,vw); adj=gtk_range_get_adjustment(GTK_RANGE(hbar)); g_object_set(G_OBJECT(adj),"page-size",(gfloat)vw,(void *)NULL); } if (bv) { gtk_range_set_range(GTK_RANGE(vbar),0,render->height()); gtk_range_set_increments(GTK_RANGE(vbar),minRowHeight(0) * 3,vh); adj=gtk_range_get_adjustment(GTK_RANGE(vbar)); g_object_set(G_OBJECT(adj),"page-size",(gfloat)vh,(void *)NULL); } } // bool gGridView::getBorder() // { // return (bool)gtk_container_get_border_width(GTK_CONTAINER(widget)); // } // // void gGridView::setBorder(bool vl) // { // if (vl == getBorder() ) return; // switch(vl) // { // case false: // gtk_container_set_border_width(GTK_CONTAINER(widget),0); break; // case true: // gtk_container_set_border_width(GTK_CONTAINER(widget),1); break; // } // } int gGridView::scrollBar() { return scroll; } void gGridView::setScrollBar(int vl) { if (vl==scroll) return; scroll=vl; if ( !(scroll & 1) ) g_object_set(G_OBJECT(hbar),"visible",FALSE,(void *)NULL); if ( !(scroll & 2) ) g_object_set(G_OBJECT(vbar),"visible",FALSE,(void *)NULL); calculateBars(); } int gGridView::scrollX() { return render->getOffsetX(); } int gGridView::scrollY() { return render->getOffsetY(); } void gGridView::setScrollX(int vl) { GtkAdjustment* adj; int max; adj = gtk_range_get_adjustment(GTK_RANGE(hbar)); max = (int)(adj->upper - adj->page_size); if (vl < 0) vl = 0; else if (vl > max) vl = max; gtk_adjustment_set_value(adj, (gdouble)vl); } void gGridView::setScrollY(int vl) { GtkAdjustment* adj; int max; adj = gtk_range_get_adjustment(GTK_RANGE(vbar)); max = (int)(adj->upper - adj->page_size); if (vl < 0) vl = 0; else if (vl > max) vl = max; gtk_adjustment_set_value(adj, (gdouble)vl); } int gGridView::rowAt(int y) { int bpos=-render->getOffsetX(); int bc; for (bc=0; bcrowCount(); bc++) { if ( (bpos<=y) && ( (bpos+render->getRowSize(bc)))>=y ) return bc; bpos+=render->getRowSize(bc); } return -1; } int gGridView::columnAt(int x) { int bpos=-render->getOffsetY(); int bc; for (bc=0; bccolumnCount(); bc++) { if ( (bpos<=x) && ( (bpos+render->getColumnSize(bc)))>=x ) return bc; bpos+=render->getColumnSize(bc); } return -1; } int gGridView::visibleLeft() { gboolean vl; int w; g_object_get(G_OBJECT(lateral),"visible",&vl,(void *)NULL); if (!vl) return 0; gtk_widget_get_size_request(lateral,&w,NULL); return w; } int gGridView::visibleTop() { gboolean vl; int h; g_object_get(G_OBJECT(header),"visible",&vl,(void *)NULL); if (!vl) return 0; gtk_widget_get_size_request(header,NULL,&h); return h; } int gGridView::visibleWidth() { return render->visibleWidth(); } int gGridView::visibleHeight() { return render->visibleHeight(); } void gGridView::ensureVisible(int row, int col) { int x, y, w, h; GtEnsureVisible arg; bool sx, sy; arg.clientWidth = visibleWidth(); arg.clientHeight = visibleHeight(); arg.scrollX = scrollX(); arg.scrollY = scrollY(); arg.scrollWidth = render->width(); arg.scrollHeight = render->height(); sy = row >= 0 && row < rowCount(); sx = col >= 0 && col < columnCount(); if (!sy) row = 0; if (!sx) col = 0; x = columnPos(col); y = rowPos(row); w = columnWidth(col); h = rowHeight(row); gt_ensure_visible(&arg, x, y, w, h); if (sx) setScrollX(arg.scrollX); if (sy) setScrollY(arg.scrollY); /* int vl; //int bc; GtkAdjustment *adj; if (col >= 0 && col < columnCount()) { vl = columnPos(col) - scrollX() + columnWidth(col) / 2; //for (bc=0; bcgetOffsetX()+vl; adj=gtk_range_get_adjustment(GTK_RANGE(hbar)); g_object_set(G_OBJECT(adj),"value",(gfloat)vl,NULL); } else if ( (vl+columnWidth(col))>visibleWidth() ) { vl=render->getOffsetX()+columnWidth(col)-(visibleWidth()-vl); adj=gtk_range_get_adjustment(GTK_RANGE(hbar)); g_object_set(G_OBJECT(adj),"value",(gfloat)vl,NULL); } } if (row >= 0 && row < rowCount()) { vl = rowPos(row) - scrollY() + rowHeight(row) / 2; //for (bc=0; bcgetOffsetY()+vl; adj=gtk_range_get_adjustment(GTK_RANGE(vbar)); g_object_set(G_OBJECT(adj),"value",(gfloat)vl,NULL); } else if ( (vl+rowHeight(col))>visibleHeight() ) { vl=render->getOffsetY()+rowHeight(col)-(visibleHeight()-vl); adj=gtk_range_get_adjustment(GTK_RANGE(vbar)); g_object_set(G_OBJECT(adj),"value",(gfloat)vl,NULL); } } */ } bool gGridView::drawGrid() { return render->drawGrid(); } void gGridView::setDrawGrid(bool vl) { render->setDrawGrid(vl); } void gGridView::getCursor(int *row,int *col) { if (row) *row=cursor_row; if (col) *col=cursor_col; } void gGridView::setCursor(int row,int col) { if (row < 0 || row >= rowCount()) row = -1; if (col < 0 || col >= columnCount()) col = -1; if (row == cursor_row && col == cursor_col) return; cursor_row = row; cursor_col = col; if (selectionMode() == SELECT_SINGLE) setRowSelected(cursor_row, true); ensureVisible(cursor_row, cursor_col); emit(SIGNAL(onChange)); } void gGridView::setColumnCount(int vl) { int old, i; if (vl<0) vl=0; old = columnCount(); if (old == vl) return; lock(); render->setColumnCount(vl); render->doNotInvalidate = true; for (i = old; i < vl; i++) render->setColumnSize(i, 80); render->doNotInvalidate = false; unlock(); _last_col_width = 0; updateLastColumn(); calculateBars(); if (vl==0) cursor_col=-1; if (vl<=cursor_col) cursor_col=vl-1; if ( (rowCount()>0) && (vl>0) && (cursor_col==-1) ) { cursor_col=0; cursor_row=0; } refresh(); } void gGridView::setRowCount(int vl) { int old, i, h; if (vl<0) vl=0; old = rowCount(); if (old == vl) return; lock(); render->setRowCount(vl); if (vl) { h = minRowHeight(vl - 1); render->doNotInvalidate = true; for (i = old; i < vl; i++) { //if ((i % 1000) == 0) // fprintf(stderr, "%d\r", i); setRowHeight(i, h); } render->doNotInvalidate = false; } unlock(); calculateBars(); if (vl==0) cursor_row=-1; if (vl<=cursor_row) cursor_row=vl-1; if ( (columnCount()>0) && (vl>0) && (cursor_row==-1) ) { cursor_row=0; cursor_col=0; } updateHeaders(); refresh(); } /* int gGridView::findColumn(int pos) { int i; int x = 0; int x2; for (i = 0; i < columnCount(); i++) { x2 = x + columnWidth(i); if (pos >= x && pos < x2) return i; x = x2; } return -1; } */ int gGridView::findColumnSeparation(int pos) { int i; int x = 0; for (i = 0; i < columnCount(); i++) { x += columnWidth(i); if (pos > (x - 2) && pos < (x + 2)) return columnResizable(i) ? i : -1; } return -1; } /* int gGridView::findRow(int pos) { int i; int y = 0; int y2; for (i = 0; i < rowCount(); i++) { y2 = y + rowHeight(i); if (pos >= y && pos < y2) return i; y = y2; } return -1; } */ int gGridView::findRowSeparation(int pos) { int i; int y = 0; for (i = 0; i < rowCount(); i++) { y += rowHeight(i); if (pos > (y - 2) && pos < (y + 2)) return rowResizable(i) ? i : -1; } return -1; } int gGridView::minColumnWidth(int index) { int w, w2; w = font()->width(headerText(index)); w2 = font()->width(footerText(index)); if (w2 > w) w = w2; return 8 + MAX(w, 8); } void gGridView::setColumnWidth(int index,int vl) { if (index < 0 || index >= columnCount()) return; if (vl < 0) vl = minColumnWidth(index); if (vl == columnWidth(index)) return; render->setColumnSize(index,vl); updateLastColumn(); gtk_widget_queue_draw(header); gtk_widget_queue_draw(footer); calculateBars(); gtk_widget_queue_draw(contents); emit(SIGNAL(onColumnResize), index); } int gGridView::minRowHeight(int index) { return 8 + font()->height(" "); // rowText(index) } void gGridView::setRowHeight(int index,int vl) { if (index < 0 || index >= rowCount()) return; if (vl < 0) vl = minRowHeight(index); render->setRowSize(index,vl); if (locked()) return; gtk_widget_queue_draw(lateral); calculateBars(); gtk_widget_queue_draw(contents); emit(SIGNAL(onRowResize), index); } int gGridView::itemX(int col) { int ret,bc; if (col<0) return -1; if (col>=render->columnCount()) return -1; ret = -render->getOffsetX(); for (bc=0; bcgetColumnSize(bc); ret += rowWidth(); return ret + 2; } int gGridView::itemY(int row) { int ret,bc; if (row<0) return -1; if (row>=render->rowCount()) return -1; ret = -render->getOffsetY(); for (bc=0; bcgetRowSize(bc); ret += headerHeight(); return ret + 2; } int gGridView::itemW(int col) { if (col<0) return -1; if (col>=render->columnCount()) return -1; return render->getColumnSize(col) - 1; } int gGridView::itemH(int row) { if (row<0) return -1; if (row>=render->rowCount()) return -1; return render->getRowSize(row) - 1; } char* gGridView::itemText(int row,int col) { return render->getFieldText(col,row); } char* gGridView::itemRichText(int row,int col) { return render->getFieldRichText(col,row); } gColor gGridView::itemFg(int row,int col) { return render->getFieldFg(col,row); } gColor gGridView::itemBg(int row,int col) { return render->getFieldBg(col,row); } int gGridView::itemPadding(int row,int col) { return render->getFieldPadding(col,row); } int gGridView::itemAlignment(int row,int col) { return render->getFieldAlignment(col,row); } gPicture *gGridView::itemPicture(int row, int col) { return render->getFieldPicture(col, row); } gFont *gGridView::itemFont(int row, int col) { return render->getFieldFont(col, row); } bool gGridView::itemSelected(int row,int col) { return render->getFieldSelected(col,row); } void gGridView::setItemText(int row,int col, const char *vl) { render->setFieldText(col,row,vl); } void gGridView::setItemRichText(int row,int col, const char *vl) { render->setFieldRichText(col,row,vl); } void gGridView::setItemFg(int row, int col, gColor vl) { render->setFieldFg(col,row,vl); } void gGridView::setItemBg(int row, int col, gColor vl) { render->setFieldBg(col,row,vl); } void gGridView::setItemPadding(int row,int col,int vl) { render->setFieldPadding(col,row,vl); } void gGridView::setItemAlignment(int row,int col,int vl) { render->setFieldAlignment(col,row,vl); } void gGridView::setItemPicture(int row, int col, gPicture *vl) { render->setFieldPicture(col, row, vl); } void gGridView::setItemFont(int row, int col, gFont *vl) { render->setFieldFont(col, row, vl); } void gGridView::setItemSelected(int row,int col,bool vl) { render->setFieldSelected(col,row,vl); } void gGridView::clearItem(int row,int col) { render->clearField(col,row); } /******************************************************************* Headers, Footers and Row Separators ********************************************************************/ int gGridView::headersVisible() { int val = 0; //g_object_get(G_OBJECT(header),"visible",&hv,NULL); //g_object_get(G_OBJECT(lateral),"visible",&lv,NULL); if (GTK_WIDGET_VISIBLE(header)) val += 1; if (GTK_WIDGET_VISIBLE(lateral)) val += 2; return val; } void gGridView::setHeadersVisible(int vl) { /*gboolean now; g_object_get(G_OBJECT(header),"visible",&now,NULL); if (vl==now) return; g_object_set(G_OBJECT(header),"visible",vl,NULL);*/ if (vl & 1) gtk_widget_show(header); else gtk_widget_hide(header); if (vl & 2) gtk_widget_show(lateral); else gtk_widget_hide(lateral); } bool gGridView::footersVisible() { return GTK_WIDGET_VISIBLE(footer); } void gGridView::setFootersVisible(bool vl) { if (vl) gtk_widget_show(footer); else gtk_widget_hide(footer); } void gridheader_destroy(gGridHeader *data) { delete data; } void gridrow_destroy(gGridRow *data) { delete data; } void gGridView::setHeaderText(int col, const char *value) { gGridHeader *head; int w; if ((col<0) || (col>=columnCount()) ) return; if (!value) value=""; if (!hdata) hdata=g_hash_table_new_full(g_direct_hash,g_direct_equal,NULL, (GDestroyNotify)gridheader_destroy); head=(gGridHeader*)g_hash_table_lookup(hdata,(gpointer)col); if (!head) { head=new gGridHeader(); g_hash_table_insert(hdata,(gpointer)col,(gpointer)head); } head->setHeader(value); w = font()->width(value) + 8; if (w > columnWidth(col)) setColumnWidth(col, w); gtk_widget_queue_draw(header); } void gGridView::setFooterText(int col, const char *value) { gGridHeader *head; if (!hdata) hdata=g_hash_table_new_full(g_direct_hash,g_direct_equal,NULL, (GDestroyNotify)gridheader_destroy); head=(gGridHeader*)g_hash_table_lookup(hdata,(gpointer)col); if (!head) { head=new gGridHeader(); g_hash_table_insert(hdata,(gpointer)col,(gpointer)head); } head->setFooter(value); gtk_widget_queue_draw(footer); } char* gGridView::headerText(int col) { gGridHeader *head; if (!hdata) return NULL; head=(gGridHeader*)g_hash_table_lookup(hdata,(gpointer)col); if (!head) return NULL; return head->header; } char* gGridView::footerText(int col) { gGridHeader *head; if (!hdata) return NULL; head=(gGridHeader*)g_hash_table_lookup(hdata,(gpointer)col); if (!head) return NULL; return head->footer; } bool gGridView::columnResizable(int index) { gGridHeader *head; if (!hdata) return true; head=(gGridHeader*)g_hash_table_lookup(hdata,(gpointer)index); if (!head) return true; return head->resizable; } void gGridView::setColumnResizable(int col,bool value) { gGridHeader *head; if (!hdata) hdata=g_hash_table_new_full(g_direct_hash,g_direct_equal,NULL, (GDestroyNotify)gridheader_destroy); head=(gGridHeader*)g_hash_table_lookup(hdata,(gpointer)col); if (!head) { head=new gGridHeader(); g_hash_table_insert(hdata,(gpointer)col,(gpointer)head); } head->resizable=value; if (!value) { gdk_window_set_cursor(header->window,NULL); gdk_window_set_cursor(footer->window,NULL); } } char* gGridView::rowText(int col) { gGridRow *head; if (vdata) { head = (gGridRow*)g_hash_table_lookup(vdata,(gpointer)col); if (head) return head->text; } sprintf(void_row_buffer, "%d", col + 1); return void_row_buffer; } void gGridView::setRowText(int col, const char *value) { gGridRow *head; if (!vdata) vdata=g_hash_table_new_full(g_direct_hash,g_direct_equal,NULL, (GDestroyNotify)gridrow_destroy); head=(gGridRow*)g_hash_table_lookup(vdata,(gpointer)col); if (!head) { head=new gGridRow(); g_hash_table_insert(vdata,(gpointer)col,(gpointer)head); } head->setText(value); updateLateralWidth(font()->width(value)); gtk_widget_queue_draw(lateral); } bool gGridView::rowResizable(int index) { gGridRow *head; if (!vdata) return true; head=(gGridRow*)g_hash_table_lookup(vdata,(gpointer)index); if (!head) return true; return head->resizable; } void gGridView::setRowResizable(int col,bool value) { gGridRow *head; if (!vdata) vdata=g_hash_table_new_full(g_direct_hash,g_direct_equal,NULL, (GDestroyNotify)gridrow_destroy); head=(gGridRow*)g_hash_table_lookup(vdata,(gpointer)col); if (!head) { head=new gGridRow(); g_hash_table_insert(vdata,(gpointer)col,(gpointer)head); } head->resizable=value; if (!value) gdk_window_set_cursor(lateral->window,NULL); } bool gGridView::rowSelected(int index) { return render->getRowSelected(index); } void gGridView::setRowSelected(int col,bool value) { if (selectionMode() == SELECT_NONE) return; if (selectionMode() == SELECT_SINGLE) clearSelection(); render->setRowSelected(col,value); } void gGridView::updateHeaders() { int h, wlat; //int i, w, wd; //char *text; h = font()->height() + 8; gtk_widget_set_size_request(header, header->requisition.width, h); gtk_widget_set_size_request(footer, footer->requisition.width, h); wlat = 8 + font()->width("9") * sprintf(void_row_buffer, "%d", rowCount()); /*wlat = 0; for (i = 0; i < rowCount(); i++) { if (rowHeight(i) < h) setRowHeight(i, h); text = rowText(i); if (text == void_row_buffer) w = wd * strlen(text); else w = font()->width(text); w += 8; if (w > wlat) wlat = w; }*/ updateLateralWidth(wlat); } void gGridView::updateLateralWidth(int w) { if (w > lateral->allocation.width) gtk_widget_set_size_request(lateral, w, lateral->allocation.height); } void gGridView::setFont(gFont *ft) { gControl::setFont(ft); gtk_widget_modify_font(contents, font()->desc()); updateHeaders(); } int gGridView::rowWidth() { if (GTK_WIDGET_VISIBLE(lateral)) return lateral->allocation.width; else return 0; } int gGridView::headerHeight() { if (GTK_WIDGET_VISIBLE(header)) return header->allocation.height; else return 0; } int gGridView::footerHeight() { if (GTK_WIDGET_VISIBLE(footer)) return footer->allocation.height; else return 0; } void gGridView::setBackground(gColor color) { gControl::setBackground(color); if (color == COLOR_DEFAULT) set_gdk_bg_color(contents, get_gdk_base_color(contents)); else set_gdk_bg_color(contents, color); //set_gdk_bg_color(header, color); //set_gdk_bg_color(lateral, color); } void gGridView::setForeground(gColor color) { gControl::setForeground(color); color = realForeground(); set_gdk_fg_color(contents, color); //set_gdk_fg_color(header, color); //set_gdk_fg_color(lateral, color); } void gGridView::updateLastColumn() { int n = columnCount() - 1; int vw = visibleWidth(); if (n < 0) return; if (_updating_last_column) return; _updating_last_column = true; if (!_last_col_width) _last_col_width = columnWidth(n); //fprintf(stderr, "updateLastColumn: vw = %d columnPos = %d %d _lcw = %d \n", vw, columnPos(n), columnPos(n), _last_col_width); if (((columnPos(n) + _last_col_width) < vw) && (columnWidth(n) != (vw - columnPos(n)))) { //fprintf(stderr, "updateLastColumn: vw = %d -> %d\n", vw, vw - columnPos(n)); setColumnWidth(n, vw - columnPos(n)); } _updating_last_column = false; } void gGridView::startScrollTimer(GSourceFunc func) { if (!scroll_timer) { (*func)(this); scroll_timer = g_timeout_add(50, (GSourceFunc)func, this); } } void gGridView::stopScrollTimer() { if (scroll_timer) { g_source_remove(scroll_timer); scroll_timer = 0; } }