diff --git a/Cargo.toml b/Cargo.toml
index 39c3d96..0c06f40 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ gtk-sys = "^0.4.0"
libpijul = "^0.7.3"
log = "^0.3.8"
base64 = "^0.6.0"
+chrono = "^0.4.0"
[dependencies.gdk]
features = [
diff --git a/data/ui/window.glade b/data/ui/window.glade
index 7623b96..59baf50 100644
--- a/data/ui/window.glade
+++ b/data/ui/window.glade
@@ -60,14 +60,89 @@
True
False
-
+
+
True
False
False
True
- 0
+ 1
+
+
+
+
+ True
+ False
+ 2
+
+
+ True
+ False
+ Hash
+
+
+ False
+ True
+ 2
+ 0
+
+
+
+
+ True
+ True
+ False
+
+
+ True
+ True
+ 2
+ 1
+
+
+
+
+ True
+ True
+ 2
+ 2
diff --git a/src/appstate.rs b/src/appstate.rs
new file mode 100644
index 0000000..4df142b
--- /dev/null
+++ b/src/appstate.rs
@@ -0,0 +1,20 @@
+use gtk;
+use ui::gui::*;
+
+
+pub struct AppS {
+ _cant_construct: (),
+ pub gui: Gui,
+}
+
+
+impl AppS {
+ pub fn new() -> Self {
+ let builder = gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/data/ui/window.glade")));
+
+ return AppS {
+ _cant_construct: (),
+ gui: Gui::new(builder),
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index fc26cfe..ae62e7e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,6 +9,7 @@ extern crate gtk;
extern crate gtk_sys;
extern crate libpijul;
extern crate base64;
+extern crate chrono;
extern crate flexi_logger;
#[macro_use]
@@ -17,9 +18,12 @@ extern crate log;
#[macro_use]
extern crate error_chain;
-mod ui;
+mod appstate;
mod errors;
+mod pijul_glue;
+mod ui;
+use appstate::*;
use ui::entry::*;
use std::rc::Rc;
diff --git a/src/pijul_glue/branches.rs b/src/pijul_glue/branches.rs
new file mode 100644
index 0000000..52d5158
--- /dev/null
+++ b/src/pijul_glue/branches.rs
@@ -0,0 +1,18 @@
+use libpijul;
+use libpijul::fs_representation::*;
+use errors::*;
+
+
+pub fn get_branches(path: &str) -> Result> {
+ let mut vec = Vec::new();
+
+ let repo = libpijul::Repository::open(pristine_dir(path), None).unwrap();
+ let txn = repo.txn_begin().unwrap();
+
+ let branches = txn.iter_branches(None).map(|x| String::from(x.name.as_str()));
+
+ vec.extend(branches);
+ return Ok(vec);
+}
+
+
diff --git a/src/pijul_glue/mod.rs b/src/pijul_glue/mod.rs
new file mode 100644
index 0000000..a5e43ea
--- /dev/null
+++ b/src/pijul_glue/mod.rs
@@ -0,0 +1,4 @@
+//! The pijul glue subsystem.
+
+pub mod branches;
+pub mod patches;
diff --git a/src/pijul_glue/patches.rs b/src/pijul_glue/patches.rs
new file mode 100644
index 0000000..634a028
--- /dev/null
+++ b/src/pijul_glue/patches.rs
@@ -0,0 +1,79 @@
+use libpijul;
+use libpijul::patch;
+use libpijul::fs_representation::*;
+use std::path::Path;
+use errors::*;
+use base64;
+
+
+pub struct Patch {
+ pub name: String,
+ pub authors: String,
+ pub created: String,
+ pub signed: bool,
+ hash: libpijul::Hash,
+}
+
+
+impl Patch {
+ pub fn from_patch(patch: patch::Patch, hash: libpijul::Hash) -> Self {
+ let p = Patch {
+ name: patch.header().name.clone(),
+ authors: patch.header().authors.join(", "),
+ created: format!("{}", patch.header().timestamp.format("%F %T")),
+ signed: patch_is_signed(patch),
+ hash: hash,
+ };
+
+ return p;
+ }
+
+ pub fn hash_to_str(&self) -> String {
+ let config = base64::Config::new(base64::CharacterSet::Standard,
+ false,
+ false,
+ base64::LineWrap::NoWrap);
+ let s = self.hash.to_base64(config);
+
+ return s;
+ }
+}
+
+
+pub fn str_to_hash(s: String) -> Option {
+ return libpijul::Hash::from_base64(s.as_ref());
+}
+
+
+pub fn get_patch_headers(path: &str, branch: &str) -> Result> {
+ let mut vec = Vec::new();
+
+ let repo = libpijul::Repository::open(pristine_dir(path), None)?;
+ let txn = repo.txn_begin().unwrap();
+
+ let branch = txn.get_branch(branch).unwrap();
+
+ let patches_ids = txn.iter_patches(&branch, None).map(|x| x.0);
+ for pid in patches_ids {
+ let hash_ref = txn.external_hash(pid);
+ let mp = libpijul::fs_representation::read_patch(Path::new(path),
+ hash_ref);
+ match mp {
+ Ok(p) => {
+ vec.push(Patch::from_patch(p, hash_ref.to_owned()));
+ },
+ Err(e) => warn!("Could not get patch for patch_id {:?}\nError: {:?}", pid, e),
+ }
+ }
+
+ return Ok(vec);
+}
+
+
+pub fn patch_is_signed(patch: patch::Patch) -> bool {
+ match patch {
+ patch::Patch::Unsigned(_) => return false,
+ patch::Patch::Signed{patch: _, signature: _ } => return true,
+ }
+}
+
diff --git a/src/ui/entry.rs b/src/ui/entry.rs
index 2903ea5..2f79bcc 100644
--- a/src/ui/entry.rs
+++ b/src/ui/entry.rs
@@ -1,49 +1,15 @@
-use gtk;
-use gtk::prelude::*;
-use std::rc::Rc;
+use appstate::*;
use gtk::WidgetExt;
-use libpijul;
-use libpijul::fs_representation::*;
-use std::path::Path;
-use errors::*;
+use gtk::prelude::*;
+use gtk;
+use pijul_glue::branches::*;
+use pijul_glue::patches::*;
+use std::rc::Rc;
-pub struct AppS {
- _cant_construct: (),
- pub gui: Gui,
-}
+const HASH_COLUMN: i32 = 3;
-impl AppS {
- pub fn new() -> Self {
- let builder = gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/data/ui/window.glade")));
-
- return AppS {
- _cant_construct: (),
- gui: Gui::new(builder),
- }
- }
-}
-
-
-pub struct Gui {
- _cant_construct: (),
- pub window: gtk::Window,
- pub branch_select: gtk::ComboBoxText,
- pub patch_tree: gtk::TreeView,
-}
-
-impl Gui {
- pub fn new(builder: gtk::Builder) -> Self {
- return Gui {
- _cant_construct: (),
- window: builder.get_object("main").unwrap(),
- branch_select: builder.get_object("branch_select").unwrap(),
- patch_tree: builder.get_object("patch_tree").unwrap(),
- }
- }
-}
-
pub fn init(appstate: Rc) {
{
@@ -56,61 +22,74 @@ pub fn init(appstate: Rc) {
}
{
- let ls = gtk::ListStore::new(&[gtk::Type::String]);
- let patches = get_patches("/home/hasufell/git/pijul",
+ let mut columns: Vec = Vec::new();
+ append_column("Patch", &mut columns, &appstate.gui.patch_tree, Some(200));
+ append_column("Authors", &mut columns, &appstate.gui.patch_tree, None);
+ append_column("Date", &mut columns, &appstate.gui.patch_tree, None);
+
+ let ls = gtk::ListStore::new(&[
+ // visible
+ gtk::Type::String, // name
+ gtk::Type::String, // authors
+ gtk::Type::String, // creation date
+ // not visible
+ gtk::Type::String, // hash
+ ]);
+ let patches = get_patch_headers("/home/hasufell/git/pijul",
"master");
for patch in patches.unwrap() {
- ls.insert_with_values(None, &[0], &[&patch.as_str()]);
+ ls.insert_with_values(None, &[0, 1, 2, 3],
+ &[&patch.name.as_str(),
+ &patch.authors.as_str(),
+ &patch.created.as_str(),
+ &patch.hash_to_str(),
+ ]);
}
-
- let renderer = gtk::CellRendererText::new();
- let col = gtk::TreeViewColumn::new();
- col.set_title("Patch");
- col.set_resizable(true);
- col.pack_start(&renderer, true);
- col.add_attribute(&renderer, "text", 0);
- col.set_clickable(true);
- col.set_sort_column_id(0);
-
- appstate.gui.patch_tree.append_column(&col);
appstate.gui.patch_tree.set_model(Some(&ls));
+ appstate.gui.patch_tree.set_headers_visible(true);
+ }
+
+ let selection = appstate.gui.patch_tree.get_selection();
+ selection.set_mode(gtk::SelectionMode::Single);
+
+ /* connect selection change */
+ {
+ let apps = appstate.clone();
+ selection.connect_changed(move |ts| {
+ if let Some((model, iter)) = ts.get_selected() {
+ let val = model.get_value(&iter, HASH_COLUMN);
+ let s: String = val.downcast().unwrap().get().unwrap();
+ apps.gui.hash_entry.set_text(s.as_ref());
+ }
+ });
}
appstate.gui.window.show_all();
}
+fn append_column(title: &str,
+ v: &mut Vec,
+ tree: >k::TreeView,
+ max_width: Option) {
-fn get_branches(path: &str) -> Result> {
- let mut vec = Vec::new();
+ let id = v.len() as i32;
- let repo = libpijul::Repository::open(pristine_dir(path), None).unwrap();
- let txn = repo.txn_begin().unwrap();
-
- let branches = txn.iter_branches(None).map(|x| String::from(x.name.as_str()));
-
- vec.extend(branches);
- return Ok(vec);
-}
-
-
-fn get_patches(path: &str, branch: &str) -> Result> {
- let mut vec = Vec::new();
-
- let repo = libpijul::Repository::open(pristine_dir(path), None)?;
- let txn = repo.txn_begin().unwrap();
-
- let branch = txn.get_branch(branch).unwrap();
-
- let patches_ids = txn.iter_patches(&branch, None).map(|x| x.0);
- for pid in patches_ids {
- let mp = libpijul::fs_representation::read_patch(Path::new(path),
- txn.external_hash(pid));
- match mp {
- Ok(p) => vec.push(p.header().name.clone()),
- Err(e) => warn!("Could not get patch for patch_id {:?}\nError: {:?}", pid, e),
- }
+ let renderer = gtk::CellRendererText::new();
+ let col = gtk::TreeViewColumn::new();
+ col.set_title(title);
+ col.set_resizable(true);
+ col.set_sort_column_id(id);
+ if let Some(max_width) = max_width {
+ col.set_max_width(max_width);
+ col.set_expand(true);
}
+ col.pack_start(&renderer, true);
+ col.add_attribute(&renderer, "text", id);
+ col.set_clickable(true);
- return Ok(vec);
+ tree.append_column(&col);
+
+ v.push(col);
}
+
diff --git a/src/ui/gui.rs b/src/ui/gui.rs
new file mode 100644
index 0000000..3c17f7e
--- /dev/null
+++ b/src/ui/gui.rs
@@ -0,0 +1,22 @@
+use gtk;
+
+
+pub struct Gui {
+ _cant_construct: (),
+ pub window: gtk::Window,
+ pub branch_select: gtk::ComboBoxText,
+ pub patch_tree: gtk::TreeView,
+ pub hash_entry: gtk::Entry,
+}
+
+impl Gui {
+ pub fn new(builder: gtk::Builder) -> Self {
+ return Gui {
+ _cant_construct: (),
+ window: builder.get_object("main").unwrap(),
+ branch_select: builder.get_object("branch_select").unwrap(),
+ patch_tree: builder.get_object("patch_tree").unwrap(),
+ hash_entry: builder.get_object("hash_entry").unwrap(),
+ }
+ }
+}
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index e3dbce6..a1e29ea 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -1,3 +1,4 @@
//! The UI subsystem.
pub mod entry;
+pub mod gui;