use std::sync::Once;
use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel, BOOL};
use objc::{class, msg_send, sel, sel_impl};
use objc_id::Id;
use crate::appkit::menu::{Menu, MenuItem};
use crate::dragdrop::DragInfo;
use crate::foundation::{id, load_or_register_class, nil, NSArray, NSInteger, NSUInteger, NO, YES};
use crate::listview::{ListViewDelegate, RowEdge, LISTVIEW_DELEGATE_PTR};
use crate::utils::load;
extern "C" fn number_of_items<T: ListViewDelegate>(this: &Object, _: Sel, _: id) -> NSInteger {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
view.number_of_items() as NSInteger
}
extern "C" fn view_for_column<T: ListViewDelegate>(
this: &Object,
_: Sel,
_table_view: id,
_table_column: id,
item: NSInteger
) -> id {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
let item = view.item_for(item as usize);
item.objc.get(|obj| unsafe { msg_send![obj, self] })
}
extern "C" fn will_display_cell<T: ListViewDelegate>(
this: &Object,
_: Sel,
_table_view: id,
_cell: id,
_column: id,
item: NSInteger
) {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
view.will_display_item(item as usize);
}
extern "C" fn menu_needs_update<T: ListViewDelegate>(this: &Object, _: Sel, menu: id) {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
let items = view.context_menu();
let _ = Menu::append(menu, items);
}
extern "C" fn selection_did_change<T: ListViewDelegate>(this: &Object, _: Sel, notification: id) {
let selected_row: NSInteger = unsafe {
let tableview: id = msg_send![notification, object];
msg_send![tableview, selectedRow]
};
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
if selected_row == -1 {
view.item_selected(None);
} else {
view.item_selected(Some(selected_row as usize));
}
}
extern "C" fn row_actions_for_row<T: ListViewDelegate>(
this: &Object,
_: Sel,
_table_view: id,
row: NSInteger,
edge: NSInteger
) -> id {
let edge: RowEdge = edge.into();
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
let mut ids: NSArray = view
.actions_for(row as usize, edge)
.iter_mut()
.map(|action| &*action.0)
.collect::<Vec<&Object>>()
.into();
&mut *ids
}
extern "C" fn enforce_normalcy(_: &Object, _: Sel) -> BOOL {
return YES;
}
extern "C" fn dragging_entered<T: ListViewDelegate>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
view.dragging_entered(DragInfo {
info: unsafe { Id::from_ptr(info) }
})
.into()
}
extern "C" fn prepare_for_drag_operation<T: ListViewDelegate>(this: &mut Object, _: Sel, info: id) -> BOOL {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
match view.prepare_for_drag_operation(DragInfo {
info: unsafe { Id::from_ptr(info) }
}) {
true => YES,
false => NO
}
}
extern "C" fn perform_drag_operation<T: ListViewDelegate>(this: &mut Object, _: Sel, info: id) -> BOOL {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
match view.perform_drag_operation(DragInfo {
info: unsafe { Id::from_ptr(info) }
}) {
true => YES,
false => NO
}
}
extern "C" fn conclude_drag_operation<T: ListViewDelegate>(this: &mut Object, _: Sel, info: id) {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
view.conclude_drag_operation(DragInfo {
info: unsafe { Id::from_ptr(info) }
});
}
extern "C" fn dragging_exited<T: ListViewDelegate>(this: &mut Object, _: Sel, info: id) {
let view = load::<T>(this, LISTVIEW_DELEGATE_PTR);
view.dragging_exited(DragInfo {
info: unsafe { Id::from_ptr(info) }
});
}
pub(crate) fn register_listview_class() -> *const Class {
static mut VIEW_CLASS: *const Class = 0 as *const Class;
static INIT: Once = Once::new();
INIT.call_once(|| unsafe {
let superclass = class!(NSTableView);
let decl = ClassDecl::new("RSTListView", superclass).unwrap();
VIEW_CLASS = decl.register();
});
unsafe { VIEW_CLASS }
}
pub(crate) fn register_listview_class_with_delegate<T: ListViewDelegate>(instance: &T) -> *const Class {
load_or_register_class("NSTableView", instance.subclass_name(), |decl| unsafe {
decl.add_ivar::<usize>(LISTVIEW_DELEGATE_PTR);
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
decl.add_method(
sel!(numberOfRowsInTableView:),
number_of_items::<T> as extern "C" fn(&Object, _, id) -> NSInteger
);
decl.add_method(
sel!(tableView:willDisplayCell:forTableColumn:row:),
will_display_cell::<T> as extern "C" fn(&Object, _, id, id, id, NSInteger)
);
decl.add_method(
sel!(tableView:viewForTableColumn:row:),
view_for_column::<T> as extern "C" fn(&Object, _, id, id, NSInteger) -> id
);
decl.add_method(
sel!(tableViewSelectionDidChange:),
selection_did_change::<T> as extern "C" fn(&Object, _, id)
);
decl.add_method(
sel!(tableView:rowActionsForRow:edge:),
row_actions_for_row::<T> as extern "C" fn(&Object, _, id, NSInteger, NSInteger) -> id
);
decl.add_method(
sel!(menuNeedsUpdate:),
menu_needs_update::<T> as extern "C" fn(&Object, _, id)
);
decl.add_method(
sel!(draggingEntered:),
dragging_entered::<T> as extern "C" fn(&mut Object, _, _) -> NSUInteger
);
decl.add_method(
sel!(prepareForDragOperation:),
prepare_for_drag_operation::<T> as extern "C" fn(&mut Object, _, _) -> BOOL
);
decl.add_method(
sel!(performDragOperation:),
perform_drag_operation::<T> as extern "C" fn(&mut Object, _, _) -> BOOL
);
decl.add_method(
sel!(concludeDragOperation:),
conclude_drag_operation::<T> as extern "C" fn(&mut Object, _, _)
);
decl.add_method(
sel!(draggingExited:),
dragging_exited::<T> as extern "C" fn(&mut Object, _, _)
);
})
}