private void add_note_to_ui(note_item last_note) { bool add_to_ui = !last_note.deleted || (last_note.deleted && showDeletedLines.Checked); if (!add_to_ui) return; int insert_idx = -1; if (last_note.reply_id != "") { // it's a note on reply to another note for (int note_idx = 0; note_idx < notesCtrl.GetItemCount() && insert_idx < 0; ++note_idx) { var i = notesCtrl.GetItem(note_idx).RowObject as note_item; if (i.note_id == last_note.reply_id) insert_idx = note_idx + 1; } } else { // new note if (!last_note.is_note_header) for (int note_idx = 0; note_idx < notesCtrl.GetItemCount(); ++note_idx) { var i = notesCtrl.GetItem(note_idx).RowObject as note_item; string line_id = i.line_id != cur_line_id_ ? i.line_id : find_cur_line_id(); if (line_id == last_note.line_id) { insert_idx = note_idx + 1; bool insert_at_the_end = i.line_id == cur_line_id_; if (insert_at_the_end) { // we need to update the line, so that it does not point to current-line anymore i.line_id = line_id; } } } else // it's a new note header, add at the end insert_idx = notesCtrl.GetItemCount(); } Debug.Assert( insert_idx >= 0); if (insert_idx >= 0) { ++ignore_change_; notesCtrl.InsertObjects(insert_idx, new object[] {last_note}); // now, select it as well notesCtrl.SelectObject(last_note); // ... so that it recomputes its UI index correctly notesCtrl.RefreshObject(last_note); --ignore_change_; notesCtrl.EnsureVisible(insert_idx); refresh_note(last_note); } }
// this sets the line the user is viewing - by default, this is what the user is commenting on private void set_current_line_impl(line l) { if (cur_line.idx == l.idx && cur_line.view_name == l.view_name) return; // we're already there if (l.idx < 0) { ++ignore_change_; for (int idx = 0; idx < notesCtrl.GetItemCount(); ++idx) { var i = notesCtrl.GetItem(idx).RowObject as note_item; if ( i.is_cur_line) notesCtrl.RemoveObject(i); } --ignore_change_; } cur_line.copy_from(l); if (l.idx < 0) { ++ignore_change_; notesCtrl.SelectedIndex = -1; --ignore_change_; update_cur_note_controls(); return; } /* Possibilities: - case 1 - there are already notes on this line 1a - there are already notes on this line, and the selection is already one of them 1b - there are already notes on this line, and the selection is on another note (or nowhere) - case 2 - there are no notes on this line */ note_item header = null; for (int idx = 0; idx < notesCtrl.GetItemCount() && header == null ; ++idx) { var i = notesCtrl.GetItem(idx).RowObject as note_item; if (i.is_note_header && lines_[i.line_id].idx == l.idx && !i.is_cur_line) header = i; } bool last_note_is_cur_line = notesCtrl.GetItemCount() > 0 && (notesCtrl.GetItem(notesCtrl.GetItemCount() - 1).RowObject as note_item).is_cur_line; if (header != null) { // already a note on this line, not needed now if (last_note_is_cur_line) notesCtrl.RemoveObject(notesCtrl.GetItem(notesCtrl.GetItemCount() - 1).RowObject); int sel = notesCtrl.SelectedIndex; var sel_item = sel >= 0 ? notesCtrl.GetItem(sel).RowObject as note_item : null; if (sel_item != null && sel_item.line_id == header.line_id) // 1a - on a note from this line return; // 1b - on a note from another line // I will select the last note from this user var last = ui_find_last_note_from_cur_user(header.line_id); notesCtrl.SelectObject( last); notesCtrl.EnsureModelVisible(last); } else { // 2 - no notes on this line if (!last_note_is_cur_line) { var new_ = new note_item(this, next_guid, cur_line_id_, false, DateTime.UtcNow); notesCtrl.AddObject(new_); refresh_note( new_); } var last = notesCtrl.GetItem(notesCtrl.GetItemCount() - 1).RowObject as note_item; refresh_note(last); bool focus_on_notes = win32.focused_ctrl() == notesCtrl; if (notesCtrl.SelectedIndex < 0 || !focus_on_notes) { // select the "last line" notesCtrl.SelectedIndex = notesCtrl.GetItemCount() - 1; notesCtrl.EnsureVisible( notesCtrl.GetItemCount() - 1); } } update_cur_note_controls(); }
// returns the ID assigned to this note that we're adding; // if null, something went wrong // // note_id - the unique ID of the note, or "" if a new unique ID is to be created // the reason we have this is that we can persist notes (together with their IDs) private note_item add_note(line l, note n, string note_id, string reply_to_note_id, bool deleted, DateTime utc_added) { // a note: can be added on a line, or as reply to someone // new notes: always at the end string line_id = ""; // ... first, try to see if we already have a valid line var found_line = lines_.FirstOrDefault(x => x.Key != cur_line_id_ && x.Value == l); if ( found_line.Value == null) found_line = lines_.FirstOrDefault(x => x.Value == l); Debug.Assert(found_line.Value != null); if (found_line.Key == cur_line_id_) { // it's the current line line_id = next_guid; lines_.Add(line_id, l); } else line_id = found_line.Key; note_id = note_id != "" ? note_id : next_guid; // ... this note should not exist already Debug.Assert( notes_sorted_by_line_index_.Count(x => x.note_id == note_id) == 0); if ( reply_to_note_id != "") // note we're replying to should exist already Debug.Assert( notes_sorted_by_line_index_.Count(x => x.note_id == reply_to_note_id) == 1); var new_ = new note_item(this, n, note_id, reply_to_note_id, line_id, deleted, utc_added); bool inserted = false; if (reply_to_note_id != "") { // add this note as the last reply to this reply note var note = notes_sorted_by_line_index_.FirstOrDefault(x => x.line_id == line_id); // when it's a reply, we need to find the original note! Debug.Assert(note != null); if (note != null) { Debug.Assert(note.is_note_header); // everything following this, is related to this line (until we get to next line) int idx_note = notes_sorted_by_line_index_.IndexOf(note); for (; idx_note < notes_sorted_by_line_index_.Count && !inserted; idx_note++) if (notes_sorted_by_line_index_[idx_note].line_id == line_id) { if (reply_to_note_id != "") { // in this case, look for this note (that we're replying to) if (notes_sorted_by_line_index_[idx_note].note_id == reply_to_note_id) { log_notes_sorted_idx(idx_note + 1, new_); notes_sorted_by_line_index_.Insert(idx_note + 1, new_); inserted = true; } } else { // look for the last note about this line, and insert it after that if ( idx_note < notes_sorted_by_line_index_.Count - 1) if (notes_sorted_by_line_index_[idx_note + 1].line_id != line_id) { // found the last note that relates to this line notes_sorted_by_line_index_.Insert(idx_note + 1, new_); log_notes_sorted_idx(idx_note + 1, new_); inserted = true; } } } else // went to next line break; } } else { var note = notes_sorted_by_line_index_.FirstOrDefault(x => x.line_id == line_id); if (note != null) { // in this case, there may be other notes - we're adding it to the end (as last note on this line) Debug.Assert(note.is_note_header); int idx = notes_sorted_by_line_index_.IndexOf(note); for ( ; idx < notes_sorted_by_line_index_.Count; ++idx) if ( idx < notes_sorted_by_line_index_.Count - 1) if (notes_sorted_by_line_index_[idx + 1].line_id != line_id) break; if (idx < notes_sorted_by_line_index_.Count) { log_notes_sorted_idx(idx + 1, new_); notes_sorted_by_line_index_.Insert(idx + 1, new_); } else { log_notes_sorted_idx(-1, new_); notes_sorted_by_line_index_.Add(new_); } inserted = true; } else { // this is the first entry that relates to this line // ... find the note before which we should insert ourselves int line_index = lines_[line_id].idx; note = notes_sorted_by_line_index_.FirstOrDefault(x => lines_[x.line_id].idx > line_index); var header = new note_item(this, next_guid, line_id, deleted, utc_added); if (note != null) { int idx = notes_sorted_by_line_index_.IndexOf(note); bool has_header = note.is_note_header && note.line_id == line_id; if (!has_header) { log_notes_sorted_idx(idx, header); notes_sorted_by_line_index_.Insert(idx, header); log_notes_sorted_idx(idx + 1, new_); notes_sorted_by_line_index_.Insert(idx + 1, new_); } else { // header is already added if (header.deleted && !deleted) // in this case, the header was deleted (probably we removed all items related to this line a while ago, // and now we have added a new note here) header.deleted = false; log_notes_sorted_idx(idx, new_); notes_sorted_by_line_index_.Insert(idx, new_); } } else { log_notes_sorted_idx(-1, header); log_notes_sorted_idx(-1, new_); notes_sorted_by_line_index_.Add(header); notes_sorted_by_line_index_.Add(new_); } inserted = true; } } Debug.Assert(inserted); // update the UI if (inserted) add_note_to_ui(new_); dirty_ = true; return new_; }
private void log_notes_sorted_idx(int idx, note_item n) { if (idx >= 0 && idx < notes_sorted_by_line_index_.Count) logger.Info("[notes] insert " + n.friendly_note + " before " + notes_sorted_by_line_index_[idx].friendly_note); else logger.Info("[notes] adding " + n.friendly_note + " at the end"); }
// the only time this can get called is when loading an already saved configuration private note_item add_note_header(string line_id, string note_id, bool deleted, DateTime utc_added) { Debug.Assert(note_id != ""); Debug.Assert(line_id != cur_line_id_ && lines_.ContainsKey(line_id)); var new_ = new note_item(this, note_id, line_id, deleted, utc_added ); notes_sorted_by_line_index_.Add(new_); add_note_to_ui(new_); return new_; }
private void refresh_note(note_item i) { notesCtrl.RefreshObject(i); var item = notesCtrl.ModelToItem(i); OLVListSubItem idx = item.GetSubItem(0), line = item.GetSubItem(1), text = item.GetSubItem(2); idx.ForeColor = i.idx_fg; line.ForeColor = i.line_fg; text.ForeColor = i.text_fg; idx.BackColor = line.BackColor = text.BackColor = i.bg; line.Font = i.font; text.Font = i.font; }
private static Tuple<Dictionary<string, line>, List<note_item>> load_settings_file(note_ctrl self, string file_name) { Dictionary<string, line> lines = new Dictionary<string, line>(); List<note_item> notes = new List<note_item>(); settings_file sett = new settings_file(file_name); // first, load lines int line_count = int.Parse(sett.get("line_count", "0")); for (int idx = 0; idx < line_count; ++idx) { string prefix = "line." + idx + "."; line l = new line(); l.idx = int.Parse(sett.get(prefix + "index", "0")); l.view_name = sett.get(prefix + "view_name"); l.msg = sett.get(prefix + "msg"); var id = sett.get(prefix + "id", ""); lines.Add(id, l); } // load notes // if author name = ourselves -> made_by_current_user = true int note_count = int.Parse(sett.get("note_count", "0")); for (int idx = 0; idx < note_count; ++idx) { string prefix = "note." + idx + "."; note n = null; string author_name = sett.get(prefix + "author_name"); if (author_name != "") { n = new note() {author_name = author_name}; n.author_initials = sett.get(prefix + "author_initials"); n.author_color = util.str_to_color(sett.get(prefix + "author_color")); n.note_text = sett.get(prefix + "note_text"); } bool deleted = sett.get(prefix + "deleted", "0") != "0"; var note_id = sett.get(prefix + "note_id", ""); var reply_id = sett.get(prefix + "reply_id", ""); var line_id = sett.get(prefix + "line_id", ""); long ticks = long.Parse(sett.get(prefix + "added_at", "0")); note_item note; if (n != null) note = new note_item(self, n, note_id, reply_id, line_id, deleted, new DateTime(ticks)); else note = new note_item(self, note_id, line_id, deleted, new DateTime(ticks)); notes.Add(note); } return new Tuple<Dictionary<string, line>, List<note_item>>(lines, notes); }