1 /* Copyright (C) 2005 sgop@users.sourceforge.net This is free software
2  * distributed under the terms of the GNU Public License.  See the
3  * file COPYING for details.
4  */
5 /* $Revision: 1.12 $
6  * $Date: 2008/05/23 14:54:28 $
7  * $Author: sgop $
8  */
9 
10 #ifdef HAVE_CONFIG_H
11 #  include <config.h>
12 #endif
13 
14 #include <sys/statvfs.h>
15 #include <stdlib.h>
16 #include <math.h>
17 #include <string.h>
18 #include <stdio.h>
19 
20 #include <gtk/gtk.h>
21 
22 #include "gui_main.h"
23 #include "l_i18n.h"
24 #include "tree.h"
25 #include "colors.h"
26 #include "utils.h"
27 #include "preferences.h"
28 #include "about.h"
29 
30 
31 typedef struct _tree_info_t tree_info_t;
32 
33 struct _tree_info_t
34 {
35     tree_t* node;
36     double s[2][2];
37     int geo[2][2];
38     GList* children;
39     tree_info_t* parent;
40 };
41 
42 typedef struct
43 {
lock_queue_iterator_reset(lock_queue_iterator_t * iter,const lock_t * lock,ulint bit_no)44     GtkProgressBar* bar;
45     GtkButton* abort;
46     gboolean cancel;
47 } progress_data_t;
48 
49 static GtkWidget* MainWin = NULL;
50 
51 static char* Buffer = NULL;
52 static int Width = 0;
53 static int Height = 0;
54 static tree_info_t* Mark1 = NULL;
55 static tree_info_t* Mark2 = NULL;
56 static int RedrawTimer = 0;
57 
58 static GtkWidget* Area = NULL;
59 static tree_info_t* CurrentItem = NULL;
60 
61 static GtkLabel* FileLabel = NULL;
62 static GtkLabel* FileSizeLabel = NULL;
63 static GtkLabel* SubLabel = NULL;
64 static GtkLabel* SubSizeLabel = NULL;
65 static GtkLabel* SizeLabel = NULL;
66 static GtkBox* PathBox = NULL;
67 static GtkBox* StatusBar = NULL;
68 
69 static GList* History = NULL;
70 static GList* HistoryPos = NULL;
71 static gboolean Loading = FALSE;
72 
73 static GtkUIManager* UIManager = NULL;
74 static GtkActionGroup* ActionGroup = NULL;
75 
76 static gboolean LUseColors = TRUE;
77 static gboolean LUseAverage = TRUE;
78 static unsigned LMaxDepth = 0;
79 static unsigned LDisplayMode = DISPLAY_SQUARE_CUSHION;
lock_queue_iterator_get_prev(lock_queue_iterator_t * iter)80 
81 static void on_action_open();
82 static void on_action_back();
83 static void on_action_forward();
84 static void on_action_up();
85 static void on_action_top();
86 static void on_action_exit();
87 static void on_action_preferences();
88 static void on_action_about();
89 static void on_action_refresh();
90 
91 static GtkActionEntry Actions[] =
92 {
93 
94     { "FileMenu", NULL, N_("_File"), 0,0,0 },
95     { "ViewMenu", NULL, N_("_View"), 0,0,0 },
96     { "HelpMenu", NULL, N_("_Help"), 0,0,0 },
97     { "Open", GTK_STOCK_OPEN, N_("_Open..."),
98       "<control>O", N_("Open a folder"),
99       G_CALLBACK(on_action_open)
100     },
101     { "Quit", GTK_STOCK_QUIT, N_("_Quit"),
102       "<control>Q", N_("Exit program"),
103       G_CALLBACK(on_action_exit)
104     },
105     { "Back", GTK_STOCK_GO_BACK, N_("_Back"),
106       NULL, N_("Go back one step"),
107       G_CALLBACK(on_action_back)
108     },
109     { "Forward", GTK_STOCK_GO_FORWARD, N_("_Forward"),
110       NULL, N_("Go forward one step"),
111       G_CALLBACK(on_action_forward)
112     },
113     { "Up", GTK_STOCK_GO_UP, N_("_Up"),
114       NULL, N_("Go up one level"),
115       G_CALLBACK(on_action_up)
116     },
117     { "Top", GTK_STOCK_GOTO_TOP, N_("_Top"),
118       NULL, N_("Goto toplevel folder"),
119       G_CALLBACK(on_action_top)
120     },
121     { "Options", GTK_STOCK_PREFERENCES, N_("_Settings..."),
122       NULL, N_("Edit settings"),
123       G_CALLBACK(on_action_preferences)
124     },
125     { "About", GTK_STOCK_PREFERENCES, N_("_About..."),
126       NULL, N_("About this program"),
127       G_CALLBACK(on_action_about)
128     },
129     { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"),
130       NULL, N_("Refresh current view"),
131       G_CALLBACK(on_action_refresh)
132     },
133 };
134 
135 static const char* MenuDescription =
136     "<ui>\n"
137     "  <menubar action=\"MainMenu\">\n"
138     "    <menu action=\"FileMenu\">\n"
139     "      <menuitem action=\"Open\"/>\n"
140     "      <separator/>\n"
141     "      <menuitem action=\"Options\"/>\n"
142     "      <separator/>\n"
143     "      <menuitem action=\"Quit\"/>\n"
144     "    </menu>\n"
145     "    <menu action=\"ViewMenu\">\n"
146     "      <menuitem action=\"Back\"/>\n"
147     "      <menuitem action=\"Forward\"/>\n"
148     "      <menuitem action=\"Up\"/>\n"
149     "      <menuitem action=\"Top\"/>\n"
150     "      <separator/>\n"
151     "      <menuitem action=\"Refresh\"/>\n"
152     "    </menu>\n"
153     "    <menu action=\"HelpMenu\">\n"
154     "      <menuitem action=\"About\"/>\n"
155     "    </menu>\n"
156     "  </menubar>\n"
157     "  <toolbar action=\"ToolBar\">\n"
158     "    <toolitem action=\"Open\"/>\n"
159     "    <separator/>\n"
160     "    <toolitem action=\"Back\"/>\n"
161     "    <toolitem action=\"Forward\"/>\n"
162     "    <toolitem action=\"Up\"/>\n"
163     "    <toolitem action=\"Top\"/>\n"
164     "    <separator/>\n"
165     "    <toolitem action=\"Refresh\"/>\n"
166     "    <separator/>\n"
167     "    <toolitem action=\"Options\"/>\n"
168     "  </toolbar>\n"
169     "</ui>\n";
170 
171 static void gui_tree_display(tree_info_t* info, gboolean destroy_old, gboolean new_history);
172 static tree_t* gui_tree_load(const char* folder, unsigned depth);
173 
174 
175 // HISTORY
176 static void history_clear()
177 {
178     g_list_free(History);
179     History = NULL;
180     HistoryPos = NULL;
181 }
182 
183 static void history_back()
184 {
185     if (!HistoryPos) return;
186     if (!HistoryPos->prev) return;
187 
188     HistoryPos = HistoryPos->prev;
189 
190     gui_tree_display(HistoryPos->data, FALSE, FALSE);
191 }
192 
193 static void history_forward()
194 {
195     if (!History) return;
196     if (HistoryPos)
197     {
198         if (!HistoryPos->next) return;
199         HistoryPos = HistoryPos->next;
200     }
201     else
202     {
203         HistoryPos = History;
204     }
205     gui_tree_display(HistoryPos->data, FALSE, FALSE);
206 }
207 
208 static void history_append(tree_info_t* info)
209 {
210     if (HistoryPos)
211     {
212         while (HistoryPos->next)
213             History = g_list_delete_link(History, HistoryPos->next);
214     }
215     History = g_list_append(History, info);
216     HistoryPos = g_list_last(History);
217 }
218 
219 
220 
221 // TREE INFO
222 static void tree_info_destroy(tree_info_t* info)
223 {
224     g_list_foreach(info->children, (GFunc)tree_info_destroy, NULL);
225     g_list_free(info->children);
226     g_free(info);
227 }
228 
229 static tree_info_t* tree_info_search(tree_info_t* info, int x, int y)
230 {
231     GList* dlist;
232     unsigned depth = LMaxDepth;
233 
234     if (depth != 0 && depth < info->node->depth) return NULL;
235 
236     if (x < info->geo[0][0] || x >= info->geo[0][1] ||
237         y < info->geo[1][0] || y >= info->geo[1][1])
238         return NULL;
239 
240     for (dlist = info->children; dlist; dlist = dlist->next)
241     {
242         tree_info_t* sinfo = dlist->data;
243         sinfo = tree_info_search(sinfo, x, y);
244         if (sinfo) return sinfo;
245     }
246     return info;
247 }
248 
249 static tree_info_t* tree_info_find_path(tree_info_t* root, tree_info_t* child)
250 {
251     tree_info_t* temp;
252 
253     for (temp = child; temp; temp = temp->parent)
254     {
255         if (temp->parent == root) return temp;
256     }
257     return NULL;
258 }
259 
260 
261 static tree_info_t* tree_info_create(tree_t* tree)
262 {
263     tree_info_t* info = g_malloc0(sizeof(*info));
264     GList* dlist;
265 
266     info->node = tree;
267     for (dlist = tree->entries; dlist; dlist = dlist->next)
268     {
269         tree_t* child = dlist->data;
270         tree_info_t* sub = tree_info_create(child);
271         sub->parent = info;
272         info->children = g_list_append(info->children, sub);
273     }
274     return info;
275 }
276 
277 static void on_action_exit()
278 {
279     gtk_main_quit();
280 }
281 
282 static void on_action_preferences()
283 {
284     gui_show_preferences(GTK_WINDOW(MainWin));
285 }
286 
287 static void on_action_about()
288 {
289     gui_show_about(GTK_WINDOW(MainWin));
290 }
291 
292 static gint comp_func(gconstpointer p1, gconstpointer p2)
293 {
294     const tree_t* t1 = (tree_t*)p1;
295     const tree_t* t2 = (tree_t*)p2;
296 
297     if (t1->size < t2->size) return 1;
298     if (t1->size > t2->size) return -1;
299     return 0;
300 }
301 
302 static gint comp_func2(gconstpointer p1, gconstpointer p2)
303 {
304     const tree_info_t* t1 = (tree_info_t*)p1;
305     const tree_info_t* t2 = (tree_info_t*)p2;
306 
307     if (t1->node->size < t2->node->size) return 1;
308     if (t1->node->size > t2->node->size) return -1;
309     return 0;
310 }
311 
312 static void gui_refresh_current()
313 {
314     char* folder;
315     tree_t* new_tree;
316 
317     if (!CurrentItem) return;
318     folder = tree_full_name(CurrentItem->node);
319     new_tree = gui_tree_load(folder, CurrentItem->node->depth);
320     if (new_tree)
321     {
322         tree_t* tree = CurrentItem->node;
323         tree_t* parent = tree->parent;
324         tree_info_t* info = tree_info_create(new_tree);
325         char* t;
326 
327         t = new_tree->name;
328         new_tree->name = tree->name;
329         tree->name = t;
330 
331         if (parent)
332         {
333             tree_t* temp;
334 
335             // remove tree from parent
336             parent->entries = g_list_remove(parent->entries, tree);
337             CurrentItem->parent->children =
338                 g_list_remove(CurrentItem->parent->children, CurrentItem);
339 
340             // adjust sizes
341             for (temp = parent; temp; temp = temp->parent)
342             {
343                 temp->size -= tree->size;
344                 temp->size += new_tree->size;
345             }
346             // insert tree in parent
347             parent->entries = g_list_insert_sorted(parent->entries, new_tree, comp_func);
348             new_tree->parent = parent;
349 
350             CurrentItem->parent->children =
351                 g_list_insert_sorted(CurrentItem->parent->children, info, comp_func2);
352             info->parent = CurrentItem->parent;
353         }
354         tree_destroy(tree);
355         tree_info_destroy(CurrentItem);
356         CurrentItem = NULL;
357         gui_tree_display(info, TRUE, TRUE);
358     }
359     g_free(folder);
360 }
361 
362 static void on_action_refresh()
363 {
364     gui_refresh_current();
365 }
366 
367 static gboolean _update_progress(void* data)
368 {
369     progress_data_t* pdata = data;
370 
371     if (pdata->cancel) return FALSE;
372 
373     gtk_progress_bar_pulse(pdata->bar);
374     while (gtk_events_pending()) if (gtk_main_iteration()) return FALSE;
375 
376     return TRUE;
377 }
378 
379 static void on_abort_load(progress_data_t* data)
380 {
381     data->cancel = TRUE;
382 }
383 
384 static tree_t* gui_tree_load(const char* folder, unsigned depth)
385 {
386     GtkWidget* item1;
387     GtkWidget* item2;
388     tree_t* tree;
389     progress_data_t* data = g_malloc0(sizeof(*data));
390 
391     data->cancel = FALSE;
392     Loading = TRUE;
393 
394     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Open"), FALSE);
395     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Refresh"), FALSE);
396 
397     item1 = gtk_progress_bar_new();
398     data->bar = GTK_PROGRESS_BAR(item1);
399     gtk_widget_show(item1);
400     gtk_box_pack_start(StatusBar, item1, FALSE, FALSE, 0);
401     gtk_progress_bar_set_pulse_step(data->bar, 0.05);
402     gtk_progress_bar_set_text(data->bar, _("scanning..."));
403 
404     item2 = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
405     data->abort = GTK_BUTTON(item2);
406     gtk_widget_show(item2);
407     gtk_box_pack_start(StatusBar, item2, FALSE, FALSE, 0);
408     g_signal_connect_swapped(G_OBJECT(item2), "clicked",
409                              G_CALLBACK(on_abort_load), data);
410 
411     tree = tree_load(folder, _update_progress, data, depth);
412 
413     gtk_widget_destroy(item1);
414     gtk_widget_destroy(item2);
415     g_free(data);
416 
417     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Open"), TRUE);
418     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Refresh"), TRUE);
419     Loading = FALSE;
420 
421     return tree;
422 }
423 
424 void gui_tree_load_and_display(const char* folder)
425 {
426     tree_t* tree = gui_tree_load(folder, 0);
427     if (tree)
428     {
429         tree_info_t* info = tree_info_create(tree);
430         gui_tree_display(info, TRUE, TRUE);
431     }
432 }
433 
434 
435 static void on_action_open()
436 {
437     GtkWidget* dialog;
438     char* res;
439 
440     dialog = gtk_file_chooser_dialog_new
441         (_("Choose folder"), GTK_WINDOW(MainWin),
442          GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
443          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
444          GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
445          NULL);
446 
447     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
448     {
449         res = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
450     }
451     else
452     {
453         res = NULL;
454     }
455     gtk_widget_destroy(dialog);
456 
457     if (res)
458     {
459         gui_tree_load_and_display(res);
460         g_free(res);
461     }
462 }
463 
464 static void gui_buttons_update()
465 {
466     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Back"),
467                              HistoryPos && HistoryPos->prev);
468     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Forward"),
469                              HistoryPos && HistoryPos->next);
470     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Up"),
471                              CurrentItem && CurrentItem->parent);
472     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Top"),
473                              CurrentItem && CurrentItem->parent);
474     gtk_action_set_sensitive(gtk_ui_manager_get_action(UIManager, "/ToolBar/Refresh"),
475                              CurrentItem != NULL && !Loading);
476 }
477 
478 static void on_action_top()
479 {
480     tree_info_t* info = CurrentItem;
481 
482     if (!info || !info->parent) return;
483     while (info->parent) info = info->parent;
484     gui_tree_display(info, FALSE, TRUE);
485 }
486 
487 static void on_action_up()
488 {
489     tree_info_t* info = CurrentItem;
490 
491     if (!info || !info->parent) return;
492     gui_tree_display(info->parent, FALSE, TRUE);
493 }
494 
495 static void on_action_back()
496 {
497     history_back();
498 }
499 
500 static void on_action_forward()
501 {
502     history_forward();
503 }
504 
505 
506 
507 /* static void on_depth_change(GtkSpinButton* button) { */
508 /*   int val = gtk_spin_button_get_value_as_int(button); */
509 /*   if (val != MaxDepth) { */
510 /*     MaxDepth = val; */
511 /*     gui_tree_display(CurrentTree); */
512 /*   } */
513 /* } */
514 
515 static GtkWidget* gui_create_path_box()
516 {
517     GtkWidget* hbox;
518     GtkWidget* label;
519 
520     hbox = gtk_hbox_new(FALSE, 0);
521     PathBox = GTK_BOX(hbox);
522     gtk_widget_show(hbox);
523 
524     label = gtk_label_new("");
525     gtk_widget_show(label);
526     gtk_box_pack_end(PathBox, label, FALSE, FALSE, 5);
527     SizeLabel = GTK_LABEL(label);
528 
529     return hbox;
530 }
531 
532 static gboolean on_drawingarea_configure(GtkWidget* area)
533 {
534 
535     if (Buffer) g_free(Buffer);
536 
537     Width = area->allocation.width;
538     Height = area->allocation.height;
539 
540     Buffer = g_malloc0(sizeof(unsigned int)*Width*Height*3);
541     if (CurrentItem) gui_tree_display(CurrentItem, FALSE, FALSE);
542 
543     return TRUE;
544 }
545 
546 static void gui_show_pix(GtkWidget* area)
547 {
548     if (Buffer)
549     {
550         gdk_draw_rgb_image(area->window,
551                            area->style->white_gc,
552                            0, 0, Width, Height,
553                            GDK_RGB_DITHER_NONE,
554                            Buffer, Width*3);
555         Mark1 = NULL;
556         Mark2 = NULL;
557     }
558 }
559 
560 static gboolean on_area_expose(GtkWidget* area)
561 {
562     gui_show_pix(area);
563     return TRUE;
564 }
565 
566 static void gui_tree_mark1(tree_info_t* info)
567 {
568     GdkGC* gc = color_get_gc_by_type(COLOR_MARK1);
569 
570     gdk_draw_rectangle(Area->window, gc, 0,
571                        info->geo[0][0],info->geo[1][0],
572                        info->geo[0][1]-info->geo[0][0]-1,
573                        info->geo[1][1]-info->geo[1][0]-1);
574 
575     if (info != Mark1)
576     {
577         char* temp;
578         char* text = g_strdup(info->node->name);
579         Mark1 = info;
580         while (info != CurrentItem && info->parent != CurrentItem &&
581                info->parent->parent != CurrentItem)
582         {
583             info = info->parent;
584             temp = g_strdup_printf("%s/%s", info->node->name, text);
585             g_free(text);
586             text = temp;
587         }
588         gtk_label_set_text(GTK_LABEL(FileLabel), to_utf8(text));
589         g_free(text);
590 
591         temp = print_filesize(Mark1->node->size);
592         text = g_strdup_printf("<b>%s</b>", temp);
593         gtk_label_set_markup(FileSizeLabel, text);
594         g_free(temp);
595         g_free(text);
596     }
597 
598 }
599 
600 static void gui_tree_unmark1(tree_info_t* info)
601 {
602     GdkGC* gc = color_get_gc_by_type(COLOR_MARK1);
603 
604     gdk_draw_rectangle(Area->window, gc, 0,
605                        info->geo[0][0],info->geo[1][0],
606                        info->geo[0][1]-info->geo[0][0]-1,
607                        info->geo[1][1]-info->geo[1][0]-1);
608     Mark1 = NULL;
609 }
610 
611 static void gui_tree_unmark2(tree_info_t* info)
612 {
613     GdkGC* gc = color_get_gc_by_type(COLOR_MARK2);
614 
615     gdk_draw_rectangle(Area->window, gc, 0,
616                        info->geo[0][0]-1, info->geo[1][0]-1,
617                        info->geo[0][1]-info->geo[0][0]+1,
618                        info->geo[1][1]-info->geo[1][0]+1);
619     Mark2 = NULL;
620 }
621 
622 static void gui_tree_mark2(tree_info_t* info)
623 {
624     GdkGC* gc = color_get_gc_by_type(COLOR_MARK2);
625 
626     gdk_draw_rectangle(Area->window, gc, 0,
627                        info->geo[0][0]-1, info->geo[1][0]-1,
628                        info->geo[0][1]-info->geo[0][0]+1,
629                        info->geo[1][1]-info->geo[1][0]+1);
630 
631     if (info != Mark2)
632     {
633         Mark2 = info;
634         if (Mark2 != Mark1)
635         {
636             char* temp;
637             char* text;
638 
639             gtk_label_set_text(SubLabel, to_utf8(info->node->name));
640 
641             temp = print_filesize(Mark2->node->size);
642             text = g_strdup_printf("<b>%s</b>", temp);
643             gtk_label_set_markup(SubSizeLabel, text);
644             g_free(temp);
645             g_free(text);
646         }
647         else
648         {
649             gtk_label_set_markup(SubSizeLabel, "<b>-</b>");
650             gtk_label_set_text(SubLabel, "-");
651         }
652     }
653 
654 }
655 
656 static void gui_tree_mark(tree_info_t* info)
657 {
658     tree_info_t* tree;
659 
660     if (!info || info == Mark1) return;
661     if (Mark1) gui_tree_unmark1(Mark1);
662     gui_tree_mark1(info);
663 
664     tree = tree_info_find_path(CurrentItem, info);
665 
666     if (tree == Mark2) return;
667     if (Mark2) gui_tree_unmark2(Mark2);
668     if (!tree) return;
669     gui_tree_mark2(tree);
670 }
671 
672 static gboolean on_area_motion(GtkWidget* widget, GdkEventMotion* event)
673 {
674     tree_info_t* info;
675 
676     (void)widget;
677     if (!CurrentItem) return FALSE;
678 
679     info = tree_info_search(CurrentItem, (int)(event->x), (int)(event->y));
680     if (info) gui_tree_mark(info);
681     return TRUE;
682 }
683 
684 
685 static gboolean on_area_button_press(GtkWidget* widget, GdkEventButton* event)
686 {
687     tree_info_t* info;
688 /*   GtkWidget* pop; */
689 
690     (void)widget;
691     if (!CurrentItem) return FALSE;
692 
693     info = tree_info_search(CurrentItem, (int)(event->x), (int)(event->y));
694 
695     if (info)
696     {
697         if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
698         {
699             tree_info_t* sub = tree_info_find_path(CurrentItem, info);
700             if (sub) gui_tree_display(sub, FALSE, TRUE);
701 /*     } else if (event->button == 3) { */
702 /*       pop = create_right_popup(sub_tree); */
703 /*       gtk_menu_popup(GTK_MENU(pop), NULL, NULL, NULL, NULL, */
704 /* 		     event->button, event->time); */
705         }
706     }
707 
708     return FALSE;
709 }
710 
711 static GtkWidget* gui_create_display_box()
712 {
713     GtkWidget* da;
714 
715     da = gtk_drawing_area_new();
716     Area = da;
717     gtk_widget_show(da);
718     gtk_widget_set_events(da, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK);
719     // no need for offscreen support of gtk, we do it manually
720     gtk_widget_set_double_buffered(da, FALSE);
721 
722     g_signal_connect(G_OBJECT(da), "configure_event",
723                      G_CALLBACK(on_drawingarea_configure), NULL);
724     g_signal_connect(G_OBJECT(da), "expose_event",
725                      G_CALLBACK(on_area_expose), NULL);
726     g_signal_connect(G_OBJECT(da), "motion_notify_event",
727                      G_CALLBACK(on_area_motion), NULL);
728     g_signal_connect(G_OBJECT(da), "button_press_event",
729                      G_CALLBACK(on_area_button_press), NULL);
730     return da;
731 }
732 
733 static GtkWidget* gui_create_status_bar()
734 {
735     GtkWidget* bar;
736     GtkWidget* table;
737     GtkWidget* label;
738 
739     bar = gtk_hbox_new(FALSE, 5);
740     gtk_widget_show(bar);
741     StatusBar = GTK_BOX(bar);
742 
743     table = gtk_table_new(2,2, FALSE);
744 /*   gtk_table_set_col_spacings(GTK_TABLE(table), 10); */
745 /*   gtk_table_set_row_spacings(GTK_TABLE(table), 0); */
746     gtk_widget_show(table);
747     gtk_box_pack_start(GTK_BOX(bar), table, TRUE, TRUE, 0);
748 
749     label = gtk_label_new("");
750     gtk_widget_show(label);
751     SubSizeLabel = GTK_LABEL(label);
752     gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
753     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 5, 0);
754 
755     label = gtk_label_new("");
756     gtk_widget_show(label);
757     SubLabel = GTK_LABEL(label);
758 /*   gtk_widget_set_size_request(label, 50, -1); */
759     gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
760     gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 5, 0);
761 
762     label = gtk_label_new("");
763     gtk_widget_show(label);
764     FileSizeLabel = GTK_LABEL(label);
765     gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
766     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 5, 0);
767 
768     label = gtk_label_new("");
769     gtk_widget_show(label);
770     FileLabel = GTK_LABEL(label);
771     gtk_widget_set_size_request(label, 50, -1);
772     gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
773     gtk_table_attach(GTK_TABLE(table), label, 1, 2, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
774 
775     return bar;
776 }
777 
778 static gboolean on_main_win_delete_event()
779 {
780     gtk_main_quit();
781     return TRUE;
782 }
783 
784 static void gui_setup_actions(GtkWidget* win)
785 {
786     GError* error = NULL;
787 
788     UIManager = gtk_ui_manager_new();
789     ActionGroup = gtk_action_group_new("Actions");
790     gtk_action_group_set_translation_domain(ActionGroup, GETTEXT_PACKAGE);
791     gtk_action_group_add_actions(ActionGroup, Actions, G_N_ELEMENTS(Actions), win);
792     gtk_action_group_set_sensitive(ActionGroup, TRUE);
793 
794     gtk_ui_manager_insert_action_group(UIManager, ActionGroup, 0);
795     if (!gtk_ui_manager_add_ui_from_string(UIManager, MenuDescription, -1, &error))
796     {
797         g_warning("building menus failed: %s", error->message);
798         g_error_free(error);
799     }
800 }
801 
802 static gboolean on_redraw(tree_info_t* item)
803 {
804     gui_tree_display(item, FALSE, FALSE);
805     if (RedrawTimer) g_source_remove(RedrawTimer);
806     RedrawTimer = 0;
807     return TRUE;
808 }
809 
810 static void gui_tree_redraw(void)
811 {
812     LUseColors = pref_get_use_colors();
813     LMaxDepth = pref_get_max_depth();
814     LDisplayMode = pref_get_display_mode();
815     LUseAverage = pref_get_use_average();
816 
817     if (CurrentItem)
818     {
819         if (RedrawTimer) g_source_remove(RedrawTimer);
820         RedrawTimer = g_timeout_add(300, (GSourceFunc)on_redraw, CurrentItem);
821     }
822 }
823 
824 static GdkPixbuf* create_pixbuf(const gchar *filename)
825 {
826     char* temp;
827     GdkPixbuf* pix;
828 
829 #ifdef PACKAGE_SOURCE_DIR
830     temp = g_strdup_printf("%s/data/%s", PACKAGE_SOURCE_DIR, filename);
831 #else
832     temp = g_strdup_printf("%s/pixmaps/%s", PACKAGE_DATA_DIR, filename);
833 #endif
834     pix = gdk_pixbuf_new_from_file(temp, NULL);
835     g_free(temp);
836 
837     return pix;
838 }
839 
840 GtkWidget* gui_create_main_win(void)
841 {
842     GtkWidget* win;
843     GtkWidget* vbox;
844     GtkWidget* item;
845 
846     win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
847     MainWin = win;
848     gtk_window_set_title(GTK_WINDOW(win), "Graphical Disk Map "VERSION);
849     gtk_window_set_role(GTK_WINDOW(win), "Main window");
850     gtk_window_set_default_icon(create_pixbuf("gdmap_icon.png"));
851     gtk_window_set_default_size(GTK_WINDOW(win), 700, 450);
852     g_object_add_weak_pointer(G_OBJECT(win), (void*)&MainWin);
853 
854 
855     vbox = gtk_vbox_new(FALSE, 0);
856     gtk_widget_show(vbox);
857     gtk_container_add(GTK_CONTAINER(win), vbox);
858 /*   gtk_container_set_border_width(GTK_CONTAINER (vbox), 0); */
859 
860 
861     gui_setup_actions(win);
862 
863     if ((item = gtk_ui_manager_get_widget(UIManager, "/ui/MainMenu")) != NULL)
864         gtk_box_pack_start(GTK_BOX (vbox), item, FALSE, FALSE, 0);
865 
866     if ((item = gtk_ui_manager_get_widget(UIManager, "/ui/ToolBar")) != NULL)
867         gtk_box_pack_start(GTK_BOX (vbox), item, FALSE, FALSE, 0);
868 
869     if ((item = gui_create_path_box()) != NULL)
870         gtk_box_pack_start(GTK_BOX (vbox), item, FALSE, FALSE, 0);
871 
872     if ((item = gui_create_display_box()) != NULL)
873         gtk_box_pack_start(GTK_BOX (vbox), item, TRUE, TRUE, 0);
874 
875     if ((item = gui_create_status_bar()) != NULL)
876         gtk_box_pack_start(GTK_BOX (vbox), item, FALSE, FALSE, 0);
877 
878     g_signal_connect(G_OBJECT (win), "delete_event",
879                      G_CALLBACK(on_main_win_delete_event), NULL);
880 
881     gui_buttons_update();
882 
883     gtk_widget_show(win);
884 
885     pref_set_redraw_callback(gui_tree_redraw);
886 
887     return win;
888 }
889 
890 
891 
892 
893 
894 GtkWidget* gui_get_main_win()
895 {
896     return MainWin;
897 }
898 
899 static void _get_average_color_rec(tree_t* tree, int* r, int* g, int* b)
900 {
901 
902     if (tree->entries)
903     {
904         GList* dlist;
905         double dr=0, dg=0, db=0;
906 
907         for (dlist = tree->entries; dlist; dlist = dlist->next)
908         {
909             tree_t* sub = dlist->data;
910             int lr=0, lg=0, lb=0;
911             _get_average_color_rec(sub, &lr, &lg, &lb);
912             dr += lr/(double)tree->size*(double)sub->size;
913             dg += lg/(double)tree->size*(double)sub->size;
914             db += lb/(double)tree->size*(double)sub->size;
915         }
916         *r += dr;
917         *g += dg;
918         *b += db;
919     }
920     else
921     {
922         const GdkColor* color = color_get_by_file(tree->name);
923         *r += color->red;
924         *g += color->green;
925         *b += color->blue;
926     }
927 }
928 
929 static const GdkColor* _get_average_color(tree_t* tree)
930 {
931     static GdkColor res;
932     int r=0, g=0, b=0;
933 
934     _get_average_color_rec(tree, &r, &g, &b);
935     res.red = r;
936     res.green = g;
937     res.blue = b;
938     return &res;
939 }
940 
941 static void gui_tree_show_item(tree_info_t* info)
942 {
943     static int Ia = 40;
944     static int Is = 215;
945     static double Lx = 0.09759;
946     static double Ly = 0.19518;
947     static double Lz = 0.9759;
948     int ix, iy;
949     double nx, ny, cosa, val;
950     long pos;
951     const GdkColor* color;
952 
953     int x1 = info->geo[0][0];
954     int y1 = info->geo[1][0];
955     int x2 = info->geo[0][1];
956     int y2 = info->geo[1][1];
957 
958     if (x1 >= x2) return;
959     if (y1 >= y2) return;
960 
961     if (LUseColors)
962     {
963         if (info->node->entries)
964         {
965             if (LUseAverage) color = _get_average_color(info->node);
966             else color = color_get_by_type(COLOR_FOLDER);
967         }
968         else
969         {
970             color = color_get_by_file(info->node->name);
971         }
972     }
973     else
974     {
975         color = color_get_by_type(COLOR_DEFAULT);
976     }
977 
978     pos = (y1*Width+x1)*3;
979     for (iy = y1; iy < y2; iy++)
980     {
981         for (ix = x1; ix < x2; ix++)
982         {
983             nx = -(2*info->s[0][1]*(ix+0.5)+info->s[0][0]);
984             ny = -(2*info->s[1][1]*(iy+0.5)+info->s[1][0]);
985             cosa = (nx*Lx + ny*Ly + Lz)/sqrt(nx*nx + ny*ny +1.0);
986             val = (Ia+MAX(0, Is*cosa));
987             Buffer[pos++] = (int)(color->red   * val/65535.);
988             Buffer[pos++] = (int)(color->green * val/65535.);
989             Buffer[pos++] = (int)(color->blue  * val/65535.);
990         }
991         pos -= (x2-x1)*3;  // cr
992         pos += Width*3;    // lf
993     }
994 }
995 
996 void AddRidge(double x1, double x2, double h, double* s1, double* s2)
997 {
998     *s1 += 4*h*(x2+x1)/(x2-x1);
999     *s2 -= 4*h/(x2-x1);
1000 }
1001 
1002 
1003 /* void AddRidge(tree_info_t* node, int geo[2][2], int d, double h) { */
1004 /*   int x1 = geo[d][0]; */
1005 /*   int x2 = geo[d][1]; */
1006 
1007 /*   node->s[d][0] += 4*h*(x2+x1)/(x2-x1); */
1008 /*   node->s[d][1] -= 4*h/(x2-x1); */
1009 /* } */
1010 
1011 static void gui_tree_evaluate_childs1(tree_info_t* info, double h, double f)
1012 {
1013     tree_t* tree = info->node;
1014     int d = (tree->depth % 2);
1015     int pos = info->geo[d][0];
1016     int width = (info->geo[d][1] - info->geo[d][0]);
1017     gint64 size = 0;
1018     GList* dlist;
1019 
1020 /*   g_message("%d evaluate '%s' with %u children", tree->depth, tree->name, */
1021 /*             g_list_length(tree->entries)); */
1022 /*   printf("  s: [%.1f,%.1f][%.1f,%.1f]\n", */
1023 /*          info->s[0][0],info->s[0][1],info->s[1][0],info->s[1][1]); */
1024 
1025     for (dlist = info->children; dlist; dlist = dlist->next)
1026     {
1027         tree_info_t* sinfo = dlist->data;
1028         tree_t* sub = sinfo->node;
1029         int end_pos;
1030 
1031         sinfo->geo[0][0] = sinfo->geo[0][1] = 0;
1032         sinfo->geo[1][0] = sinfo->geo[1][1] = 0;
1033 
1034         if (sub->size <= 0) continue;
1035 
1036         size += sub->size;
1037         end_pos = info->geo[d][0] + size*width/tree->size;
1038 
1039         if (end_pos > pos+1 || (!dlist->next && end_pos > pos))
1040         {
1041             sinfo->geo[d][0] = pos;
1042             sinfo->geo[d][1] = end_pos;
1043             sinfo->geo[1-d][0] = info->geo[1-d][0];
1044             sinfo->geo[1-d][1] = info->geo[1-d][1];
1045 
1046             memcpy(sinfo->s, info->s, sizeof(sinfo->s));
1047             AddRidge(sinfo->geo[d][0], sinfo->geo[d][1], h,
1048                      &sinfo->s[d][0], &sinfo->s[d][1]);
1049 
1050             pos = end_pos;
1051             if (sub->entries) gui_tree_evaluate_childs1(sinfo, h*f, f);
1052         }
1053     }
1054 
1055 }
1056 
1057 static gint64
1058 _find_next_size(GList* children, gint64 rsize, int width, int height)
1059 {
1060     GList* dlist;
1061     gint64 size = 0;
1062     double best_ratio = 0.0;
1063     gint64 best_size = 0;
1064 
1065     g_assert(width >= height);
1066 
1067 /*   g_message("   have %ld in (%d,%d) (%u children)", rsize, width, height, */
1068 /*             g_list_length(children)); */
1069 
1070     for (dlist = children; dlist; dlist = dlist->next)
1071     {
1072         tree_info_t* sinfo = dlist->data;
1073         tree_t* sub = sinfo->node;
1074         int dx, dy;
1075         double ratio;
1076 
1077         size += sub->size;
1078         if (size == 0) continue;
1079 
1080         // get the width of the col
1081         dx = (gint64)width * size / rsize;
1082         // get the height of the last item.
1083         dy = (gint64)height * sub->size / size;
1084         // calculate the aspect ratio
1085 /*     g_message("   -size is %ld (%d %d)", sub->size, dx, dy); */
1086         if (dx == 0 && dy == 0) continue;
1087 
1088 
1089         if (dx < dy) ratio = (double)dx/(double)dy;
1090         else ratio = (double)dy/(double)dx;
1091 
1092 /*     g_message("   -ratio (%d,%d) %.1f (size %ld)", dx, dy, ratio, size); */
1093 
1094         if (ratio >= best_ratio)
1095         {
1096             best_ratio = ratio;
1097             best_size = size;
1098         }
1099         else
1100         {
1101             break;
1102         }
1103     }
1104     return best_size;
1105 }
1106 
1107 static void gui_tree_evaluate_childs2(tree_info_t* info, double h, double f)
1108 {
1109     tree_t* tree = info->node;
1110     GList* children = info->children;
1111     gint64 rsize, size;
1112     int d;
1113     int geo[2][2];
1114     double s[2][2];
1115 
1116     memcpy(geo, info->geo, sizeof(geo));
1117     rsize = tree->size;
1118 
1119     while (children)
1120     {
1121         int width, height;
1122         int dx, pos;
1123         gint64 ssize;
1124 
1125         if (geo[0][1]-geo[0][0] < geo[1][1]-geo[1][0]) d = 1;
1126         else d = 0;
1127 
1128         width = geo[d][1]-geo[d][0];
1129         height = geo[1-d][1]-geo[1-d][0];
1130 
1131         size = _find_next_size(children, rsize, width, height);
1132 
1133         if (rsize) dx = (gint64)width * size / rsize;
1134         else dx = 0;
1135 
1136         ssize = 0;
1137         pos = geo[1-d][0];
1138 
1139         memcpy(s, info->s, sizeof(s));
1140         AddRidge(geo[d][0], geo[d][0]+dx, h, &s[d][0], &s[d][1]);
1141 
1142         for ( ; children; children = children->next)
1143         {
1144             tree_info_t* sinfo = children->data;
1145             tree_t* sub = sinfo->node;
1146             int end_pos;
1147 
1148             sinfo->geo[0][0] = sinfo->geo[0][1] = 0;
1149             sinfo->geo[1][0] = sinfo->geo[1][1] = 0;
1150 
1151             if (sub->size <= 0) continue;
1152             ssize += sub->size;
1153             if (ssize > size) break;
1154 
1155             end_pos = geo[1-d][0] + ssize*height/size;
1156 
1157             if (end_pos > pos+1 || (ssize == size && end_pos > pos))
1158             {
1159                 sinfo->geo[1-d][0] = pos;
1160                 sinfo->geo[1-d][1] = end_pos;
1161                 sinfo->geo[d][0] = geo[d][0];
1162                 sinfo->geo[d][1] = geo[d][0]+dx;
1163 
1164                 memcpy(sinfo->s, s, sizeof(sinfo->s));
1165                 AddRidge(sinfo->geo[1-d][0], sinfo->geo[1-d][1], h,
1166                          &sinfo->s[1-d][0], &sinfo->s[1-d][1]);
1167 
1168                 pos = end_pos;
1169                 if (sub->entries) gui_tree_evaluate_childs2(sinfo, h*f, f);
1170             }
1171         }
1172         geo[d][0] += dx;
1173         rsize -= size;
1174     }
1175 }
1176 
1177 
1178 static tree_info_t* gui_tree_evaluate(tree_info_t* info)
1179 {
1180     int Border = 1;
1181 
1182     info->geo[0][0] = Border;
1183     info->geo[0][1] = Width-Border;
1184     info->geo[1][0] = Border;
1185     info->geo[1][1] = Height-Border;
1186     info->s[0][0] = .0;
1187     info->s[0][1] = .0;
1188     info->s[1][0] = .0;
1189     info->s[1][1] = .0;
1190 
1191     if (LDisplayMode == DISPLAY_STANDARD_CUSHION)
1192     {
1193         gui_tree_evaluate_childs1(info, pref_get_h(), pref_get_f());
1194     }
1195     else
1196     {
1197         gui_tree_evaluate_childs2(info, pref_get_h(), pref_get_f());
1198     }
1199 
1200     return info;
1201 }
1202 
1203 static void gui_tree_show(tree_info_t* info)
1204 {
1205     unsigned depth = LMaxDepth;
1206 
1207     if (info->geo[0][0] >= info->geo[0][1]) return;
1208     if (info->geo[1][0] >= info->geo[1][1]) return;
1209 
1210     if (info->children && (depth == 0 || info->node->depth < depth))
1211     {
1212         GList* dlist;
1213         for (dlist = info->children; dlist; dlist = dlist->next)
1214         {
1215             tree_info_t* sub = dlist->data;
1216             gui_tree_show(sub);
1217         }
1218     }
1219     else
1220     {
1221         gui_tree_show_item(info);
1222     }
1223 }
1224 
1225 static void on_path_toggled(GtkToggleButton* button, tree_info_t* info)
1226 {
1227     if (gtk_toggle_button_get_active(button))
1228     {
1229         gui_tree_display(info, FALSE, TRUE/*FALSE*/);
1230     }
1231 }
1232 
1233 static void gui_paths_update()
1234 {
1235     static GList* Paths = NULL;
1236     static GSList* Group = NULL;
1237     GtkWidget* button;
1238     GList* dlist;
1239     GList* temp;
1240     tree_info_t* dtemp;
1241     tree_info_t* item;
1242 
1243     if (!CurrentItem) return;
1244 
1245     for (item = CurrentItem; item; item = item->parent)
1246     {
1247 
1248         for (dlist = Paths; dlist; dlist = dlist->next)
1249         {
1250             GtkWidget* button = dlist->data;
1251             tree_info_t* info = g_object_get_data(G_OBJECT(button), "info");
1252 
1253             if (info == item)
1254             {
1255                 if (item == CurrentItem)
1256                     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
1257                 break;
1258             }
1259         }
1260         if (dlist) break;
1261     }
1262 
1263     if (dlist)
1264     {
1265         // only delete if we have to add a new end path.
1266         if (item != CurrentItem) temp = dlist->next;
1267         else temp = NULL;
1268 /*     temp = dlist->next; */
1269     }
1270     else
1271     {
1272         // delete all
1273         temp = Paths;
1274     }
1275 
1276     for (dlist = temp; dlist; )
1277     {
1278         GtkWidget* button = dlist->data;
1279         dlist = dlist->next;
1280         Paths = g_list_remove(Paths, button);
1281         gtk_widget_destroy(button);
1282     }
1283 
1284     // update radio button group
1285     if (Paths)
1286         Group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(Paths->data));
1287     else
1288         Group = NULL;
1289 
1290     temp = NULL;
1291     dtemp = CurrentItem;
1292     while (dtemp != item)
1293     {
1294         temp = g_list_prepend(temp, dtemp);
1295         dtemp = dtemp->parent;
1296     }
1297 
1298     for (dlist = temp; dlist; dlist = dlist->next)
1299     {
1300         tree_info_t* info = dlist->data;
1301         button = gtk_radio_button_new_with_label(Group, info->node->name);
1302         Group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
1303         gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(button), FALSE);
1304         g_object_set_data(G_OBJECT(button), "info", info);
1305         gtk_widget_show(button);
1306         gtk_box_pack_start(PathBox, button, FALSE, FALSE, 0);
1307         Paths = g_list_append(Paths, button);
1308         if (info == CurrentItem)
1309             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
1310         g_signal_connect(G_OBJECT(button), "toggled",
1311                          G_CALLBACK(on_path_toggled), info);
1312     }
1313 }
1314 
1315 static void gui_tree_display(tree_info_t* info, gboolean destroy_old, gboolean new_history)
1316 {
1317     if (destroy_old)
1318     {
1319         if (CurrentItem)
1320         {
1321             while (CurrentItem->parent) CurrentItem = CurrentItem->parent;
1322             tree_destroy(CurrentItem->node);
1323             tree_info_destroy(CurrentItem);
1324         }
1325         CurrentItem = NULL;
1326         history_clear();
1327     }
1328 
1329 
1330     if (CurrentItem != info)
1331     {
1332         if (new_history) history_append(info);
1333         CurrentItem = info;
1334         gui_paths_update();
1335     }
1336 
1337     gui_tree_evaluate(info);
1338     gui_tree_show(info);
1339     gui_show_pix(Area);
1340 
1341     gui_buttons_update();
1342 
1343     {
1344         char* temp1 = print_filesize(info->node->size);
1345         char* temp2 = g_strdup_printf("<b>%s</b>", temp1);
1346         gtk_label_set_markup(SizeLabel, temp2);
1347         g_free(temp1);
1348         g_free(temp2);
1349     }
1350 
1351 }
1352 
1353