]> uap-core.de Git - note.git/commitdiff
add LockManager, set note textarea to readonly when the same note is already opened
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 14 Jun 2026 09:19:12 +0000 (11:19 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 14 Jun 2026 09:19:12 +0000 (11:19 +0200)
15 files changed:
application/src/backend.rs
application/src/lockmanager.rs [new file with mode: 0644]
application/src/main.rs
application/src/note.rs
application/src/notebook.rs
application/src/window.rs
ui-rs/src/ui/toolkit.rs
ui/common/action.c
ui/common/context.c
ui/common/context.h
ui/common/document.c
ui/common/types.c
ui/gtk/text.c
ui/gtk/text.h
ui/ui/toolkit.h

index 84ddcfc36ad759cc8aceec1d6ada92166ee66b42..169c382fe5bf39fcfeb344762d5fd17645cf7113 100644 (file)
@@ -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<Runtime>,
     db: DatabaseConnection,
 
+    pub lockmgr: Arc<LockManager>,
+
     pub current_profile: profile::Model,
     pub broadcast: tokio::sync::broadcast::Sender<BroadcastMessage>,
 
@@ -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 (file)
index 0000000..1f0d9a0
--- /dev/null
@@ -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<std::sync::Mutex<HashMap<i32, bool>>>,
+}
+
+impl LockManager {
+    pub fn new() -> Self {
+        LockManager {
+            locks: Arc::new(std::sync::Mutex::new(HashMap::new())),
+        }
+    }
+
+    fn locks(&self) -> std::sync::MutexGuard<'_, HashMap<i32, bool>> {
+        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<NoteLock> {
+        let mut locks = self.locks();
+        if locks.contains_key(&note_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<std::sync::Mutex<HashMap<i32, bool>>>,
+    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
index abd842d35167c98aed1a9df020174412318cabd0..636fb01abb50e2ef18117cf228674190a04f69d2 100644 (file)
@@ -31,6 +31,7 @@ mod backend;
 mod newnotebook;
 mod notebook;
 mod note;
+mod lockmanager;
 
 use std::env;
 use entity::collection::{create_notebook_hierarchy, Node};
index 98b72c36b6804c1e0cd6188564ef62ec00930d7a..ecc1c0144b464ac140ff198c0120d87b421d2be7 100644 (file)
@@ -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<BackendHandle>,
 
     pub id: NoteId,
+    pub lock: Option<NoteLock>,
 
     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<Note> {
+        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);
 
index 7173693d3301d63a3f17bb960bbcfd0f9b0c4603..8a9f36e134db77b1a13652d0e70f6545bf38c088 100644 (file)
@@ -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(&note.data);
-                    n.load_content(d);
-                }
-            })
+            note_data.into_doc(&note.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()
     }
 }
 
index 02592549587394a09ff940e485a2912edb14c44d..c41d0107768b05bb526fffe7c31836536ec6eaa7 100644 (file)
@@ -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<MainWindow>) -> UiObject<MainWi
                             let notebook_doc = nb.get_doc(&e.data.backend);
                             e.obj.ctx.attach(&notebook_doc);
                             e.data.selected_notebook = Some(notebook_doc);
+                            e.obj.splitview_set_visible(0, !nav.note_maximized);
 
                             e.data.navigation.push(nav);
                         }
index 1bae83018a27ec2b02e4194470b7869062d84560..09b0cd149fc4e203702f0a4061ce409a146019fc 100644 (file)
@@ -107,14 +107,14 @@ impl UiContext {
     pub fn onattach_action(&self, action: &str) {
         let cstr = CString::new(action).unwrap();
         unsafe {
-            ui_onattach_action(self.ptr, cstr.as_ptr());
+            ui_context_onattach_action(self.ptr, cstr.as_ptr());
         }
     }
 
     pub fn ondetach_action(&self, action: &str) {
         let cstr = CString::new(action).unwrap();
         unsafe {
-            ui_ondetach_action(self.ptr, cstr.as_ptr());
+            ui_context_ondetach_action(self.ptr, cstr.as_ptr());
         }
     }
 
@@ -767,8 +767,15 @@ impl UiText {
             ui_text_remove(self.ptr, begin as c_int, end as c_int);
         }
     }
+
+    pub fn set_readonly(&mut self, readonly: bool) {
+        unsafe {
+            ui_text_setreadonly(self.ptr, readonly as c_int);
+        }
+    }
 }
 
+
 impl UiString {
     pub fn init(&mut self, ctx: &UiContext, name: Option<&str>) {
         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;
index fea640710cee12f605c17fcdefcd6f06a471447d..683ed376d6c635da0174e14f1d55ce685b99f387 100644 (file)
@@ -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;
index c2eb53df8b7a57e152c6ce4eb2308cb967a3a893..dfca8be73e20ab2fe3519c90e077df517fce9706 100644 (file)
@@ -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);
+}
index 75be090e2a5cabcf2514bc5d2b3236b4147e1a49..76eca287e2dd7cff81b283c3afb035dae38e01ea 100644 (file)
@@ -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
index 0dc20f80a6d985bf7c3e526a768bc025f276ea95..460ca8193016b575792367bd1eccc9ebc69ca8e8 100644 (file)
@@ -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);
+}
+
index fb5f49dd380a39ed3b7fd2890aac0d13e5bc4c9c..fd1712bf8bec715599d71297c7f221bd32b2655d 100644 (file)
@@ -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;
+    }
+}
index c9f83c0b340e8017b5c90c741d0f06447947dc3d..65e507f8e12fe66fa271289778701ec6e752a72f 100644 (file)
@@ -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);
 }
index 772ab78e4f9d2694b81e1b0641a84643ca907228..cb12ea1d788d25c571f185e6f0810b8d05a9b86a 100644 (file)
@@ -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);
index cb06ae8ff8e7eeb23ad835b40abb7030ff02ed9a..1b476e4074057c2be8ab07796b162c1c54a00ca9 100644 (file)
@@ -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));