/// <summary> /// Unassign provided tag if it assigned /// </summary> /// <param name="label">use null for autosearch</param> public void UnassignTag(TagItem tag) { if (tag == null) { return; } Item.Tags.Remove(tag); Item.ModifyDate = (DateTime.UtcNow.Subtract(Glob.Epoch)).TotalSeconds; //save to db using (SQLiteTransaction tr = f.sql.BeginTransaction()) { using (SQLiteCommand cmd = new SQLiteCommand(f.sql)) { cmd.CommandText = "DELETE FROM nt WHERE note=" + Item.Id + " AND tag=" + tag.Id; cmd.ExecuteNonQuery(); cmd.CommandText = "UPDATE notes SET modifydate=? WHERE id=" + Item.Id; //update modifydate for sync cmd.Parameters.AddWithValue(null, Item.ModifyDate); cmd.ExecuteNonQuery(); } tr.Commit(); } //update tree if (Item.Tags.Count == 0) { f.tree.SelectObject(f.tagAll, true); //set focus to All when Note lost last tag } f.tree.RefreshObject(f.tagAll); f.tree.RefreshObject(tag); FillAutocomplete(); }
/// <summary> /// push tag order info /// </summary> internal static TagMeta pushTag(TagItem i) { var js = new JavaScriptSerializer(); var node = new TagMeta(); node.index = i.Index; node.name = i.Name; node.version = i.Version; var data = js.Serialize(node); var url = (i.Version == 0) ? "/api2/tags" : "/api2/tags/" + i.Name; var s = RequestRetry(url, "POST", data); return(js.Deserialize <TagMeta>(s)); }
/// <summary> /// remove label if exist, optionally find label by tag /// </summary> public void RemoveLabel(Label label, TagItem tag = null, bool redraw = true) { if (label == null) { label = Labels.Find(x => x.Text == tag.Name); } if (label == null) { return; } f.splitContainer1.Panel2.Controls.Remove(label); label.Dispose(); Labels.Remove(label); if (redraw) { drawTags(); //not used in del last label } }
//init tree with root items from db private void initTree() { //read tags from db TagItem node; using (SQLiteCommand cmd = new SQLiteCommand("SELECT id, name, `index`, expanded, lexer, version FROM tags ORDER BY `index`", sql)) { using (SQLiteDataReader rdr = cmd.ExecuteReader()) { while (rdr.Read()) { node = new TagItem(notes); node.Id = rdr.GetInt32(0); node.Name = rdr.GetString(1); node.Index = rdr.GetInt32(2); node.Expanded = rdr.GetBoolean(3); if (!rdr.IsDBNull(4)) node.Lexer = rdr.GetString(4); if (!rdr.IsDBNull(5)) node.Version = rdr.GetInt32(5); tags.Add(node); } } } //All tagAll = new TagItem(notes); tagAll.Name = Glob.All; tagAll.System = true; tagAll.Index = int.MaxValue - 1; tags.Add(tagAll); //Deleted tagDeleted = new TagItem(notes); tagDeleted.Name = Glob.Deleted; tagDeleted.System = true; tagDeleted.Index = int.MaxValue; tags.Add(tagDeleted); //get notes from db (filling tags) notes.AddRange(readNotes(sql, tags)); treeAsTags();// startup view is tags view //restore view foreach (var tag in tags) { if (tag.Expanded) tree.Expand(tag); } //search last opened note using (SQLiteCommand cmd = new SQLiteCommand("SELECT value FROM config WHERE name='lastNote'", sql)) { var res = cmd.ExecuteScalar(); if (res == null) createNote(); //first run - create new note and select it else tree.Reveal(notes.Find(x => x.Id == Convert.ToInt32(res)), true); if (tree.SelectedObject == null) tree.Reveal(notes[0], true); treeTopLine = tree.TopItemIndex; treeSelLine = tree.SelectedIndex; } }
// purge tag private void deleteTag(TagItem tag, bool doSync = true) { using (SQLiteCommand cmd = new SQLiteCommand(sql)) { cmd.CommandText = "DELETE FROM tags WHERE id=" + tag.Id; // no need to cleanup `nt` because of cascade cmd.ExecuteNonQuery(); } tags.Remove(tag); notes.ForEach(x => x.Tags.Remove(tag)); if (tree.RowHeight == -1) { tree.RemoveObject(tag); cName.Renderer = fancyRenderer; //OLV drop renderer when Roots refreshed } note.RemoveLabel(null, tag); //del from tagBox if exist // task to sync deletion if (doSync && Sync.Freq > 0 && tag.Version>0) { try { Task.Factory.StartNew(() => { Sync.delTag(tag); }); } catch { statusText.Text = "Unable to sync delete"; } } }
// update tag order/ver private void UpdateTag(TagItem tag, TagMeta raw, TaskScheduler ui) { tag.Index = raw.index; tag.Version = raw.version; //save to db var conn = new SQLiteConnection(ConnString); conn.Open(); using (SQLiteTransaction tr = conn.BeginTransaction()) { using (SQLiteCommand cmd = new SQLiteCommand(conn)) { cmd.CommandText = "UPDATE tags SET `index`=?, version=? WHERE id=?"; // don't update name cmd.Parameters.AddWithValue(null, tag.Index); cmd.Parameters.AddWithValue(null, tag.Version); cmd.Parameters.AddWithValue(null, tag.Id); cmd.ExecuteNonQuery(); } tr.Commit(); } conn.Close(); // sync with ui Task t1 = new Task(() => { tree.RefreshObject(tag); tree.Sort(); }); t1.Start(ui); }
//draw tag with collapse/expand icon and badge private void drawTag(Graphics g, Rectangle r, TagItem tag) { //collapse icon of tag ImageList il = this.ListView.SmallImageList; TreeListView tree = (TreeListView)this.ListView; TreeListView.Branch br = tree.TreeModel.GetBranch(this.RowObject); if (br.IsExpanded) {//opened if (this.IsItemSelected) this.DrawImage(g, r, 8); else this.DrawImage(g, r, 2); } else {//closed if (this.IsItemSelected) this.DrawImage(g, r, 9); else this.DrawImage(g, r, 3); } r.X += 16; r.Width -= 16; //system tag icon if (tag.System) { if (tag.Name == Glob.All) this.DrawImage(g, r, 4); else this.DrawImage(g, r, 5); r.X += 16; r.Width -= 16; } //tag title using (StringFormat fmt = new StringFormat(StringFormatFlags.NoWrap)) { fmt.LineAlignment = StringAlignment.Center; fmt.Trimming = StringTrimming.EllipsisCharacter; fmt.Alignment = StringAlignment.Near; SizeF stringSize = new SizeF(); using (var f = new Font(this.Font, FontStyle.Bold)) { g.DrawString(this.GetText(), f, this.TextBrush, r, fmt); stringSize = g.MeasureString(this.GetText(), f); } var pad = (int)stringSize.Width + 3; r.X += pad; r.Width -= pad; //count badge using (var f = new Font(this.Font.FontFamily, this.Font.Size - 1, FontStyle.Regular)) { stringSize = g.MeasureString(tag.Count.ToString(), f); var badgerect = new Rectangle(r.X + 3, r.Y + 3, (int)stringSize.Width + 3, r.Height - 6); using (GraphicsPath path = this.GetRoundedRect(badgerect, 10)) { using (SolidBrush b = new SolidBrush(Color.FromArgb(80, Color.Black))) { g.FillPath(b, path); //bg } using (SolidBrush b = new SolidBrush(Color.White)) { fmt.Alignment = StringAlignment.Center; fmt.LineAlignment = StringAlignment.Center; badgerect.Y = badgerect.Y + 1; g.DrawString(tag.Count.ToString(), f, b, badgerect, fmt); //count } } } } }
//rearrange tags private void moveTag(IList from, TagItem to, int offset = 0) { foreach (var t in from) tags.Remove((TagItem)t); tags.InsertRange(tags.IndexOf(to) + offset, from.Cast<TagItem>()); var i = 1; tags.ForEach(x => { if (!x.System) x.Index = i++; }); tree.Roots = tags; tree.SelectedObjects = from; //save to db using (SQLiteTransaction tr = sql.BeginTransaction()) { using (SQLiteCommand cmd = new SQLiteCommand(sql)) { SQLiteParameter index = new SQLiteParameter(); SQLiteParameter id = new SQLiteParameter(); cmd.CommandText = "UPDATE tags SET `index`=? WHERE id=?"; cmd.Parameters.Add(index); cmd.Parameters.Add(id); foreach (var t in tags) if (!t.System) { index.Value = t.Index; id.Value = t.Id; cmd.ExecuteNonQuery(); } } tr.Commit(); } }
//note change/add tag private void moveNote(IList from, TagItem to, DragDropEffects how) { using (SQLiteTransaction tr = sql.BeginTransaction()) { using (SQLiteCommand cmd = new SQLiteCommand(sql)) { var sqlTag = new SQLiteParameter(); foreach (var i in from) { var n = i as NoteItem; if (n.Deleted && to == tagDeleted) continue; if (!n.Deleted && to == tagDeleted) { //delete n.Deleted = true; n.ModifyDate = (DateTime.UtcNow.Subtract(Glob.Epoch)).TotalSeconds; cmd.CommandText = "UPDATE notes SET deleted=1, modifydate=? WHERE id=" + n.Id; cmd.Parameters.AddWithValue(null, n.ModifyDate); cmd.ExecuteNonQuery(); } else { if (n.Deleted && to != tagDeleted) { //undelete n.Deleted = false; cmd.CommandText = "UPDATE notes SET deleted=0 WHERE id=" + n.Id; cmd.ExecuteNonQuery(); } n.ModifyDate = (DateTime.UtcNow.Subtract(Glob.Epoch)).TotalSeconds; cmd.CommandText = "UPDATE notes SET modifydate=? WHERE id=" + n.Id; cmd.Parameters.AddWithValue(null, n.ModifyDate); cmd.ExecuteNonQuery(); if (to == tagAll) continue; if (how == DragDropEffects.Move) n.Tags.Clear(); n.Tags.Add(to); //save to db cmd.CommandText = "DELETE FROM nt WHERE note=" + n.Id; cmd.ExecuteNonQuery(); cmd.CommandText = "INSERT INTO nt(note,tag) VALUES(?,?)"; cmd.Parameters.Clear(); cmd.Parameters.AddWithValue(null, n.Id); cmd.Parameters.Add(sqlTag); foreach (var t in n.Tags) { sqlTag.Value = t.Id; cmd.ExecuteNonQuery(); } } } } tr.Commit(); } tree.SelectedObjects = from; //tree.Sort(); note.drawTags(); //redraw tagbox }
/// <summary> /// Unassign provided tag if it assigned /// </summary> /// <param name="label">use null for autosearch</param> public void UnassignTag(TagItem tag) { if (tag == null) return; Item.Tags.Remove(tag); Item.ModifyDate = (DateTime.UtcNow.Subtract(Glob.Epoch)).TotalSeconds; //save to db using (SQLiteTransaction tr = f.sql.BeginTransaction()) { using (SQLiteCommand cmd = new SQLiteCommand(f.sql)) { cmd.CommandText = "DELETE FROM nt WHERE note="+Item.Id+" AND tag="+tag.Id; cmd.ExecuteNonQuery(); cmd.CommandText = "UPDATE notes SET modifydate=? WHERE id=" + Item.Id; //update modifydate for sync cmd.Parameters.AddWithValue(null, Item.ModifyDate); cmd.ExecuteNonQuery(); } tr.Commit(); } //update tree if (Item.Tags.Count == 0) f.tree.SelectObject(f.tagAll, true); //set focus to All when Note lost last tag f.tree.RefreshObject(f.tagAll); f.tree.RefreshObject(tag); FillAutocomplete(); }
/// <summary> /// remove label if exist, optionally find label by tag /// </summary> public void RemoveLabel(Label label, TagItem tag=null, bool redraw = true) { if (label == null) label = Labels.Find(x => x.Text == tag.Name); if (label == null) return; f.splitContainer1.Panel2.Controls.Remove(label); label.Dispose(); Labels.Remove(label); if(redraw) drawTags(); //not used in del last label }
/// <summary> /// parse textbox for tags to list /// </summary> public void ParseTags(String tagstr=null, NoteItem note=null) { bool draw = false; //called not for current item, don't draw tags/autocompl if (tagstr == null) { tagstr = f.tagBox.Text; f.tagBox.Text = ""; draw = true; } if (note == null) note = Item; var isSearch = f.tree.RowHeight > 0; using (SQLiteTransaction tr = f.sql.BeginTransaction()) { using (SQLiteCommand cmd = new SQLiteCommand(f.sql)) { //cleanup existing tags, as it is replace if (!draw) { cmd.CommandText = "DELETE FROM nt WHERE note=" + note.Id; cmd.ExecuteNonQuery(); note.Tags.Clear(); } var added = false; foreach (var tag in tagstr.Split(new char[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries)) { if (String.IsNullOrEmpty(tag)) continue; if (note.Tags.Exists(x => x.Name.ToLower() == tag.ToLower())) continue; //skip already assigned var tagItem = f.tags.Find(x => !x.System && x.Name.ToLower() == tag.ToLower()); //search if exist if (tagItem==null) { //create tag in db if it is new cmd.Parameters.Clear(); cmd.CommandText = "INSERT INTO tags(name,`index`) VALUES(?,?)"; cmd.Parameters.AddWithValue(null, tag); cmd.Parameters.AddWithValue(null, f.tags.Count - 1); cmd.ExecuteNonQuery(); //refresh tree tagItem = new TagItem(f.notes); tagItem.Name = tag; tagItem.Id = f.sql.LastInsertRowId; tagItem.Index = f.tags.Count - 1; if (!isSearch) { f.tree.AddObject(tagItem); f.cName.Renderer = f.fancyRenderer; //OLV drop renderer when Roots refreshed if (draw) f.tree.SelectedObject = note; } f.tags.Add(tagItem); } cmd.CommandText = "INSERT INTO nt(note,tag) VALUES(" + note.Id + "," + tagItem.Id + ")"; cmd.ExecuteNonQuery(); note.Tags.Add(tagItem); if (!isSearch) f.tree.RefreshObject(tagItem); if (draw) drawTag(tagItem.Name); added = true; } if (draw && added) { // there was some tags actually added, update modifydate for sync note.ModifyDate = (DateTime.UtcNow.Subtract(Glob.Epoch)).TotalSeconds; cmd.CommandText = "UPDATE notes SET modifydate=? WHERE id=" + note.Id; cmd.Parameters.AddWithValue(null, note.ModifyDate); cmd.ExecuteNonQuery(); } } tr.Commit(); } if (draw) FillAutocomplete(); //refill autocomplete except for parsed tags }
/// <summary> /// parse textbox for tags to list /// </summary> public void ParseTags(String tagstr = null, NoteItem note = null) { bool draw = false; //called not for current item, don't draw tags/autocompl if (tagstr == null) { tagstr = f.tagBox.Text; f.tagBox.Text = ""; draw = true; } if (note == null) { note = Item; } var isSearch = f.tree.RowHeight > 0; using (SQLiteTransaction tr = f.sql.BeginTransaction()) { using (SQLiteCommand cmd = new SQLiteCommand(f.sql)) { //cleanup existing tags, as it is replace if (!draw) { cmd.CommandText = "DELETE FROM nt WHERE note=" + note.Id; cmd.ExecuteNonQuery(); note.Tags.Clear(); } var added = false; foreach (var tag in tagstr.Split(new char[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries)) { if (String.IsNullOrEmpty(tag)) { continue; } if (note.Tags.Exists(x => x.Name.ToLower() == tag.ToLower())) { continue; //skip already assigned } var tagItem = f.tags.Find(x => !x.System && x.Name.ToLower() == tag.ToLower()); //search if exist if (tagItem == null) { //create tag in db if it is new cmd.Parameters.Clear(); cmd.CommandText = "INSERT INTO tags(name,`index`) VALUES(?,?)"; cmd.Parameters.AddWithValue(null, tag); cmd.Parameters.AddWithValue(null, f.tags.Count - 1); cmd.ExecuteNonQuery(); //refresh tree tagItem = new TagItem(f.notes); tagItem.Name = tag; tagItem.Id = f.sql.LastInsertRowId; tagItem.Index = f.tags.Count - 1; if (!isSearch) { f.tree.AddObject(tagItem); f.cName.Renderer = f.fancyRenderer; //OLV drop renderer when Roots refreshed if (draw) { f.tree.SelectedObject = note; } } f.tags.Add(tagItem); } cmd.CommandText = "INSERT INTO nt(note,tag) VALUES(" + note.Id + "," + tagItem.Id + ")"; cmd.ExecuteNonQuery(); note.Tags.Add(tagItem); if (!isSearch) { f.tree.RefreshObject(tagItem); } if (draw) { drawTag(tagItem.Name); } added = true; } if (draw && added) // there was some tags actually added, update modifydate for sync { note.ModifyDate = (DateTime.UtcNow.Subtract(Glob.Epoch)).TotalSeconds; cmd.CommandText = "UPDATE notes SET modifydate=? WHERE id=" + note.Id; cmd.Parameters.AddWithValue(null, note.ModifyDate); cmd.ExecuteNonQuery(); } } tr.Commit(); } if (draw) { FillAutocomplete(); //refill autocomplete except for parsed tags } }
/// <summary> /// push tag order info /// </summary> internal static TagMeta pushTag(TagItem i) { var js = new JavaScriptSerializer(); var node = new TagMeta(); node.index = i.Index; node.name = i.Name; node.version = i.Version; var data = js.Serialize(node); var url = (i.Version == 0) ? "/api2/tags" : "/api2/tags/"+i.Name; var s = RequestRetry(url, "POST", data); return js.Deserialize<TagMeta>(s); }
/// <summary> /// delete tag permanently /// </summary> internal static void delTag(TagItem tag) { RequestRetry("/api2/tags/" + tag.Name, "DELETE"); }