Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add lock id to node for collaboration in documents #1558

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ct/ct_actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class CtActions
bool _is_there_selected_node_or_error();
bool _is_tree_not_empty_or_error();
bool _is_curr_node_not_read_only_or_error();
bool _is_curr_node_or_parent_not_locked_or_error();
bool _is_curr_node_not_syntax_highlighting_or_error(bool plain_text_ok = false);
bool _is_there_text_selection_or_error();

Expand Down Expand Up @@ -178,6 +179,7 @@ class CtActions
void node_inherit_syntax();
void node_delete();
void node_toggle_read_only();
void node_toggle_lock();
void node_date();
void node_up();
void node_down();
Expand Down
1 change: 1 addition & 0 deletions src/ct/ct_actions_import.cc
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ void CtActions::_create_imported_nodes(ct_imported_node* imported_nodes, const b
node_data.nodeId = imported_node->node_id;
node_data.isBold = false;
node_data.customIconId = 0;
node_data.lockId = 0;
node_data.isRO = false;
node_data.syntax = imported_node->node_syntax;
node_data.tsCreation = std::time(nullptr);
Expand Down
31 changes: 31 additions & 0 deletions src/ct/ct_actions_tree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,27 @@ bool CtActions::_is_tree_not_empty_or_error()
return true;
}

bool CtActions::_is_curr_node_or_parent_not_locked_or_error()
{
CtTreeIter node;
if (_pCtMainWin->curr_tree_iter().get_is_node_or_parent_locked(node)) {
CtDialogs::error_dialog(str::format(_("The Node {} is Locked by {}"), node.get_node_name().c_str(), node.get_node_lock_id()), *_pCtMainWin);
return true;
}
return false;
}

bool CtActions::_is_curr_node_not_read_only_or_error()
{
if (_pCtMainWin->curr_tree_iter().get_node_read_only()) {
CtDialogs::error_dialog(_("The Selected Node is Read Only"), *_pCtMainWin);
return false;
}

if (_is_curr_node_or_parent_not_locked_or_error()) {
return false;
}

return true;
}

