1use crate::common::Rational32;
2
3#[derive(Debug, Clone, Copy)]
5pub struct ObjectHandle {
6 pub(crate) internal: aviutl2_sys::plugin2::OBJECT_HANDLE,
7}
8impl From<aviutl2_sys::plugin2::OBJECT_HANDLE> for ObjectHandle {
9 fn from(value: aviutl2_sys::plugin2::OBJECT_HANDLE) -> Self {
10 Self { internal: value }
11 }
12}
13impl From<ObjectHandle> for aviutl2_sys::plugin2::OBJECT_HANDLE {
14 fn from(value: ObjectHandle) -> Self {
15 value.internal
16 }
17}
18
19unsafe impl Send for ObjectHandle {}
21unsafe impl Sync for ObjectHandle {}
22
23#[derive(Debug, Clone, Copy)]
29pub struct EditInfo {
30 pub width: usize,
32 pub height: usize,
34 pub fps: Rational32,
36 pub sample_rate: usize,
38 pub frame: usize,
40 pub layer: usize,
42 pub frame_max: usize,
44 pub layer_max: usize,
46}
47
48impl EditInfo {
49 pub unsafe fn from_ptr(ptr: *const aviutl2_sys::plugin2::EDIT_INFO) -> Self {
53 let raw = unsafe { &*ptr };
54 Self {
55 width: raw.width as usize,
56 height: raw.height as usize,
57 fps: Rational32::new(raw.rate, raw.scale),
58 sample_rate: raw.sample_rate as usize,
59 frame: raw.frame as usize,
60 layer: raw.layer as usize,
61 frame_max: raw.frame_max as usize,
62 layer_max: raw.layer_max as usize,
63 }
64 }
65}
66
67#[derive(Debug, Clone, Copy)]
69pub struct ObjectLayerFrame {
70 pub layer: usize,
71 pub start: usize,
72 pub end: usize,
73}
74
75#[derive(thiserror::Error, Debug)]
77pub enum EditSectionError {
78 #[error("api call failed")]
79 ApiCallFailed,
80 #[error("object does not exist")]
81 ObjectDoesNotExist,
82 #[error("input utf-8 string contains null byte")]
83 InputCstrContainsNull(#[from] std::ffi::NulError),
84 #[error("input utf-16 string contains null byte")]
85 InputCwstrContainsNull(#[from] crate::common::NullByteError),
86 #[error("value is out of range")]
87 ValueOutOfRange(#[from] std::num::TryFromIntError),
88 #[error("api returned non-utf8 data")]
89 NonUtf8Data(#[from] std::str::Utf8Error),
90
91 #[cfg(feature = "aviutl2-alias")]
92 #[error("alias parse error: {0}")]
93 ParseFailed(#[from] aviutl2_alias::TableParseError),
94}
95
96pub type EditSectionResult<T> = Result<T, EditSectionError>;
97
98#[derive(Debug)]
100pub struct EditSection {
101 pub info: EditInfo,
103
104 pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_SECTION,
105}
106
107impl EditSection {
108 pub unsafe fn from_ptr(ptr: *mut aviutl2_sys::plugin2::EDIT_SECTION) -> Self {
114 Self {
115 internal: ptr,
116 info: unsafe { EditInfo::from_ptr((*ptr).info) },
117 }
118 }
119
120 pub fn create_object_from_alias(
136 &self,
137 alias: &str,
138 layer: usize,
139 frame: usize,
140 length: usize,
141 ) -> EditSectionResult<ObjectHandle> {
142 let c_alias = std::ffi::CString::new(alias)?;
143 let object_handle = unsafe {
144 ((*self.internal).create_object_from_alias)(
145 c_alias.as_ptr(),
146 layer.try_into()?,
147 frame.try_into()?,
148 length.try_into()?,
149 )
150 };
151 if object_handle.is_null() {
152 return Err(EditSectionError::ApiCallFailed);
153 }
154 Ok(ObjectHandle {
155 internal: object_handle,
156 })
157 }
158
159 pub fn find_object_after(
166 &self,
167 layer: usize,
168 frame: usize,
169 ) -> EditSectionResult<Option<ObjectHandle>> {
170 let object_handle =
171 unsafe { ((*self.internal).find_object)(layer.try_into()?, frame.try_into()?) };
172 if object_handle.is_null() {
173 Ok(None)
174 } else {
175 Ok(Some(ObjectHandle {
176 internal: object_handle,
177 }))
178 }
179 }
180
181 pub fn count_object_effect(
192 &self,
193 object: &ObjectHandle,
194 effect: &str,
195 ) -> EditSectionResult<usize> {
196 self.ensure_object_exists(object)?;
197 let c_effect = crate::common::CWString::new(effect)?;
198 let count =
199 unsafe { ((*self.internal).count_object_effect)(object.internal, c_effect.as_ptr()) };
200 Ok(count.try_into()?)
201 }
202
203 pub fn get_object_layer_frame(
205 &self,
206 object: &ObjectHandle,
207 ) -> EditSectionResult<ObjectLayerFrame> {
208 self.ensure_object_exists(object)?;
209 let object = unsafe { ((*self.internal).get_object_layer_frame)(object.internal) };
210 Ok(ObjectLayerFrame {
211 layer: object.layer.try_into()?,
212 start: object.start.try_into()?,
213 end: object.end.try_into()?,
214 })
215 }
216
217 pub fn get_object_alias(&self, object: &ObjectHandle) -> EditSectionResult<String> {
219 self.ensure_object_exists(object)?;
220 let alias_ptr = unsafe { ((*self.internal).get_object_alias)(object.internal) };
221 if alias_ptr.is_null() {
222 return Err(EditSectionError::ApiCallFailed);
223 }
224 let c_str = unsafe { std::ffi::CStr::from_ptr(alias_ptr) };
225 let alias = c_str.to_str()?.to_owned();
226 Ok(alias)
227 }
228
229 pub fn get_object_effect_item(
238 &self,
239 object: &ObjectHandle,
240 effect_name: &str,
241 effect_index: usize,
242 item: &str,
243 ) -> EditSectionResult<String> {
244 self.ensure_object_exists(object)?;
245 let c_effect_name = crate::common::CWString::new(&format!("{effect_name}:{effect_index}"))?;
246 let c_item = crate::common::CWString::new(item)?;
247 let value_ptr = unsafe {
248 ((*self.internal).get_object_item_value)(
249 object.internal,
250 c_effect_name.as_ptr(),
251 c_item.as_ptr(),
252 )
253 };
254 if value_ptr.is_null() {
255 return Err(EditSectionError::ApiCallFailed);
256 }
257 let c_str = unsafe { std::ffi::CStr::from_ptr(value_ptr) };
258 let value = c_str.to_str()?.to_owned();
259 Ok(value)
260 }
261
262 pub fn set_object_effect_item(
272 &self,
273 object: &ObjectHandle,
274 effect_name: &str,
275 effect_index: usize,
276 item: &str,
277 value: &str,
278 ) -> EditSectionResult<()> {
279 self.ensure_object_exists(object)?;
280 let c_effect_name = crate::common::CWString::new(&format!("{effect_name}:{effect_index}"))?;
281 let c_item = crate::common::CWString::new(item)?;
282 let c_value = std::ffi::CString::new(value)?;
283 let success = unsafe {
284 ((*self.internal).set_object_item_value)(
285 object.internal,
286 c_effect_name.as_ptr(),
287 c_item.as_ptr(),
288 c_value.as_ptr(),
289 )
290 };
291 if !success {
292 return Err(EditSectionError::ApiCallFailed);
293 }
294 Ok(())
295 }
296
297 pub fn move_object(
299 &self,
300 object: &ObjectHandle,
301 new_layer: usize,
302 new_start_frame: usize,
303 ) -> EditSectionResult<()> {
304 self.ensure_object_exists(object)?;
305 let success = unsafe {
306 ((*self.internal).move_object)(
307 object.internal,
308 new_layer.try_into()?,
309 new_start_frame.try_into()?,
310 )
311 };
312 if !success {
313 return Err(EditSectionError::ApiCallFailed);
314 }
315 Ok(())
316 }
317
318 pub fn delete_object(&self, object: &ObjectHandle) -> EditSectionResult<()> {
320 self.ensure_object_exists(object)?;
321 unsafe { ((*self.internal).delete_object)(object.internal) };
322 Ok(())
323 }
324
325 pub fn get_focused_object(&self) -> EditSectionResult<Option<ObjectHandle>> {
327 let object_handle = unsafe { ((*self.internal).get_focus_object)() };
328 if object_handle.is_null() {
329 Ok(None)
330 } else {
331 Ok(Some(ObjectHandle {
332 internal: object_handle,
333 }))
334 }
335 }
336
337 pub fn get_selected_objects(&self) -> EditSectionResult<Vec<ObjectHandle>> {
339 let mut handles = Vec::new();
340 let num_objects = unsafe { ((*self.internal).get_selected_object_num)() };
341 for i in 0..num_objects {
342 let object_handle = unsafe { ((*self.internal).get_selected_object)(i) };
343 if object_handle.is_null() {
344 return Err(EditSectionError::ApiCallFailed);
345 }
346 handles.push(ObjectHandle {
347 internal: object_handle,
348 });
349 }
350 Ok(handles)
351 }
352
353 pub fn focus_object(&self, object: &ObjectHandle) -> EditSectionResult<()> {
359 self.ensure_object_exists(object)?;
360 unsafe { ((*self.internal).set_focus_object)(object.internal) };
361 Ok(())
362 }
363
364 pub fn object_exists(&self, object: &ObjectHandle) -> bool {
370 let object = unsafe { ((*self.internal).get_object_layer_frame)(object.internal) };
371 object.layer != -1
372 }
373
374 fn ensure_object_exists(&self, object: &ObjectHandle) -> EditSectionResult<()> {
375 if !self.object_exists(object) {
376 return Err(EditSectionError::ObjectDoesNotExist);
377 }
378 Ok(())
379 }
380
381 #[doc(hidden)]
382 #[expect(private_bounds)]
383 pub fn __output_log_if_error<T: MenuCallbackReturn>(&self, result: T) {
384 if let Some(err_msg) = result.into_optional_error() {
385 let _ = crate::logger::write_error_log(&err_msg);
386 }
387 }
388
389 #[doc(hidden)]
390 #[expect(private_bounds)]
391 pub fn __alert_if_error<T: MenuCallbackReturn>(&self, result: T) {
392 if let Some(err_msg) = result.into_optional_error() {
393 crate::common::alert_error(&anyhow::anyhow!(err_msg));
394 }
395 }
396
397 pub fn layers(&self) -> EditSectionLayersIterator<'_> {
399 EditSectionLayersIterator::new(self)
400 }
401
402 pub fn layer<'a>(&'a self, layer: usize) -> EditSectionLayerCaller<'a> {
404 EditSectionLayerCaller::new(self, layer)
405 }
406 pub fn object<'a>(&'a self, object: &'a ObjectHandle) -> EditSectionObjectCaller<'a> {
408 EditSectionObjectCaller::new(self, object)
409 }
410}
411
412pub struct EditSectionObjectCaller<'a> {
416 edit_section: &'a EditSection,
417 pub handle: &'a ObjectHandle,
418}
419impl<'a> EditSectionObjectCaller<'a> {
420 pub fn new(edit_section: &'a EditSection, object: &'a ObjectHandle) -> Self {
421 Self {
422 edit_section,
423 handle: object,
424 }
425 }
426
427 pub fn get_layer_frame(&self) -> EditSectionResult<ObjectLayerFrame> {
429 self.edit_section.get_object_layer_frame(self.handle)
430 }
431 pub fn get_alias(&self) -> EditSectionResult<String> {
433 self.edit_section.get_object_alias(self.handle)
434 }
435 #[cfg(feature = "aviutl2-alias")]
437 pub fn get_alias_parsed(&self) -> EditSectionResult<aviutl2_alias::Table> {
438 self.edit_section
439 .get_object_alias(self.handle)?
440 .parse()
441 .map_err(Into::into)
442 }
443
444 pub fn count_effect(&self, effect: &str) -> EditSectionResult<usize> {
454 self.edit_section.count_object_effect(self.handle, effect)
455 }
456
457 pub fn get_effect_item(
465 &self,
466 effect_name: &str,
467 effect_index: usize,
468 item: &str,
469 ) -> EditSectionResult<String> {
470 self.edit_section
471 .get_object_effect_item(self.handle, effect_name, effect_index, item)
472 }
473
474 pub fn set_effect_item(
483 &self,
484 effect_name: &str,
485 effect_index: usize,
486 item: &str,
487 value: &str,
488 ) -> EditSectionResult<()> {
489 self.edit_section.set_object_effect_item(
490 self.handle,
491 effect_name,
492 effect_index,
493 item,
494 value,
495 )
496 }
497
498 pub fn move_object(&self, new_layer: usize, new_start_frame: usize) -> EditSectionResult<()> {
505 self.edit_section
506 .move_object(self.handle, new_layer, new_start_frame)
507 }
508
509 pub fn delete_object(&self) -> EditSectionResult<()> {
511 self.edit_section.delete_object(self.handle)
512 }
513
514 pub fn focus_object(&self) -> EditSectionResult<()> {
520 self.edit_section.focus_object(self.handle)
521 }
522
523 pub fn exists(&self) -> bool {
525 self.edit_section.object_exists(self.handle)
526 }
527}
528
529pub struct EditSectionLayerCaller<'a> {
533 edit_section: &'a EditSection,
534 pub index: usize,
535}
536impl<'a> EditSectionLayerCaller<'a> {
537 pub fn new(edit_section: &'a EditSection, layer: usize) -> Self {
538 Self {
539 edit_section,
540 index: layer,
541 }
542 }
543 pub fn find_object_after(&self, frame: usize) -> EditSectionResult<Option<ObjectHandle>> {
549 self.edit_section.find_object_after(self.index, frame)
550 }
551
552 pub fn objects(&self) -> EditSectionLayerObjectsIterator<'a> {
555 EditSectionLayerObjectsIterator::new(self.edit_section, self.index)
556 }
557}
558
559#[derive(Debug, Clone)]
561pub struct EditSectionLayersIterator<'a> {
562 edit_section: &'a EditSection,
563 current: usize,
564 total: usize,
565}
566
567impl<'a> EditSectionLayersIterator<'a> {
568 fn new(edit_section: &'a EditSection) -> Self {
569 Self {
570 edit_section,
571 current: 0,
572 total: edit_section.info.layer_max,
573 }
574 }
575}
576
577impl<'a> Iterator for EditSectionLayersIterator<'a> {
578 type Item = EditSectionLayerCaller<'a>;
579
580 fn next(&mut self) -> Option<Self::Item> {
581 if self.current > self.total {
582 return None;
583 }
584 let layer = self.current;
585 self.current += 1;
586 Some(EditSectionLayerCaller::new(self.edit_section, layer))
587 }
588}
589
590#[derive(Debug, Clone)]
593pub struct EditSectionLayerObjectsIterator<'a> {
594 edit_section: &'a EditSection,
595 layer: usize,
596 next_frame: usize,
597}
598
599impl<'a> EditSectionLayerObjectsIterator<'a> {
600 fn new(edit_section: &'a EditSection, layer: usize) -> Self {
601 Self {
602 edit_section,
603 layer,
604 next_frame: 0,
605 }
606 }
607}
608
609impl<'a> Iterator for EditSectionLayerObjectsIterator<'a> {
610 type Item = (ObjectLayerFrame, ObjectHandle);
611
612 fn next(&mut self) -> Option<Self::Item> {
613 let Ok(Some(handle)) = self
615 .edit_section
616 .find_object_after(self.layer, self.next_frame)
617 else {
618 return None;
619 };
620
621 let lf = match self.edit_section.get_object_layer_frame(&handle) {
622 Ok(lf) => lf,
623 Err(_) => return None,
624 };
625
626 self.next_frame = lf.end.saturating_add(1);
628
629 Some((lf, handle))
630 }
631}
632
633trait MenuCallbackReturn {
634 fn into_optional_error(self) -> Option<String>;
635}
636impl<E> MenuCallbackReturn for Result<(), E>
637where
638 Box<dyn std::error::Error>: From<E>,
639{
640 fn into_optional_error(self) -> Option<String> {
641 match self {
642 Ok(_) => None,
643 Err(e) => {
644 let boxed: Box<dyn std::error::Error> = e.into();
645 Some(format!("{}", boxed))
646 }
647 }
648 }
649}
650impl MenuCallbackReturn for () {
651 fn into_optional_error(self) -> Option<String> {
652 None
653 }
654}