From 35526fb8ce5199e325bad0c8237ccd2cf82c7e6e Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Sun, 14 Jun 2026 11:19:12 +0200 Subject: [PATCH] add LockManager, set note textarea to readonly when the same note is already opened --- application/src/backend.rs | 39 +++++++++++++++++ application/src/lockmanager.rs | 79 ++++++++++++++++++++++++++++++++++ application/src/main.rs | 1 + application/src/note.rs | 62 ++++++++++++++++++++++++++ application/src/notebook.rs | 41 +++++++++++++----- application/src/window.rs | 2 + ui-rs/src/ui/toolkit.rs | 16 +++++-- ui/common/action.c | 16 +++++++ ui/common/context.c | 55 ++++++++++++++++++++--- ui/common/context.h | 15 ++++++- ui/common/document.c | 21 +++++++++ ui/common/types.c | 14 ++++++ ui/gtk/text.c | 14 ++++++ ui/gtk/text.h | 1 + ui/ui/toolkit.h | 8 ++++ 15 files changed, 360 insertions(+), 24 deletions(-) create mode 100644 application/src/lockmanager.rs diff --git a/application/src/backend.rs b/application/src/backend.rs index 84ddcfc..169c382 100644 --- a/application/src/backend.rs +++ b/application/src/backend.rs @@ -1,3 +1,32 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + use std::future::Future; use std::pin::Pin; use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, DbErr, EntityTrait, QueryFilter, ColumnTrait, Set, QueryOrder}; @@ -14,11 +43,14 @@ use entity::profile::Entity as Profile; use entity::collection::{create_notebook_hierarchy, CollectionType, Entity as Collection, Node}; use entity::note::{Entity as Note}; use entity::notecontent::{Entity as NoteContent}; +use crate::lockmanager::LockManager; pub struct Backend { rt: Arc, db: DatabaseConnection, + pub lockmgr: Arc, + pub current_profile: profile::Model, pub broadcast: tokio::sync::broadcast::Sender, @@ -62,6 +94,12 @@ impl Default for NoteId { } } +impl NoteId { + pub fn is_new(&self) -> bool { + matches!(self, NoteId::TmpId(_)) + } +} + #[derive(Clone, Default)] pub struct NoteTitleUpdate { pub collection_id: i32, @@ -103,6 +141,7 @@ impl Backend { Ok(Self { rt, db, + lockmgr: Arc::new(LockManager::new()), current_profile: profile, broadcast: tx, broadcast_notify: None diff --git a/application/src/lockmanager.rs b/application/src/lockmanager.rs new file mode 100644 index 0000000..1f0d9a0 --- /dev/null +++ b/application/src/lockmanager.rs @@ -0,0 +1,79 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +use std::collections::HashMap; +use std::sync::Arc; + +pub struct LockManager { + locks: Arc>>, +} + +impl LockManager { + pub fn new() -> Self { + LockManager { + locks: Arc::new(std::sync::Mutex::new(HashMap::new())), + } + } + + fn locks(&self) -> std::sync::MutexGuard<'_, HashMap> { + self.locks.lock().unwrap_or_else(|e| { + // Very unlikely that the mutex gets poisoned (probably only by OOM), and if it does, who cares + println!("Error: LockManager poisoned"); + self.locks.clear_poison(); + e.into_inner() + }) + } + + pub fn lock_note(&self, note_id: i32) -> Option { + let mut locks = self.locks(); + if locks.contains_key(¬e_id) { + println!("note {} already locked", note_id); + return None + } + println!("lock note {}", note_id); + locks.insert(note_id, true); + + let note_lock = NoteLock { + locks: self.locks.clone(), + note_id: note_id + }; + Some(note_lock) + } +} + +pub struct NoteLock { + locks: Arc>>, + note_id: i32 +} + +impl Drop for NoteLock { + fn drop(&mut self) { + println!("note {} unlocked", self.note_id); + let mut locks = self.locks.lock().unwrap_or_else(|e|{ e.into_inner()}); + locks.remove(&self.note_id); + } +} \ No newline at end of file diff --git a/application/src/main.rs b/application/src/main.rs index abd842d..636fb01 100644 --- a/application/src/main.rs +++ b/application/src/main.rs @@ -31,6 +31,7 @@ mod backend; mod newnotebook; mod notebook; mod note; +mod lockmanager; use std::env; use entity::collection::{create_notebook_hierarchy, Node}; diff --git a/application/src/note.rs b/application/src/note.rs index 98b72c3..ecc1c01 100644 --- a/application/src/note.rs +++ b/application/src/note.rs @@ -1,3 +1,31 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + use std::rc::Rc; use std::sync::atomic::{AtomicU64, Ordering}; use sea_orm::sea_query::prelude::Utc; @@ -6,6 +34,7 @@ use entity::note::NoteType; use ui_rs::{action, ui_actions, UiModel}; use ui_rs::ui::*; use crate::backend::{BackendHandle, BroadcastMessage, NoteId, NoteTitleUpdate}; +use crate::lockmanager::NoteLock; use crate::window::NoteTypeTabView; static TMP_ID: AtomicU64 = AtomicU64::new(1); @@ -16,6 +45,7 @@ pub struct Note { pub backend: Rc, pub id: NoteId, + pub lock: Option, pub collection_id: i32, pub content_id: i32, @@ -44,6 +74,7 @@ impl Note { collection_id: collection_id, id: id, + lock: None, content_id: 0, kind: NoteType::PlainTextNote, extract_title: false, @@ -56,6 +87,37 @@ impl Note { } } + pub fn into_doc(self, model: &entity::note::Model) -> UiDoc { + UiDoc::new2(self, |n,d| { + n.doc = d.doc_ref(); + if n.id.is_new() { + n.init_new(); + } else { + n.init_from_model(model); + n.load_content(d); + } + + d.ctx.onattach_action("attached"); + d.ctx.ondetach_action("detached"); + }) + } + + #[action] + pub fn attached(&mut self, _event: &ActionEvent) { + if let NoteId::Id(note_id) = self.id { + self.lock = self.backend.backend.lockmgr.lock_note(note_id); + if self.lock.is_none() { + self.text.set_readonly(true); + } + } + } + + #[action] + pub fn detached(&mut self, _event: &ActionEvent) { + self.lock = None; + self.text.set_readonly(false); + } + pub fn init_from_model(&mut self, model: &entity::note::Model) { self.id = NoteId::Id(model.note_id); diff --git a/application/src/notebook.rs b/application/src/notebook.rs index 7173693..8a9f36e 100644 --- a/application/src/notebook.rs +++ b/application/src/notebook.rs @@ -1,3 +1,31 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + use std::any::Any; use std::rc::Rc; use ui_rs::{action, ui_actions, UiModel}; @@ -124,16 +152,7 @@ impl Notebook { } else { // Create the new note let note_data = crate::note::Note::new(note.id.clone(), self.collection_id, self.backend.clone()); - // Create the note document object - UiDoc::new2(note_data, |n,d| { - n.doc = d.doc_ref(); - if note.is_new() { - n.init_new(); - } else { - n.init_from_model(¬e.data); - n.load_content(d); - } - }) + note_data.into_doc(¬e.data) }; note.model = Some(doc.clone()); @@ -386,7 +405,7 @@ impl NoteItem { } pub fn is_new(&self) -> bool { - return matches!(self.id, NoteId::TmpId(_)) + self.id.is_new() } } diff --git a/application/src/window.rs b/application/src/window.rs index 0259254..c41d010 100644 --- a/application/src/window.rs +++ b/application/src/window.rs @@ -25,6 +25,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ + use std::any::Any; use ui_rs::{action, ui_actions, UiModel}; use ui_rs::ui::*; @@ -211,6 +212,7 @@ pub fn create_window(app: &App, ctx: &AppContext) -> UiObject) { let c_string = name.map(|n| CString::new(n).unwrap()); @@ -1288,8 +1295,8 @@ extern "C" { fn ui_detach_document(ctx: *mut ffi::UiContext, doc: *mut c_void); fn ui_onattach(ctx: *mut ffi::UiContext, callback: UiCallback, data: *mut c_void); fn ui_ondetach(ctx: *mut ffi::UiContext, callback: UiCallback, data: *mut c_void); - fn ui_onattach_action(ctx: *mut ffi::UiContext, action: *const c_char); - fn ui_ondetach_action(ctx: *mut ffi::UiContext, action: *const c_char); + fn ui_context_onattach_action(ctx: *mut ffi::UiContext, action: *const c_char); + fn ui_context_ondetach_action(ctx: *mut ffi::UiContext, action: *const c_char); fn ui_context_document(ctx: *mut ffi::UiContext) -> *mut c_void; fn ui_context_obj(ctx: *mut ffi::UiContext) -> *mut ffi::UiObject; @@ -1376,6 +1383,7 @@ extern "C" { fn ui_text_selection(text: *const ffi::UiText, begin: *mut c_int, end: *mut c_int); fn ui_text_length(text: *const ffi::UiText) -> c_int; fn ui_text_remove(text: *mut ffi::UiText, begin: c_int, end: c_int); + fn ui_text_setreadonly(text: *mut ffi::UiText, readonly: c_int); fn ui_call_action(ctx: *mut ffi::UiContext, action: *const c_char) -> c_int; fn ui_call_action3(ctx: *mut ffi::UiContext, action: *const c_char, data: *mut c_void, typeid: u64) -> c_int; diff --git a/ui/common/action.c b/ui/common/action.c index fea6407..683ed37 100644 --- a/ui/common/action.c +++ b/ui/common/action.c @@ -227,6 +227,22 @@ void ui_broadcast_action2(const char *action_name, void *eventdata, int intval) broadcast_action(action_name, eventdata, UI_EVENT_DATA_POINTER, intval); } +void ui_call_action_on(UiContext *ctx, const char *action) { + UiEvent event; + memset(&event, 0, sizeof(UiEvent)); + event.obj = ctx->obj; + event.window = event.obj ? event.obj->window : NULL; + event.document = ctx->self_doc ? ctx->self_doc : ctx->document; + + UiAction *a = NULL; + if(ctx->actions) { + a = cxMapGet(ctx->actions, action); + } + if(a && a->callback) { + a->callback(&event, a->userdata); + } +} + typedef struct UiActionBroadcast { char *action; void *eventdata; diff --git a/ui/common/context.c b/ui/common/context.c index c2eb53d..dfca8be 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -124,8 +124,8 @@ void uic_context_destroy(UiContext *ctx, void *document) { ev.intval = 0; ev.set = 0; - if(ctx->close_callback) { - ctx->close_callback(&ev, ctx->close_data); + if(ctx->onclose) { + ctx->onclose(&ev, ctx->onclosedata); } CxIterator i = cxListIterator(ctx->destroy_handler); @@ -190,6 +190,12 @@ void uic_context_attach_document(UiContext *ctx, void *document) { doc_ctx->ref++; uic_context_update_bindings(doc_ctx); + if(doc_ctx->onattach) { + UiEvent event; + memset(&event, 0, sizeof(UiEvent)); + event.document = document; + doc_ctx->onattach(&event, doc_ctx->onattachdata); + } } static void uic_context_unbind_vars(UiContext *ctx) { @@ -233,12 +239,18 @@ void uic_context_detach_document(UiContext *ctx, void *document) { cxListRemove(ctx->documents, docIndex); ctx->document = cxListAt(ctx->documents, 0); - UiContext *docctx = ui_document_context(document); - uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent - docctx->parent = NULL; + UiContext *doc_ctx = ui_document_context(document); + uic_context_unbind_vars(doc_ctx); // unbind all doc/subdoc vars from the parent + doc_ctx->parent = NULL; ui_document_unref(document); ui_update_action_bindings(ctx); + if(doc_ctx->ondetach) { + UiEvent event; + memset(&event, 0, sizeof(UiEvent)); + event.document = document; + doc_ctx->ondetach(&event, doc_ctx->ondetachdata); + } } void uic_context_detach_all(UiContext *ctx) { @@ -600,8 +612,8 @@ void ui_detach_document(UiContext *ctx, void *document) { } void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) { - ctx->close_callback = fnc; - ctx->close_data = udata; + ctx->onclose = fnc; + ctx->onclosedata = udata; } UiContext* ui_context_parent(UiContext *ctx) { @@ -931,3 +943,32 @@ UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name) { UiVar *var = uic_get_var_t(ctx, name, UI_VAR_GENERIC); return var ? var->value : NULL; } + + + +void ui_context_onattach(UiContext *ctx, ui_callback cb, void *data) { + ctx->onattach = cb; + ctx->onattachdata = data; +} + +void ui_context_ondetach(UiContext *ctx, ui_callback cb, void *data) { + ctx->ondetach = cb; + ctx->ondetachdata = data; +} + +static void attachment_action_callback(UiEvent *event, void *action) { + if(event->document) { + UiContext *ctx = ui_document_context(event->document); + ui_call_action_on(ctx, action); + } +} + +void ui_context_onattach_action(UiContext *ctx, const char *action) { + ctx->onattach = attachment_action_callback; + ctx->onattachdata = ui_strdup(ctx, action); +} + +void ui_context_ondetach_action(UiContext *ctx, const char *action) { + ctx->ondetach = attachment_action_callback; + ctx->ondetachdata = ui_strdup(ctx, action); +} diff --git a/ui/common/context.h b/ui/common/context.h index 75be090..76eca28 100644 --- a/ui/common/context.h +++ b/ui/common/context.h @@ -98,8 +98,14 @@ struct UiContext { // attaching a document will automatically detach the current document UiBool single_document_mode; - ui_callback close_callback; - void *close_data; + ui_callback onattach; + void *onattachdata; + + ui_callback ondetach; + void *ondetachdata; + + ui_callback onclose; + void *onclosedata; unsigned int ref; }; @@ -167,6 +173,11 @@ void uic_add_state_widget(UiContext *ctx, void *widget, ui_enablefunc enable, Cx void uic_add_state_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *states, size_t numstates); void uic_remove_state_widget(UiContext *ctx, void *widget); +UIEXPORT void ui_context_onattach(UiContext *ctx, ui_callback cb, void *data); +UIEXPORT void ui_context_ondetach(UiContext *ctx, ui_callback cb, void *data); +UIEXPORT void ui_context_onattach_action(UiContext *ctx, const char *action); +UIEXPORT void ui_context_ondetach_action(UiContext *ctx, const char *action); + #ifdef __cplusplus } #endif diff --git a/ui/common/document.c b/ui/common/document.c index 0dc20f8..460ca81 100644 --- a/ui/common/document.c +++ b/ui/common/document.c @@ -86,3 +86,24 @@ void* ui_context_document(UiContext *ctx) { UiObject* ui_context_obj(UiContext *ctx) { return ctx->obj; } + +void ui_document_onattach(void *doc, ui_callback cb, void *data) { + UiContext *ctx = ui_document_context(doc); + ui_context_onattach(ctx, cb, data); +} + +void ui_document_ondetach(void *doc, ui_callback cb, void *data) { + UiContext *ctx = ui_document_context(doc); + ui_context_ondetach(ctx, cb, data); +} + +void ui_document_onattach_action(void *doc, const char *action) { + UiContext *ctx = ui_document_context(doc); + ui_context_onattach_action(ctx, action); +} + +void ui_document_ondetach_action(void *doc, const char *action) { + UiContext *ctx = ui_document_context(doc); + ui_context_ondetach_action(ctx, action); +} + diff --git a/ui/common/types.c b/ui/common/types.c index fb5f49d..fd1712b 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -438,6 +438,9 @@ UiString* ui_string_new(UiContext *ctx, const char *name) { } static void text_destroy(UiText *t) { + if(t->value.free) { + t->value.free(t->value.ptr); + } if(t->destroy) { t->destroy(t); } @@ -702,6 +705,7 @@ void uic_text_copy(UiText *from, UiText *to) { to->selection = from->selection; to->length = from->length; to->remove = from->remove; + to->setreadonly = from->setreadonly; to->restore = from->restore; to->save = from->save; to->destroy = from->destroy; @@ -801,10 +805,12 @@ void uic_text_unbind(UiText *t) { t->replace = NULL; t->setposition = NULL; t->position = NULL; + t->showposition = NULL; t->selection = NULL; t->setselection = NULL; t->length = NULL; t->remove = NULL; + t->setreadonly = NULL; } void uic_range_unbind(UiRange *r) { @@ -1081,3 +1087,11 @@ void ui_text_remove(UiText *text, int begin, int end) { text->remove(text, begin, end); } } + +void ui_text_setreadonly(UiText *text, int readonly) { + if(text->setreadonly) { + text->setreadonly(text, readonly); + } else { + text->readonly = readonly; + } +} diff --git a/ui/gtk/text.c b/ui/gtk/text.c index c9f83c0..65e507f 100644 --- a/ui/gtk/text.c +++ b/ui/gtk/text.c @@ -192,6 +192,11 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) { value->value.free(value->value.ptr); } } + + if(value->readonly) { + gtk_text_view_set_editable(GTK_TEXT_VIEW(text_area), FALSE); + } + gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_area), buf); value->obj = text_area; value->save = ui_textarea_save; @@ -209,6 +214,7 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) { value->selection = ui_textarea_selection; value->length = ui_textarea_length; value->remove = ui_textarea_remove; + value->setreadonly = ui_textarea_setreadonly; value->data1 = buf; value->data2 = NULL; value->datatype == UI_TEXT_TYPE_BUFFER; @@ -341,6 +347,7 @@ void ui_textarea_restore(UiText *text) { text->datatype = UI_TEXT_TYPE_BUFFER; } gtk_text_view_set_buffer(GTK_TEXT_VIEW(textarea), text->data1); + gtk_text_view_set_editable(GTK_TEXT_VIEW(textarea), !text->readonly); } void ui_textarea_text_destroy(UiText *text) { @@ -465,6 +472,13 @@ void ui_textarea_remove(UiText *text, int begin, int end) { gtk_text_buffer_delete(buf, &ib, &ie); } +void ui_textarea_setreadonly(UiText *text, int readonly) { + if(text->obj) { + gtk_text_view_set_editable(GTK_TEXT_VIEW(text->obj), !readonly); + } + text->readonly = readonly; +} + void ui_textarea_realize_event(GtkWidget *widget, gpointer data) { gtk_widget_grab_focus(widget); } diff --git a/ui/gtk/text.h b/ui/gtk/text.h index 772ab78..cb12ea1 100644 --- a/ui/gtk/text.h +++ b/ui/gtk/text.h @@ -132,6 +132,7 @@ void ui_textarea_setselection(UiText *text, int begin, int end); void ui_textarea_selection(UiText *text, int *begin, int *end); int ui_textarea_length(UiText *text); void ui_textarea_remove(UiText *text, int begin, int end); +void ui_textarea_setreadonly(UiText *text, int readonly); void ui_textarea_realize_event(GtkWidget *widget, gpointer data); //void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea); diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index cb06ae8..1b476e4 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -408,8 +408,10 @@ struct UiText { void (*selection)(UiText*, int*, int*); /* text, begin, end */ int (*length)(UiText*); void (*remove)(UiText*, int, int); /* text, begin, end */ + void (*setreadonly)(UiText*, int); UiStr value; int pos; + int readonly; void *obj; int datatype; void *data1; @@ -576,6 +578,10 @@ UIEXPORT void* ui_document_new(size_t size); UIEXPORT void ui_document_ref(void *doc); UIEXPORT void ui_document_unref(void *doc); UIEXPORT void ui_document_destroy(void *doc); +UIEXPORT void ui_document_onattach(void *doc, ui_callback cb, void *data); +UIEXPORT void ui_document_ondetach(void *doc, ui_callback cb, void *data); +UIEXPORT void ui_document_onattach_action(void *doc, const char *action); +UIEXPORT void ui_document_ondetach_action(void *doc, const char *action); UIEXPORT UiContext* ui_document_context(void *doc); UIEXPORT void* ui_context_document(UiContext *ctx); @@ -608,6 +614,7 @@ UIEXPORT void ui_broadcast_action3(const char *action_name, void *ptr, uint64_t UIEXPORT void ui_mainthread_broadcast(const char *action_name); UIEXPORT void ui_mainthread_broadcast2(const char *action_name, void *eventdata, int intval); UIEXPORT void ui_mainthread_broadcast3(const char *action_name, void *ptr, uint64_t type_id); +UIEXPORT void ui_call_action_on(UiContext *ctx, const char *action); UIEXPORT void ui_set_state(UiContext *ctx, int state); UIEXPORT void ui_unset_state(UiContext *ctx, int state); @@ -740,6 +747,7 @@ UIEXPORT void ui_text_setselection(UiText *text, int begin, int end); UIEXPORT void ui_text_selection(UiText *text, int *begin, int *end); UIEXPORT int ui_text_length(UiText *text); UIEXPORT void ui_text_remove(UiText *text, int begin, int end); +UIEXPORT void ui_text_setreadonly(UiText *text, int readonly); UIEXPORT UiStr ui_str(char *cstr); UIEXPORT UiStr ui_str_free(char *str, void (*free)(void *v)); -- 2.52.0