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