Hm.
[oweals/tinc.git] / 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.5 2002/05/02 11:50:07 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 /* Node tree & main window stuff */
54 static GladeXML *xml;
55 static GtkWidget *nodetree;
56 static GtkCTreeNode *hosts_ctn;
57
58
59 /* Graph canvas stuff */
60 static GladeXML *canvas_xml;
61
62 static GnomeCanvasGroup *edge_group = NULL;
63
64 static int canvas_width;
65 static int canvas_height;
66
67 static GtkWidget *canvas = NULL;
68
69 static int canvas_visible = 0;
70
71 int build_graph = 0;
72
73 static GdkColormap *colormap = NULL;
74 static GdkColor timecolor;
75
76 #define MAX_NODES 25
77 #define K 10.0
78
79 #ifdef INFINITY
80 #undef INFINITY
81 #endif
82 #define INFINITY 1.0e10
83
84 node_t *nodes[MAX_NODES];
85 double x[MAX_NODES];
86 double y[MAX_NODES];
87 double k[MAX_NODES][MAX_NODES];
88 double d[MAX_NODES][MAX_NODES];
89 double l[MAX_NODES][MAX_NODES];
90 static const double epsilon = 0.001;
91
92 static int inited = 0;
93
94 static int number_of_nodes = 0;
95
96 static double canvas_zoom = 1.00;
97
98
99 /* Log window stuff */
100 #ifdef MAXBUFSIZE
101 #undef MAXBUFSIZE
102 #endif
103
104 #define MAXBUFSIZE 1024
105
106 static int log_inited = 0;
107 static int follow_log = 1;
108
109 static int keep_drawing = 1;
110
111 static int log_visible = 0;
112 static GtkWidget *log_window = NULL;
113
114
115 void if_node_add(const char *hooktype, va_list ap);
116 void if_node_del(const char *hooktype, va_list ap);
117 void if_subnet_add(const char *hooktype, va_list ap);
118 void if_subnet_del(const char *hooktype, va_list ap);
119 void if_edge_add(const char *hooktype, va_list ap);
120 void if_edge_del(const char *hooktype, va_list ap);
121 void if_node_visible(const char *hooktype, va_list ap);
122 void if_node_invisible(const char *hooktype, va_list ap);
123
124 void if_node_create(node_t *n);
125
126 GtkWidget *create_canvas(void)
127 {
128   canvas_xml = glade_xml_new(INTERFACE_FILE, "GraphWindow");
129   if(!canvas_xml)
130     {
131       log(0, TLOG_ERROR,
132           _("Could not find widget `%s'"),
133           "GraphWindow");
134       return NULL;
135     }
136   
137   canvas = glade_xml_get_widget(xml, "canvas1");
138   if(!canvas)
139     {
140       fprintf(stderr, "Could not find widget `canvas1'\n");
141       return NULL;
142     }
143   
144   gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas), 0.0, 0.0, 700, 500);
145
146   canvas_width = 300.0;
147   canvas_height = 500.0;
148
149   return canvas;
150 }
151
152 void log_gtk(int level, int priority, char *fmt, va_list ap)
153 {
154   char buffer1[MAXBUFSIZE];
155   char buffer2[MAXBUFSIZE];
156   int len;
157   char *p;
158   struct tm *tm;
159   time_t t;
160
161   if(!log_visible)
162     return;
163
164   /* Use vsnprintf instead of vasprintf: faster, no memory
165      fragmentation, cleanup is automatic, and there is a limit on the
166      input buffer anyway */
167   len = vsnprintf(buffer1, MAXBUFSIZE, fmt, ap);
168
169   buffer1[MAXBUFSIZE-1] = '\0';
170   if((p = strrchr(buffer1, '\n')))
171     *p = '\0';
172
173   t = time(NULL);
174   tm = localtime(&t);
175   snprintf(buffer2, MAXBUFSIZE, "%02d:%02d:%02d ",
176            tm->tm_hour, tm->tm_min, tm->tm_sec);
177
178   if(!colormap)
179     {
180       colormap = gdk_colormap_new(gdk_visual_get_system(), FALSE);
181       timecolor.red = 0xffff;
182       timecolor.green = 0;
183       timecolor.blue = 0;
184       if(gdk_colormap_alloc_color(colormap, &timecolor, FALSE, TRUE) != TRUE)
185         {
186           fprintf(stderr, "Failed to allocate color\n");
187           exit(1);
188         }
189     }
190   
191   gtk_text_freeze(GTK_TEXT(log_window));
192
193   if(log_inited)
194     gtk_text_insert(GTK_TEXT(log_window), NULL, NULL, NULL, "\n", 1);
195
196   gtk_text_insert(GTK_TEXT(log_window), NULL, &timecolor, NULL, buffer2, strlen(buffer2));
197   gtk_text_insert(GTK_TEXT(log_window), NULL, NULL, NULL, buffer1, len);
198   gtk_text_thaw(GTK_TEXT(log_window));
199
200   log_inited = 1;
201   if(follow_log)
202 /*     gtk_text_set_point(GTK_TEXT(w), -1); */
203     gtk_editable_set_position(GTK_EDITABLE(log_window), gtk_text_get_length(GTK_TEXT(log_window)));
204 }
205
206 void if_hostinfoclosebutton_clicked(GtkWidget *w, gpointer data)
207 {
208   gtk_widget_destroy(GTK_WIDGET(data));
209 }
210
211 void update_hostinfo_dialog(GladeXML *x, node_t *n)
212 {
213   GtkWidget *w;
214   char s[100];
215   avl_node_t *avlnode;
216   char *l[1];
217
218   w = glade_xml_get_widget(x, "HostInfoNameEntry");
219   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoNameEntry"); return; }
220   gtk_entry_set_text(GTK_ENTRY(w), n->name);
221
222   w = glade_xml_get_widget(x, "HostInfoHostnameEntry");
223   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoHostnameEntry"); return; }
224   gtk_entry_set_text(GTK_ENTRY(w), n->hostname);
225
226   w = glade_xml_get_widget(x, "HostInfoPortEntry");
227   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoPortEntry"); return; }
228 /*   snprintf(s, sizeof(s)-1, "%hd", "0"); */
229   gtk_entry_set_text(GTK_ENTRY(w), "port");
230
231   w = glade_xml_get_widget(x, "HostInfoVersionEntry");
232   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoVersionEntry"); return; }
233   gtk_entry_set_text(GTK_ENTRY(w), n->name);
234
235   w = glade_xml_get_widget(x, "HostInfoStatusEntry");
236   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoStatusEntry"); return; }
237 /*   snprintf(s, sizeof(s)-1, "%x", n->status); */
238   gtk_entry_set_text(GTK_ENTRY(w), "0");
239
240   w = glade_xml_get_widget(x, "HostInfoActiveCheckbutton");
241   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoActiveCheckbutton"); return; }
242   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.active);
243
244   w = glade_xml_get_widget(x, "HostInfoValidkeyCheckbutton");
245   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoValidkeyCheckbutton"); return; }
246   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.validkey);
247
248   w = glade_xml_get_widget(x, "HostInfoWaitingforkeyCheckbutton");
249   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoWaitingforkeyCheckbutton"); return; }
250   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.waitingforkey);
251
252   w = glade_xml_get_widget(x, "HostInfoVisitedCheckbutton");
253   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoVisitedCheckbutton"); return; }
254   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.visited);
255
256   w = glade_xml_get_widget(x, "HostInfoReachableCheckbutton");
257   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoReachableCheckbutton"); return; }
258   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.reachable);
259
260   w = glade_xml_get_widget(x, "HostInfoIndirectCheckbutton");
261   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoIndirectCheckbutton"); return; }
262   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.indirect);
263
264   w = glade_xml_get_widget(x, "HostInfoVisibleCheckbutton");
265   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoVisibleCheckbutton"); return; }
266 /*   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n->status.visible); */
267
268   w = glade_xml_get_widget(x, "HostInfoTCPOnlyCheckbutton");
269   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoTCPOnlyCheckbutton"); return; }
270   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), (n->options & OPTION_TCPONLY) != 0);
271
272   w = glade_xml_get_widget(x, "HostInfoIndirectdataCheckbutton");
273   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoIndirectdataCheckbutton"); return; }
274   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), (n->options & OPTION_INDIRECT) != 0);
275
276 /*   w = glade_xml_get_widget(x, "HostInfoWindow"); */
277 /*   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostInfoWindow"); return; } */
278 /*   glade_xml_signal_connect_data(x, "on_HostInfoCloseButton_clicked", if_hostinfoclosebutton_clicked, (gpointer)w); */
279   w = glade_xml_get_widget(x, "HostConnectionsCList");
280   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "HostConnectionsCList"); return; }
281   for(avlnode = n->edge_tree->head; avlnode; avlnode = avlnode->next)
282     {
283       if(((edge_t*)(avlnode->data))->to.node == n)
284         l[0] = ((edge_t*)(avlnode->data))->from.node->name;
285       else
286         l[0] = ((edge_t*)(avlnode->data))->to.node->name;
287       gtk_clist_append(GTK_CLIST(w), l);
288     }
289 }
290
291 void on_preferences1_activate(GtkMenuItem *mi, gpointer data)
292 {
293   GtkWidget *w;
294   GladeXML *x;
295   
296   x = glade_xml_new(INTERFACE_FILE, "PropertyBox");
297   if(x == NULL)
298     {
299       log(0, TLOG_ERROR,
300           _("Could not find widget `%s'"),
301           "PropertyBox");
302       return;
303     }
304   
305   w = glade_xml_get_widget(x, "PropertyBox");
306   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "PropertyBox"); return; }
307   glade_xml_signal_autoconnect(x);
308 }
309
310 void on_logcontext_clear_activate(GtkMenuItem *mi, gpointer data)
311 {
312   gtk_editable_delete_text(GTK_EDITABLE(log_window), 0, -1); /* Delete from 0 to end of buffer */
313   log_inited = 0;
314 }
315
316 void on_logcontext_follow_activate(GtkMenuItem *mi, gpointer data)
317 {
318   follow_log = !follow_log;
319 }
320
321 void on_logcontext_close1_activate(GtkMenuItem *mi, gpointer data)
322 {
323   
324 }
325
326 void on_messages_button_press_event(GtkWidget *w, GdkEventButton *event, gpointer data)
327 {
328   GladeXML *x;
329   GtkWidget *menu;
330   
331   if (event->button == 3)
332     {
333       x = glade_xml_new(INTERFACE_FILE, "LogContextMenu");
334       if(x == NULL)
335         {
336           log(0, TLOG_ERROR,
337               _("Could not find widget `%s'"),
338               "LogContextMenu");
339           return;
340         }
341
342       menu = glade_xml_get_widget(x, "LogContextMenu");
343       if(!menu) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "LogContextMenu"); return; }
344       
345       glade_xml_signal_connect_data(x, "on_logcontext_clear_activate", on_logcontext_clear_activate, (gpointer)x);
346       glade_xml_signal_connect_data(x, "on_logcontext_follow_activate", on_logcontext_follow_activate, (gpointer)x);
347       w = glade_xml_get_widget(x, "LogContextFollow");
348       if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "LogContextFollow"); return; }
349       GTK_CHECK_MENU_ITEM(w)->active = follow_log;
350       gnome_popup_menu_do_popup_modal(menu, NULL, NULL, event, NULL);
351       gtk_widget_destroy(menu);
352     }
353 }
354
355 void shuffle_nodes(void)
356 {
357   avl_node_t *avlnode;
358   double newx, newy;
359   
360   for(avlnode = node_tree->head; avlnode; avlnode = avlnode->next)
361     {
362       newx = ((double)random()) / ((double)RAND_MAX) * 500.0;
363       newy = ((double)random()) / ((double)RAND_MAX) * 300.0;
364       ((struct if_node_data*)((node_t *)(avlnode->data))->data)->x = newx;
365       ((struct if_node_data*)((node_t *)(avlnode->data))->data)->y = newy;
366
367       if(!((struct if_node_data*)((node_t*)(avlnode->data)))->visible)
368         continue;
369       
370       x[((struct if_node_data*)((node_t*)(avlnode->data))->data)->id] = newx;
371       y[((struct if_node_data*)((node_t*)(avlnode->data))->data)->id] = newy;
372     }
373   inited = 0;
374   build_graph = 1;
375 }
376
377 void on_canvascontext_shuffle_activate(GtkMenuItem *mi, gpointer data)
378 {
379   shuffle_nodes();
380 }
381
382 void on_canvascontext_keep_drawing_activate(GtkMenuItem *mi, gpointer data)
383 {
384   GtkWidget *w;
385   
386   keep_drawing = !keep_drawing;
387
388   /* No need to fuss with the checkbox in the menu, because that is
389      transient.  Do need to update the checkbox at the bottom of the
390      window though. */
391   w = glade_xml_get_widget(canvas_xml, "KeepDrawingButton");
392   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "KeepDrawingButton"); return; }
393   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), keep_drawing);
394 }
395
396 void on_canvascontext_minus50_activate(GtkMenuItem *mi, gpointer data)
397 {
398   canvas_zoom *= 0.50;
399   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
400 }
401
402 void on_canvascontext_minus25_activate(GtkMenuItem *mi, gpointer data)
403 {
404   canvas_zoom *= 0.75;
405   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
406 }
407
408 void on_canvascontext_minus10_activate(GtkMenuItem *mi, gpointer data)
409 {
410   canvas_zoom *= 0.90;
411   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
412 }
413
414 void on_canvascontext_default_activate(GtkMenuItem *mi, gpointer data)
415 {
416   canvas_zoom = 1.00;
417   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), 1.00);
418 }
419
420 void on_canvascontext_plus10_activate(GtkMenuItem *mi, gpointer data)
421 {
422   canvas_zoom *= 1.10;
423   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
424 }
425
426 void on_canvascontext_plus25_activate(GtkMenuItem *mi, gpointer data)
427 {
428   canvas_zoom *= 1.25;
429   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
430 }
431
432 void on_canvascontext_plus50_activate(GtkMenuItem *mi, gpointer data)
433 {
434   canvas_zoom *= 1.50;
435   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
436 }
437
438 void on_canvas_button_press_event(GtkWidget *w, GdkEventButton *event, gpointer data)
439 {
440   GladeXML *x;
441   GtkWidget *menu;
442   
443   if (event->button == 3)
444     {
445       x = glade_xml_new(INTERFACE_FILE, "CanvasContextMenu");
446       if(x == NULL)
447         {
448           log(0, TLOG_ERROR,
449               _("Could not find widget `%s'"),
450               "CanvasContextMenu");
451           return;
452         }
453
454       menu = glade_xml_get_widget(x, "CanvasContextMenu");
455       if(!menu) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "CanvasContextMenu"); return; }
456       
457       glade_xml_signal_autoconnect(x);
458       glade_xml_signal_connect_data(x, "on_canvascontext_shuffle_activate", on_canvascontext_shuffle_activate, (gpointer)x);
459       glade_xml_signal_connect_data(x, "on_canvascontext_keep_drawing_activate", on_canvascontext_keep_drawing_activate, (gpointer)x);
460       w = glade_xml_get_widget(x, "CanvasContextKeepDrawing");
461       if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "CanvasContextKeepDrawing"); return; }
462       GTK_CHECK_MENU_ITEM(w)->active = keep_drawing;
463       gnome_popup_menu_do_popup_modal(menu, NULL, NULL, event, NULL);
464       gtk_widget_destroy(menu);
465     }
466 }
467
468 void on_nodetree_button_press_event(GtkWidget *w, GdkEventButton *event, gpointer data)
469 {
470   GtkCTreeNode *node;
471   int row, col;
472   gpointer lt;
473   GladeXML *x;
474   
475   gtk_clist_get_selection_info(GTK_CLIST(w), event->x, event->y,
476                                &row, &col);
477
478   node = gtk_ctree_node_nth(GTK_CTREE(w), row);
479   if(node == NULL)
480     return;
481   lt = gtk_ctree_node_get_row_data(GTK_CTREE(w), node);
482   if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
483     {
484       /* Double left click on an item */
485       if(lt == NULL)
486         /* this is only a branch, double click wil (un)expand. */
487         return;
488
489       if(GTK_CTREE_ROW(node)->parent == hosts_ctn)
490         {
491           x = ((struct if_node_data*)(((node_t*)lt)->data))->hi_xml = glade_xml_new(INTERFACE_FILE, "HostInfoWindow");
492           if(x == NULL)
493             {
494               log(0, TLOG_ERROR,
495                   _("Could not find widget `%s'"),
496                   "HostInfoWindow");
497               return;
498             }
499           glade_xml_signal_autoconnect(x);
500           update_hostinfo_dialog(x, (node_t*)lt);
501         }
502       else
503         {
504           log(0, TLOG_ERROR,
505               "WHERE did you click?!");
506         }
507       /* so now we have access to all the data we want. */
508 /*       gldap_show_details(lt); */
509       return;
510     }
511 /*   else */
512 /*     if (event->button == 3) */
513 /*       { */
514 /*         GtkWidget *temp_menu; */
515 /*         temp_menu = gnome_popup_menu_new(data); */
516 /*         gnome_popup_menu_do_popup_modal(temp_menu, NULL, NULL, event, NULL); */
517 /*         gtk_widget_destroy(temp_menu); */
518 /*       } */
519 }
520
521 void on_exit1_activate(GtkMenuItem *mi, gpointer data)
522 {
523   close_network_connections();
524   gtk_exit(0);
525 }
526
527 void on_about1_activate(GtkMenuItem *mi, gpointer data)
528 {
529   GladeXML *x;
530   x = glade_xml_new(INTERFACE_FILE, "AboutWindow");
531   if(x == NULL)
532     {
533       log(0, TLOG_ERROR,
534           _("Could not find widget `%s'"),
535           "AboutWindow");
536       return;
537     }
538   glade_xml_signal_autoconnect(x);
539 }
540
541 void on_graph_window1_activate(GtkMenuItem *mi, gpointer data)
542 {
543   int i;
544   avl_node_t *avlnode;
545   double newx, newy;
546   
547   canvas_xml = glade_xml_new(INTERFACE_FILE, "GraphWindow");
548   if(canvas_xml == NULL)
549     {
550       log(0, TLOG_ERROR,
551           _("Could not find widget `%s'"),
552           "GraphWindow");
553       return;
554     }
555   glade_xml_signal_autoconnect(canvas_xml);
556   canvas = glade_xml_get_widget(canvas_xml, "canvas1");
557   if(canvas == NULL) { log(0, TLOG_ERROR, _("Could not find widget `%s'"), "canvas1"); return; }
558
559   for(i = 0, avlnode = node_tree->head; avlnode; avlnode = avlnode->next)
560     {
561       node_t *n = (node_t*)(avlnode->data);
562       
563       if(!((struct if_node_data*)(n->data))->item)
564         if_node_create(n);
565
566       if(!n->status.reachable)
567         continue;
568       
569       newx = 250.0 + 200.0 * sin(i / 10.0 * M_PI);
570       newy = 150.0 - 100.0 * cos(i / 10.0 * M_PI);
571       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);
572       ((struct if_node_data*)(n->data))->x = newx;
573       ((struct if_node_data*)(n->data))->y = newy;
574       
575       ((struct if_node_data*)(n->data))->id = i;
576
577       gnome_canvas_item_show(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item));
578       gnome_canvas_update_now(GNOME_CANVAS(canvas));
579       nodes[i] = n;
580       i++;
581     }
582
583   number_of_nodes = i;
584   
585   inited = 0;
586   build_graph = 1;
587   canvas_visible = 1;
588 }
589
590 void on_log_window1_activate(GtkMenuItem *mi, gpointer data)
591 {
592   GladeXML *x;
593   GtkWidget *w;
594
595   x = glade_xml_new(INTERFACE_FILE, "LogWindow");
596   if(x == NULL)
597     {
598       log(0, TLOG_ERROR,
599           _("Could not find widget `%s'"),
600           "LogWindow");
601       return;
602     }
603   log_window = glade_xml_get_widget(x, "Messages");
604   if(!log_window)
605     {
606       log(0, TLOG_ERROR,
607           _("Could not find widget `%s'"),
608           "Messages");
609       return;
610     }
611   w = glade_xml_get_widget(x, "DebugLevelSpinbutton");
612   if(!w)
613     {
614       log(0, TLOG_ERROR,
615           _("Could not find widget `%s'"),
616           "DebugLevelSpinbutton");
617       return;
618     }
619   gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), (float)debug_lvl);
620   
621   glade_xml_signal_autoconnect(x);
622   log_visible = 1;
623   log_add_hook(log_gtk);
624   log(0, TLOG_NOTICE, "Logging started.\n");
625
626 }
627
628 void on_debug_level_changed(GtkSpinButton *sb, gpointer data)
629 {
630   debug_lvl = gtk_spin_button_get_value_as_int(sb);
631 }
632
633 void on_logwindow_close_clicked(GtkButton *b, gpointer data)
634 {
635   GladeXML *x;
636   GtkWidget *w;
637
638   x = glade_xml_new(INTERFACE_FILE, "LogWindow");
639   if(x == NULL)
640     {
641       log(0, TLOG_ERROR,
642           _("Could not find widget `%s'"),
643           "LogWindow");
644       return;
645     }
646   w = glade_xml_get_widget(x, "LogWindow");
647   if(!w)
648     {
649       log(0, TLOG_ERROR,
650           _("Could not find widget `%s'"),
651           "LogWindow");
652       return;
653     }
654   gtk_widget_destroy(w);
655 }
656
657 void on_spinbutton2_changed(GtkSpinButton *sb, gpointer data)
658 {
659   canvas_zoom = gtk_spin_button_get_value_as_float(sb) / 100.0;
660   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), canvas_zoom);
661 }
662
663 void on_checkbutton1_toggled(GtkCheckButton *cb, gpointer data)
664 {
665   keep_drawing = !keep_drawing;
666 }
667
668 void on_button19_clicked(GtkWidget *bt, GdkEventButton *ev, gpointer data)
669 {
670   shuffle_nodes();
671 }
672
673 void on_button18_clicked(GtkWidget *bt, GdkEventButton *ev, gpointer data)
674 {
675   GtkWidget *w;
676
677   w = glade_xml_get_widget(canvas_xml, "GraphWindow");
678   if(!w) { log(0, TLOG_ERROR, _("Couldn't find widget `%s'"), "GraphWindow"); return; }
679   gtk_object_destroy(GTK_OBJECT(w));
680   build_graph = 0;
681   canvas_visible = 0;
682 }
683
684 int init_interface(void)
685 {
686   char *l[1];
687
688   glade_gnome_init();
689
690   xml = glade_xml_new("pokey.glade", "AppWindow");
691
692   if(!xml)
693     {
694       log(0, TLOG_ERROR,
695           _("Something bad happened while creating the interface.\n"));
696       return -1;
697     }
698
699   nodetree = glade_xml_get_widget(xml, "NodeTree");
700   if(!nodetree)
701     {
702       log(0, TLOG_ERROR,
703           _("Could not find widget `%s'"),
704           "NodeTree");
705       return -1;
706     }
707
708   gtk_clist_freeze(GTK_CLIST(nodetree));
709   l[0] = _("Hosts");
710   hosts_ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
711                               NULL, NULL, l, 1,
712                               NULL, NULL, NULL, NULL,
713                               FALSE, TRUE);
714   gtk_clist_thaw(GTK_CLIST(nodetree));
715
716   glade_xml_signal_autoconnect(xml);
717
718   log_del_hook(log_default);
719
720   add_hook("node-add", if_node_add);
721   add_hook("node-del", if_node_del);
722   add_hook("subnet-add", if_subnet_add);
723   add_hook("subnet-del", if_subnet_del);
724   add_hook("edge-add", if_edge_add);
725   add_hook("edge-del", if_edge_del);
726   add_hook("node-visible", if_node_visible);
727   add_hook("node-invisible", if_node_invisible);
728   
729   return 0;
730 }
731
732 static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
733 {
734   static double item_x, old_x, new_x, item_y, old_y, new_y;
735   static int dragging = FALSE;
736   GdkCursor *fleur;
737   node_t *n;
738   
739   item_x = event->button.x;
740   item_y = event->button.y;
741   gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
742   
743   switch(event->type)
744     {
745     case GDK_BUTTON_PRESS:
746       switch(event->button.button)
747         {
748         case 1:
749           old_x = item_x;
750           old_y = item_y;
751
752           fleur = gdk_cursor_new(GDK_FLEUR);
753           gnome_canvas_item_grab(item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, fleur, event->button.time);
754           gdk_cursor_destroy(fleur);
755           dragging = TRUE;
756           break;
757
758         default:
759           break;
760         }
761       break;
762
763     case GDK_MOTION_NOTIFY:
764       if(dragging && (event->motion.state & GDK_BUTTON1_MASK))
765         {
766           new_x = item_x,
767           new_y = item_y;
768           gnome_canvas_item_move(item, new_x - old_x, new_y - old_y);
769           old_x = new_x;
770           old_y = new_y;
771         }
772       break;
773       
774     case GDK_BUTTON_RELEASE:
775       gnome_canvas_item_ungrab(item, event->button.time);
776       dragging = FALSE;
777       n = (node_t *)gtk_object_get_user_data(GTK_OBJECT(item));
778       ((struct if_node_data*)(n->data))->x = item_x;
779       ((struct if_node_data*)(n->data))->y = item_y;
780       x[((struct if_node_data*)(n->data))->id] = item_x;
781       y[((struct if_node_data*)(n->data))->id] = item_y;
782       build_graph = 1;
783       break;
784
785     default:
786       break;
787     }
788   return FALSE;
789 }
790
791 void if_node_create(node_t *n)
792 {
793   GnomeCanvasGroup *group;
794   
795   group = gnome_canvas_root(GNOME_CANVAS(canvas));
796   group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(group,
797                                                    gnome_canvas_group_get_type(),
798                                                    "x", 0.0,
799                                                    "y", 0.0,
800                                                    NULL));
801   
802   gnome_canvas_item_new(group, gnome_canvas_ellipse_get_type(),
803                         "x1", -30.0,
804                         "y1", -08.0,
805                         "x2", 30.0,
806                         "y2", 08.0,
807                         "fill_color_rgba", 0x5f9ea080,
808                         "outline_color", "black",
809                         "width_pixels", 0,
810                         NULL);
811   
812   gnome_canvas_item_new(group,
813                         gnome_canvas_text_get_type(),
814                         "x", 0.0,
815                         "y", 0.0,
816                         "text", n->name,
817                         "anchor", GTK_ANCHOR_CENTER,
818                         "fill_color", "white",
819                         "font", "-*-verdana-medium-r-*-*-10-*-*-*-*-*-iso8859-1",
820                         NULL);
821   
822   ((struct if_node_data*)(n->data))->item = GNOME_CANVAS_ITEM(group);
823   ((struct if_node_data*)(n->data))->x = ((struct if_node_data*)(n->data))->y = 0.0;
824   gtk_object_set_user_data(GTK_OBJECT(group), (gpointer)n);
825   
826   gtk_signal_connect(GTK_OBJECT(((struct if_node_data*)(n->data))->item), "event", (GtkSignalFunc) item_event, NULL);
827
828   gnome_canvas_item_hide(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item));
829 }
830
831 void if_node_visible(const char *hooktype, va_list ap)
832 {
833   int i;
834   avl_node_t *avlnode;
835   double newx, newy;
836   node_t *n = va_arg(ap, node_t*);
837
838   if(!n->data)
839     return;
840   
841   if(!((struct if_node_data*)(n->data))->item)
842     /* No GnomeCanvasItem has been created for this node yet */
843     return;
844
845   if(((struct if_node_data*)(n->data))->visible)
846     /* This node is already shown */
847     return;
848
849   ((struct if_node_data*)(n->data))->visible = 1;
850
851   newx = 250.0 + 200.0 * sin(number_of_nodes / 10.0 * M_PI);
852   newy = 150.0 - 100.0 * cos(number_of_nodes / 10.0 * M_PI);
853   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);
854   ((struct if_node_data*)(n->data))->x = newx;
855   ((struct if_node_data*)(n->data))->y = newy;
856   
857   for(i = 0, avlnode = node_tree->head; avlnode; avlnode = avlnode->next, i++)
858     {
859       if(!((struct if_node_data*)(((node_t*)(avlnode->data))->data))->visible)
860         continue;
861       
862       nodes[i] = (node_t *)(avlnode->data);
863       ((struct if_node_data*)(nodes[i]->data))->id = i;
864     }
865   number_of_nodes = i;
866
867   gnome_canvas_item_show(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item));
868   gnome_canvas_update_now(GNOME_CANVAS(canvas));
869
870   /* (Re)start calculations */
871   inited = 0;
872   build_graph = 1;
873 }
874
875 void if_node_invisible(const char *hooktype, va_list ap)
876 {
877   int i;
878   avl_node_t *avlnode;
879   node_t *n = va_arg(ap, node_t*);
880   
881   if(!((struct if_node_data*)(n->data))->item)
882     return;
883
884   if(!((struct if_node_data*)(n->data))->visible)
885     /* This node is already invisible */
886     return;
887
888   ((struct if_node_data*)(n->data))->visible = 0;
889
890   for(i = 0, avlnode = node_tree->head; avlnode; avlnode = avlnode->next, i++)
891     {
892       if(!((struct if_node_data*)((node_t*)(avlnode->data))->data)->visible)
893         continue;
894       
895       nodes[i] = (node_t *)(avlnode->data);
896       ((struct if_node_data*)(nodes[i]->data))->id = i;
897     }
898   number_of_nodes = i;
899   
900   gnome_canvas_item_hide(GNOME_CANVAS_ITEM(((struct if_node_data*)(n->data))->item));
901   gnome_canvas_update_now(GNOME_CANVAS(canvas));
902
903   /* (Re)start calculations */
904   inited = 0;
905   build_graph = 1;
906 }
907
908 void if_node_add(const char *hooktype, va_list ap)
909 {
910   node_t *n = va_arg(ap, node_t*);
911   char *l[1];
912   struct if_node_data *nd;
913
914   if(!xml)
915     return;
916
917   nd = xmalloc_and_zero(sizeof(*nd));
918   l[0] = n->name;
919   gtk_clist_freeze(GTK_CLIST(nodetree));
920   nd->ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
921                                   hosts_ctn, NULL, l, 1,
922                                   NULL, NULL, NULL, NULL,
923                                   FALSE, FALSE);
924   gtk_clist_thaw(GTK_CLIST(nodetree));
925   gtk_ctree_node_set_row_data(GTK_CTREE(nodetree), nd->ctn, n);
926
927   n->data = (void*)nd;
928
929   if(canvas_visible)
930     {
931       if_node_create(n);
932       if_node_visible(hooktype, ap);
933     }
934 }
935
936 void if_node_del(const char *hooktype, va_list ap)
937 {
938   node_t *n = va_arg(ap, node_t*);
939   struct if_node_data *nd;
940
941   nd = (struct if_node_data*)(n->data);
942   if(nd &&nd->ctn)
943     {
944       gtk_clist_freeze(GTK_CLIST(nodetree));
945       gtk_ctree_remove_node(GTK_CTREE(nodetree), nd->ctn);
946       gtk_clist_thaw(GTK_CLIST(nodetree));
947     }
948
949   if(canvas_visible)
950     {
951       if_node_invisible(hooktype, ap);
952     }
953
954   free(nd);
955   n->data = NULL;
956 }
957
958 void if_subnet_add(const char *hooktype, va_list ap)
959 {
960   char *l[1];
961   subnet_t *subnet = va_arg(ap, subnet_t*);
962   struct if_subnet_data *sd;
963   GtkCTreeNode *parent;
964
965   sd = xmalloc_and_zero(sizeof(*sd));
966   l[0] = net2str(subnet);
967   parent = subnet->owner->data ?
968     ((struct if_subnet_data*)(subnet->owner->data))->ctn
969       : NULL;
970
971   gtk_clist_freeze(GTK_CLIST(nodetree));
972   sd->ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
973                                   parent, NULL, l, 1,
974                                   NULL, NULL, NULL, NULL,
975                                   TRUE, FALSE);
976   gtk_clist_thaw(GTK_CLIST(nodetree));
977   gtk_ctree_node_set_row_data(GTK_CTREE(nodetree), sd->ctn, subnet);
978
979   subnet->data = (void*)sd;
980 }
981
982 void if_subnet_del(const char *hooktype, va_list ap)
983 {
984   subnet_t *subnet = va_arg(ap, subnet_t*);
985   struct if_subnet_data *sd;
986
987   sd = (struct if_subnet_data*)(subnet->data);
988   if(sd && sd->ctn)
989     {
990       gtk_clist_freeze(GTK_CLIST(nodetree));
991       gtk_ctree_remove_node(GTK_CTREE(nodetree), sd->ctn);
992       gtk_clist_thaw(GTK_CLIST(nodetree));
993     }
994   
995   free(sd);
996   subnet->data = NULL;
997 }
998
999 void redraw_edges(void)
1000 {
1001   GnomeCanvasGroup *group;
1002   GnomeCanvasPoints *points;
1003   avl_node_t *avlnode;
1004   edge_t *e;
1005   struct if_node_data *fd, *td;
1006
1007   if(edge_group)
1008     gtk_object_destroy(GTK_OBJECT(edge_group));
1009   
1010   group = gnome_canvas_root(GNOME_CANVAS(canvas));
1011   group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(group,
1012                                                    gnome_canvas_group_get_type(),
1013                                                    "x", 0.0,
1014                                                    "y", 0.0,
1015                                                    NULL));
1016   
1017   for(avlnode = edge_tree->head; avlnode; avlnode = avlnode->next)
1018     {
1019       e = (edge_t *)avlnode->data;
1020       fd = (struct if_node_data*)(e->from.node->data);
1021       td = (struct if_node_data*)(e->to.node->data);
1022
1023 /*       if(!e->from.node->status.visible || */
1024 /*       !e->to.node->status.visible) */
1025 /*      /\* We shouldn't draw this line *\/ */
1026 /*      continue; */
1027       
1028       points = gnome_canvas_points_new(2);
1029       
1030       points->coords[0] = fd->x;
1031       points->coords[1] = fd->y;
1032       points->coords[2] = td->x;
1033       points->coords[3] = td->y;
1034       gnome_canvas_item_new(group,
1035                             gnome_canvas_line_get_type(),
1036                             "points", points,
1037                             "fill_color_rgba", 0xe080c080,
1038                             "width_pixels", 2,
1039                             NULL);
1040       gnome_canvas_points_unref(points);
1041     }
1042
1043   gnome_canvas_update_now(GNOME_CANVAS(canvas));
1044
1045   edge_group = group;
1046 }
1047
1048 void if_edge_add(const char *hooktype, va_list ap)
1049 {
1050   redraw_edges();
1051
1052   inited = 0;
1053   build_graph = 1;
1054 }
1055
1056 void if_edge_del(const char *hooktype, va_list ap)
1057 {
1058   redraw_edges();
1059
1060   inited = 0;
1061   build_graph = 1;
1062 }
1063
1064 void if_move_node(node_t *n, double dx, double dy)
1065 {
1066   double newx, newy;
1067   
1068   newx = ((struct if_node_data*)(n->data))->x + dx;
1069   newy = ((struct if_node_data*)(n->data))->y + dy;
1070   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);
1071   ((struct if_node_data*)(n->data))->x = newx;
1072   ((struct if_node_data*)(n->data))->y = newy;
1073 }
1074
1075 #define X_MARGIN 50.0
1076 #define X_MARGIN_BUFFER 25.0
1077 #define Y_MARGIN 20.0
1078 #define Y_MARGIN_BUFFER 10.0
1079
1080 void set_zooming(void)
1081 {
1082   int i;
1083   double minx, miny, maxx, maxy;
1084   static double ominx = 0.0, ominy = 0.0, omaxx = 0.0, omaxy = 0.0;
1085
1086   minx = miny = maxx = maxy = 0.0;
1087   for(i = 0; i < number_of_nodes; i++)
1088     {
1089       if(((struct if_node_data*)(nodes[i]->data))->x < minx)
1090         minx = ((struct if_node_data*)(nodes[i]->data))->x;
1091       else
1092         if(((struct if_node_data*)(nodes[i]->data))->x > maxx)
1093           maxx = ((struct if_node_data*)(nodes[i]->data))->x;
1094
1095       if(((struct if_node_data*)(nodes[i]->data))->y < miny)
1096         miny = ((struct if_node_data*)(nodes[i]->data))->y;
1097       else
1098         if(((struct if_node_data*)(nodes[i]->data))->y > maxy)
1099           maxy = ((struct if_node_data*)(nodes[i]->data))->y;
1100     }
1101
1102   if(minx > ominx - X_MARGIN_BUFFER && ominx > minx)
1103     minx = ominx;
1104   if(maxx < omaxx + X_MARGIN_BUFFER && omaxx < maxx)
1105     maxx = omaxx;
1106   if(miny > ominy - Y_MARGIN_BUFFER && ominy > miny)
1107     miny = ominy;
1108   if(maxy < omaxy + Y_MARGIN_BUFFER && omaxy < maxy)
1109     maxy = omaxy;
1110
1111   ominx = minx; ominy = miny; omaxx = maxx; omaxy = maxy;
1112
1113 /*   ppux = canvas_width / (maxx - minx); */
1114 /*   ppuy = canvas_height / (maxy - miny); */
1115 /*   if(ppux < ppuy) */
1116 /*     ppu = ppux; */
1117 /*   else */
1118 /*     ppu = ppuy; */
1119
1120 /*   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), ppu); */
1121   gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas), minx - X_MARGIN, miny - Y_MARGIN, maxx + X_MARGIN, maxy + Y_MARGIN);
1122   gnome_canvas_update_now(GNOME_CANVAS(canvas));
1123 }
1124
1125 double calculate_delta_m(int m)
1126 {
1127   double dedxm, dedym, xmxi, ymyi;
1128   int i;
1129
1130   dedxm = dedym = 0.0;
1131   for(i = 0; i < number_of_nodes; i++)
1132     {
1133       if(i == m)
1134         continue;
1135
1136       xmxi = x[m] - x[i];
1137       ymyi = y[m] - y[i];
1138
1139       dedxm += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
1140       dedym += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
1141     }
1142
1143   return sqrt(dedxm * dedxm + dedym * dedym);
1144 }
1145
1146 void move_node(int m, double *dx, double *dy)
1147 {
1148   double d2edxm2, d2edym2, d2edxmdym, dedxm, dedym;
1149   double xmxi, ymyi, denominator;
1150   int i;
1151
1152   d2edxm2 = d2edym2 = d2edxmdym = dedxm = dedym = 0.0;
1153   for(i = 0; i < number_of_nodes; i++)
1154     {
1155       if(i == m)
1156         continue;
1157       
1158       xmxi = x[m] - x[i];
1159       ymyi = y[m] - y[i];
1160
1161       denominator = pow(sqrt(xmxi * xmxi + ymyi * ymyi), 3.0);
1162
1163       d2edxm2 += k[m][i] * (1 - ((l[m][i] * ymyi * ymyi) / denominator));
1164       d2edxmdym += k[m][i] * l[m][i] * xmxi * ymyi / denominator;
1165       d2edym2 += k[m][i] * (1 - ((l[m][i] * xmxi * xmxi) / denominator));
1166       dedxm += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
1167       dedym += k[m][i] * (ymyi - ((l[m][i] * ymyi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
1168     }
1169
1170   denominator = ((d2edxm2 * d2edym2) - (d2edxmdym * d2edxmdym));
1171   *dx = (-(d2edym2 * dedxm) + (d2edxmdym * dedym)) / denominator;
1172   *dy = ((d2edxmdym * dedxm) - (d2edxm2 * dedym)) / denominator;
1173 }
1174
1175 void if_build_graph(void)
1176 {
1177   int i, j, p, max_i;
1178   double delta_m, max_delta_m;
1179   double dx, dy, s, L, min_d, old_x, old_y;
1180   edge_t *e;
1181
1182   if(!keep_drawing)
1183     return;
1184   
1185   if(!inited)
1186     {
1187       for(i = 0; i < number_of_nodes; i++)
1188         {
1189           x[i] = ((struct if_node_data*)(nodes[i]->data))->x;
1190           y[i] = ((struct if_node_data*)(nodes[i]->data))->y;
1191         }
1192
1193       /* Initialize Floyd */
1194       for(i = 0; i < number_of_nodes; i++)
1195         {
1196           d[i][i] = 0.0;
1197           for(j = i + 1; j < number_of_nodes; j++)
1198             {
1199               e = lookup_edge(nodes[i], nodes[j]);
1200               if(e)
1201                 d[i][j] = d[j][i] = (double)e->weight;
1202               else
1203                 d[i][j] = d[j][i] = INFINITY;
1204             }
1205         }
1206
1207       /* Floyd's shortest path algorithm */
1208       for(i = 0; i < number_of_nodes; i++)
1209         {
1210           for(j = 0; j < number_of_nodes; j++)
1211             {
1212               if(i == j)
1213                 continue;
1214               
1215               if(d[j][i] < INFINITY)
1216                 {
1217                   for(p = 0; p < number_of_nodes; p++)
1218                     {
1219                       if(d[i][j] < INFINITY)
1220                         {
1221                           s = d[j][i] + d[i][p];
1222                           if(s < d[j][p])
1223                             {
1224                               d[j][p] = s;
1225                             }
1226                         }
1227                     }
1228                 }
1229             }
1230         }
1231
1232       min_d = INFINITY;
1233       for(i = 0; i < number_of_nodes; i++)
1234         for(j = i + 1; j < number_of_nodes; j++)
1235           if(d[i][j] < min_d && d[i][j] > 0.0)
1236             min_d = d[i][j];
1237
1238       L = 5.0 / sqrt(min_d + 1.0);
1239
1240       for(i = 0; i < number_of_nodes; i++)
1241         {
1242           for(j = i + 1; j < number_of_nodes; j++)
1243             {
1244               d[i][j] = d[j][i] = sqrt(d[i][j]+1.0);
1245               l[i][j] = l[j][i] = L * d[i][j];
1246               k[i][j] = k[j][i] = K / (d[i][j] * d[i][j]);
1247             }
1248         }
1249
1250       inited = 1;
1251     }
1252
1253   max_delta_m = 0.0;
1254   /* Find node with maximal local energy */
1255   for(i = 0; i < number_of_nodes; i++)
1256     {
1257       delta_m = calculate_delta_m(i);
1258       if(delta_m > max_delta_m)
1259         {
1260           max_delta_m = delta_m;
1261           max_i = i;
1262         }
1263     }
1264
1265   if(max_delta_m <= epsilon)
1266     {
1267       fprintf(stderr, "Graph building is done; max_delta_m = %f\n", max_delta_m);
1268       build_graph = 0;
1269     }
1270   else
1271     {
1272       int iter = 0, maxiter = 20;
1273       delta_m = max_delta_m;
1274       old_x = x[max_i];
1275       old_y = y[max_i];
1276       while(delta_m > epsilon && iter < maxiter)
1277         {
1278           move_node(max_i, &dx, &dy);
1279           x[max_i] += dx;
1280           y[max_i] += dy;
1281           delta_m = calculate_delta_m(max_i);
1282           iter++;
1283         }
1284           
1285       if_move_node(nodes[max_i], x[max_i] - old_x, y[max_i] - old_y);
1286           
1287       redraw_edges();
1288
1289       set_zooming();
1290     }
1291
1292 /*   build_graph = 0; */
1293 }