Expand Down Expand Up @@ -172,6 +187,7 @@ void CtActions::_node_add(bool duplicate, bool add_child)
std::string title = add_child ? _("New Child Node Properties") : _("New Node Properties");
nodeData.isBold = false;
nodeData.customIconId = 0;
nodeData.lockId = 0;
nodeData.syntax = _pCtMainWin->curr_tree_iter() ? _pCtMainWin->curr_tree_iter().get_node_syntax_highlighting() : CtConst::RICH_TEXT_ID;
nodeData.isRO = false;
if (not CtDialogs::node_prop_dialog(title, _pCtMainWin, nodeData, _pCtMainWin->get_tree_store().get_used_tags()))
Expand Down Expand Up @@ -220,6 +236,7 @@ void CtActions::node_child_exist_or_create(Gtk::TreeIter parentIter, const std::
nodeData.name = nodeName;
nodeData.isBold = false;
nodeData.customIconId = 0;
nodeData.lockId = 0;
nodeData.syntax = CtConst::RICH_TEXT_ID;
nodeData.isRO = false;
_node_add_with_data(parentIter, nodeData, true, nullptr);
Expand Down Expand Up @@ -447,6 +464,7 @@ void CtActions::node_delete()
void CtActions::node_toggle_read_only()
{
if (!_is_there_selected_node_or_error()) return;
if (_is_curr_node_or_parent_not_locked_or_error()) return;
bool node_is_ro = !_pCtMainWin->curr_tree_iter().get_node_read_only();
_pCtMainWin->curr_tree_iter().set_node_read_only(node_is_ro);
_pCtMainWin->get_text_view().set_editable(!node_is_ro);
Expand All @@ -457,6 +475,19 @@ void CtActions::node_toggle_read_only()
_pCtMainWin->get_text_view().grab_focus();
}

void CtActions::node_toggle_lock()
{
if (!_is_there_selected_node_or_error()) return;
if (_is_curr_node_or_parent_not_locked_or_error()) return;
guint32 node_lock_id = _pCtMainWin->curr_tree_iter().get_node_lock_id();
node_lock_id = node_lock_id > 0u ? 0u : (guint32) _pCtMainWin->get_ct_config()->userLockId;
_pCtMainWin->curr_tree_iter().set_node_lock_id(node_lock_id);
_pCtMainWin->update_selected_node_statusbar_info();
_pCtMainWin->get_tree_store().update_node_aux_icon(_pCtMainWin->curr_tree_iter());
_pCtMainWin->update_window_save_needed(CtSaveNeededUpdType::npro);
_pCtMainWin->get_text_view().grab_focus();
}

void CtActions::node_date()
{
time_t time = std::time(nullptr);
Expand Down
14 changes: 14 additions & 0 deletions src/ct/ct_config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ void CtConfig::_populate_keyfile_from_data()
_uKeyFile->set_boolean(_currentGroup, "enable_custom_backup_dir", customBackupDirOn);
_uKeyFile->set_string(_currentGroup, "custom_backup_dir", customBackupDir);
_uKeyFile->set_integer(_currentGroup, "limit_undoable_steps", limitUndoableSteps);
_uKeyFile->set_integer(_currentGroup, "user_lock_id", userLockId);

// [keyboard]
_currentGroup = "keyboard";
Expand Down Expand Up @@ -643,6 +644,8 @@ void CtConfig::_populate_data_from_keyfile()
_populate_bool_from_keyfile("enable_custom_backup_dir", &customBackupDirOn);
_populate_string_from_keyfile("custom_backup_dir", &customBackupDir);
_populate_int_from_keyfile("limit_undoable_steps", &limitUndoableSteps);
_populate_int_from_keyfile("user_lock_id", &userLockId);
userLockId = _build_lock_id(userLockId);

// [keyboard]
_currentGroup = "keyboard";
Expand All @@ -661,6 +664,17 @@ void CtConfig::_populate_data_from_keyfile()
_populate_map_from_current_group(&customCodexecExt);
}

int CtConfig::_build_lock_id(const int id)
{
if (0 == userLockId) {
auto duration = std::chrono::system_clock::now().time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
srand(millis);
return rand();
}
return id;
}

void CtConfig::_ensure_user_styles_exist()
{
for (unsigned n = 1; n <= CtConst::NUM_USER_STYLES; ++n) {
Expand Down
2 changes: 2 additions & 0 deletions src/ct/ct_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ class CtConfig
bool customBackupDirOn{false};
std::string customBackupDir{""};
int limitUndoableSteps{20};
int userLockId{0};
bool usePandoc{true}; // Whether to use Pandoc for exporting

// [keyboard]
Expand Down Expand Up @@ -242,6 +243,7 @@ class CtConfig
void _populate_keyfile_from_data();
void _unexpected_keyfile_error(const gchar* key, const Glib::KeyFileError& kferror);

int _build_lock_id(const int id);
void _ensure_user_styles_exist();

static const size_t _maxTempKeySize{20};
Expand Down
2 changes: 2 additions & 0 deletions src/ct/ct_menu.cc
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ void CtMenu::init_actions(CtActions* pActions)
_actions.push_back(CtMenuAction{tree_cat, "tree_dup_node_subnodes", "ct_tree-nodesub-dupl", _("_Duplicate Node and Sub Nodes"), None, _("Duplicate the Selected Node With SubNodes"), sigc::mem_fun(*pActions, &CtActions::node_subnodes_duplicate)});
_actions.push_back(CtMenuAction{tree_cat, "tree_node_prop", "ct_cherry_edit", _("Change Node _Properties"), "F2", _("Edit the Properties of the Selected Node"), sigc::mem_fun(*pActions, &CtActions::node_edit)});
_actions.push_back(CtMenuAction{tree_cat, "tree_node_toggle_ro", "ct_locked", _("Toggle _Read Only"), KB_CONTROL+KB_ALT+"R", _("Toggle the Read Only Property of the Selected Node"), sigc::mem_fun(*pActions, &CtActions::node_toggle_read_only)});
_actions.push_back(CtMenuAction{tree_cat, "tree_node_toggle_lock", "ct_locked", _("Lock"), KB_CONTROL+KB_ALT+"L", _("Acquire/Release Lock on the Selected Node"), sigc::mem_fun(*pActions, &CtActions::node_toggle_lock)});
_actions.push_back(CtMenuAction{tree_cat, "tree_node_date", "ct_calendar", _("Insert Today's Node"), "F8", _("Insert a Node with Hierarchy Year/Month/Day"), sigc::mem_fun(*pActions, &CtActions::node_date)});
_actions.push_back(CtMenuAction{tree_cat, "tree_parse_info", "ct_info", _("Tree _Info"), None, _("Tree Summary Information"), sigc::mem_fun(*pActions, &CtActions::tree_info)});
_actions.push_back(CtMenuAction{tree_cat, "tree_node_up", "ct_go-up", _("Node _Up"), KB_SHIFT+KB_ALT+CtConst::STR_KEY_UP, _("Move the Selected Node Up"), sigc::mem_fun(*pActions, &CtActions::node_up)});
Expand Down Expand Up @@ -844,6 +845,7 @@ const char* CtMenu::_get_ui_str_menu()
<separator/>
<menuitem action='tree_node_prop'/>
<menuitem action='tree_node_toggle_ro'/>
<menuitem action='tree_node_toggle_lock'/>
<menuitem action='node_bookmark'/>
<menuitem action='node_unbookmark'/>
<menuitem action='tree_node_link'/>
Expand Down
4 changes: 3 additions & 1 deletion src/ct/ct_storage_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@ Gtk::TreeIter CtStorageSqlite::_node_from_db(gint64 node_id, gint64 sequence, Gt
nodeData.tags = safe_sqlite3_column_text(*uStmt, 2);
gint64 readonly_n_custom_icon_id = sqlite3_column_int64(*uStmt, 3);
nodeData.isRO = static_cast<bool>(readonly_n_custom_icon_id & 0x01);
nodeData.customIconId = readonly_n_custom_icon_id >> 1;
nodeData.customIconId = (readonly_n_custom_icon_id >> 1) & 0xffff;
nodeData.lockId = readonly_n_custom_icon_id >> 17;
gint64 richtxt_bold_foreground = sqlite3_column_int64(*uStmt, 4);
nodeData.isBold = static_cast<bool>((richtxt_bold_foreground >> 1) & 0x01);
nodeData.sequence = sequence;
Expand Down Expand Up @@ -622,6 +623,7 @@ void CtStorageSqlite::_write_node_to_db(CtTreeIter* ct_tree_iter,
// is_ro is packed with additional bitfield data
gint64 is_ro = ct_tree_iter->get_node_read_only() ? 0x01 : 0x00;
is_ro |= ct_tree_iter->get_node_custom_icon_id() << 1;
is_ro |= ((gint64)ct_tree_iter->get_node_lock_id()) << 17;
// is_richtxt is packed with additional bitfield data
gint64 is_richtxt = ct_tree_iter->get_node_is_rich_text() ? 0x01 : 0x00;
if (ct_tree_iter->get_node_is_bold())
Expand Down
2 changes: 2 additions & 0 deletions src/ct/ct_storage_xml.cc
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ Gtk::TreeIter CtStorageXml::_node_from_xml(xmlpp::Element* xml_element, gint64 s
node_data.tags = xml_element->get_attribute_value("tags");
node_data.isRO = CtStrUtil::is_str_true(xml_element->get_attribute_value("readonly"));
node_data.customIconId = (guint32)CtStrUtil::gint64_from_gstring(xml_element->get_attribute_value("custom_icon_id").c_str());
node_data.lockId = (guint32)CtStrUtil::gint64_from_gstring(xml_element->get_attribute_value("lock_id").c_str());
node_data.isBold = CtStrUtil::is_str_true(xml_element->get_attribute_value("is_bold"));
node_data.foregroundRgb24 = xml_element->get_attribute_value("foreground");
node_data.tsCreation = CtStrUtil::gint64_from_gstring(xml_element->get_attribute_value("ts_creation").c_str());
Expand Down Expand Up @@ -304,6 +305,7 @@ xmlpp::Element* CtStorageXmlHelper::node_to_xml(CtTreeIter* ct_tree_iter,
p_node_node->set_attribute("tags", ct_tree_iter->get_node_tags());
p_node_node->set_attribute("readonly", std::to_string(ct_tree_iter->get_node_read_only()));
p_node_node->set_attribute("custom_icon_id", std::to_string(ct_tree_iter->get_node_custom_icon_id()));
p_node_node->set_attribute("lock_id", std::to_string(ct_tree_iter->get_node_lock_id()));
p_node_node->set_attribute("is_bold", std::to_string(ct_tree_iter->get_node_is_bold()));
p_node_node->set_attribute("foreground", ct_tree_iter->get_node_foreground());
p_node_node->set_attribute("ts_creation", std::to_string(ct_tree_iter->get_node_creating_time()));
Expand Down
28 changes: 27 additions & 1 deletion src/ct/ct_treestore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ guint16 CtTreeIter::get_node_custom_icon_id() const
return (*this) ? (*this)->get_value(_pColumns->colCustomIconId) : 0;
}

guint32 CtTreeIter::get_node_lock_id() const
{
return (*this) ? (*this)->get_value(_pColumns->colNodeLockId) : 0;
}

void CtTreeIter::set_node_lock_id(const guint32 node_lock_id)
{
(*this)->set_value(_pColumns->colNodeLockId, node_lock_id);
}

Glib::ustring CtTreeIter::get_node_name() const
{
return (*this) ? (*this)->get_value(_pColumns->colNodeName) : "";
Expand Down Expand Up @@ -186,6 +196,19 @@ Glib::RefPtr<Gsv::Buffer> CtTreeIter::get_node_text_buffer() const
return rRetTextBuffer;
}

bool CtTreeIter::get_is_node_or_parent_locked(CtTreeIter& node) const
{
CtTreeIter iter = *this;
while (iter != nullptr) {
if ((iter.get_node_lock_id() > 0) && (iter.get_node_lock_id() != (guint32) _pCtMainWin->get_ct_config()->userLockId)) {
node = iter;
return true;
}
iter = iter.parent();
}
return false;
}

bool CtTreeIter::get_node_buffer_already_loaded() const
{
return static_cast<bool>((*this)->get_value(_pColumns->rColTextBuffer));
Expand Down Expand Up @@ -492,7 +515,8 @@ void CtTreeStore::text_view_apply_textbuffer(CtTreeIter& treeIter, CtTextView* p
pTextView->set_buffer(rTextBuffer);
pTextView->set_spell_check(treeIter.get_node_is_rich_text());
pTextView->set_sensitive(true);
pTextView->set_editable(not treeIter.get_node_read_only());
CtTreeIter locked_node;
pTextView->set_editable((not treeIter.get_node_read_only()) && (not treeIter.get_is_node_or_parent_locked(locked_node)));

for (CtAnchoredWidget* pCtAnchoredWidget : treeIter.get_anchored_widgets_fast())
{
Expand Down Expand Up @@ -587,6 +611,7 @@ void CtTreeStore::get_node_data(const Gtk::TreeIter& treeIter, CtNodeData& nodeD
nodeData.isRO = row[_columns.colNodeRO];
//row[_columns.rColPixbufAux] = ;
nodeData.customIconId = row[_columns.colCustomIconId];
nodeData.lockId = row[_columns.colNodeLockId];
nodeData.isBold = CtTreeIter::get_is_bold_from_pango_weight(row[_columns.colWeight]);
nodeData.foregroundRgb24 = row[_columns.colForeground];
nodeData.tsCreation = row[_columns.colTsCreation];
Expand All @@ -607,6 +632,7 @@ void CtTreeStore::update_node_data(const Gtk::TreeIter& treeIter, const CtNodeDa
row[_columns.colNodeRO] = nodeData.isRO;
//row[_columns.rColPixbufAux] = ; // will be updated by update_node_aux_icon
row[_columns.colCustomIconId] = (guint16)nodeData.customIconId;
row[_columns.colNodeLockId] = nodeData.lockId;
row[_columns.colWeight] = CtTreeIter::get_pango_weight_from_is_bold(nodeData.isBold);
row[_columns.colForeground] = nodeData.foregroundRgb24;
row[_columns.colTsCreation] = nodeData.tsCreation;
Expand Down
7 changes: 6 additions & 1 deletion src/ct/ct_treestore.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct CtNodeData
Glib::ustring tags;
bool isRO{false};
guint32 customIconId{0};
guint32 lockId{0};
bool isBold{false};
std::string foregroundRgb24;
gint64 tsCreation{0};
Expand All @@ -57,7 +58,7 @@ class CtTreeModelColumns final : public Gtk::TreeModel::ColumnRecord
{
add(rColPixbuf); add(colNodeName); add(rColTextBuffer); add(colNodeUniqueId);
add(colSyntaxHighlighting); add(colNodeSequence); add(colNodeTags); add(colNodeRO);
add(rColPixbufAux); add(colCustomIconId); add(colWeight); add(colForeground);
add(rColPixbufAux); add(colCustomIconId); add(colNodeLockId); add(colWeight); add(colForeground);
add(colTsCreation); add(colTsLastSave); add(colAnchoredWidgets);
}
~CtTreeModelColumns() final {}
Expand All @@ -71,6 +72,7 @@ class CtTreeModelColumns final : public Gtk::TreeModel::ColumnRecord
Gtk::TreeModelColumn<bool> colNodeRO;
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> rColPixbufAux;
Gtk::TreeModelColumn<guint16> colCustomIconId;
Gtk::TreeModelColumn<guint32> colNodeLockId;
Gtk::TreeModelColumn<int> colWeight;
Gtk::TreeModelColumn<std::string> colForeground;
Gtk::TreeModelColumn<gint64> colTsCreation;
Expand All @@ -97,6 +99,8 @@ class CtTreeIter : public Gtk::TreeIter
void set_node_id(const gint64 new_id);
std::vector<gint64> get_children_node_ids() const;
guint16 get_node_custom_icon_id() const;
guint32 get_node_lock_id() const;
void set_node_lock_id(guint32 node_lock_id);
Glib::ustring get_node_name() const;
void set_node_name(const Glib::ustring& node_name);
Glib::ustring get_node_tags() const;
Expand All @@ -112,6 +116,7 @@ class CtTreeIter : public Gtk::TreeIter
void set_node_text_buffer(Glib::RefPtr<Gsv::Buffer> new_buffer, const std::string& new_syntax_hilighting);
Glib::RefPtr<Gsv::Buffer> get_node_text_buffer() const;
bool get_node_buffer_already_loaded() const;
bool get_is_node_or_parent_locked(CtTreeIter& node) const;

void remove_all_embedded_widgets();
std::list<CtAnchoredWidget*> get_anchored_widgets_fast(const char doSort = 'n');
Expand Down