950f6596d584d30b956490437ff0bdda6ecd00af
[tinc] / src / pokey / interface.c
1 /*
2     interface.c -- GTK+/GNOME interface functions
3     Copyright (C) 2002 Guus Sliepen <guus@sliepen.warande.net>,
4                   2002 Ivo Timmermans <ivo@o2w.nl>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id: interface.c,v 1.4 2002/04/28 12:46:26 zarq Exp $
21 */
22
23 #include "config.h"
24
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/time.h>
29
30 #define log mathlog
31 #include <math.h>
32 #undef log
33
34 #include <gtk/gtk.h>
35 #include <glade/glade.h>
36 #include <libgnomeui/gnome-canvas.h>
37 #include <libgnomeui/gnome-canvas-rect-ellipse.h>
38 #include <libgnomeui/gnome-canvas-text.h>
39 #include <libgnomeui/gnome-canvas-line.h>
40 #include <libgnomeui/gnome-canvas-util.h>
41
42 #include "node.h"
43 #include "connection.h"
44 #include "edge.h"
45 #include "interface.h"
46 #include "logging.h"
47
48 #include <hooks.h>
49 #include <xalloc.h>
50
51 #include "system.h"
52
53 extern GladeXML *xml;
54
55 #ifdef MAXBUFSIZE
56 #undef MAXBUFSIZE
57 #endif
58
59 #define MAXBUFSIZE 1024
60
61 int build_graph = 0;
62
63 static GdkColormap *colormap = NULL;
64 static GdkColor timecolor;
65
66 #define MAX_NODES 25
67 #define K 10.0
68
69 #ifdef INFINITY
70 #undef INFINITY
71 #endif
72 #define INFINITY 1.0e10
73
74 node_t *nodes[MAX_NODES];
75 double x[MAX_NODES];
76 double y[MAX_NODES];
77 double k[MAX_NODES][MAX_NODES];
78 double d[MAX_NODES][MAX_NODES];
79 double l[MAX_NODES][MAX_NODES];
80 const double epsilon = 0.001;
81
82 static int inited = 0;
83
84 static int number_of_nodes = 0;
85
86 static GtkWidget *nodetree;
87 static GtkCTreeNode *hosts_ctn;
88
89 static GnomeCanvasGroup *edge_group = NULL;
90
91 static int canvas_width;
92 static int canvas_height;
93
94 static GtkWidget *canvas = NULL;
95
96 static int log_inited = 0;
97 static int follow_log = 1;
98
99 static int keep_drawing = 1;
100
101 static GtkCList *connlist = NULL;
102
103 static double canvas_zoom = 1.00;
104
105 void if_node_add(const char *hooktype, va_list ap);
106 void if_node_del(const char *hooktype, va_list ap);
107 void if_subnet_add(const char *hooktype, va_list ap);
108 void if_subnet_del(const char *hooktype, va_list ap);
109 void if_edge_add(const char *hooktype, va_list ap);
110 void if_edge_del(const char *hooktype, va_list ap);
111 void if_node_visible(const char *hooktype, va_list ap);
112 void if_node_invisible(const char *hooktype, va_list ap);
113
114 GtkWidget *create_canvas(void)
115 {
116   canvas = glade_xml_get_widget(xml, "canvas1");
117   if(!canvas)
118     {
119       fprintf(stderr, "Could not find widget `canvas1'\n");
120       return NULL;
121     }
122   
123   gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas), -00.0, -00.0, 700, 500);
124
125   canvas_width = 300.0;
126   canvas_height = 500.0;
127
128   return canvas;
129 }
130
131 void log_gtk(int level, int priority, char *fmt, va_list ap)
132 {
133   char buffer1[MAXBUFSIZE];
134   char buffer2[MAXBUFSIZE];
135   GtkWidget *w;
136   int len;
137   char *p;
138   struct tm *tm;
139   time_t t;
140
141   if(!xml)
142     return;
143   
144   w = glade_xml_get_widget(xml, "Messages");
145   if(!w)
146     return;
147
148   /* Use vsnprintf instead of vasprintf: faster, no memory
149      fragmentation, cleanup is automatic, and there is a limit on the
150      input buffer anyway */
151   len = vsnprintf(buffer1, MAXBUFSIZE, fmt, ap);
152
153   buffer1[MAXBUFSIZE-1] = '\0';
154   if((p = strrchr(buffer1, '\n')))
155     *p = '\0';
156
157   t = time(NULL);
158   tm = localtime(&t);
159   snprintf(buffer2, MAXBUFSIZE, "%02d:%02d:%02d ",
160            tm->tm_hour, tm->tm_min, tm->tm_sec);
161
162   if(!colormap)
163     {
164       colormap = gdk_colormap_new(gdk_visual_get_system(), FALSE);
165       timecolor.red = 0xffff;
166       timecolor.green = 0;
167       timecolor.blue = 0;
168       if(gdk_colormap_alloc_color(colormap, &timecolor, FALSE, TRUE) != TRUE)
169         {
170           fprintf(stderr, "Failed to allocate color\n");
171           exit(1);
172         }
173     }
174   
175   gtk_text_freeze(GTK_TEXT(w));
176
177   if(log_inited)
178     gtk_text_insert(GTK_TEXT(w), NULL, NULL, NULL, "\n", 1);
179
180   gtk_text_insert(GTK_TEXT(w), NULL, &timecolor, NULL, buffer2, strlen(buffer2));
181   gtk_text_insert(GTK_TEXT(w), NULL, NULL, NULL, buffer1, len);
182   gtk_text_thaw(GTK_TEXT(w));
183
184   log_inited = 1;
185   if(follow_log)
186 /*     gtk_text_set_point(GTK_TEXT(w), -1); */
187     gtk_editable_set_position(GTK_EDITABLE(w), gtk_text_get_length(GTK_TEXT(w)));
188 }
189
190 void if_hostinfoclosebutton_clicked(GtkWidget *w, gpointer data)
191 {
192   gtk_widget_destroy(GTK_WIDGET(data));
193 }
194
195 void update_hostinfo_dialog(GladeXML *x, node_t *n)
196 {
197   GtkWidget *w;
198   char s[100];
199   avl_node_t *avlnode;
200   char *l[1];
201
202   w = glade_xml_get_widget(x, "HostInfoNameEntry");
203   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoNameEntry"); return; }
204   gtk_entry_set_text(GTK_ENTRY(w), n->name);
205
206   w = glade_xml_get_widget(x, "HostInfoHostnameEntry");
207   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoHostnameEntry"); return; }
208   gtk_entry_set_text(GTK_ENTRY(w), n->hostname);
209
210   w = glade_xml_get_widget(x, "HostInfoPortEntry");
211   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoPortEntry"); return; }
212 /*   snprintf(s, sizeof(s)-1, "%hd", "0"); */
213   gtk_entry_set_text(GTK_ENTRY(w), "port");
214
215   w = glade_xml_get_widget(x, "HostInfoVersionEntry");
216   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoVersionEntry"); return; }
217   gtk_entry_set_text(GTK_ENTRY(w), n->name);
218
219   w = glade_xml_get_widget(x, "HostInfoStatusEntry");
220   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoStatusEntry"); return; }
221 /*   snprintf(s, sizeof(s)-1, "%x", n->status); */
222   gtk_entry_set_text(GTK_ENTRY(w), "0");
223
224   w = glade_xml_get_widget(x, "HostInfoActiveCheckbutton");
225   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoActiveCheckbutton"); return; }
226   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.active);
227
228   w = glade_xml_get_widget(x, "HostInfoValidkeyCheckbutton");
229   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoValidkeyCheckbutton"); return; }
230   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.validkey);
231
232   w = glade_xml_get_widget(x, "HostInfoWaitingforkeyCheckbutton");
233   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoWaitingforkeyCheckbutton"); return; }
234   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.waitingforkey);
235
236   w = glade_xml_get_widget(x, "HostInfoVisitedCheckbutton");
237   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoVisitedCheckbutton"); return; }
238   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.visited);
239
240   w = glade_xml_get_widget(x, "HostInfoReachableCheckbutton");
241   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoReachableCheckbutton"); return; }
242   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.reachable);
243
244   w = glade_xml_get_widget(x, "HostInfoIndirectCheckbutton");
245   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoIndirectCheckbutton"); return; }
246   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.indirect);
247
248   w = glade_xml_get_widget(x, "HostInfoVisibleCheckbutton");
249   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoVisibleCheckbutton"); return; }
250 /*   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.visible); */
251
252   w = glade_xml_get_widget(x, "HostInfoTCPOnlyCheckbutton");
253   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoTCPOnlyCheckbutton"); return; }
254   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), (n->options & OPTION_TCPONLY) != 0);
255
256   w = glade_xml_get_widget(x, "HostInfoIndirectdataCheckbutton");
257   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoIndirectdataCheckbutton"); return; }
258   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), (n->options & OPTION_INDIRECT) != 0);
259
260 /*   w = glade_xml_get_widget(x, "HostInfoWindow"); */
261 /*   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoWindow"); return; } */
262 /*   glade_xml_signal_connect_data(x, "on_HostInfoCloseButton_clicked", if_hostinfoclosebutton_clicked, (gpointer)w); */
263   w = glade_xml_get_widget(x, "HostConnectionsCList");
264   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostConnectionsCList"); return; }
265   for(avlnode = n->edge_tree->head; avlnode; avlnode = avlnode->next)
266     {
267       if(((edge_t*)(avlnode->data))->to.node == n)
268         l[0] = ((edge_t*)(avlnode->data))->from.node->name;
269       else
270         l[0] = ((edge_t*)(avlnode->data))->to.node->name;
271       gtk_clist_append(GTK_CLIST(w), l);
272     }
273 }
274
275 void on_settings1_activate(GtkMenuItem *mi, gpointer data)
276 {
277   GtkWidget *w;
278   GladeXML *x;
279   
280   x = glade_xml_new(INTERFACE_FILE, "PropertyBox");
281   if(x == NULL)
282     {
283       log(0, TLOG_ERROR,
284           _("Could not find widget `%s'"),
285           "PropertyBox");
286       return;
287     }
288   
289   w = glade_xml_get_widget(x, "PropertyBox");
290   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "PropertyBox"); return; }
291   glade_xml_signal_autoconnect(x);
292 }
293
294 void on_logcontext_clear_activate(GtkMenuItem *mi, gpointer data)
295 {
296   GtkWidget *l = glade_xml_get_widget(xml, "Messages");
297   if(!l) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "Messages"); return; }
298   gtk_editable_delete_text(GTK_EDITABLE(l), 0, -1); /* Delete from 0 to end of buffer */
299   log_inited = 0;
300 }
301
302 void on_logcontext_follow_activate(GtkMenuItem *mi, gpointer data)
303 {
304   follow_log = !follow_log;
305 }
306
307 void on_messages_button_press_event(GtkWidget *w, GdkEventButton *event, gpointer data)
308 {
309   GladeXML *x;
310   GtkWidget *menu;
311   
312   if (event->button == 3)
313     {
314       x = glade_xml_new(INTERFACE_FILE, "LogContextMenu");
315       if(x == NULL)
316         {
317           log(0, TLOG_ERROR,
318               _("Could not find widget `%s'"),
319               "LogContextMenu");
320           return;
321         }
322
323       menu = glade_xml_get_widget(x, "LogContextMenu");
324       if(!menu) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "LogContextMenu"); return; }
325       
326       glade_xml_signal_connect_data(x, "on_logcontext_clear_activate", on_logcontext_clear_activate, (gpointer)x);
327       glade_xml_signal_connect_data(x, "on_logcontext_follow_activate", on_logcontext_follow_activate, (gpointer)x);
328       w = glade_xml_get_widget(x, "LogContextFollow");
329       if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "LogContextFollow"); return; }
330       GTK_CHECK_MENU_ITEM(w)->active = follow_log;
331       gnome_popup_menu_do_popup_modal(menu, NULL, NULL, event, NULL);
332       gtk_widget_destroy(menu);
333     }
334 }
335
336 void on_canvascontext_shuffle_activate(GtkMenuItem *mi, gpointer data)
337 {
338   avl_node_t *avlnode;
339   double newx, newy;
340   
341   for(avlnode = node_tree->head; avlnode; avlnode = avlnode->next)
342     {
343       newx = ((double)random()) / ((double)RAND_MAX) * 500.0;
344       newy = ((double)random()) / ((double)RAND_MAX) * 300.0;
345       ((struct if_node_data*)((node_t *)(avlnode->data))->data)->x = newx;
346       ((struct if_node_data*)((node_t *)(avlnode->data))->data)->y = newy;
347
348       if(!((struct if_node_data*)((node_t*)(avlnode->data)))->visible)
349         continue;
350       
351       x[((struct if_node_data*)((node_t*)(avlnode->data))->data)->id] = newx;
352       y[((struct if_node_data*)((node_t*)(avlnode->data))->data)->id] = newy;
353     }
354   inited = 0;
355   build_graph = 1;
356 }
357
358 void on_canvascontext_keep_drawing_activate(GtkMenuItem *mi, gpointer data)
359 {
360   keep_drawing = !keep_drawing;
361 }
362
363 void on_canvascontext_minus50_activate(GtkMenuItem *mi, gpointer data)
364 {
365   canvas_zoom *= 0.50;
366   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
367 }
368
369 void on_canvascontext_minus25_activate(GtkMenuItem *mi, gpointer data)
370 {
371   canvas_zoom *= 0.75;
372   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
373 }
374
375 void on_canvascontext_minus10_activate(GtkMenuItem *mi, gpointer data)
376 {
377   canvas_zoom *= 0.90;
378   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
379 }
380
381 void on_canvascontext_default_activate(GtkMenuItem *mi, gpointer data)
382 {
383   canvas_zoom = 1.00;
384   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), 1.00);
385 }
386
387 void on_canvascontext_plus10_activate(GtkMenuItem *mi, gpointer data)
388 {
389   canvas_zoom *= 1.10;
390   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
391 }
392
393 void on_canvascontext_plus25_activate(GtkMenuItem *mi, gpointer data)
394 {
395   canvas_zoom *= 1.25;
396   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
397 }
398
399 void on_canvascontext_plus50_activate(GtkMenuItem *mi, gpointer data)
400 {
401   canvas_zoom *= 1.50;
402   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
403 }
404
405 void on_canvas_button_press_event(GtkWidget *w, GdkEventButton *event, gpointer data)
406 {
407   GladeXML *x;
408   GtkWidget *menu;
409   
410   if (event->button == 3)
411     {
412       x = glade_xml_new(INTERFACE_FILE, "CanvasContextMenu");
413       if(x == NULL)
414         {
415           log(0, TLOG_ERROR,
416               _("Could not find widget `%s'"),
417               "CanvasContextMenu");
418           return;
419         }
420
421       menu = glade_xml_get_widget(x, "CanvasContextMenu");
422       if(!menu) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "CanvasContextMenu"); return; }
423       
424       glade_xml_signal_autoconnect(x);
425       glade_xml_signal_connect_data(x, "on_canvascontext_shuffle_activate", on_canvascontext_shuffle_activate, (gpointer)x);
426       glade_xml_signal_connect_data(x, "on_canvascontext_keep_drawing_activate", on_canvascontext_keep_drawing_activate, (gpointer)x);
427       w = glade_xml_get_widget(x, "CanvasContextKeepDrawing");
428       if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "CanvasContextKeepDrawing"); return; }
429       GTK_CHECK_MENU_ITEM(w)->active = keep_drawing;
430       gnome_popup_menu_do_popup_modal(menu, NULL, NULL, event, NULL);
431       gtk_widget_destroy(menu);
432     }
433 }
434
435 void on_nodetree_button_press_event(GtkWidget *w, GdkEventButton *event, gpointer data)
436 {
437   GtkCTreeNode *node;
438   int row, col;
439   gpointer lt;
440   GladeXML *x;
441   
442   gtk_clist_get_selection_info(GTK_CLIST(w), event->x, event->y,
443                                &row, &col);
444
445   node = gtk_ctree_node_nth(GTK_CTREE(w), row);
446   if(node == NULL)
447     return;
448   lt = gtk_ctree_node_get_row_data(GTK_CTREE(w), node);
449   if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
450     {
451       /* Double left click on an item */
452       if(lt == NULL)
453         /* this is only a branch, double click wil (un)expand. */
454         return;
455
456       if(GTK_CTREE_ROW(node)->parent == hosts_ctn)
457         {
458           x = ((struct if_node_data*)(((node_t*)lt)->data))->hi_xml = glade_xml_new(INTERFACE_FILE, "HostInfoWindow");
459           if(x == NULL)
460             {
461               log(0, TLOG_ERROR,
462                   _("Could not find widget `%s'"),
463                   "HostInfoWindow");
464               return;
465             }
466           glade_xml_signal_autoconnect(x);
467           update_hostinfo_dialog(x, (node_t*)lt);
468         }
469       else
470         {
471           log(0, TLOG_ERROR,
472               "WHERE did you click?!");
473         }
474       /* so now we have access to all the data we want. */
475 /*       gldap_show_details(lt); */
476       return;
477     }
478 /*   else */
479 /*     if (event->button == 3) */
480 /*       { */
481 /*         GtkWidget *temp_menu; */
482 /*         temp_menu = gnome_popup_menu_new(data); */
483 /*         gnome_popup_menu_do_popup_modal(temp_menu, NULL, NULL, event, NULL); */
484 /*         gtk_widget_destroy(temp_menu); */
485 /*       } */
486 }
487
488 void on_exit1_activate(GtkMenuItem *mi, gpointer data)
489 {
490   close_network_connections();
491   gtk_exit(0);
492 }
493
494 void on_info1_activate(GtkMenuItem *mi, gpointer data)
495 {
496   GladeXML *x;
497   x = glade_xml_new(INTERFACE_FILE, "AboutWindow");
498   if(x == NULL)
499     {
500       log(0, TLOG_ERROR,
501           _("Could not find widget `%s'"),
502           "AboutWindow");
503       return;
504     }
505 }
506
507 int init_interface(void)
508 {
509   char *l[1];
510
511   glade_gnome_init();
512
513   xml = glade_xml_new("pokey.glade", "AppWindow");
514
515   if(!xml)
516     {
517       log(0, TLOG_ERROR,
518           _("Something bad happened while creating the interface.\n"));
519       return -1;
520     }
521
522   nodetree = glade_xml_get_widget(xml, "NodeTree");
523   if(!nodetree)
524     {
525       log(0, TLOG_ERROR,
526           _("Could not find widget `%s'"),
527           "NodeTree");
528       return -1;
529     }
530
531   gtk_clist_freeze(GTK_CLIST(nodetree));
532   l[0] = _("Hosts");
533   hosts_ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
534                               NULL, NULL, l, 1,
535                               NULL, NULL, NULL, NULL,
536                               FALSE, TRUE);
537   gtk_clist_thaw(GTK_CLIST(nodetree));
538
539   create_canvas();
540
541   glade_xml_signal_autoconnect(xml);
542
543   log_add_hook(log_gtk);
544   log_del_hook(log_default);
545
546   add_hook("node-add", if_node_add);
547   add_hook("node-del", if_node_del);
548   add_hook("subnet-add", if_subnet_add);
549   add_hook("subnet-del", if_subnet_del);
550   add_hook("edge-add", if_edge_add);
551   add_hook("edge-del", if_edge_del);
552   add_hook("node-visible", if_node_visible);
553   add_hook("node-invisible", if_node_invisible);
554   
555   return 0;
556 }
557
558 static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
559 {
560   static double item_x, old_x, new_x, item_y, old_y, new_y;
561   static int dragging = FALSE;
562   GdkCursor *fleur;
563   node_t *n;
564   
565   item_x = event->button.x;
566   item_y = event->button.y;
567   gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
568   
569   switch(event->type)
570     {
571     case GDK_BUTTON_PRESS:
572       switch(event->button.button)
573         {
574         case 1:
575           old_x = item_x;
576           old_y = item_y;
577
578           fleur = gdk_cursor_new(GDK_FLEUR);
579           gnome_canvas_item_grab(item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, fleur, event->button.time);
580           gdk_cursor_destroy(fleur);
581           dragging = TRUE;
582           break;
583
584         default:
585           break;
586         }
587       break;
588
589     case GDK_MOTION_NOTIFY:
590       if(dragging && (event->motion.state & GDK_BUTTON1_MASK))
591         {
592           new_x = item_x,
593           new_y = item_y;
594           gnome_canvas_item_move(item, new_x - old_x, new_y - old_y);
595           old_x = new_x;
596           old_y = new_y;
597         }
598       break;
599       
600     case GDK_BUTTON_RELEASE:
601       gnome_canvas_item_ungrab(item, event->button.time);
602       dragging = FALSE;
603       n = (node_t *)gtk_object_get_user_data(GTK_OBJECT(item));
604       ((struct if_node_data*)(n->data))->x = item_x;
605       ((struct if_node_data*)(n->data))->y = item_y;
606       x[((struct if_node_data*)(n->data))->id] = item_x;
607       y[((struct if_node_data*)(n->data))->id] = item_y;
608       build_graph = 1;
609       break;
610
611     default:
612       break;
613     }
614   return FALSE;
615 }
616
617 void if_node_create(node_t *n)
618 {
619   GnomeCanvasGroup *group;
620   
621   group = gnome_canvas_root(GNOME_CANVAS(canvas));
622   group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(group,
623                                                    gnome_canvas_group_get_type(),
624                                                    "x", 0.0,
625                                                    "y", 0.0,
626                                                    NULL));
627   
628   gnome_canvas_item_new(group, gnome_canvas_ellipse_get_type(),
629                         "x1", -30.0,
630                         "y1", -08.0,
631                         "x2", 30.0,
632                         "y2", 08.0,
633                         "fill_color_rgba", 0x5f9ea080,
634                         "outline_color", "black",
635                         "width_pixels", 0,
636                         NULL);
637   
638   gnome_canvas_item_new(group,
639                         gnome_canvas_text_get_type(),
640                         "x", 0.0,
641                         "y", 0.0,
642                         "text", n->name,
643                         "anchor", GTK_ANCHOR_CENTER,
644                         "fill_color", "white",
645                         "font", "-*-verdana-medium-r-*-*-10-*-*-*-*-*-iso8859-1",
646                         NULL);
647   
648   ((struct if_node_data*)(n->data))->item = GNOME_CANVAS_ITEM(group);
649   ((struct if_node_data*)(n->data))->x = ((struct if_node_data*)(n->data))->y = 0.0;
650   gtk_object_set_user_data(GTK_OBJECT(group), (gpointer)n);
651   
652   gtk_signal_connect(GTK_OBJECT(((struct if_node_data*)(n->data))->item), "event", (GtkSignalFunc) item_event, NULL);
653
654   gnome_canvas_item_hide(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item));
655 }
656
657 void if_node_visible(const char *hooktype, va_list ap)
658 {
659   int i;
660   avl_node_t *avlnode;
661   double newx, newy;
662   node_t *n = va_arg(ap, node_t*);
663   
664   if(!((struct if_node_data*)(n->data))->item)
665     return;
666
667   if(((struct if_node_data*)(n->data))->visible)
668     /* This node is already shown */
669     return;
670
671   ((struct if_node_data*)(n->data))->visible = 1;
672
673   newx = 250.0 + 200.0 * sin(number_of_nodes / 10.0 * M_PI);
674   newy = 150.0 - 100.0 * cos(number_of_nodes / 10.0 * M_PI);
675   gnome_canvas_item_move(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item), newx - ((struct if_node_data*)(n->data))->x, newy - ((struct if_node_data*)(n->data))->y);
676   ((struct if_node_data*)(n->data))->x = newx;
677   ((struct if_node_data*)(n->data))->y = newy;
678   
679   for(i = 0, avlnode = node_tree->head; avlnode; avlnode = avlnode->next, i++)
680     {
681       if(!((struct if_node_data*)(((node_t*)(avlnode->data))->data))->visible)
682         continue;
683       
684       nodes[i] = (node_t *)(avlnode->data);
685       ((struct if_node_data*)(nodes[i]->data))->id = i;
686     }
687   number_of_nodes = i;
688
689   gnome_canvas_item_show(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item));
690   gnome_canvas_update_now(GNOME_CANVAS(canvas));
691
692   /* (Re)start calculations */
693   inited = 0;
694   build_graph = 1;
695 }
696
697 void if_node_invisible(const char *hooktype, va_list ap)
698 {
699   int i;
700   avl_node_t *avlnode;
701   node_t *n = va_arg(ap, node_t*);
702   
703   if(!((struct if_node_data*)(n->data))->item)
704     return;
705
706   if(!((struct if_node_data*)(n->data))->visible)
707     /* This node is already invisible */
708     return;
709
710   ((struct if_node_data*)(n->data))->visible = 0;
711
712   for(i = 0, avlnode = node_tree->head; avlnode; avlnode = avlnode->next, i++)
713     {
714       if(!((struct if_node_data*)((node_t*)(avlnode->data))->data)->visible)
715         continue;
716       
717       nodes[i] = (node_t *)(avlnode->data);
718       ((struct if_node_data*)(nodes[i]->data))->id = i;
719     }
720   number_of_nodes = i;
721   
722   gnome_canvas_item_hide(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item));
723   gnome_canvas_update_now(GNOME_CANVAS(canvas));
724
725   /* (Re)start calculations */
726   inited = 0;
727   build_graph = 1;
728 }
729
730 void if_node_add(const char *hooktype, va_list ap)
731 {
732   node_t *n = va_arg(ap, node_t*);
733   char *l[1];
734   struct if_node_data *nd;
735
736   if(!xml)
737     return;
738
739   nd = xmalloc(sizeof(*nd));
740   l[0] = n->name;
741   gtk_clist_freeze(GTK_CLIST(nodetree));
742   nd->ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
743                                   hosts_ctn, NULL, l, 1,
744                                   NULL, NULL, NULL, NULL,
745                                   FALSE, FALSE);
746   gtk_clist_thaw(GTK_CLIST(nodetree));
747   gtk_ctree_node_set_row_data(GTK_CTREE(nodetree), nd->ctn, n);
748
749   n->data = (void*)nd;
750
751   if_node_create(n);
752   if_node_visible(hooktype, ap);
753 }
754
755 void if_node_del(const char *hooktype, va_list ap)
756 {
757   node_t *n = va_arg(ap, node_t*);
758   struct if_node_data *nd;
759
760   nd = (struct if_node_data*)(n->data);
761   if(nd &&nd->ctn)
762     {
763       gtk_clist_freeze(GTK_CLIST(nodetree));
764       gtk_ctree_remove_node(GTK_CTREE(nodetree), nd->ctn);
765       gtk_clist_thaw(GTK_CLIST(nodetree));
766     }
767
768   if_node_invisible(hooktype, ap);
769
770   free(nd);
771   n->data = NULL;
772 }
773
774 void if_subnet_add(const char *hooktype, va_list ap)
775 {
776   char *l[1];
777   subnet_t *subnet = va_arg(ap, subnet_t*);
778   struct if_subnet_data *sd;
779   GtkCTreeNode *parent;
780
781   sd = xmalloc(sizeof(*sd));
782   l[0] = net2str(subnet);
783   parent = subnet->owner->data ?
784     ((struct if_subnet_data*)(subnet->owner->data))->ctn
785       : NULL;
786
787   gtk_clist_freeze(GTK_CLIST(nodetree));
788   sd->ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
789                                   parent, NULL, l, 1,
790                                   NULL, NULL, NULL, NULL,
791                                   TRUE, FALSE);
792   gtk_clist_thaw(GTK_CLIST(nodetree));
793   gtk_ctree_node_set_row_data(GTK_CTREE(nodetree), sd->ctn, subnet);
794
795   subnet->data = (void*)sd;
796 }
797
798 void if_subnet_del(const char *hooktype, va_list ap)
799 {
800   subnet_t *subnet = va_arg(ap, subnet_t*);
801   struct if_subnet_data *sd;
802
803   sd = (struct if_subnet_data*)(subnet->data);
804   if(sd && sd->ctn)
805     {
806       gtk_clist_freeze(GTK_CLIST(nodetree));
807       gtk_ctree_remove_node(GTK_CTREE(nodetree), sd->ctn);
808       gtk_clist_thaw(GTK_CLIST(nodetree));
809     }
810   
811   free(sd);
812   subnet->data = NULL;
813 }
814
815 void redraw_edges(void)
816 {
817   GnomeCanvasGroup *group;
818   GnomeCanvasPoints *points;
819   avl_node_t *avlnode;
820   edge_t *e;
821   struct if_node_data *fd, *td;
822
823   if(edge_group)
824     gtk_object_destroy(GTK_OBJECT(edge_group));
825   
826   group = gnome_canvas_root(GNOME_CANVAS(canvas));
827   group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(group,
828                                                    gnome_canvas_group_get_type(),
829                                                    "x", 0.0,
830                                                    "y", 0.0,
831                                                    NULL));
832   
833   for(avlnode = edge_tree->head; avlnode; avlnode = avlnode->next)
834     {
835       e = (edge_t *)avlnode->data;
836       fd = (struct if_node_data*)(e->from.node->data);
837       td = (struct if_node_data*)(e->to.node->data);
838
839 /*       if(!e->from.node->status.visible || */
840 /*       !e->to.node->status.visible) */
841 /*      /\* We shouldn't draw this line *\/ */
842 /*      continue; */
843       
844       points = gnome_canvas_points_new(2);
845       
846       points->coords[0] = fd->x;
847       points->coords[1] = fd->y;
848       points->coords[2] = td->x;
849       points->coords[3] = td->y;
850       gnome_canvas_item_new(group,
851                             gnome_canvas_line_get_type(),
852                             "points", points,
853                             "fill_color_rgba", 0xe080c080,
854                             "width_pixels", 2,
855                             NULL);
856       gnome_canvas_points_unref(points);
857     }
858
859   gnome_canvas_update_now(GNOME_CANVAS(canvas));
860
861   edge_group = group;
862 }
863
864 void if_edge_add(const char *hooktype, va_list ap)
865 {
866   redraw_edges();
867
868   inited = 0;
869   build_graph = 1;
870 }
871
872 void if_edge_del(const char *hooktype, va_list ap)
873 {
874   redraw_edges();
875
876   inited = 0;
877   build_graph = 1;
878 }
879
880 void if_move_node(node_t *n, double dx, double dy)
881 {
882   double newx, newy;
883   
884   newx = ((struct if_node_data*)(n->data))->x + dx;
885   newy = ((struct if_node_data*)(n->data))->y + dy;
886   gnome_canvas_item_move(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item), newx - ((struct if_node_data*)(n->data))->x, newy - ((struct if_node_data*)(n->data))->y);
887   ((struct if_node_data*)(n->data))->x = newx;
888   ((struct if_node_data*)(n->data))->y = newy;
889 }
890
891 #define X_MARGIN 50.0
892 #define X_MARGIN_BUFFER 25.0
893 #define Y_MARGIN 20.0
894 #define Y_MARGIN_BUFFER 10.0
895
896 void set_zooming(void)
897 {
898   int i;
899   double minx, miny, maxx, maxy;
900   static double ominx = 0.0, ominy = 0.0, omaxx = 0.0, omaxy = 0.0;
901
902   minx = miny = maxx = maxy = 0.0;
903   for(i = 0; i < number_of_nodes; i++)
904     {
905       if(((struct if_node_data*)(nodes[i]->data))->x < minx)
906         minx = ((struct if_node_data*)(nodes[i]->data))->x;
907       else
908         if(((struct if_node_data*)(nodes[i]->data))->x > maxx)
909           maxx = ((struct if_node_data*)(nodes[i]->data))->x;
910
911       if(((struct if_node_data*)(nodes[i]->data))->y < miny)
912         miny = ((struct if_node_data*)(nodes[i]->data))->y;
913       else
914         if(((struct if_node_data*)(nodes[i]->data))->y > maxy)
915           maxy = ((struct if_node_data*)(nodes[i]->data))->y;
916     }
917
918   if(minx > ominx - X_MARGIN_BUFFER && ominx > minx)
919     minx = ominx;
920   if(maxx < omaxx + X_MARGIN_BUFFER && omaxx < maxx)
921     maxx = omaxx;
922   if(miny > ominy - Y_MARGIN_BUFFER && ominy > miny)
923     miny = ominy;
924   if(maxy < omaxy + Y_MARGIN_BUFFER && omaxy < maxy)
925     maxy = omaxy;
926
927   ominx = minx; ominy = miny; omaxx = maxx; omaxy = maxy;
928
929 /*   ppux = canvas_width / (maxx - minx); */
930 /*   ppuy = canvas_height / (maxy - miny); */
931 /*   if(ppux < ppuy) */
932 /*     ppu = ppux; */
933 /*   else */
934 /*     ppu = ppuy; */
935
936 /*   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), ppu); */
937   gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas), minx - X_MARGIN, miny - Y_MARGIN, maxx + X_MARGIN, maxy + Y_MARGIN);
938   gnome_canvas_update_now(GNOME_CANVAS(canvas));
939 }
940
941 double calculate_delta_m(int m)
942 {
943   double dedxm, dedym, xmxi, ymyi;
944   int i;
945
946   dedxm = dedym = 0.0;
947   for(i = 0; i < number_of_nodes; i++)
948     {
949       if(i == m)
950         continue;
951
952       xmxi = x[m] - x[i];
953       ymyi = y[m] - y[i];
954
955       dedxm += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
956       dedym += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
957     }
958
959   return sqrt(dedxm * dedxm + dedym * dedym);
960 }
961
962 void move_node(int m, double *dx, double *dy)
963 {
964   double d2edxm2, d2edym2, d2edxmdym, dedxm, dedym;
965   double xmxi, ymyi, denominator;
966   int i;
967
968   d2edxm2 = d2edym2 = d2edxmdym = dedxm = dedym = 0.0;
969   for(i = 0; i < number_of_nodes; i++)
970     {
971       if(i == m)
972         continue;
973       
974       xmxi = x[m] - x[i];
975       ymyi = y[m] - y[i];
976
977       denominator = pow(sqrt(xmxi * xmxi + ymyi * ymyi), 3.0);
978
979       d2edxm2 += k[m][i] * (1 - ((l[m][i] * ymyi * ymyi) / denominator));
980       d2edxmdym += k[m][i] * l[m][i] * xmxi * ymyi / denominator;
981       d2edym2 += k[m][i] * (1 - ((l[m][i] * xmxi * xmxi) / denominator));
982       dedxm += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
983       dedym += k[m][i] * (ymyi - ((l[m][i] * ymyi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
984     }
985
986   denominator = ((d2edxm2 * d2edym2) - (d2edxmdym * d2edxmdym));
987   *dx = (-(d2edym2 * dedxm) + (d2edxmdym * dedym)) / denominator;
988   *dy = ((d2edxmdym * dedxm) - (d2edxm2 * dedym)) / denominator;
989 }
990
991 void if_build_graph(void)
992 {
993   int i, j, p, max_i;
994   double delta_m, max_delta_m;
995   double dx, dy, s, L, min_d, old_x, old_y;
996   edge_t *e;
997
998   if(!keep_drawing)
999     return;
1000   
1001   if(!inited)
1002     {
1003       for(i = 0; i < number_of_nodes; i++)
1004         {
1005           x[i] = ((struct if_node_data*)(nodes[i]->data))->x;
1006           y[i] = ((struct if_node_data*)(nodes[i]->data))->y;
1007         }
1008
1009       /* Initialize Floyd */
1010       for(i = 0; i < number_of_nodes; i++)
1011         {
1012           d[i][i] = 0.0;
1013           for(j = i + 1; j < number_of_nodes; j++)
1014             {
1015               e = lookup_edge(nodes[i], nodes[j]);
1016               if(e)
1017                 d[i][j] = d[j][i] = (double)e->weight;
1018               else
1019                 d[i][j] = d[j][i] = INFINITY;
1020             }
1021         }
1022
1023       /* Floyd's shortest path algorithm */
1024       for(i = 0; i < number_of_nodes; i++)
1025         {
1026           for(j = 0; j < number_of_nodes; j++)
1027             {
1028               if(i == j)
1029                 continue;
1030               
1031               if(d[j][i] < INFINITY)
1032                 {
1033                   for(p = 0; p < number_of_nodes; p++)
1034                     {
1035                       if(d[i][j] < INFINITY)
1036                         {
1037                           s = d[j][i] + d[i][p];
1038                           if(s < d[j][p])
1039                             {
1040                               d[j][p] = s;
1041                             }
1042                         }
1043                     }
1044                 }
1045             }
1046         }
1047
1048       min_d = 0.0;
1049       for(i = 0; i < number_of_nodes; i++)
1050         for(j = i + 1; j < number_of_nodes; j++)
1051           if(d[i][j] < min_d && d[i][j] > 0)
1052             min_d = d[i][j];
1053
1054       L = 5.0 / sqrt(min_d + 1.0);
1055
1056       for(i = 0; i < number_of_nodes; i++)
1057         {
1058           for(j = i + 1; j < number_of_nodes; j++)
1059             {
1060               d[i][j] = d[j][i] = sqrt(d[i][j]+1.0);
1061               l[i][j] = l[j][i] = L * d[i][j];
1062               k[i][j] = k[j][i] = K / (d[i][j] * d[i][j]);
1063             }
1064         }
1065
1066       inited = 1;
1067     }
1068
1069   max_delta_m = 0.0;
1070   /* Find node with maximal local energy */
1071   for(i = 0; i < number_of_nodes; i++)
1072     {
1073       delta_m = calculate_delta_m(i);
1074       if(delta_m > max_delta_m)
1075         {
1076           max_delta_m = delta_m;
1077           max_i = i;
1078         }
1079     }
1080
1081   if(max_delta_m <= epsilon)
1082     {
1083       fprintf(stderr, "Graph building is done; max_delta_m = %f\n", max_delta_m);
1084       build_graph = 0;
1085     }
1086   else
1087     {
1088       int iter = 0, maxiter = 20;
1089       delta_m = max_delta_m;
1090       old_x = x[max_i];
1091       old_y = y[max_i];
1092       while(delta_m > epsilon && iter < maxiter)
1093         {
1094           move_node(max_i, &dx, &dy);
1095           x[max_i] += dx;
1096           y[max_i] += dy;
1097           delta_m = calculate_delta_m(max_i);
1098           iter++;
1099         }
1100           
1101       if_move_node(nodes[max_i], x[max_i] - old_x, y[max_i] - old_y);
1102           
1103       redraw_edges();
1104
1105       set_zooming();
1106     }
1107
1108 /*   build_graph = 0; */
1109 }