/*
* ExcludeBox.vala
*
* Copyright 2012-2018 Tony George <teejeetech@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
using Gtk;
using Gee;
using TeeJee.Logging;
using TeeJee.FileSystem;
using TeeJee.JsonHelper;
using TeeJee.ProcessHelper;
using TeeJee.GtkHelper;
using TeeJee.System;
using TeeJee.Misc;
class ExcludeBox : Gtk.Box{
private Gtk.TreeView treeview;
private Gtk.Window parent_window;
private UsersBox users_box;
private Gtk.Label lbl_message;
public ExcludeBox (Gtk.Window _parent_window) {
log_debug("ExcludeBox: ExcludeBox()");
//base(Gtk.Orientation.VERTICAL, 6); // issue with vala
GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around
parent_window = _parent_window;
margin = 12;
var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
add(box);
add_label_header(box, _("Include / Exclude Patterns"), true);
var buffer = add_label(box, "");
buffer.hexpand = true;
var label = add_label(box, _("Click to edit. Drag-drop to re-order."), false, true);
lbl_message = label;
init_treeview();
init_actions();
refresh_treeview();
log_debug("ExcludeBox: ExcludeBox(): exit");
}
public void set_users_box(UsersBox _users_box){
users_box = _users_box;
}
private void init_treeview(){
// treeview
treeview = new TreeView();
treeview.get_selection().mode = SelectionMode.MULTIPLE;
treeview.headers_visible = true;
//treeview.rules_hint = true;
treeview.reorderable = true;
treeview.set_tooltip_text(_("Click to edit. Drag and drop to re-order."));
//treeview.row_activated.connect(treeview_row_activated);
// scrolled
var scrolled = new ScrolledWindow(null, null);
scrolled.set_shadow_type (ShadowType.ETCHED_IN);
scrolled.add (treeview);
scrolled.expand = true;
add(scrolled);
// column
var col = new TreeViewColumn();
col.title = "+";
treeview.append_column(col);
// radio_include
var cell_radio = new Gtk.CellRendererToggle();
cell_radio.xpad = 2;
cell_radio.radio = true;
cell_radio.activatable = true;
col.pack_start (cell_radio, false);
col.set_attributes(cell_radio, "active", 2);
cell_radio.toggled.connect((cell, path)=>{
log_debug("cell_include.toggled()");
var model = (Gtk.ListStore) treeview.model;
string pattern;
TreeIter iter;
model.get_iter_from_string (out iter, path);
model.get (iter, 0, out pattern);
if (!pattern.has_prefix("+ ")){
pattern = "+ %s".printf(pattern);
}
treeview_update_item(ref iter, pattern);
save_changes();
});
// column
col = new TreeViewColumn();
col.title = "-";
treeview.append_column(col);
// radio_exclude
cell_radio = new Gtk.CellRendererToggle();
cell_radio.xpad = 2;
cell_radio.radio = true;
cell_radio.activatable = true;
col.pack_start (cell_radio, false);
col.set_attributes(cell_radio, "active", 3);
cell_radio.toggled.connect((cell, path)=>{
log_debug("cell_exclude.toggled()");
var model = (Gtk.ListStore) treeview.model;
string pattern;
TreeIter iter;
model.get_iter_from_string (out iter, path);
model.get (iter, 0, out pattern);
//bool exclude = true;
if (pattern.has_prefix("+ ")){
pattern = pattern[2:pattern.length];
}
treeview_update_item(ref iter, pattern);
save_changes();
});
// column
col = new TreeViewColumn();
col.title = _("Pattern");
treeview.append_column(col);
// margin
var cell_text = new CellRendererText ();
cell_text.text = "";
col.pack_start (cell_text, false);
// icon
var cell_icon = new CellRendererPixbuf ();
col.pack_start (cell_icon, false);
col.set_attributes(cell_icon, "icon-name", 1);
// pattern
cell_text = new CellRendererText ();
col.pack_start (cell_text, false);
col.set_cell_data_func (cell_text, (cell_layout, cell, model, iter)=>{
string pattern;
model.get (iter, 0, out pattern, -1);
((Gtk.CellRendererText)cell).text =
pattern.has_prefix("+ ") ? pattern[2:pattern.length] : pattern;
});
cell_text.editable = true;
cell_text.edited.connect ((path, new_text)=>{
TreeIter iter;
var model = (Gtk.ListStore) treeview.model;
model.get_iter_from_string (out iter, path);
bool include;
model.get (iter, 2, out include, -1);
string pattern = new_text;
if (include){
if (!pattern.has_prefix("+ ")){
pattern = "+ %s".printf(pattern);
}
}
else{
if (pattern.has_prefix("+ ")){
pattern = pattern[2:pattern.length];
}
}
model.set (iter, 0, pattern, -1);
save_changes();
});
}
private void init_actions(){
var hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
add(hbox);
var size_group = new Gtk.SizeGroup(SizeGroupMode.HORIZONTAL);
var button = add_button(hbox, _("Add"), _("Add custom pattern"), size_group, null);
button.clicked.connect(()=>{
string pattern = gtk_inputbox(
_("Exclude Pattern"),
_("Enter the pattern to exclude (Ex: *.mp3, *.bak)"),
parent_window, false);
if ((pattern != null) && (pattern.strip().length > 0)){
treeview_add_item(treeview, pattern); // don't strip
Main.first_snapshot_size = 0; //re-calculate
}
save_changes();
});
button = add_button(hbox, _("Add Files"), _("Add files"), size_group, null);
button.clicked.connect(()=>{
add_files_clicked();
});
button = add_button(hbox, _("Add Folders"), _("Add directories"), size_group, null);
button.clicked.connect(()=>{
add_folder_clicked();
});
// for exclude only - Including contents without including directory is not logical
/*size_group = null;
button = add_button(hbox, _("Add Contents"),
_("Add directory contents"), ref size_group, null);
button.clicked.connect(()=>{
add_folder_contents_clicked();
});*/
button = add_button(hbox, _("Remove"), "", size_group, null);
button.clicked.connect(()=>{
remove_clicked();
});
button = add_button(hbox, _("Summary"), "", size_group, null);
button.clicked.connect(()=>{
save_changes();
new ExcludeListSummaryWindow(false);
});
}
// actions
private void remove_clicked(){
var sel = treeview.get_selection();
var store = (Gtk.ListStore) treeview.model;
if (sel.count_selected_rows() == 0){
string title = _("Items Not Selected");
string message = _("Select the items to be removed from the list");
gtk_messagebox(title, message, parent_window, true);
return;
}
TreeIter iter;
var iter_list = new Gee.ArrayList<TreeIter?>();
bool iterExists = store.get_iter_first (out iter);
while (iterExists) {
if (sel.iter_is_selected (iter)){
string pattern;
store.get (iter, 0, out pattern);
App.exclude_list_user.remove(pattern);
iter_list.add(iter);
log_debug("removed item: %s".printf(pattern));
Main.first_snapshot_size = 0; //re-calculate
}
iterExists = store.iter_next (ref iter);
}
refresh_treeview();
save_changes();
}
private void add_files_clicked(){
var list = browse_files();
if (list.length() > 0){
foreach(string item in list){
string pattern = item;
if (!App.exclude_list_user.contains(pattern)){
App.exclude_list_user.add(pattern);
treeview_add_item(treeview, pattern);
log_debug("file: %s".printf(pattern));
Main.first_snapshot_size = 0; //re-calculate
}
else{
log_debug("exclude_list_user contains: %s".printf(pattern));
}
}
}
save_changes();
}
private void add_folder_clicked(){
var list = browse_folder();
if (list.length() > 0){
foreach(string item in list){
string pattern = item;
if (!pattern.has_suffix("/***")){
pattern = "%s/***".printf(pattern);
}
/*
NOTE:
+ <dir>/*** will include the directory along with the contents
+ <dir>/ will include only the directory without the contents
<dir>/*** will exclude the directory along with the contents
<dir>/ is same as exclude <dir>/***
*/
if (!App.exclude_list_user.contains(pattern)){
App.exclude_list_user.add(pattern);
treeview_add_item(treeview, pattern);
log_debug("folder: %s".printf(pattern));
Main.first_snapshot_size = 0; //re-calculate
}
else{
log_debug("exclude_list_user contains: %s".printf(pattern));
}
}
}
save_changes();
}
private void add_folder_contents_clicked(){
var list = browse_folder();
if (list.length() > 0){
foreach(string item in list){
string pattern = item;
if (!pattern.has_suffix("/**")){
pattern = "%s/**".printf(pattern);
}
/*
NOTE:
+ <dir>/** will include the directory along with the contents
+ <dir>/ will include only the directory without the contents
<dir>/** will exclude the directory contents but include the empty directory
<dir>/ will exclude the directory along with the contents
*/
if (!App.exclude_list_user.contains(pattern)){
App.exclude_list_user.add(pattern);
treeview_add_item(treeview, pattern);
log_debug("contents: %s".printf(pattern));
Main.first_snapshot_size = 0; //re-calculate
}
else{
log_debug("exclude_list_user contains: %s".printf(pattern));
}
}
}
save_changes();
}
private SList<string> browse_files(){
var list = new SList<string>();
var dialog = new Gtk.FileChooserDialog(
_("Select file(s)"), parent_window,
Gtk.FileChooserAction.OPEN,
"gtk-cancel", Gtk.ResponseType.CANCEL,
"gtk-open", Gtk.ResponseType.ACCEPT);
dialog.action = FileChooserAction.OPEN;
dialog.set_transient_for(parent_window);
dialog.local_only = true;
dialog.set_modal (true);
dialog.set_select_multiple (true);
var resp = dialog.run();
if (resp != Gtk.ResponseType.CANCEL){
list = dialog.get_filenames();
}
dialog.destroy();
return list;
}
private SList<string> browse_folder(){
var list = new SList<string>();
var dialog = new Gtk.FileChooserDialog(
_("Select directory"), parent_window,
Gtk.FileChooserAction.SELECT_FOLDER,
"gtk-cancel", Gtk.ResponseType.CANCEL,
"gtk-open", Gtk.ResponseType.ACCEPT);
dialog.action = FileChooserAction.SELECT_FOLDER;
dialog.local_only = true;
dialog.set_transient_for(parent_window);
dialog.set_modal (true);
dialog.set_select_multiple (false);
var resp = dialog.run();
if (resp != Gtk.ResponseType.CANCEL){
list = dialog.get_filenames();
}
dialog.destroy();
return list;
}
// helpers
public void refresh_treeview(){
var model = new Gtk.ListStore(4, typeof(string), typeof(string), typeof(bool), typeof(bool));
treeview.model = model;
foreach(string pattern in App.exclude_list_user){
treeview_add_item(treeview, pattern);
}
}
private void treeview_add_item(Gtk.TreeView treeview, string pattern){
log_debug("treeview_add_item(): %s".printf(pattern));
TreeIter iter;
var model = (Gtk.ListStore) treeview.model;
model.append(out iter);
bool include = pattern.has_prefix("+ ");
model.set (iter, 0, pattern);
model.set (iter, 1, include ? "list-add" : "list-remove");
model.set (iter, 2, include);
model.set (iter, 3, !include);
var adj = ((Gtk.Scrollable)treeview).get_hadjustment();
adj.value = adj.upper;
}
private void treeview_update_item(ref TreeIter iter, string pattern){
log_debug("treeview_update_item(): %s".printf(pattern));
bool include = pattern.has_prefix("+ ");
var model = (Gtk.ListStore) treeview.model;
model.set (iter, 0, pattern);
model.set (iter, 1, include ? "list-add" : "list-remove");
model.set (iter, 2, include);
model.set (iter, 3, !include);
}
private void cell_exclude_text_edited(string path, string new_text) {
string old_pattern;
string new_pattern;
TreeIter iter;
var model = (Gtk.ListStore) treeview.model;
model.get_iter_from_string (out iter, path);
model.get (iter, 0, out old_pattern, -1);
if (old_pattern.has_prefix("+ ")){
new_pattern = "+ " + new_text;
}
else{
new_pattern = new_text;
}
model.set (iter, 0, new_pattern);
}
public void save_changes(){
App.exclude_list_user.clear();
// add include patterns from treeview
TreeIter iter;
var store = (Gtk.ListStore) treeview.model;
bool iterExists = store.get_iter_first (out iter);
while (iterExists) {
string pattern;
store.get(iter, 0, out pattern);
if (!App.exclude_list_user.contains(pattern)
&& !App.exclude_list_default.contains(pattern)
&& !App.exclude_list_home.contains(pattern)){
App.exclude_list_user.add(pattern);
}
iterExists = store.iter_next(ref iter);
}
log_debug("save_changes(): exclude_list_user:");
foreach(var item in App.exclude_list_user){
log_debug(item);
}
log_debug("");
users_box.refresh();
}
}