Commit bc96ac7c authored by Petr Štětka's avatar Petr Štětka
Browse files

Add Storage Graph

Improve generating colors of files.
parent e485ff7c
......@@ -111,35 +111,30 @@ PieChart.available, ColorRectangle.available {
color: #232729;
}
ColorRectangle.system {
ColorRectangle.system, StorageGraph.system {
color: #3c4447;
}
ColorRectangle.applications {
ColorRectangle.applications, StorageGraph.applications {
color: #4e9a06;
}
ColorRectangle.trash {
ColorRectangle.trash, StorageGraph.trash {
color: #ef2929;
}
ColorRectangle.user {
ColorRectangle.user, StorageGraph.user {
color: #ad7fa8;
}
ColorRectangle.available-storage {
ColorRectangle.available-storage, StorageGraph.available-storage {
color: #d3d7cf;
}
row.folders {
row.folders, list.folders {
color: #457fd3;
}
list.folders {
color: #457fd3;
}
box.storage
{
box.storage {
background-color: #232729
}
\ No newline at end of file
......@@ -117,35 +117,30 @@ PieChart.available, ColorRectangle.available {
color: #fafafa;
}
ColorRectangle.system {
ColorRectangle.system, StorageGraph.system {
color: #2e3436;
}
ColorRectangle.applications {
ColorRectangle.applications, StorageGraph.applications {
color: #4e9a06;
}
ColorRectangle.trash {
ColorRectangle.trash, StorageGraph.trash {
color: #ef2929;
}
ColorRectangle.user {
ColorRectangle.user, StorageGraph.user {
color: #ad7fa8;
}
ColorRectangle.available-storage {
ColorRectangle.available-storage, StorageGraph.available-storage {
color: #d3d7cf;
}
row.folders {
row.folders, list.folders {
color: #457fd3;
}
list.folders {
color: #457fd3;
}
box.storage
{
box.storage {
background-color: #ffffff
}
\ No newline at end of file
......@@ -13,6 +13,8 @@ namespace Usage
private Gtk.StackSwitcher stack_switcher;
private Gtk.Button? storage_back_button;
private bool show_storage_back_btn = false;
private bool show_storage_rescan_btn = false;
private string title_text = "";
private Gtk.Button? storage_rescan_button;
private HeaderBarMode mode;
......@@ -55,18 +57,26 @@ namespace Usage
show_stack_switcher();
break;
case HeaderBarMode.STORAGE:
show_stack_switcher();
if(title_text == "")
show_stack_switcher();
else
show_title();
storage_back_button = new Gtk.Button.from_icon_name("go-previous-symbolic");
storage_back_button.clicked.connect(() => {
((StorageView) (GLib.Application.get_default() as Application).get_window().get_views()[2]).get_storage_list_box().on_back_button_clicked();
});
show_storage_back_button(show_storage_back_btn);
storage_rescan_button = new Gtk.Button.from_icon_name("view-refresh-symbolic");
storage_rescan_button.clicked.connect(() => {
show_stack_switcher();
show_storage_rescan_button(false);
show_storage_back_button(false);
(GLib.Application.get_default() as Application).get_storage_analyzer().create_cache.begin(true);
((StorageView) (GLib.Application.get_default() as Application).get_window().get_views()[2]).get_storage_list_box().reload();
});
storage_rescan_button.show();
show_storage_rescan_button(show_storage_rescan_btn);
pack_start(storage_back_button);
pack_end(storage_rescan_button);
break;
......@@ -77,12 +87,22 @@ namespace Usage
this.mode = mode;
}
public void show_title_text(string title)
public HeaderBarMode get_mode()
{
return mode;
}
public void show_title()
{
set_custom_title(null);
set_title(title);
set_title(title_text);
}
public void set_title_text(string title)
{
this.title_text = title;
}
public void show_stack_switcher()
{
set_custom_title(stack_switcher);
......@@ -90,20 +110,34 @@ namespace Usage
public void show_storage_back_button(bool show)
{
if(storage_back_button != null)
if(show)
{
if(show)
{
if(storage_back_button != null)
storage_back_button.show();
show_storage_back_btn = true;
}
else
{
show_storage_back_btn = true;
}
else
{
if(storage_back_button != null)
storage_back_button.hide();
show_storage_back_btn = false;
}
show_storage_back_btn = false;
}
}
public void show_storage_rescan_button(bool show)
{
if(show)
{
if(storage_rescan_button != null)
storage_rescan_button.show();
show_storage_rescan_btn = true;
}
else
{
if(storage_rescan_button != null)
storage_rescan_button.hide();
show_storage_rescan_btn = false;
}
}
}
}
\ No newline at end of file
......@@ -6,14 +6,15 @@ namespace Usage
{
public signal void cache_complete();
public bool cache { private set; get; }
public bool separate_home = false;
private bool separate_home = false;
private Cancellable cancellable;
private HashTable<string, Directory?> directory_table;
private HashTable<string, Storage?> storages;
private AsyncQueue<StorageResult> results_queue;
private string[] exclude_from_home;
private static bool path_null;
private bool can_scan = true;
private struct Directory
{
......@@ -45,6 +46,11 @@ namespace Usage
cancellable.cancel();
}
public bool get_separate_home()
{
return separate_home;
}
public async List<StorageItem> prepare_items(string? path, Gdk.RGBA color)
{
if(path == null)
......@@ -144,7 +150,14 @@ namespace Usage
private void set_colors(ref List<StorageItem> items, Gdk.RGBA default_color)
{
for(int i = 0; i < items.length(); i++)
uint showed_items_length = 1;
foreach(StorageItem item in items)
{
if(item.get_percentage() > StorageGraph.MIN_PERCENTAGE_SHOWN_FILES)
showed_items_length++;
}
for(uint i = 0; i < items.length(); i++)
{
unowned StorageItem item = items.nth_data(i);
switch(item.get_item_type())
......@@ -155,18 +168,22 @@ namespace Usage
case StorageItemType.MUSIC:
case StorageItemType.PICTURES:
case StorageItemType.VIDEOS:
item.set_color(generate_color(default_color, i-2, 6, false)); //6 because DOCUMENTS, DOWNLOADS, DESKTOP, MUSIC, PICTURES, VIDEOS, 2 because header and Home
//6 because DOCUMENTS, DOWNLOADS, DESKTOP, MUSIC, PICTURES, VIDEOS, 2 because header and Home
item.set_color(generate_color(default_color, i-2, 6, false));
break;
case StorageItemType.DIRECTORY:
case StorageItemType.FILE:
item.set_color(generate_color(default_color, i, items.length(), true));
item.set_color(generate_color(default_color, i, showed_items_length, true));
break;
}
}
}
private Gdk.RGBA generate_color(Gdk.RGBA default_color, int order, uint all_color, bool reverse = false)
private Gdk.RGBA generate_color(Gdk.RGBA default_color, uint order, uint all_color, bool reverse = false)
{
if(order >= all_color)
order = all_color - 1;
order += 1;
double step = 100 / all_color;
......@@ -200,12 +217,15 @@ namespace Usage
public async void create_cache(bool force_override = false)
{
if(force_override == true)
cache = false;
bool scan = false;
if(force_override == true || cache == false)
scan = true;
if(cache == false)
if(scan && can_scan)
{
cache = true;
cache = false;
can_scan = false;
analyze_storages();
stop_scanning();
cancellable.reset();
......@@ -221,8 +241,11 @@ namespace Usage
var thread = new Thread<void*>("storage_analyzer", run);
yield;
thread.join();
cache = true;
cache_complete();
}
can_scan = true;
}
private void scan_cache()
......@@ -279,7 +302,7 @@ namespace Usage
private void add_root_items(ref List<StorageItem> items, Storage storage, int section)
{
items.insert_sorted(new StorageItem.system(_("Operation System"), storage.used, ((float) storage.free / storage.total) * 100, section), (CompareFunc) sort);
items.insert_sorted(new StorageItem.system(_("Operating System"), storage.used, ((float) storage.used / storage.total) * 100, section), (CompareFunc) sort);
}
private void add_home_items(ref List<StorageItem> items, int section)
......
......@@ -4,9 +4,225 @@ namespace Usage
{
public class StorageGraph : Gtk.DrawingArea
{
public const uint MIN_PERCENTAGE_SHOWN_FILES = 2;
class construct
{
set_css_name("StorageGraph");
}
public StorageGraph()
{
this.draw.connect(draw_storage_graph);
}
public enum Circle
{
HOME,
ROOT,
BASE
}
private void draw_circle(Cairo.Context context, GLib.ListStore model, double x, double y, double radius, int section, Circle circle)
{
double start_angle = 0;
double final_angle = - Math.PI / 2.0;
double ratio = 0;
var fill_color = Gdk.RGBA();
var background_color = get_toplevel().get_style_context().get_background_color(get_toplevel().get_style_context().get_state());
for(int i = 0; i < model.get_n_items(); i++)
{
StorageItem item = (StorageItem) model.get_item(i);
if(item.get_percentage() > 0 && item.get_item_type() != StorageItemType.STORAGE && item.get_section() == section)
{
var style_context = get_style_context();
switch(item.get_item_type())
{
case StorageItemType.SYSTEM:
style_context.add_class("system");
fill_color = style_context.get_color(style_context.get_state());
style_context.remove_class("system");
break;
case StorageItemType.TRASH:
style_context.add_class("trash");
fill_color = style_context.get_color(style_context.get_state());
style_context.remove_class("trash");
break;
case StorageItemType.USER:
style_context.add_class("user");
fill_color = style_context.get_color(style_context.get_state());
style_context.remove_class("user");
break;
case StorageItemType.AVAILABLE:
style_context.add_class("available-storage");
fill_color = style_context.get_color(style_context.get_state());
style_context.remove_class("available-storage");
break;
case StorageItemType.DOCUMENTS:
case StorageItemType.DOWNLOADS:
case StorageItemType.DESKTOP:
case StorageItemType.MUSIC:
case StorageItemType.PICTURES:
case StorageItemType.VIDEOS:
case StorageItemType.DIRECTORY:
case StorageItemType.FILE:
fill_color = item.get_color();
break;
}
context.set_line_width (2.0);
start_angle = final_angle;
ratio = ratio + ((double) item.get_percentage() / 100);
final_angle = ratio * 2 * Math.PI - Math.PI / 2.0;
context.move_to (x, y);
Gdk.cairo_set_source_rgba (context, fill_color);
context.arc (x, y, radius, start_angle, final_angle);
if(item.get_percentage() == 100)
context.fill();
else
context.fill_preserve();
Gdk.cairo_set_source_rgba (context, background_color);
context.stroke();
if(item.get_percentage() > MIN_PERCENTAGE_SHOWN_FILES)
{
double midle_angle = start_angle + (final_angle - start_angle) / 2;
midle_angle += Math.PI / 2;
double distance = radius + radius/10;
double x_text = 0;
double y_text = 0;
CornerType quadrant = 0;
if(midle_angle <= Math.PI / 2)
{
x_text = Math.sin(midle_angle) * distance;
x_text += x;
y_text = Math.cos(midle_angle) * distance;
y_text = y - y_text;
quadrant = CornerType.TOP_RIGHT;
}
else if (midle_angle <= Math.PI)
{
midle_angle = Math.PI - midle_angle;
x_text = Math.sin(midle_angle) * distance;
x_text += x;
y_text = Math.cos(midle_angle) * distance;
y_text += y;
quadrant = CornerType.BOTTOM_RIGHT;
}
else if (midle_angle <= Math.PI + Math.PI / 2)
{
midle_angle = midle_angle - Math.PI;
x_text = Math.sin(midle_angle) * distance;
x_text = x - x_text;
y_text = Math.cos(midle_angle) * distance;
y_text += y;
quadrant = CornerType.BOTTOM_LEFT;
}
else
{
midle_angle = Math.PI * 2 - midle_angle;
x_text = Math.sin(midle_angle) * distance;
x_text = x - x_text;
y_text = Math.cos(midle_angle) * distance;
y_text = y - y_text;
quadrant = CornerType.TOP_LEFT;
}
double space = 0;
const int SIDE_MARGIN = 5;
switch(circle)
{
case Circle.HOME:
case Circle.ROOT:
space = get_allocated_width ();
space -= x + distance;
break;
case Circle.BASE:
space = get_allocated_width () / 2;
space -= radius + (distance-radius);
break;
}
space -= SIDE_MARGIN;
x_text += SIDE_MARGIN;
draw_text(context, item.get_name(), x_text, y_text, quadrant, space);
}
}
}
}
private void draw_text(Cairo.Context context, string text, double x, double y, CornerType start_corner, double width)
{
var layout = create_pango_layout (null);
var markup = "<span font='10'>" + text + "</span>";
layout.set_markup (markup, -1);
layout.set_width ((int) (Pango.SCALE * width));
layout.set_ellipsize (Pango.EllipsizeMode.END);
Pango.Rectangle layout_rect;
layout.get_pixel_extents (null, out layout_rect);
switch(start_corner)
{
case CornerType.TOP_RIGHT:
y -= layout_rect.height;
break;
case CornerType.BOTTOM_LEFT:
x -= layout_rect.width;
break;
case CornerType.TOP_LEFT:
x -= layout_rect.width;
y -= layout_rect.height;
break;
}
get_style_context().render_layout (context, x, y, layout);
}
private bool draw_storage_graph(Cairo.Context context)
{
int height = this.get_allocated_height ();
int width = this.get_allocated_width ();
var storage_list_box = ((StorageView) (GLib.Application.get_default() as Application).get_window().get_views()[2]).get_storage_list_box();
var model = storage_list_box.get_model();
var two_graphs = false;
if(storage_list_box.get_root() && (GLib.Application.get_default() as Application).get_storage_analyzer().get_separate_home())
two_graphs = true;
double x = 0;
double y = 0;
double radius = 0;
if(two_graphs)
{
double border = 5.5;
radius = int.min (width, height) / 3.5;
x = width / 1.8;
x -= x / border;
y = height / 2.1;
y -= y / border;
draw_circle(context, model, x, y, radius, 0, Circle.HOME);
border = 1.75;
radius = int.min (width, height) / 11.0;
x = width / 2.0;
x += x / border;
y = height / 1.9;
y += y / border;
draw_circle(context, model, x, y, radius, 1, Circle.ROOT);
}
else
{
radius = int.min (width, height) / 2.0;
radius -= radius / 3;
x = width / 2.0;
y = height / 2.0;
draw_circle(context, model, x, y, radius, 0, Circle.BASE);
}
return true;
}
}
}
......@@ -4,6 +4,7 @@ namespace Usage
{
public signal void loading();
public signal void loaded();
public signal void empty();
private List<string?> path_history;
private List<string?> name_history;
......@@ -29,9 +30,23 @@ namespace Usage
actual_path = null;
actual_name = null;
storage_analyzer = (GLib.Application.get_default() as Application).get_storage_analyzer();
get_style_context().add_class("folders");
color = get_style_context().get_color(get_style_context().get_state());
get_style_context().remove_class("folders");
reload();
}
public ListStore get_model()
{
return model;
}
public bool get_root()
{
return root;
}
public void on_back_button_clicked()
{
unowned List<string>? path = path_history.last();
......@@ -44,28 +59,43 @@ namespace Usage
if(root)
{
(GLib.Application.get_default() as Application).get_window().get_header_bar().show_storage_back_button(false);
(GLib.Application.get_default() as Application).get_window().get_header_bar().set_title_text("");
(GLib.Application.get_default() as Application).get_window().get_header_bar().show_stack_switcher();
}
else
(GLib.Application.get_default() as Application).get_window().get_header_bar().show_title_text(actual_name);
{
(GLib.Application.get_default() as Application).get_window().get_header_bar().set_title_text(actual_name);
(GLib.Application.get_default() as Application).get_window().get_header_bar().show_title();
}
}
public void reload()
{
get_style_context().add_class("folders");
color = get_style_context().get_color(get_style_context().get_state());
get_style_context().remove_class("folders");
this.hide();
loading();
root = true;
storage_analyzer.cache_complete.connect(() => {
storage_analyzer.prepare_items.begin(actual_path, color, (obj, res) => {
var header_bar = (GLib.Application.get_default() as Application).get_window().get_header_bar();
if(root == false)
{
header_bar.show_storage_back_button(true);
if(header_bar.get_mode() == HeaderBarMode.STORAGE)
{
header_bar.set_title_text(actual_name);
header_bar.show_title();
}
}
header_bar.show_storage_rescan_button(true);
loaded();
this.show();
model.remove_all();
foreach(unowned StorageItem item in storage_analyzer.prepare_items.end(res))
model.append(item);
if(model.get_n_items() == 0)
empty();
});
});
}
......@@ -91,6 +121,9 @@ namespace Usage
foreach(unowned StorageItem item in storage_analyzer.prepare_items.end(res))
model.append(item);
if(model.get_n_items() == 0)
empty();
});