1 /*
2  * ===========================
3  * VDK Visual Development Kit
4  * Version 0.5
5  * December 1998
6  * ===========================
7  *
8  * Copyright (C) 1998, Mario Motta
9  * Developed by Mario Motta <mmotta@guest.net>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24  * 02111-130
25  */
26 #include "vdk/vdkctree.h"
27 #include <cstring>
28 typedef struct { VDKTreeNodeList* list; char* key; } FindInfo;
29 
30 static void IterateOnTree(GtkCTree     *ctree,
31 			  GtkCTreeNode *node,
32 			  gpointer      i);
33 /*
34 answer to selection on
35 single selection mode
36  */
NodeSelection(GtkWidget * wid,GtkCTreeNode * node,int column,gpointer s)37 void VDKCustomTree::NodeSelection(GtkWidget* wid,
38 		  GtkCTreeNode* node,
39 		  int column,
40 		  gpointer s)
41 {
42   g_return_if_fail(s != NULL);
43   VDKObjectSignal* signal =
44     reinterpret_cast<VDKObjectSignal*>(s);
45   VDKCustomTree* obj = reinterpret_cast<VDKCustomTree*>(signal->obj);
46   obj->SelectedNode(node);
47   obj->SelectedColumn(column);
48   if(obj->mode != GTK_SELECTION_EXTENDED
49      &&
50      obj->mode != GTK_SELECTION_MULTIPLE
51      )
52     {
53       obj->SignalEmit(signal->signal);
54       obj->SignalEmit("tree_select_row");
55     }
56 #ifdef USE_SIGCPLUSPLUS
57   obj->OnNodeSelect(obj, node, column);
58 #endif /* USE_SIGCPLUSPLUS */
59 }
60 /*
61 answer to unselection on
62 single selection mode
63  */
NodeUnselection(GtkWidget * wid,GtkCTreeNode * node,int column,gpointer s)64 void VDKCustomTree::NodeUnselection(GtkWidget* wid,
65 		  GtkCTreeNode* node,
66 		  int column,
67 		  gpointer s)
68 {
69   g_return_if_fail(s != NULL);
70   VDKObjectSignal* signal =
71     reinterpret_cast<VDKObjectSignal*>(s);
72   VDKCustomTree* obj = reinterpret_cast<VDKCustomTree*>(signal->obj);
73   obj->UnselectedNode(node);
74   obj->UnselectedColumn(column);
75   obj->SelectedNode(NULL);
76   obj->SelectedColumn(-1);
77   if(obj->mode != GTK_SELECTION_EXTENDED
78      &&
79      obj->mode != GTK_SELECTION_MULTIPLE
80      )
81     {
82       obj->SignalEmit(signal->signal);
83       obj->SignalEmit("tree_unselect_row");
84     }
85 #ifdef USE_SIGCPLUSPLUS
86   obj->OnNodeUnselect(obj, node, column);
87 #endif /* USE_SIGCPLUSPLUS */
88 }
89 /*
90 answer to dbl click on extended mode,
91 emits select_node signal
92  */
ButtonPress(GtkWidget * wid,GdkEventButton * ev,gpointer s)93 int VDKCustomTree::ButtonPress (GtkWidget* wid,
94 				GdkEventButton *ev,
95 				gpointer s)
96 {
97   g_return_val_if_fail(wid != NULL,FALSE);
98   g_return_val_if_fail(ev != NULL,FALSE);
99   g_return_val_if_fail(s != NULL,FALSE);
100   VDKCustomTree* obj = reinterpret_cast<VDKCustomTree*>(s);
101   if(obj->mode != GTK_SELECTION_EXTENDED)
102     return FALSE;
103   int row;
104   int column;
105   VDKTreeNode node;
106   int res;
107   res = gtk_clist_get_selection_info (GTK_CLIST (wid),
108 				      int(ev->x), int(ev->y),
109 				      &row, &column);
110   if( (!res) || (ev->type != GDK_2BUTTON_PRESS))
111       return FALSE;
112   node = GTK_CTREE_NODE (g_list_nth (GTK_CLIST (wid)->row_list, row));
113   if(node)
114     {
115       obj->SelectedNode(node);
116       obj->SelectedColumn(column);
117       obj->SignalEmit(select_node_signal);
118     }
119   return FALSE; //TRUE;
120 }
121 
122 //////////////////////////////////
123 /*
124 constructor
125  */
VDKCustomTree(VDKForm * owner,int columns,char ** titles,GtkSelectionMode mode,int tree_column)126 VDKCustomTree::VDKCustomTree(VDKForm* owner,
127 			     int columns,
128 			     char **titles,
129 			     GtkSelectionMode mode,
130 			     int tree_column):
131   VDKCustom(owner,columns,titles,mode),
132   tree_column(tree_column),
133   Spacing("Spacing",this,5,&VDKCustomTree::SetSpacing),
134   SelectedNode("SelectedNode",this,NULL,&VDKCustomTree::SetSelectedNode),
135   SelectedColumn("SelectedColumn",this,-1),
136   UnselectedNode("UnselectedNode",this,NULL,&VDKCustomTree::SetUnselectedNode),
137   UnselectedColumn("UnselectedColumn",this,-1),
138   LineStyle("LineStyle",this,GTK_CTREE_LINES_SOLID,
139 	    &VDKCustomTree::SetLineStyle),
140   ExpanderStyle("ExpanderStyle",this,GTK_CTREE_EXPANDER_SQUARE,
141 		&VDKCustomTree::SetExpanderStyle)
142 
143 {
144 if(!titles)
145     sigwid = custom_widget = gtk_ctree_new(columns,tree_column);
146 else
147     sigwid = custom_widget = gtk_ctree_new_with_titles (columns, tree_column, titles);
148   GtkRcStyle *rc_style = gtk_widget_get_modifier_style (custom_widget);
149   GdkFont* font = rc_style ?
150     gdk_font_from_description (rc_style->font_desc): NULL;
151   if(font)
152     {
153       int rh = font->ascent + font->descent+1;
154       RowHeight(rh);
155     }
156   /*
157  int rh = custom_widget->style->font->ascent +
158    custom_widget->style->font->descent+1;
159  RowHeight(rh);
160   */
161  // gtk_clist_set_row_height(GTK_CLIST(custom_widget),rh);
162  gtk_clist_set_selection_mode(GTK_CLIST(custom_widget),mode);
163  gtk_clist_set_shadow_type(GTK_CLIST(custom_widget), GTK_SHADOW_ETCHED_OUT);
164 
165 if(titles)
166   {
167     int t;
168     for (t = 0; t < columns; t++)
169       {
170 	Titles[t] = new VDKObject(owner,
171 				  GTK_CLIST(custom_widget)->column[t].button);
172 	AddItem(Titles[t]);
173       }
174   }
175  gtk_container_add (GTK_CONTAINER (widget), custom_widget);
176  gtk_widget_show(GTK_WIDGET(custom_widget));
177  ConnectSignals();
178 #ifdef USE_SIGCPLUSPLUS
179  make_gtksigc_connection(this);
180 #endif
181 }
182 /*
183  */
184 void
ConnectSignals()185 VDKCustomTree::ConnectSignals()
186 {
187   // call ancestor
188   VDKCustom::ConnectSignals();
189 
190   s_list_select.obj = this;
191   s_list_select.signal = select_node_signal;
192   s_list_unselect.obj = this;
193   s_list_unselect.signal = unselect_node_signal;
194 
195   select_connect =
196     gtk_signal_connect( GTK_OBJECT(custom_widget),
197 			"tree_select_row",
198 			GTK_SIGNAL_FUNC(VDKCustomTree::NodeSelection),
199 			(gpointer) &s_list_select);
200     unselect_connect =
201       gtk_signal_connect(GTK_OBJECT(custom_widget),
202 			 "tree_unselect_row",
203 			 GTK_SIGNAL_FUNC(VDKCustomTree::NodeUnselection),
204 			 (gpointer) &s_list_unselect);
205     gtk_signal_connect (GTK_OBJECT (custom_widget),
206 			"button_press_event",
207 			GTK_SIGNAL_FUNC (VDKCustomTree::ButtonPress),
208 			this);
209   // specialized connect to realize signal
210   s_realize.obj = this;
211   s_realize.signal = realize_signal;
212   gtk_signal_connect(GTK_OBJECT(CustomWidget()),"realize",
213 		     GTK_SIGNAL_FUNC(VDKObject::VDKSignalPipe),
214 		     (gpointer) &s_realize);
215 }
216 
217 /*
218  */
219 void
SetStyle(VDKTreeNode node)220 VDKCustomTree::SetStyle(VDKTreeNode node)
221 {
222   /*
223   GtkStyle *style =
224     gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(custom_widget)));
225   g_return_if_fail(style != NULL);
226   gtk_style_ref(style);
227   if(UnselectedBackground)
228     {
229       GdkColor c = *(*UnselectedBackground);
230       style->base[GTK_STATE_NORMAL] = c;
231     }
232   if(SelectedBackground)
233     style->bg[GTK_STATE_SELECTED] = *(*SelectedBackground);
234   if(UnselectedForeground)
235     {
236       GdkColor c = *(*UnselectedForeground);
237       style->fg[GTK_STATE_NORMAL] = c;
238     }
239   if(SelectedForeground)
240     style->fg[GTK_STATE_SELECTED] = *(*SelectedForeground);
241   gtk_ctree_node_set_row_style (GTK_CTREE (custom_widget), node, style);
242   */
243 }
244 
245 /*
246  */
~VDKCustomTree()247 VDKCustomTree::~VDKCustomTree()
248 {
249 }
250 /*
251 add a node to tree,
252 if parent == NULL add a root,
253 otherwise a child
254  */
255 VDKTreeNode
AddNode(char * text[],VDKTreeNode parent,bool expanded,bool isLeaf,char ** pixmap_closed,char ** pixmap_opened)256 VDKCustomTree::AddNode(
257 		       char  *text[],
258 		       VDKTreeNode parent,
259 		       bool expanded,
260 		       bool isLeaf,
261 		       char **pixmap_closed,
262 		       char **pixmap_opened)
263 {
264  VDKTreeNode node;
265  GdkBitmap *mask = NULL,*mask1 = NULL;
266  GdkPixmap *pixmap = NULL,*pixmap1 = NULL;
267  GtkStyle* style = gtk_widget_get_style(owner->Window());
268  if(pixmap_closed)
269    pixmap =
270      gdk_pixmap_create_from_xpm_d(owner->Window()->window,
271 				  &mask,
272 				  &style->bg[GTK_STATE_NORMAL],
273 				  pixmap_closed);
274   if(pixmap_opened)
275     pixmap1 =
276       gdk_pixmap_create_from_xpm_d(owner->Window()->window,
277 				 &mask,
278 				 &style->bg[GTK_STATE_NORMAL],
279 				 pixmap_opened);
280 
281   node = gtk_ctree_insert_node (GTK_CTREE(custom_widget),
282 				parent, NULL,
283 				text, Spacing,
284 				pixmap,	mask,
285 				pixmap1, mask1,
286 				isLeaf, expanded);
287   // sets row height
288   // unuseful
289   /*
290     if(pixmap || pixmap1)
291     {
292     int w = 0, h = 0;
293     sscanf (pixmap_closed ? pixmap_closed[0] : pixmap_opened[0],
294     "%d %d", &w, &h);
295     printf("\nh:%d",h);
296     fflush(stdout);
297     gtk_clist_set_row_height (GTK_CLIST(custom_widget),h);
298     }
299   */
300   if(node)
301     SetStyle(node);
302   return node;
303 }
304 ////////////////////////////////////////////////////
305 Tuple
operator [](VDKTreeNode node)306 VDKCustomTree::operator[](VDKTreeNode node)
307 {
308 int t;
309 Tuple temp(columns,tree_column);
310 char *text;
311 if(node == NULL)
312   return temp;
313 for(t=0; t < columns;t++)
314   {
315     if (
316 	(GTK_CTREE_ROW (node)->row.cell[t].type == GTK_CELL_TEXT)
317 	&&
318 	(gtk_ctree_node_get_text(GTK_CTREE(custom_widget), node, t, &text))
319 	)
320       temp[t] = text;
321     else if (
322 	(GTK_CTREE_ROW (node)->row.cell[t].type == GTK_CELL_PIXTEXT)
323 	&&
324 	(gtk_ctree_node_get_pixtext (GTK_CTREE(custom_widget),
325 			    node, t,&text,NULL,NULL,NULL))
326 	)
327       temp[t] = text;
328   }
329 return temp;
330 }
331 
332 /*
333 return an array filled with selected nodes,
334 empty on single selection mode.
335 Tip:
336 array sequence is in a recursive form,
337  */
338 VDKTreeNodeArray&
Selections()339 VDKCustomTree::Selections()
340 {
341   GList* list,*head;
342   int listSize = 0, t;
343   WideSelection = VDKTreeNodeArray(0);
344   if(Size() == 0)
345     return WideSelection;
346   else if(
347 	  (mode == GTK_SELECTION_EXTENDED)
348 	  ||
349 	  (mode == GTK_SELECTION_MULTIPLE)
350 	  )
351 
352     {
353       list = head = GTK_CLIST(custom_widget)->selection;
354       // count list size and goes to tail as well
355       if(head)
356 	for(t=0; head; head = head->next,listSize++)
357 	  ;
358       // load array;
359       WideSelection = VDKTreeNodeArray(listSize);
360       for(t = 0; t < WideSelection.size();t++,list = list->next)
361 	WideSelection[t] = (VDKTreeNode) list->data;
362     }
363   return WideSelection;
364 }
365 /*
366 some useful node functions
367  */
368 bool
IsLeaf(VDKTreeNode node)369 VDKCustomTree::IsLeaf(VDKTreeNode node)
370 {
371   int is_leaf;
372   if(node == NULL)
373     return false;
374   else if(gtk_ctree_get_node_info(GTK_CTREE(custom_widget),
375 				  node,NULL,NULL,NULL,NULL,
376 				  NULL,NULL,&is_leaf,NULL))
377     return is_leaf == 1;
378   else
379     return false;
380 }
381 /*
382 return node key
383  */
384 char*
Key(VDKTreeNode node)385 VDKCustomTree::Key(VDKTreeNode node)
386 {
387   char* text;
388   if(Size() == 0)
389   return NULL;
390   else if(node == NULL)
391     node = GTK_CTREE_NODE (GTK_CLIST(custom_widget)->row_list);
392   if(gtk_ctree_get_node_info(GTK_CTREE(custom_widget),
393 			     node,&text,NULL,NULL,NULL, NULL,
394 			     NULL,NULL,NULL))
395     return text;
396   else
397     return NULL;
398 }
399 /*
400  */
401 bool
IsExpanded(VDKTreeNode node)402 VDKCustomTree::IsExpanded(VDKTreeNode node)
403 {
404   int expanded = 0;
405   if(node == NULL)
406     return false;
407   gtk_ctree_get_node_info(GTK_CTREE(custom_widget),
408 			  node,NULL,NULL,NULL,NULL, NULL,
409 			  NULL,NULL,&expanded);
410     return expanded;
411 }
412 /*
413 prunes entire tree
414  */
415 void
Clear()416 VDKCustomTree::Clear()
417 {
418   VDKCustom::Clear();
419   SelectedNode(NULL);
420   UnselectedNode(NULL);
421 }
422 /*
423 
424  */
425 bool
RemoveNode(VDKTreeNode node)426 VDKCustomTree::RemoveNode(VDKTreeNode node)
427 {
428 if(gtk_ctree_find(GTK_CTREE(custom_widget),NULL,node) )
429    gtk_ctree_remove_node (GTK_CTREE(custom_widget),node);
430 else
431   return false;
432 if(Size() == 0)
433   {
434     SelectedNode(NULL);
435     UnselectedNode(NULL);
436   }
437 return true;
438 }
439 /*
440  */
441 void
SetSelectedNode(VDKTreeNode node)442 VDKCustomTree::SetSelectedNode(VDKTreeNode node)
443 {
444   if(Size() == 0)
445     return;
446   else if(node == NULL)
447     node = GTK_CTREE_NODE (GTK_CLIST(custom_widget)->row_list);
448   gtk_ctree_select (GTK_CTREE(custom_widget),node);
449 }
450 /*
451  */
452 void
SetUnselectedNode(VDKTreeNode node)453 VDKCustomTree::SetUnselectedNode(VDKTreeNode node)
454 {
455   if(Size() == 0)
456     return;
457   else if(node == NULL)
458     node = GTK_CTREE_NODE (GTK_CLIST(custom_widget)->row_list);
459   gtk_ctree_unselect (GTK_CTREE(custom_widget),node);
460 }
461 /*
462 called by Find(char* key)->gtk_ctree_post_recursive(),
463 if node key matches with key
464 add that node to list
465  */
466 void
IterateOnTree(GtkCTree * ctree,GtkCTreeNode * node,gpointer i)467 IterateOnTree(GtkCTree     *ctree,
468 	      GtkCTreeNode *node,
469 	      gpointer      i)
470 {
471 FindInfo* info = (FindInfo*) i;
472 char *text;
473 int res = gtk_ctree_get_node_info (ctree, node, &text,
474 			 NULL, NULL, NULL, NULL,NULL,NULL,NULL);
475  if(res && !std::strcmp(text,info->key))
476    info->list->add(node);
477 }
478 /*
479 return a list of nodes that match
480 <key>, user should delete list after use
481  */
482 VDKTreeNodeList*
Find(char * key)483 VDKCustomTree::Find(char* key)
484 {
485 
486   VDKTreeNodeList* nodelist = new VDKTreeNodeList;
487   FindInfo info = { nodelist, key };
488   gtk_ctree_post_recursive(GTK_CTREE(custom_widget),
489 			   NULL, IterateOnTree, (gpointer) &info);
490   return nodelist;
491 }
492 
493 /*
494 remove all nodes that match <key>,
495 answer how many nodes has been removed
496  */
497 int
RemoveKey(char * key)498 VDKCustomTree::RemoveKey(char* key)
499 {
500 VDKTreeNodeList* list = Find(key);
501 int size = 0;
502 if( (size = list->size()) > 0)
503   {
504     VDKTreeNodeListIterator li(*list);
505     for(;li;li++)
506       RemoveNode(li.current());
507   }
508 delete list;
509 return size;
510 }
511 
512 #ifdef USE_SIGCPLUSPLUS
513 void
make_gtksigc_connection(VDKCustomTree * obj)514 VDKCustomTree::make_gtksigc_connection(VDKCustomTree* obj)
515 {
516      VDKCustom::make_gtksigc_connection(obj);
517      gtk_signal_connect(GTK_OBJECT(obj->CustomWidget()),
518 			"tree_move",
519 			GTK_SIGNAL_FUNC(&VDKCustomTree::_handle_tree_move),
520 			reinterpret_cast<gpointer>(obj));
521      gtk_signal_connect(GTK_OBJECT(obj->CustomWidget()),
522 			"tree_expand",
523 			GTK_SIGNAL_FUNC(&VDKCustomTree::_handle_tree_expand),
524 			reinterpret_cast<gpointer>(obj));
525 }
526 
527 void
_handle_tree_move(GtkWidget *,GtkCTreeNode * node,GtkCTreeNode * new_parent,GtkCTreeNode * new_sibling,gpointer gp)528 VDKCustomTree::_handle_tree_move(GtkWidget*, GtkCTreeNode* node,
529 				 GtkCTreeNode* new_parent,
530 				 GtkCTreeNode* new_sibling,
531 				 gpointer gp)
532 {
533      VDKCustomTree* obj=reinterpret_cast<VDKCustomTree*>(gp);
534      obj->OnTreeMove(obj, node, new_parent, new_sibling);
535 }
536 
537 void
_handle_tree_expand(GtkWidget *,GtkCTreeNode * node,gpointer gp)538 VDKCustomTree::_handle_tree_expand(GtkWidget*, GtkCTreeNode* node,
539 				    gpointer gp)
540 {
541      VDKCustomTree* obj=reinterpret_cast<VDKCustomTree*>(gp);
542      obj->OnTreeExpand(obj, node);
543 }
544 #endif
545