1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /*
3  * xap_TableWidget.cpp
4  * Copyright 2002 Joaquin Cuenca Abela
5  * Copyright 2016 Hubert Figuiere
6  *
7  * Authors:
8  *   Joaquin Cuenca Abela (e98cuenc@yahoo.com)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License, version 2, as published by the Free Software Foundation.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 
28 #include <algorithm>
29 
30 #include <gdk/gdkkeysyms.h>
31 #include <gdk/gdk.h>
32 #include <gtk/gtk.h>
33 
34 #include "xap_Gtk2Compat.h"
35 #if GTK_CHECK_VERSION(3,0,0)
36 #include "xap_GtkStyle.h"
37 #endif
38 #include "xap_UnixTableWidget.h"
39 #include "ut_debugmsg.h"
40 #include "ut_assert.h"
41 
42 /* NONE:UINT,UINT (/dev/stdin:1) */
43 static void
g_cclosure_user_marshal_VOID__UINT_UINT(GClosure * closure,GValue *,guint n_param_values,const GValue * param_values,gpointer,gpointer marshal_data)44 g_cclosure_user_marshal_VOID__UINT_UINT (GClosure     *closure,
45                                          GValue       * /*return_value*/,
46                                          guint         n_param_values,
47                                          const GValue *param_values,
48                                          gpointer      /*invocation_hint*/,
49                                          gpointer      marshal_data)
50 {
51   typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer     data1,
52                                                 guint        arg_1,
53                                                 guint        arg_2,
54                                                 gpointer     data2);
55   register GMarshalFunc_VOID__UINT_UINT callback;
56   register GCClosure *cc = (GCClosure*) closure;
57   register gpointer data1, data2;
58 
59   UT_return_if_fail (n_param_values == 3);
60   UT_DEBUGMSG(("IN AbiTable g_cclose_.. \n"));
61   if (G_CCLOSURE_SWAP_DATA (closure))
62     {
63       data1 = closure->data;
64       data2 = g_value_get_pointer (param_values + 0);
65     }
66   else
67     {
68       data1 = g_value_get_pointer (param_values + 0);
69       data2 = closure->data;
70     }
71   callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback);
72   UT_DEBUGMSG(("Calling callback marshell data %p cc %p \n",callback,cc));
73 
74   callback (data1,
75             g_value_get_uint (param_values + 1),
76             g_value_get_uint (param_values + 2),
77             data2);
78 }
79 
80 enum
81 {
82 	SELECTED,
83 	LAST_SIGNAL
84 };
85 
86 static gint abi_table_signals [LAST_SIGNAL] = { 0 };
87 
88 static GtkWidgetClass *abi_table_parent_class;
89 
90 /* ------------------- now the guts of AbiTable ---------------------- */
91 
92 static const guint cell_width = 24;
93 static const guint cell_height = 24;
94 static const guint cell_spacing = 4;
95 static const guint init_rows = 0;
96 static const guint init_cols = 0;
97 
98 static inline void
cells_to_pixels(guint cols,guint rows,guint * w,guint * h)99 cells_to_pixels(guint cols, guint rows, guint* w, guint* h)
100 {
101 	UT_return_if_fail(w);
102 	UT_return_if_fail(h);
103 
104 	*w = cell_width * cols + cell_spacing * (cols + 1);
105 	*h = cell_height * rows + cell_spacing * (rows + 1);
106 }
107 
108 static inline void
pixels_to_cells(guint w,guint h,guint * cols,guint * rows)109 pixels_to_cells(guint w, guint h, guint* cols, guint* rows)
110 {
111 	UT_return_if_fail(cols);
112 	UT_return_if_fail(rows);
113 
114 	*cols = w / (cell_width + cell_spacing) + 1;
115 	*rows = h / (cell_height + cell_spacing) + 1;
116 }
117 
118 static void
abi_table_resize(AbiTable * table)119 abi_table_resize(AbiTable* table)
120 {
121 	guint width;
122 	guint height;
123 	char* text;
124 	GtkRequisition size;
125 
126 	UT_return_if_fail(table);
127 
128 	if (table->selected_rows == 0 && table->selected_cols == 0)
129 		text = g_strdup(table->szCancel);
130 	else
131 	{
132 		text = g_strdup_printf("%d x %d %s", table->selected_rows, table->selected_cols, table->szTable);
133 	}
134 	cells_to_pixels(table->total_cols, table->total_rows, &width, &height);
135 	gtk_widget_get_preferred_size(GTK_WIDGET(table->window_label), &size, NULL);
136 
137 	gtk_label_set_text(table->window_label, text);
138 	gtk_window_resize(table->window, width + 1, height + size.height);
139 
140 	g_free(text);
141 }
142 
143 extern "C" void
abi_table_set_selected(AbiTable * abi_table,guint rows,guint cols)144 abi_table_set_selected (AbiTable* abi_table, guint rows, guint cols)
145 {
146 	UT_return_if_fail (abi_table);
147 
148 	abi_table->selected_rows = rows;
149 	abi_table->selected_cols = cols;
150 
151 	abi_table_resize(abi_table);
152 }
153 
154 extern "C" void
abi_table_get_selected(const AbiTable * abi_table,guint * rows,guint * cols)155 abi_table_get_selected (const AbiTable* abi_table, guint* rows, guint* cols)
156 {
157 	UT_return_if_fail (abi_table);
158 
159 	if (rows)
160 		*rows = abi_table->selected_rows;
161 
162 	if (cols)
163 		*cols = abi_table->selected_cols;
164 }
165 
166 extern "C" void
abi_table_set_max_size(AbiTable * abi_table,guint rows,guint cols)167 abi_table_set_max_size (AbiTable* abi_table, guint rows, guint cols)
168 {
169 	UT_return_if_fail (abi_table);
170 
171 	abi_table->total_rows = rows;
172 	abi_table->total_cols = cols;
173 
174 	abi_table_resize(abi_table);
175 }
176 
177 
178 extern "C" void
abi_table_set_labels(AbiTable * abi_table,const gchar * szTable,const gchar * szCancel)179 abi_table_set_labels(AbiTable* abi_table, const gchar * szTable, const gchar * szCancel)
180 {
181 	if(abi_table->szTable)
182 		g_free(abi_table->szTable);
183 	abi_table->szTable = g_strdup(szTable);
184 	if(abi_table->szCancel)
185 		g_free(abi_table->szCancel);
186 	abi_table->szCancel = g_strdup(szCancel);
187 }
188 
189 extern "C" void
abi_table_get_max_size(const AbiTable * abi_table,guint * rows,guint * cols)190 abi_table_get_max_size (const AbiTable* abi_table, guint* rows, guint* cols)
191 {
192 	UT_return_if_fail (abi_table);
193 
194 	if (rows)
195 		*rows = abi_table->total_rows;
196 
197 	if (cols)
198 		*cols = abi_table->total_cols;
199 }
200 
201 static gboolean
202 #if GTK_CHECK_VERSION(3,0,0)
on_drawing_area_event(GtkWidget * area,cairo_t * cr,gpointer user_data)203 on_drawing_area_event (GtkWidget *area, cairo_t *cr, gpointer user_data)
204 #else
205 on_drawing_area_event (GtkWidget *area, GdkEventExpose *, gpointer user_data)
206 #endif
207 {
208 	AbiTable* table = static_cast<AbiTable*>(user_data);
209 
210 #if GTK_CHECK_VERSION(3,0,0)
211 	if (!table || !table->style_context) {
212 		return TRUE;
213 	}
214 #endif
215 
216 	guint i;
217 	guint j;
218 	guint selected_rows = table->selected_rows;
219 	guint selected_cols = table->selected_cols;
220 	guint x;
221 	guint y;
222 
223 #if GTK_CHECK_VERSION(3,0,0)
224 	GtkStyleContext* ctxt = gtk_widget_get_style_context(GTK_WIDGET(area));
225 	gtk_style_context_save(ctxt);
226 	gtk_style_context_set_state(ctxt, GTK_STATE_FLAG_FOCUSED);
227 #else
228 	gdk_draw_rectangle (area->window,
229 			    area->style->bg_gc[GTK_STATE_NORMAL],
230 			    TRUE,
231 			    0, 0,
232 			    area->allocation.width,
233 			    area->allocation.height);
234 #endif
235 
236 	for (i = 0; i < table->total_rows; ++i) {
237 		for (j = 0; j < table->total_cols; ++j) {
238 			cells_to_pixels(j, i, &x, &y);
239 
240 #if GTK_CHECK_VERSION(3,0,0)
241 			if (i < selected_rows && j < selected_cols) {
242 				gtk_style_context_set_state(table->style_context, GTK_STATE_FLAG_SELECTED);
243 			} else {
244 				gtk_style_context_set_state(table->style_context, GTK_STATE_FLAG_NORMAL);
245 			}
246 			gtk_render_background(table->style_context, cr, x + 1, y + 1,
247 								  cell_width - 1, cell_height - 1);
248 
249 			gtk_render_frame(ctxt, cr, x, y, cell_width, cell_height);
250 #else
251 			gdk_draw_rectangle (area->window,
252 					    area->style->dark_gc[GTK_STATE_NORMAL],
253 					    FALSE,
254 					    x - 1, y - 1,
255 					    cell_width + 1,
256 					    cell_height + 1);
257 			if (i < selected_rows && j < selected_cols) {
258 				gdk_draw_rectangle (area->window,
259 						    table->selected_gc,
260 						    TRUE,
261 						    x, y,
262 						    cell_width,
263 						    cell_height);
264 			}
265 			else {
266 				gdk_draw_rectangle (area->window,
267 						    area->style->white_gc,
268 						    TRUE,
269 						    x, y,
270 						    cell_width,
271 						    cell_height);
272 			}
273 			/* black border line */
274 			gdk_draw_line (area->window,
275 				       area->style->black_gc,
276 				       area->allocation.width - 1, 0, area->allocation.width - 1, area->allocation.height - 1);
277 			gdk_draw_line (area->window,
278 				       area->style->black_gc,
279 				       area->allocation.width - 1, area->allocation.height - 1, 0, area->allocation.height - 1);
280 
281 			/* dark border line */
282 			gdk_draw_line (area->window,
283 				       area->style->dark_gc[GTK_STATE_NORMAL],
284 				       area->allocation.width - 2, 1, area->allocation.width - 2,
285 				       area->allocation.height - 2);
286 			gdk_draw_line (area->window,
287 				       area->style->dark_gc[GTK_STATE_NORMAL],
288 				       area->allocation.width - 2, area->allocation.height - 2, 1,
289 				       area->allocation.height - 2);
290 
291 			/* ligth border line */
292 			gdk_draw_line (area->window,
293 				       area->style->light_gc[GTK_STATE_NORMAL],
294 				       0, 0, area->allocation.width - 3, 0);
295 			gdk_draw_line (area->window,
296 				       area->style->light_gc[GTK_STATE_NORMAL],
297 				       0, 0, 0, area->allocation.height - 2);
298 #endif
299 		}
300 	}
301 #if GTK_CHECK_VERSION(3,0,0)
302 	gtk_style_context_restore(ctxt);
303 #endif
304 
305 	return TRUE;
306 }
307 
308 static inline guint
my_max(guint a,guint b)309 my_max(guint a, guint b)
310 {
311 	return a < b ? b : a;
312 }
313 
314 static gboolean
on_motion_notify_event(GtkWidget * window,GdkEventMotion * ev,gpointer user_data)315 on_motion_notify_event (GtkWidget *window, GdkEventMotion *ev, gpointer user_data)
316 {
317 	AbiTable* table = static_cast<AbiTable*>(user_data);
318 	guint selected_cols;
319 	guint selected_rows;
320 
321 	if (ev->x < 0 || ev->y < 0)
322 		return TRUE;
323 
324 	pixels_to_cells(static_cast<guint>(ev->x), static_cast<guint>(ev->y), &selected_cols, &selected_rows);
325 
326 	if ((selected_cols != table->selected_cols) || (selected_rows != table->selected_rows))
327 	{
328 		/* grow or shrink the table widget as necessary */
329 		table->selected_cols = selected_cols;
330 		table->selected_rows = selected_rows;
331 
332 		if (table->selected_rows <= 0 || table->selected_cols <= 0)
333 			table->selected_rows = table->selected_cols = 0;
334 
335 		table->total_rows = my_max(table->selected_rows + 1, 3);
336 		table->total_cols = my_max(table->selected_cols + 1, 3);
337 
338 		abi_table_resize(table);
339 		gtk_widget_queue_draw (window);
340 	}
341 
342 	return TRUE;
343 }
344 
345 static void
restart_widget(AbiTable * table)346 restart_widget (AbiTable *table)
347 {
348 	table->selected_cols = init_cols;
349 	table->selected_rows = init_rows;
350 	table->total_cols = my_max(init_cols + 1, 5);
351 	table->total_rows = my_max(init_rows + 1, 6);
352 	g_signal_emit_by_name(table, "released");
353 	gtk_widget_hide(GTK_WIDGET(table->window));
354 }
355 
356 /*
357  * Fires signal "selected", and reset and hide the widget
358  */
359 static void
emit_selected(AbiTable * table)360 emit_selected (AbiTable *table)
361 {
362 	gtk_widget_hide(GTK_WIDGET(table->window));
363 
364 	while (gtk_events_pending())
365 		gtk_main_iteration();
366 
367 	if (table->selected_rows > 0 && table->selected_cols > 0)
368 		g_signal_emit (G_OBJECT (table),
369 			       abi_table_signals [SELECTED], 0,
370 			       table->selected_rows, table->selected_cols);
371 
372 	restart_widget(table);
373 }
374 
375 static gboolean
on_button_release_event(GtkWidget *,GdkEventButton * ev,gpointer user_data)376 on_button_release_event (GtkWidget *, GdkEventButton *ev, gpointer user_data)
377 {
378 	AbiTable* table = static_cast<AbiTable*>(user_data);
379 
380 	/* Quick test to know if we're possibly over the button */
381 	if (ev->y < 0.0 && ev->x >= 0.0)
382 	{
383 		GtkRequisition size;
384 
385 		gtk_widget_get_preferred_size(GTK_WIDGET(table), &size, NULL);
386 
387 		/* And now, precise and slightly slower test.
388 		   I wonder if the double test really matters from a speed pov */
389 		if (-ev->y < size.height && ev->x < size.width)
390 			return TRUE;
391 	}
392 
393 	emit_selected(table);
394 
395 	return TRUE;
396 }
397 
398 static gboolean
on_leave_event(GtkWidget * area,GdkEventCrossing * event,gpointer user_data)399 on_leave_event (GtkWidget *area,
400 				GdkEventCrossing *event,
401 				gpointer user_data)
402 {
403 	AbiTable* table = static_cast<AbiTable*>(user_data);
404 
405 	if (gtk_widget_get_visible(GTK_WIDGET(table->window)) && (event->x < 0 || event->y < 0))
406 	{
407 		table->selected_rows = 0;
408 		table->selected_cols = 0;
409 		table->total_rows = my_max(table->selected_rows + 1, 3);
410 		table->total_cols = my_max(table->selected_cols + 1, 3);
411 
412 		abi_table_resize(table);
413 		gtk_widget_queue_draw (area);
414 	}
415 
416 	return TRUE;
417 }
418 
419 static gboolean
popup_grab_on_window(GdkWindow * window,guint32 activate_time)420 popup_grab_on_window (GdkWindow *window,
421 					  guint32    activate_time)
422 {
423 	GdkEventMask emask = static_cast<GdkEventMask>(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
424 												   GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK |
425 												   GDK_ENTER_NOTIFY_MASK) ;
426 	if ((XAP_gdk_pointer_grab (window, FALSE,emask,
427 						   NULL, NULL, activate_time) == 0))
428 	{
429 		if (XAP_gdk_keyboard_grab (window, FALSE,
430 							   activate_time) == 0)
431 			return TRUE;
432 		else
433 		{
434 			XAP_gdk_pointer_ungrab (activate_time);
435 			return FALSE;
436 		}
437 	}
438 
439 	return FALSE;
440 }
441 
442 static void
on_pressed(GtkButton * button,gpointer user_data)443 on_pressed(GtkButton* button, gpointer user_data)
444 {
445 	AbiTable* table = static_cast<AbiTable*>(user_data);
446 	int left, top;
447 	GtkAllocation alloc;
448 
449 	/* Temporarily grab pointer and keyboard on a window we know exists; we
450 	 * do this so that the grab (with owner events == TRUE) affects
451 	 * events generated when the window is mapped, such as enter
452 	 * notify events on subwidgets. If the grab fails, bail out.
453 	 */
454 	if (!popup_grab_on_window (gtk_widget_get_window(GTK_WIDGET(button)),
455 							   gtk_get_current_event_time ()))
456 		return;
457 
458 	auto toplevel = gtk_widget_get_toplevel(GTK_WIDGET(table));
459 	gtk_window_set_transient_for(table->window, GTK_WINDOW(toplevel));
460 	gdk_window_get_origin (gtk_widget_get_window(GTK_WIDGET(table)), &left, &top);
461 	gtk_widget_get_allocation(GTK_WIDGET(table), &alloc);
462 	gtk_window_move(table->window,
463 	                left + alloc.x,	top + alloc.y + alloc.height);
464 	abi_table_resize(table);
465 
466 	gtk_widget_show(GTK_WIDGET(table->window));
467 	gtk_widget_grab_focus(GTK_WIDGET(table->window));
468 
469 	/* Now transfer our grabs to the popup window; this
470 	 * should always succeed.
471 	 */
472 	popup_grab_on_window (gtk_widget_get_window(GTK_WIDGET(table->area)),
473 			      gtk_get_current_event_time ());
474 #if !GTK_CHECK_VERSION(3,0,0)
475 	GdkColor selected_color = (GTK_WIDGET (button))->style->base[GTK_STATE_SELECTED];
476 
477 	/* leak */
478 	table->selected_gc = gdk_gc_new(GTK_WIDGET(button)->window);
479 	gdk_gc_set_rgb_fg_color(table->selected_gc, &selected_color);
480 #endif
481 }
482 
483 gboolean
on_key_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)484 on_key_event(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
485 {
486 	AbiTable* table = static_cast<AbiTable*>(user_data);
487 	gboolean grew = FALSE;
488 
489 	switch (event->keyval)
490 	{
491 	case GDK_KEY_Up:
492 	case GDK_KEY_KP_Up:
493 		if (table->selected_rows > 0)
494 			--table->selected_rows;
495 		break;
496 	case GDK_KEY_Down:
497 	case GDK_KEY_KP_Down:
498 		grew = TRUE;
499 		++table->selected_rows;
500 		break;
501 	case GDK_KEY_Left:
502 	case GDK_KEY_KP_Left:
503 		if (table->selected_cols > 0)
504 			--table->selected_cols;
505 		break;
506 	case GDK_KEY_Right:
507 	case GDK_KEY_KP_Right:
508 		grew = TRUE;
509 		++table->selected_cols;
510 		break;
511 	case GDK_KEY_Escape:
512 		restart_widget(table);
513 		return TRUE;
514 	case GDK_KEY_KP_Space:
515 	case GDK_KEY_KP_Enter:
516 	case GDK_KEY_space:
517 	case GDK_KEY_3270_Enter:
518 	case GDK_KEY_ISO_Enter:
519 	case GDK_KEY_Return:
520 		emit_selected(table);
521 		return TRUE;
522 	}
523 
524 	if(table->selected_rows == 0 || table->selected_cols == 0)
525 		table->selected_rows = table->selected_cols = (grew ? 1 : 0) ;
526 
527 	table->total_rows = my_max(table->selected_rows + 1, 3);
528 	table->total_cols = my_max(table->selected_cols + 1, 3);
529 
530 	abi_table_resize(table);
531 	gtk_widget_queue_draw (widget);
532 
533 	return TRUE;
534 }
535 
536 
537 /* XPM */
538 static const char * widget_tb_insert_table_xpm[] = {
539 	"24 24 32 1",
540 	" 	c None",
541 	".	c #000000",
542 	"+	c #FFFFFF",
543 	"@	c #B1B1B1",
544 	"#	c #BABABA",
545 	"$	c #E4E4E4",
546 	"%	c #ECECEC",
547 	"&	c #A8A8A8",
548 	"*	c #ADADAD",
549 	"=	c #B9B9B9",
550 	"-	c #ABABAB",
551 	";	c #B8B8B8",
552 	">	c #B5B5B5",
553 	",	c #AAAAAA",
554 	"'	c #858585",
555 	")	c #828282",
556 	"!	c #707070",
557 	"~	c #BCBCBC",
558 	"{	c #A7A7A7",
559 	"]	c #8D8D8D",
560 	"^	c #737373",
561 	"/	c #ACACAC",
562 	"(	c #878787",
563 	"_	c #747474",
564 	":	c #A9A9A9",
565 	"<	c #E9E9E9",
566 	"[	c #C3C3C3",
567 	"}	c #BFBFBF",
568 	"|	c #757575",
569 	"1	c #7E7E7E",
570 	"2	c #BBBBBB",
571 	"3	c #A3A3A3",
572 	"                        ",
573 	"                        ",
574 	"                        ",
575 	"   ...................  ",
576 	"   .+++++@+++++#++++$.  ",
577 	"   .+%%%%&%%%%%*%%%%=.  ",
578 	"   .+%%%%-%%%%%-%%%%;.  ",
579 	"   .>&,,&'&----)&---!.  ",
580 	"   .+%%%%-%%%%%-%%%%~.  ",
581 	"   .+%%%%-%%%%%-%%%%~.  ",
582 	"   .+%%%%-%%%%%-%%%%~.  ",
583 	"   .#{---'-----]*---^.  ",
584 	"   .+%%%%-%%%%%-%%%%~.  ",
585 	"   .+%%%%-%%%%%-%%%%;.  ",
586 	"   .+%%%%-%%%%%-%%%%~.  ",
587 	"   .;&--/(&{--&'&---_.  ",
588 	"   .+%%%%-%%%%%-%%%%;.  ",
589 	"   .+%%%%&%%%%%:%%%%~.  ",
590 	"   .<[[[}|~~~~~1;~~23.  ",
591 	"   ...................  ",
592 	"                        ",
593 	"                        ",
594 	"                        ",
595 	"                        "};
596 
597 
598 static void
register_stock_icon(void)599 register_stock_icon(void)
600 {
601 	static gboolean registered = FALSE;
602 
603 	if (!registered)
604 	{
605 		GdkPixbuf *pixbuf;
606 		GtkIconFactory *factory;
607 
608 		static GtkStockItem items[] = {
609 			{ (gchar*)"abi-table-widget",
610 			  (gchar*)"_Table",
611 			  static_cast<GdkModifierType>(0), 0, NULL }
612 		};
613 
614 		registered = TRUE;
615 
616 		/* Register our stock items */
617 		gtk_stock_add (items, G_N_ELEMENTS (items));
618 
619 		/* Add our custom icon factory to the list of defaults */
620 		factory = gtk_icon_factory_new ();
621 		gtk_icon_factory_add_default (factory);
622 
623 		// Must be C cast
624 		pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)widget_tb_insert_table_xpm);
625 
626 		/* Register icon to accompany stock item */
627 		if (pixbuf != NULL)
628 		{
629 			GtkIconSet *icon_set;
630 
631 			icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
632 			gtk_icon_factory_add (factory, "abi-table-widget", icon_set);
633 			gtk_icon_set_unref (icon_set);
634 			g_object_unref (G_OBJECT (pixbuf));
635 		}
636 		else
637 		{
638 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
639 		}
640 
641 		/* Drop our reference to the factory, GTK will hold a reference. */
642 		g_object_unref (G_OBJECT (factory));
643 	}
644 }
645 
abi_table_set_icon(AbiTable * abi_table,GtkWidget * gtkImageIcon)646 void abi_table_set_icon(AbiTable* abi_table,GtkWidget * gtkImageIcon)
647 {
648 	if(!GTK_IS_IMAGE(G_OBJECT(gtkImageIcon)))
649 	   return;
650 	g_object_unref (G_OBJECT (abi_table->icon));
651 	abi_table->icon = gtkImageIcon;
652 }
653 
654 /* ------------------- and now the GObject part ---------------------- */
655 
656 static void
abi_table_dispose(GObject * instance)657 abi_table_dispose (GObject *instance)
658 {
659 	AbiTable* self = ABITABLE_WIDGET(instance);
660 
661 // For some reason I get an alert
662 	if(self->label) {
663 		g_object_unref(self->label);
664 		self->label = NULL;
665 	}
666 
667 	if(self->szTable) {
668 		g_free(self->szTable);
669 		self->szTable = NULL;
670 	}
671 	if(self->szCancel) {
672 		g_free(self->szCancel);
673 		self->szCancel = NULL;
674 	}
675 
676 #if GTK_CHECK_VERSION(3,0,0)
677 	g_clear_object(&self->style_context);
678 #endif
679 
680 	G_OBJECT_CLASS (abi_table_parent_class)->dispose (instance);
681 }
682 
683 static void
abi_table_class_init(AbiTableClass * klass)684 abi_table_class_init (AbiTableClass *klass)
685 {
686 	GtkWidgetClass *object_class = reinterpret_cast<GtkWidgetClass*>(klass);
687 
688 	G_OBJECT_CLASS(object_class)->dispose = abi_table_dispose;
689 
690 	abi_table_parent_class = static_cast<GtkWidgetClass *>(g_type_class_peek (GTK_TYPE_BUTTON));
691 	abi_table_signals [SELECTED] =
692 		g_signal_new ("selected",
693 			      G_OBJECT_CLASS_TYPE (object_class),
694 			      G_SIGNAL_RUN_LAST,
695 			      G_STRUCT_OFFSET (AbiTableClass, selected),
696 			      NULL, NULL,
697 			      g_cclosure_user_marshal_VOID__UINT_UINT,
698 			      G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
699 }
700 
701 static void
abi_table_init(AbiTable * table)702 abi_table_init (AbiTable* table)
703 {
704 	char* text = g_strdup_printf("%d x %d ", init_rows, init_cols);
705 
706 	register_stock_icon();
707 
708 #if GTK_CHECK_VERSION(3,0,0)
709 	table->style_context = XAP_GtkStyle_get_style(NULL, "GtkTreeView.view"); // "textview.view"
710 #endif
711 
712 	table->button_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
713 
714 	table->window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP));
715 	table->window_vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
716 
717 	table->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
718 
719 	table->handlers = 0;
720 	table->window_label = GTK_LABEL(gtk_label_new(text));
721 	g_free(text);
722 	table->szTable = NULL;
723 	table->szCancel = NULL;
724 	gtk_container_add(GTK_CONTAINER(table->window), GTK_WIDGET(table->window_vbox));
725 	gtk_box_pack_end(GTK_BOX(table->window_vbox), GTK_WIDGET(table->window_label), FALSE, FALSE, 0);
726 	gtk_box_pack_end(GTK_BOX(table->window_vbox), GTK_WIDGET(table->area), TRUE, TRUE, 0);
727 
728 	gtk_widget_show_all(GTK_WIDGET(table->window_vbox));
729 
730 	table->selected_rows = init_rows;
731 	table->selected_cols = init_cols;
732 
733 	table->total_rows = my_max(init_rows + 1, 5);
734 	table->total_cols = my_max(init_cols + 1, 6);
735 
736 	abi_table_resize(table);
737 
738 	table->icon = NULL;
739 	if (gtk_stock_lookup ("abi-table-widget", &table->stock_item))
740 	{
741 		table->label = gtk_label_new_with_mnemonic(table->stock_item.label);
742 		table->icon = gtk_image_new_from_stock ("abi-table-widget", GTK_ICON_SIZE_LARGE_TOOLBAR);
743 		gtk_widget_show(table->icon);
744 		gtk_widget_show(table->label);
745 		g_object_ref_sink(table->label);
746 		//
747 		// We actually never want this label in toolbar
748 		//		gtk_box_pack_end(GTK_BOX(table->button_box), table->label, FALSE, FALSE, 0);
749 		gtk_box_pack_end(GTK_BOX(table->button_box), table->icon, FALSE, FALSE, 0);
750 		UT_DEBUGMSG(("abi-table icon loaded %p !\n",table->icon));
751 	}
752 	else
753 	{
754 		/* it should not happen... */
755 		UT_DEBUGMSG(("abi-table icon did not load !\n"));
756 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
757 		table->label = gtk_label_new_with_mnemonic("_Table");
758 		g_object_ref_sink(table->label);
759 		//		gtk_box_pack_end(GTK_BOX(table->button_box), table->label, FALSE, FALSE, 0);
760 	}
761 
762 	gtk_container_add(GTK_CONTAINER(table), GTK_WIDGET(table->button_box));
763 
764 	g_signal_connect(G_OBJECT(table), "pressed",
765 			 G_CALLBACK(on_pressed), static_cast<gpointer>(table));
766 #if GTK_CHECK_VERSION(3,0,0)
767 	g_signal_connect(G_OBJECT(table->area), "draw",
768 			 G_CALLBACK(on_drawing_area_event), static_cast<gpointer>(table));
769 #else
770 	g_signal_connect(G_OBJECT(table->area), "expose_event",
771 			 G_CALLBACK(on_drawing_area_event), static_cast<gpointer>(table));
772 #endif
773 	g_signal_connect(G_OBJECT(table->area), "motion_notify_event",
774 			 G_CALLBACK(on_motion_notify_event), static_cast<gpointer>(table));
775 	g_signal_connect(G_OBJECT(table->area), "button_release_event",
776 			 G_CALLBACK(on_button_release_event), static_cast<gpointer>(table));
777 	g_signal_connect(G_OBJECT(table->area), "button_press_event",
778 			 G_CALLBACK(on_button_release_event), static_cast<gpointer>(table));
779 	g_signal_connect(G_OBJECT(table->area), "leave_notify_event",
780 			 G_CALLBACK(on_leave_event), static_cast<gpointer>(table));
781 	g_signal_connect(G_OBJECT(table->window), "key_press_event",
782 			 G_CALLBACK(on_key_event), static_cast<gpointer>(table));
783 
784 	gtk_widget_set_events (GTK_WIDGET(table->area), GDK_EXPOSURE_MASK
785 						   | GDK_LEAVE_NOTIFY_MASK
786 						   | GDK_BUTTON_PRESS_MASK
787 						   | GDK_BUTTON_RELEASE_MASK
788 						   | GDK_POINTER_MOTION_MASK
789 						   | GDK_KEY_PRESS_MASK
790 						   | GDK_KEY_RELEASE_MASK);
791 
792 	gtk_button_set_relief (GTK_BUTTON (table), GTK_RELIEF_NORMAL);
793 }
794 
795 
796  GType
abi_table_get_type(void)797 abi_table_get_type (void)
798 {
799 	static GType type = 0;
800 
801 	if (!type)
802 	{
803 		static const GTypeInfo info =
804 			{
805 				sizeof (AbiTableClass),
806 				NULL,		/* base_init */
807 				NULL,		/* base_finalize */
808 				reinterpret_cast<GClassInitFunc>(abi_table_class_init),
809 				NULL,		/* class_finalize */
810 				NULL,		/* class_data */
811 				sizeof (AbiTable),
812 				0,		/* n_preallocs */
813 				reinterpret_cast<GInstanceInitFunc>(abi_table_init),
814 				NULL
815 			};
816 
817 		type = g_type_register_static (GTK_TYPE_BUTTON, "AbiTable", &info, static_cast<GTypeFlags>(0));
818 	}
819 
820 	return type;
821 }
822 
823  GtkWidget*
abi_table_new(void)824 abi_table_new (void)
825 {
826 	UT_DEBUGMSG(("COnstructing ABITABLE widget \n"));
827 	return GTK_WIDGET (g_object_new (abi_table_get_type (), NULL));
828 }
829 
830 /**
831  * abi_table_get_label:
832  * @abi_table: #AbiTable
833  *
834  * Returns: (transfer none): the label widget.
835  */
abi_table_get_label(AbiTable * abi_table)836  GtkWidget* abi_table_get_label(AbiTable* abi_table)
837 {
838 	return abi_table->label;
839 }
840