3212 lines
108 KiB
C++
3212 lines
108 KiB
C++
|
/*
|
||
|
* Mr. 4th Dimention - Allen Webster
|
||
|
*
|
||
|
* ??.??.????
|
||
|
*
|
||
|
* Implementation of the API functions.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
// TOP
|
||
|
|
||
|
function void
|
||
|
output_file_append(Thread_Context *tctx, Models *models, Editing_File *file, String_Const_u8 value){
|
||
|
i64 end = buffer_size(&file->state.buffer);
|
||
|
Edit_Behaviors behaviors = {};
|
||
|
behaviors.pos_before_edit = end;
|
||
|
edit_single(tctx, models, file, Ii64(end), value, behaviors);
|
||
|
}
|
||
|
|
||
|
function void
|
||
|
file_cursor_to_end(Thread_Context *tctx, Models *models, Editing_File *file){
|
||
|
Assert(file != 0);
|
||
|
i64 pos = buffer_size(&file->state.buffer);
|
||
|
Layout *layout = &models->layout;
|
||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||
|
panel != 0;
|
||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||
|
View *view = panel->view;
|
||
|
if (view->file != file){
|
||
|
continue;
|
||
|
}
|
||
|
view_set_cursor(tctx, models, view, pos);
|
||
|
view->mark = pos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
function b32
|
||
|
access_test(Access_Flag object_flags, Access_Flag access_flags){
|
||
|
return((object_flags & access_flags) == access_flags);
|
||
|
}
|
||
|
|
||
|
function b32
|
||
|
api_check_panel(Panel *panel){
|
||
|
b32 result = false;
|
||
|
if (panel != 0 && panel->kind != PanelKind_Unused){
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
function b32
|
||
|
api_check_buffer(Editing_File *file){
|
||
|
return(file != 0);
|
||
|
}
|
||
|
|
||
|
function b32
|
||
|
api_check_buffer(Editing_File *file, Access_Flag access){
|
||
|
return(api_check_buffer(file) && access_test(file_get_access_flags(file), access));
|
||
|
}
|
||
|
|
||
|
function b32
|
||
|
api_check_view(View *view){
|
||
|
return(view != 0 && view->in_use);
|
||
|
}
|
||
|
|
||
|
function b32
|
||
|
api_check_view(View *view, Access_Flag access){
|
||
|
return(api_check_view(view) && access_test(view_get_access_flags(view), access));
|
||
|
}
|
||
|
|
||
|
function b32
|
||
|
is_running_coroutine(Application_Links *app){
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Thread_Context_Extra_Info *info = (Thread_Context_Extra_Info*)tctx->user_data;
|
||
|
return(info->coroutine != 0);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
global_set_setting(Application_Links *app, Global_Setting_ID setting, i64 value){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
b32 result = true;
|
||
|
switch (setting){
|
||
|
case GlobalSetting_LAltLCtrlIsAltGr:
|
||
|
{
|
||
|
models->settings.lctrl_lalt_is_altgr = (b32)(value != 0);
|
||
|
}break;
|
||
|
default:
|
||
|
{
|
||
|
result = false;
|
||
|
}break;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
global_get_screen_rectangle(Application_Links *app){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(Rf32(V2f32(0, 0), V2f32(layout_get_root_size(&models->layout))));
|
||
|
}
|
||
|
|
||
|
api(custom) function Thread_Context*
|
||
|
get_thread_context(Application_Links *app){
|
||
|
return(app->tctx);
|
||
|
}
|
||
|
|
||
|
api(custom) function Child_Process_ID
|
||
|
create_child_process(Application_Links *app, String_Const_u8 path, String_Const_u8 command){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Child_Process_ID result = 0;
|
||
|
if (!child_process_call(app->tctx, models, path, command, &result)){
|
||
|
result = 0;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
child_process_set_target_buffer(Application_Links *app, Child_Process_ID child_process_id, Buffer_ID buffer_id, Child_Process_Set_Target_Flags flags){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Child_Process *child_process = child_process_from_id(&models->child_processes, child_process_id);
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file) && child_process != 0){
|
||
|
result = child_process_set_target_buffer(models, child_process, file, flags);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Child_Process_ID
|
||
|
buffer_get_attached_child_process(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Child_Process_ID result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = file->state.attached_child_process;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_ID
|
||
|
child_process_get_attached_buffer(Application_Links *app, Child_Process_ID child_process_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Child_Process *child_process = child_process_from_id(&models->child_processes, child_process_id);
|
||
|
Buffer_ID result = 0;
|
||
|
if (child_process != 0 && child_process->out_file != 0){
|
||
|
result = child_process->out_file->id;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Process_State
|
||
|
child_process_get_state(Application_Links *app, Child_Process_ID child_process_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(child_process_get_state(&models->child_processes, child_process_id));
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
enqueue_virtual_event(Application_Links *app, Input_Event *event){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
b32 result = false;
|
||
|
if (InputEventKind_None < event->kind && event->kind < InputEventKind_COUNT){
|
||
|
models_push_virtual_event(models, event);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i32
|
||
|
get_buffer_count(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Working_Set *working_set = &models->working_set;
|
||
|
return(working_set->active_file_count);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_ID
|
||
|
get_buffer_next(Application_Links *app, Buffer_ID buffer_id, Access_Flag access)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Working_Set *working_set = &models->working_set;
|
||
|
Editing_File *file = working_set_get_file(working_set, buffer_id);
|
||
|
file = file_get_next(working_set, file);
|
||
|
for (;file != 0 && !access_test(file_get_access_flags(file), access);){
|
||
|
file = file_get_next(working_set, file);
|
||
|
}
|
||
|
Buffer_ID result = 0;
|
||
|
if (file != 0){
|
||
|
result = file->id;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_ID
|
||
|
get_buffer_by_name(Application_Links *app, String_Const_u8 name, Access_Flag access)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Working_Set *working_set = &models->working_set;
|
||
|
Editing_File *file = working_set_contains_name(working_set, name);
|
||
|
Buffer_ID result = 0;
|
||
|
if (api_check_buffer(file, access)){
|
||
|
result = file->id;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_ID
|
||
|
get_buffer_by_file_name(Application_Links *app, String_Const_u8 file_name, Access_Flag access)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File_Name canon = {};
|
||
|
Buffer_ID result = false;
|
||
|
Scratch_Block scratch(app);
|
||
|
if (get_canon_name(scratch, file_name, &canon)){
|
||
|
Working_Set *working_set = &models->working_set;
|
||
|
Editing_File *file = working_set_contains_canon(working_set, string_from_file_name(&canon));
|
||
|
if (api_check_buffer(file, access)){
|
||
|
result = file->id;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_read_range(Application_Links *app, Buffer_ID buffer_id, Range_i64 range, u8 *out)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
i64 size = buffer_size(&file->state.buffer);
|
||
|
if (0 <= range.min && range.min <= range.max && range.max <= size){
|
||
|
Scratch_Block scratch(app);
|
||
|
String_Const_u8 string = buffer_stringify(scratch, &file->state.buffer, range);
|
||
|
block_copy(out, string.str, string.size);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
function Edit_Behaviors
|
||
|
get_active_edit_behaviors(Models *models, Editing_File *file){
|
||
|
Panel *panel = layout_get_active_panel(&models->layout);
|
||
|
Assert(panel != 0);
|
||
|
View *view = panel->view;
|
||
|
Assert(view != 0);
|
||
|
Edit_Behaviors behaviors = {};
|
||
|
if (view->file == file){
|
||
|
behaviors.pos_before_edit = view->edit_pos_.cursor_pos;
|
||
|
}
|
||
|
else{
|
||
|
behaviors.pos_before_edit = -1;
|
||
|
}
|
||
|
return(behaviors);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_replace_range(Application_Links *app, Buffer_ID buffer_id, Range_i64 range, String_Const_u8 string)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
i64 size = buffer_size(&file->state.buffer);
|
||
|
if (0 <= range.first && range.first <= range.one_past_last && range.one_past_last <= size){
|
||
|
Edit_Behaviors behaviors = get_active_edit_behaviors(models, file);
|
||
|
edit_single(app->tctx, models, file, range, string, behaviors);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_batch_edit(Application_Links *app, Buffer_ID buffer_id, Batch_Edit *batch)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
Edit_Behaviors behaviors = get_active_edit_behaviors(models, file);
|
||
|
result = edit_batch(app->tctx, models, file, batch, behaviors);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function String_Match
|
||
|
buffer_seek_string(Application_Links *app, Buffer_ID buffer, String_Const_u8 needle, Scan_Direction direction, i64 start_pos){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer);
|
||
|
String_Match result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
if (needle.size == 0){
|
||
|
result.flags = StringMatch_CaseSensitive;
|
||
|
result.range = Ii64(start_pos);
|
||
|
}
|
||
|
else{
|
||
|
Scratch_Block scratch(app);
|
||
|
Gap_Buffer *gap_buffer = &file->state.buffer;
|
||
|
i64 size = buffer_size(gap_buffer);
|
||
|
List_String_Const_u8 chunks = buffer_get_chunks(scratch, gap_buffer);
|
||
|
Range_i64 range = {};
|
||
|
if (direction == Scan_Forward){
|
||
|
i64 adjusted_pos = start_pos + 1;
|
||
|
start_pos = clamp_top(adjusted_pos, size);
|
||
|
range = Ii64(adjusted_pos, size);
|
||
|
}
|
||
|
else{
|
||
|
i64 adjusted_pos = start_pos - 1 + needle.size;
|
||
|
start_pos = clamp_bot(0, adjusted_pos);
|
||
|
range = Ii64(0, adjusted_pos);
|
||
|
}
|
||
|
buffer_chunks_clamp(&chunks, range);
|
||
|
if (chunks.first != 0){
|
||
|
u64_Array jump_table = string_compute_needle_jump_table(scratch, needle, direction);
|
||
|
Character_Predicate dummy = {};
|
||
|
String_Match_List list = find_all_matches(scratch, 1,
|
||
|
chunks, needle, jump_table, &dummy, direction,
|
||
|
range.min, buffer, 0);
|
||
|
if (list.count == 1){
|
||
|
result = *list.first;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.range = Ii64(start_pos);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function String_Match
|
||
|
buffer_seek_character_class(Application_Links *app, Buffer_ID buffer, Character_Predicate *predicate, Scan_Direction direction, i64 start_pos){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer);
|
||
|
String_Match result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
Scratch_Block scratch(app);
|
||
|
Gap_Buffer *gap_buffer = &file->state.buffer;
|
||
|
List_String_Const_u8 chunks_list = buffer_get_chunks(scratch, gap_buffer);
|
||
|
|
||
|
if (chunks_list.node_count > 0){
|
||
|
// TODO(allen): If you are reading this comment, then I haven't revisited this to tighten it up yet.
|
||
|
// buffer_seek_character_class was originally implemented using the chunk indexer helper
|
||
|
// Buffer_Chunk_Position, and it was written when buffer chunks were in an array instead
|
||
|
// of the new method of listing strings in a linked list.
|
||
|
// This should probably be implemented as a direct iteration-in-an-iteration that avoids the
|
||
|
// extra function calls and branches to achieve the iteration. However, this is a very easy API to
|
||
|
// get wrong. There are _a lot_ of opportunities for off by one errors and necessary code duplication,
|
||
|
// really tedious stuff. Anyway, this is all just to say, cleaning this up would be really nice, but
|
||
|
// there are almost certainly lower hanging fruit with higher payoffs elsewhere... unless need to change
|
||
|
// this anyway or whatever.
|
||
|
String_Const_u8 chunk_mem[3] = {};
|
||
|
String_Const_u8_Array chunks = {chunk_mem};
|
||
|
for (Node_String_Const_u8 *node = chunks_list.first;
|
||
|
node != 0;
|
||
|
node = node->next){
|
||
|
chunks.vals[chunks.count] = node->string;
|
||
|
chunks.count += 1;
|
||
|
}
|
||
|
|
||
|
i64 size = buffer_size(gap_buffer);
|
||
|
start_pos = clamp(-1, start_pos, size);
|
||
|
Buffer_Chunk_Position pos = buffer_get_chunk_position(chunks, size, start_pos);
|
||
|
for (;;){
|
||
|
i32 past_end = buffer_chunk_position_iterate(chunks, &pos, direction);
|
||
|
if (past_end == -1){
|
||
|
break;
|
||
|
}
|
||
|
else if (past_end == 1){
|
||
|
result.range = Ii64(size);
|
||
|
break;
|
||
|
}
|
||
|
u8 v = chunks.vals[pos.chunk_index].str[pos.chunk_pos];
|
||
|
if (character_predicate_check_character(*predicate, v)){
|
||
|
result.buffer = buffer;
|
||
|
result.range = Ii64(pos.real_pos, pos.real_pos + 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function f32
|
||
|
buffer_line_y_difference(Application_Links *app, Buffer_ID buffer_id,
|
||
|
f32 width, Face_ID face_id,
|
||
|
i64 line_a, i64 line_b){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
f32 result = 0.0f;
|
||
|
if (api_check_buffer(file)){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||
|
result = file_line_y_difference(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
line_a, line_b);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Line_Shift_Vertical
|
||
|
buffer_line_shift_y(Application_Links *app, Buffer_ID buffer_id,
|
||
|
f32 width, Face_ID face_id,
|
||
|
i64 line, f32 y_shift){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Line_Shift_Vertical result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||
|
result = file_line_shift_y(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
line, y_shift);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
buffer_pos_at_relative_xy(Application_Links *app, Buffer_ID buffer_id,
|
||
|
f32 width, Face_ID face_id,
|
||
|
i64 base_line, Vec2_f32 relative_xy){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
i64 result = -1;
|
||
|
if (api_check_buffer(file)){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||
|
result = file_pos_at_relative_xy(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
base_line, relative_xy);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
buffer_relative_box_of_pos(Application_Links *app, Buffer_ID buffer_id, f32 width, Face_ID face_id, i64 base_line, i64 pos){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Rect_f32 result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||
|
result = file_relative_box_of_pos(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
base_line, pos);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
buffer_padded_box_of_pos(Application_Links *app, Buffer_ID buffer_id, f32 width, Face_ID face_id, i64 base_line, i64 pos){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Rect_f32 result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||
|
result = file_padded_box_of_pos(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
base_line, pos);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
buffer_relative_character_from_pos(Application_Links *app, Buffer_ID buffer_id,
|
||
|
f32 width, Face_ID face_id, i64 base_line, i64 pos)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
i64 result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||
|
result = file_relative_character_from_pos(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
base_line, pos);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
buffer_pos_from_relative_character(Application_Links *app, Buffer_ID buffer_id, f32 width, Face_ID face_id, i64 base_line, i64 relative_character)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
i64 result = -1;
|
||
|
if (api_check_buffer(file)){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||
|
result = file_pos_from_relative_character(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
base_line, relative_character);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
|
||
|
api(custom) function f32
|
||
|
view_line_y_difference(Application_Links *app, View_ID view_id, i64 line_a, i64 line_b){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
f32 result = 0.0f;
|
||
|
if (api_check_view(view)){
|
||
|
result = view_line_y_difference(app->tctx, models, view, line_a, line_b);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Line_Shift_Vertical
|
||
|
view_line_shift_y(Application_Links *app, View_ID view_id, i64 line, f32 y_shift){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
Line_Shift_Vertical result = {};
|
||
|
if (api_check_view(view)){
|
||
|
result = view_line_shift_y(app->tctx, models, view, line, y_shift);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
view_pos_at_relative_xy(Application_Links *app, View_ID view_id, i64 base_line, Vec2_f32 relative_xy){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
i64 result = -1;
|
||
|
if (api_check_view(view)){
|
||
|
result = view_pos_at_relative_xy(app->tctx, models, view, base_line, relative_xy);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
view_relative_box_of_pos(Application_Links *app, View_ID view_id, i64 base_line, i64 pos){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
Rect_f32 result = {};
|
||
|
if (api_check_view(view)){
|
||
|
result = view_relative_box_of_pos(app->tctx, models, view, base_line, pos);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
view_padded_box_of_pos(Application_Links *app, View_ID view_id, i64 base_line, i64 pos){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
Rect_f32 result = {};
|
||
|
if (api_check_view(view)){
|
||
|
result = view_padded_box_of_pos(app->tctx, models, view, base_line, pos);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
view_relative_character_from_pos(Application_Links *app, View_ID view_id, i64 base_line, i64 pos){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
i64 result = 0;
|
||
|
if (api_check_view(view)){
|
||
|
result = view_relative_character_from_pos(app->tctx, models, view, base_line, pos);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
view_pos_from_relative_character(Application_Links *app, View_ID view_id, i64 base_line, i64 character){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
i64 result = 0;
|
||
|
if (api_check_view(view)){
|
||
|
result = view_pos_from_relative_character(app->tctx, models, view, base_line, character);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_exists(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
return(api_check_buffer(file));
|
||
|
}
|
||
|
|
||
|
api(custom) function Access_Flag
|
||
|
buffer_get_access_flags(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Access_Flag result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = file_get_access_flags(file);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
buffer_get_size(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
i64 result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = buffer_size(&file->state.buffer);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
buffer_get_line_count(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
u64 result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = buffer_line_count(&file->state.buffer);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function String_Const_u8
|
||
|
push_buffer_base_name(Application_Links *app, Arena *arena, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
String_Const_u8 result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
result = push_string_copy(arena, string_from_file_name(&file->base_name));
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function String_Const_u8
|
||
|
push_buffer_unique_name(Application_Links *app, Arena *out, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
String_Const_u8 result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
result = push_string_copy(out, string_from_file_name(&file->unique_name));
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function String_Const_u8
|
||
|
push_buffer_file_name(Application_Links *app, Arena *arena, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
String_Const_u8 result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
result = push_string_copy(arena, string_from_file_name(&file->canon));
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Dirty_State
|
||
|
buffer_get_dirty_state(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Dirty_State result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = file->state.dirty;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_set_dirty_state(Application_Links *app, Buffer_ID buffer_id, Dirty_State dirty_state){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = true;
|
||
|
file->state.dirty = dirty_state;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_set_layout(Application_Links *app, Buffer_ID buffer_id, Layout_Function *layout_func){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = true;
|
||
|
file->settings.layout_func = layout_func;
|
||
|
file_clear_layout_cache(file);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_clear_layout_cache(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = true;
|
||
|
file_clear_layout_cache(file);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Layout_Function*
|
||
|
buffer_get_layout(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Layout_Function *result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = file->settings.layout_func;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_get_setting(Application_Links *app, Buffer_ID buffer_id, Buffer_Setting_ID setting, i64 *value_out)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = true;
|
||
|
switch (setting){
|
||
|
case BufferSetting_Unimportant:
|
||
|
{
|
||
|
*value_out = file->settings.unimportant;
|
||
|
}break;
|
||
|
|
||
|
case BufferSetting_Unkillable:
|
||
|
{
|
||
|
*value_out = (file->settings.never_kill || file->settings.unkillable);
|
||
|
}break;
|
||
|
|
||
|
case BufferSetting_ReadOnly:
|
||
|
{
|
||
|
*value_out = file->settings.read_only;
|
||
|
}break;
|
||
|
|
||
|
case BufferSetting_RecordsHistory:
|
||
|
{
|
||
|
*value_out = history_is_activated(&file->state.history);
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
result = false;
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_set_setting(Application_Links *app, Buffer_ID buffer_id, Buffer_Setting_ID setting, i64 value)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = true;
|
||
|
switch (setting){
|
||
|
case BufferSetting_Unimportant:
|
||
|
{
|
||
|
if (value != 0){
|
||
|
file_set_unimportant(file, true);
|
||
|
}
|
||
|
else{
|
||
|
file_set_unimportant(file, false);
|
||
|
}
|
||
|
}break;
|
||
|
|
||
|
case BufferSetting_Unkillable:
|
||
|
{
|
||
|
file->settings.unkillable = (value != 0);
|
||
|
}break;
|
||
|
|
||
|
case BufferSetting_ReadOnly:
|
||
|
{
|
||
|
file->settings.read_only = (value != 0);
|
||
|
}break;
|
||
|
|
||
|
case BufferSetting_RecordsHistory:
|
||
|
{
|
||
|
if (value){
|
||
|
if (!history_is_activated(&file->state.history)){
|
||
|
history_init(app->tctx, models, &file->state.history);
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
if (history_is_activated(&file->state.history)){
|
||
|
history_free(app->tctx, &file->state.history);
|
||
|
}
|
||
|
}
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
result = 0;
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Scope
|
||
|
buffer_get_managed_scope(Application_Links *app, Buffer_ID buffer_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Managed_Scope result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = file_get_managed_scope(file);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_send_end_signal(Application_Links *app, Buffer_ID buffer_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
file_end_file(app->tctx, models, file);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_ID
|
||
|
create_buffer(Application_Links *app, String_Const_u8 file_name, Buffer_Create_Flag flags)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *new_file = create_file(app->tctx, models, file_name, flags);
|
||
|
Buffer_ID result = 0;
|
||
|
if (new_file != 0){
|
||
|
result = new_file->id;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_save(Application_Links *app, Buffer_ID buffer_id, String_Const_u8 file_name, Buffer_Save_Flag flags)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
b32 skip_save = false;
|
||
|
if (!HasFlag(flags, BufferSave_IgnoreDirtyFlag)){
|
||
|
if (file->state.dirty == DirtyState_UpToDate){
|
||
|
skip_save = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!skip_save){
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Scratch_Block scratch(tctx);
|
||
|
String_Const_u8 name = push_string_copy(scratch, file_name);
|
||
|
save_file_to_name(tctx, models, file, name.str);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_Kill_Result
|
||
|
buffer_kill(Application_Links *app, Buffer_ID buffer_id, Buffer_Kill_Flag flags)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Working_Set *working_set = &models->working_set;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Buffer_Kill_Result result = BufferKillResult_DoesNotExist;
|
||
|
if (api_check_buffer(file)){
|
||
|
if (!file->settings.never_kill && !file->settings.unkillable){
|
||
|
b32 needs_to_save = file_needs_save(file);
|
||
|
if (!needs_to_save || (flags & BufferKill_AlwaysKill) != 0){
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
if (models->end_buffer != 0){
|
||
|
models->end_buffer(app, file->id);
|
||
|
}
|
||
|
|
||
|
buffer_unbind_name_low_level(working_set, file);
|
||
|
if (file->canon.name_size != 0){
|
||
|
buffer_unbind_file(working_set, file);
|
||
|
}
|
||
|
file_free(tctx, models, file);
|
||
|
working_set_free_file(&models->heap, working_set, file);
|
||
|
|
||
|
Layout *layout = &models->layout;
|
||
|
|
||
|
Node *order = &working_set->touch_order_sentinel;
|
||
|
Node *file_node = order->next;
|
||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||
|
panel != 0;
|
||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||
|
View *view = panel->view;
|
||
|
if (view->file == file){
|
||
|
Assert(file_node != order);
|
||
|
view->file = 0;
|
||
|
Editing_File *new_file = CastFromMember(Editing_File, touch_node, file_node);
|
||
|
view_set_file(tctx, models, view, new_file);
|
||
|
file_node = file_node->next;
|
||
|
if (file_node == order){
|
||
|
file_node = file_node->next;
|
||
|
}
|
||
|
Assert(file_node != order);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Child_Process_Container *child_processes = &models->child_processes;
|
||
|
for (Node *node = child_processes->child_process_active_list.next;
|
||
|
node != &child_processes->child_process_active_list;
|
||
|
node = node->next){
|
||
|
Child_Process *child_process = CastFromMember(Child_Process, node, node);
|
||
|
if (child_process->out_file == file){
|
||
|
child_process->out_file = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
result = BufferKillResult_Killed;
|
||
|
}
|
||
|
else{
|
||
|
result = BufferKillResult_Dirty;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result = BufferKillResult_Unkillable;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_Reopen_Result
|
||
|
buffer_reopen(Application_Links *app, Buffer_ID buffer_id, Buffer_Reopen_Flag flags)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Scratch_Block scratch(tctx);
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Buffer_Reopen_Result result = BufferReopenResult_Failed;
|
||
|
if (api_check_buffer(file)){
|
||
|
if (file->canon.name_size > 0){
|
||
|
Plat_Handle handle = {};
|
||
|
if (system_load_handle(scratch, (char*)file->canon.name_space, &handle)){
|
||
|
File_Attributes attributes = system_load_attributes(handle);
|
||
|
|
||
|
char *file_memory = push_array(scratch, char, (i32)attributes.size);
|
||
|
|
||
|
if (file_memory != 0){
|
||
|
if (system_load_file(handle, file_memory, (i32)attributes.size)){
|
||
|
system_load_close(handle);
|
||
|
|
||
|
// TODO(allen): try(perform a diff maybe apply edits in reopen)
|
||
|
|
||
|
i32 line_numbers[16];
|
||
|
i32 column_numbers[16];
|
||
|
View *vptrs[16];
|
||
|
i32 vptr_count = 0;
|
||
|
|
||
|
Layout *layout = &models->layout;
|
||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||
|
panel != 0;
|
||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||
|
View *view_it = panel->view;
|
||
|
if (view_it->file == file){
|
||
|
vptrs[vptr_count] = view_it;
|
||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view_it);
|
||
|
Buffer_Cursor cursor = file_compute_cursor(view_it->file, seek_pos(edit_pos.cursor_pos));
|
||
|
line_numbers[vptr_count] = (i32)cursor.line;
|
||
|
column_numbers[vptr_count] = (i32)cursor.col;
|
||
|
view_it->file = models->scratch_buffer;
|
||
|
++vptr_count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Working_Set *working_set = &models->working_set;
|
||
|
file_free(tctx, models, file);
|
||
|
working_set_file_default_settings(working_set, file);
|
||
|
file_create_from_string(tctx, models, file, SCu8(file_memory, attributes.size), attributes);
|
||
|
|
||
|
for (i32 i = 0; i < vptr_count; ++i){
|
||
|
view_set_file(tctx, models, vptrs[i], file);
|
||
|
|
||
|
vptrs[i]->file = file;
|
||
|
i64 line = line_numbers[i];
|
||
|
i64 col = column_numbers[i];
|
||
|
Buffer_Cursor cursor = file_compute_cursor(file, seek_line_col(line, col));
|
||
|
view_set_cursor(tctx, models, vptrs[i], cursor.pos);
|
||
|
}
|
||
|
result = BufferReopenResult_Reopened;
|
||
|
}
|
||
|
else{
|
||
|
system_load_close(handle);
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
system_load_close(handle);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function File_Attributes
|
||
|
buffer_get_file_attributes(Application_Links *app, Buffer_ID buffer_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
File_Attributes result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
block_copy_struct(&result, &file->attributes);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
function View*
|
||
|
get_view_next__inner(Layout *layout, View *view){
|
||
|
if (view != 0){
|
||
|
Panel *panel = view->panel;
|
||
|
panel = layout_get_next_open_panel(layout, panel);
|
||
|
if (panel != 0){
|
||
|
view = panel->view;
|
||
|
}
|
||
|
else{
|
||
|
view = 0;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
Panel *panel = layout_get_first_open_panel(layout);
|
||
|
view = panel->view;
|
||
|
}
|
||
|
return(view);
|
||
|
}
|
||
|
|
||
|
function View*
|
||
|
get_view_prev__inner(Layout *layout, View *view){
|
||
|
if (view != 0){
|
||
|
Panel *panel = view->panel;
|
||
|
panel = layout_get_prev_open_panel(layout, panel);
|
||
|
if (panel != 0){
|
||
|
view = panel->view;
|
||
|
}
|
||
|
else{
|
||
|
view = 0;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
Panel *panel = layout_get_last_open_panel(layout);
|
||
|
view = panel->view;
|
||
|
}
|
||
|
return(view);
|
||
|
}
|
||
|
|
||
|
api(custom) function View_ID
|
||
|
get_view_next(Application_Links *app, View_ID view_id, Access_Flag access)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
view = get_view_next__inner(layout, view);
|
||
|
for (;view != 0 && !access_test(view_get_access_flags(view), access);){
|
||
|
view = get_view_next__inner(layout, view);
|
||
|
}
|
||
|
View_ID result = 0;
|
||
|
if (view != 0){
|
||
|
result = view_get_id(&models->view_set, view);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function View_ID
|
||
|
get_view_prev(Application_Links *app, View_ID view_id, Access_Flag access)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
view = get_view_prev__inner(layout, view);
|
||
|
for (;view != 0 && !access_test(view_get_access_flags(view), access);){
|
||
|
view = get_view_prev__inner(layout, view);
|
||
|
}
|
||
|
View_ID result = 0;
|
||
|
if (view != 0){
|
||
|
result = view_get_id(&models->view_set, view);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function View_ID
|
||
|
get_this_ctx_view(Application_Links *app, Access_Flag access)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Thread_Context_Extra_Info *tctx_info = (Thread_Context_Extra_Info*)tctx->user_data;
|
||
|
View_ID result = 0;
|
||
|
if (tctx_info->coroutine != 0){
|
||
|
Coroutine *coroutine = (Coroutine*)tctx_info->coroutine;
|
||
|
View *view = (View*)coroutine->user_data;
|
||
|
if (view != 0){
|
||
|
result = view_get_id(&models->view_set, view);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function View_ID
|
||
|
get_active_view(Application_Links *app, Access_Flag access)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Panel *panel = layout_get_active_panel(&models->layout);
|
||
|
Assert(panel != 0);
|
||
|
View *view = panel->view;
|
||
|
Assert(view != 0);
|
||
|
View_ID result = 0;
|
||
|
if (api_check_view(view, access)){
|
||
|
result = view_get_id(&models->view_set, view);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_exists(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_ID
|
||
|
view_get_buffer(Application_Links *app, View_ID view_id, Access_Flag access){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
Buffer_ID result = 0;
|
||
|
if (api_check_view(view)){
|
||
|
Editing_File *file = view->file;
|
||
|
if (api_check_buffer(file, access)){
|
||
|
result = file->id;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
view_get_cursor_pos(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
i64 result = 0;
|
||
|
if (api_check_view(view)){
|
||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||
|
result = edit_pos.cursor_pos;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
view_get_mark_pos(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
i64 result = 0;;
|
||
|
if (api_check_view(view)){
|
||
|
result = view->mark;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function f32
|
||
|
view_get_preferred_x(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
f32 result = 0.f;;
|
||
|
if (api_check_view(view)){
|
||
|
result = view->preferred_x;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_set_preferred_x(Application_Links *app, View_ID view_id, f32 x){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
view->preferred_x = x;
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
view_get_screen_rect(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Rect_f32 result = {};
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
if (api_check_view(view)){
|
||
|
result = Rf32(view->panel->rect_full);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Panel_ID
|
||
|
view_get_panel(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
Panel_ID result = 0;
|
||
|
if (api_check_view(view)){
|
||
|
Panel *panel = view->panel;
|
||
|
result = panel_get_id(layout, panel);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function View_ID
|
||
|
panel_get_view(Application_Links *app, Panel_ID panel_id, Access_Flag access){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Panel *panel = imp_get_panel(models, panel_id);
|
||
|
View_ID result = 0;
|
||
|
if (api_check_panel(panel)){
|
||
|
if (panel->kind == PanelKind_Final){
|
||
|
View *view = panel->view;
|
||
|
if (api_check_view(view, access)){
|
||
|
result = view_get_id(&models->view_set, view);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
panel_is_split(Application_Links *app, Panel_ID panel_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
b32 result = false;
|
||
|
Panel *panel = imp_get_panel(models, panel_id);
|
||
|
if (api_check_panel(panel)){
|
||
|
if (panel->kind == PanelKind_Intermediate){
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
panel_is_leaf(Application_Links *app, Panel_ID panel_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
b32 result = false;
|
||
|
Panel *panel = imp_get_panel(models, panel_id);
|
||
|
if (api_check_panel(panel)){
|
||
|
if (panel->kind == PanelKind_Final){
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
panel_split(Application_Links *app, Panel_ID panel_id, Dimension split_dim){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
b32 result = false;
|
||
|
Panel *panel = imp_get_panel(models, panel_id);
|
||
|
if (api_check_panel(panel)){
|
||
|
Panel *new_panel = 0;
|
||
|
if (layout_split_panel(layout, panel, (split_dim == Dimension_X), &new_panel)){
|
||
|
Live_Views *view_set = &models->view_set;
|
||
|
View *new_view = live_set_alloc_view(&models->lifetime_allocator, view_set, new_panel);
|
||
|
view_init(app->tctx, models, new_view, models->scratch_buffer,
|
||
|
models->view_event_handler);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
panel_set_split(Application_Links *app, Panel_ID panel_id, Panel_Split_Kind kind,
|
||
|
f32 t){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
b32 result = false;
|
||
|
Panel *panel = imp_get_panel(models, panel_id);
|
||
|
if (api_check_panel(panel)){
|
||
|
if (panel->kind == PanelKind_Intermediate){
|
||
|
panel->split.kind = kind;
|
||
|
switch (kind){
|
||
|
case PanelSplitKind_Ratio_Max:
|
||
|
case PanelSplitKind_Ratio_Min:
|
||
|
{
|
||
|
panel->split.v_f32 = clamp(0.f, t, 1.f);
|
||
|
}break;
|
||
|
|
||
|
case PanelSplitKind_FixedPixels_Max:
|
||
|
case PanelSplitKind_FixedPixels_Min:
|
||
|
{
|
||
|
panel->split.v_i32 = i32_round32(t);
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
print_message(app, string_u8_litexpr("Invalid split kind passed to panel_set_split, no change made to view layout"));
|
||
|
}break;
|
||
|
}
|
||
|
layout_propogate_sizes_down_from_node(layout, panel);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
panel_swap_children(Application_Links *app, Panel_ID panel_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
b32 result = false;
|
||
|
Panel *panel = imp_get_panel(models, panel_id);
|
||
|
if (api_check_panel(panel)){
|
||
|
if (panel->kind == PanelKind_Intermediate){
|
||
|
Swap(Panel*, panel->tl_panel, panel->br_panel);
|
||
|
layout_propogate_sizes_down_from_node(layout, panel);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Panel_ID
|
||
|
panel_get_root(Application_Links *app){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
Panel *panel = layout->root;
|
||
|
return(panel_get_id(layout, panel));
|
||
|
}
|
||
|
|
||
|
api(custom) function Panel_ID
|
||
|
panel_get_parent(Application_Links *app, Panel_ID panel_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
Panel *panel = imp_get_panel(models, panel_id);
|
||
|
Panel_ID result = false;
|
||
|
if (api_check_panel(panel)){
|
||
|
result = panel_get_id(layout, panel->parent);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Panel_ID
|
||
|
panel_get_child(Application_Links *app, Panel_ID panel_id, Side which_child){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
Panel *panel = imp_get_panel(models, panel_id);
|
||
|
Panel_ID result = 0;
|
||
|
if (api_check_panel(panel)){
|
||
|
if (panel->kind == PanelKind_Intermediate){
|
||
|
Panel *child = 0;
|
||
|
switch (which_child){
|
||
|
case Side_Min:
|
||
|
{
|
||
|
child = panel->tl_panel;
|
||
|
}break;
|
||
|
case Side_Max:
|
||
|
{
|
||
|
child = panel->br_panel;
|
||
|
}break;
|
||
|
}
|
||
|
if (child != 0){
|
||
|
result = panel_get_id(layout, child);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_close(Application_Links *app, View_ID view_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Layout *layout = &models->layout;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
result = view_close(models, view);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
view_get_buffer_region(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
Rect_f32 result = {};
|
||
|
if (api_check_view(view)){
|
||
|
result = view_get_buffer_rect(app->tctx, models, view);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_Scroll
|
||
|
view_get_buffer_scroll(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Buffer_Scroll result = {};
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
if (api_check_view(view)){
|
||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||
|
result = edit_pos.scroll;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_set_active(Application_Links *app, View_ID view_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
models->layout.active_panel = view->panel;
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_enqueue_command_function(Application_Links *app, View_ID view_id, Custom_Command_Function *custom_func)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
models_push_view_command_function(models, view_id, custom_func);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_get_setting(Application_Links *app, View_ID view_id, View_Setting_ID setting, i64 *value_out)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
result = true;
|
||
|
switch (setting){
|
||
|
case ViewSetting_ShowWhitespace:
|
||
|
{
|
||
|
*value_out = view->show_whitespace;
|
||
|
}break;
|
||
|
|
||
|
case ViewSetting_ShowScrollbar:
|
||
|
{
|
||
|
*value_out = !view->hide_scrollbar;
|
||
|
}break;
|
||
|
|
||
|
case ViewSetting_ShowFileBar:
|
||
|
{
|
||
|
*value_out = !view->hide_file_bar;
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
result = false;
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_set_setting(Application_Links *app, View_ID view_id, View_Setting_ID setting, i64 value)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
result = true;
|
||
|
switch (setting){
|
||
|
case ViewSetting_ShowWhitespace:
|
||
|
{
|
||
|
view->show_whitespace = (b8)value;
|
||
|
}break;
|
||
|
|
||
|
case ViewSetting_ShowScrollbar:
|
||
|
{
|
||
|
view->hide_scrollbar = (b8)!value;
|
||
|
}break;
|
||
|
|
||
|
case ViewSetting_ShowFileBar:
|
||
|
{
|
||
|
view->hide_file_bar = (b8)!value;
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
result = false;
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Scope
|
||
|
view_get_managed_scope(Application_Links *app, View_ID view_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
Managed_Scope result = 0;
|
||
|
if (api_check_view(view)){
|
||
|
Assert(view->lifetime_object != 0);
|
||
|
result = (Managed_Scope)(view->lifetime_object->workspace.scope_id);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_Cursor
|
||
|
buffer_compute_cursor(Application_Links *app, Buffer_ID buffer, Buffer_Seek seek)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer);
|
||
|
Buffer_Cursor result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
result = file_compute_cursor(file, seek);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_Cursor
|
||
|
view_compute_cursor(Application_Links *app, View_ID view_id, Buffer_Seek seek){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
Buffer_Cursor result = {};
|
||
|
if (api_check_view(view)){
|
||
|
result = view_compute_cursor(view, seek);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_set_camera_bounds(Application_Links *app, View_ID view_id, Vec2_f32 margin, Vec2_f32 push_in_multiplier)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
result = true;
|
||
|
margin.x = clamp_bot(0.f, margin.x);
|
||
|
margin.y = clamp_bot(0.f, margin.y);
|
||
|
push_in_multiplier.x = clamp_bot(1.5f, push_in_multiplier.x);
|
||
|
push_in_multiplier.y = clamp_bot(1.5f, push_in_multiplier.y);
|
||
|
view->cursor_margin = margin;
|
||
|
view->cursor_push_in_multiplier = push_in_multiplier;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_get_camera_bounds(Application_Links *app, View_ID view_id, Vec2_f32 *margin, Vec2_f32 *push_in_multiplier)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
result = true;
|
||
|
*margin = view->cursor_margin;
|
||
|
*push_in_multiplier = view->cursor_push_in_multiplier;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_set_cursor(Application_Links *app, View_ID view_id, Buffer_Seek seek)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
Editing_File *file = view->file;
|
||
|
Assert(file != 0);
|
||
|
if (api_check_buffer(file)){
|
||
|
Buffer_Cursor cursor = file_compute_cursor(file, seek);
|
||
|
view_set_cursor(app->tctx, models, view, cursor.pos);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_set_buffer_scroll(Application_Links *app, View_ID view_id, Buffer_Scroll scroll,
|
||
|
Set_Buffer_Scroll_Rule rule)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
b32 result = false;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
if (api_check_view(view)){
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
scroll.position = view_normalize_buffer_point(tctx, models, view, scroll.position);
|
||
|
scroll.target = view_normalize_buffer_point(tctx, models, view, scroll.target);
|
||
|
scroll.target.pixel_shift.x = f32_round32(scroll.target.pixel_shift.x);
|
||
|
scroll.target.pixel_shift.y = f32_round32(scroll.target.pixel_shift.y);
|
||
|
scroll.target.pixel_shift.x = clamp_bot(0.f, scroll.target.pixel_shift.x);
|
||
|
Layout_Item_List line = view_get_line_layout(tctx, models, view,
|
||
|
scroll.target.line_number);
|
||
|
scroll.target.pixel_shift.y =
|
||
|
clamp(0.f, scroll.target.pixel_shift.y, line.height);
|
||
|
if (rule == SetBufferScroll_SnapCursorIntoView){
|
||
|
view_set_scroll(tctx, models, view, scroll);
|
||
|
}
|
||
|
else{
|
||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||
|
edit_pos.scroll = scroll;
|
||
|
view_set_edit_pos(view, edit_pos);
|
||
|
}
|
||
|
view->new_scroll_target = true;
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_set_mark(Application_Links *app, View_ID view_id, Buffer_Seek seek)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
Editing_File *file = view->file;
|
||
|
Assert(file != 0);
|
||
|
if (api_check_buffer(file)){
|
||
|
if (seek.type != buffer_seek_pos){
|
||
|
Buffer_Cursor cursor = file_compute_cursor(file, seek);
|
||
|
view->mark = cursor.pos;
|
||
|
}
|
||
|
else{
|
||
|
view->mark = seek.pos;
|
||
|
}
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_quit_ui(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (view != 0){
|
||
|
view_quit_ui(app->tctx, models, view);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_set_buffer(Application_Links *app, View_ID view_id, Buffer_ID buffer_id, Set_Buffer_Flag flags)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
Editing_File *file = working_set_get_file(&models->working_set, buffer_id);
|
||
|
if (api_check_buffer(file)){
|
||
|
if (file != view->file){
|
||
|
view_set_file(app->tctx, models, view, file);
|
||
|
if (!(flags & SetBuffer_KeepOriginalGUI)){
|
||
|
view_quit_ui(app->tctx, models, view);
|
||
|
}
|
||
|
}
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_push_context(Application_Links *app, View_ID view_id, View_Context *ctx){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
view_push_context(view, ctx);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_pop_context(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
view_pop_context(view);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
view_alter_context(Application_Links *app, View_ID view_id, View_Context *ctx){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
view_alter_context(view, ctx);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function View_Context
|
||
|
view_current_context(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
View_Context result = {};
|
||
|
if (api_check_view(view)){
|
||
|
result = view_current_context(view);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function String_Const_u8
|
||
|
view_current_context_hook_memory(Application_Links *app, View_ID view_id,
|
||
|
Hook_ID hook_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
String_Const_u8 result = {};
|
||
|
if (api_check_view(view)){
|
||
|
View_Context_Node *ctx = view_current_context_node(view);
|
||
|
if (ctx != 0){
|
||
|
switch (hook_id){
|
||
|
case HookID_DeltaRule:
|
||
|
{
|
||
|
result = make_data(ctx->delta_rule_memory,
|
||
|
ctx->ctx.delta_rule_memory_size);
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
function Dynamic_Workspace*
|
||
|
get_dynamic_workspace(Models *models, Managed_Scope handle){
|
||
|
Dynamic_Workspace *result = 0;
|
||
|
Table_Lookup lookup = table_lookup(&models->lifetime_allocator.scope_id_to_scope_ptr_table, handle);
|
||
|
if (lookup.found_match){
|
||
|
u64 val = 0;
|
||
|
table_read(&models->lifetime_allocator.scope_id_to_scope_ptr_table, lookup, &val);
|
||
|
result = (Dynamic_Workspace*)IntAsPtr(val);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Scope
|
||
|
create_user_managed_scope(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Lifetime_Object *object = lifetime_alloc_object(&models->lifetime_allocator, DynamicWorkspace_Unassociated, 0);
|
||
|
object->workspace.user_back_ptr = object;
|
||
|
Managed_Scope scope = (Managed_Scope)object->workspace.scope_id;
|
||
|
return(scope);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
destroy_user_managed_scope(Application_Links *app, Managed_Scope scope)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, scope);
|
||
|
b32 result = false;
|
||
|
if (workspace != 0 && workspace->user_type == DynamicWorkspace_Unassociated){
|
||
|
Lifetime_Object *lifetime_object = (Lifetime_Object*)workspace->user_back_ptr;
|
||
|
lifetime_free_object(&models->lifetime_allocator, lifetime_object);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Scope
|
||
|
get_global_managed_scope(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return((Managed_Scope)models->dynamic_workspace.scope_id);
|
||
|
}
|
||
|
|
||
|
function Lifetime_Object*
|
||
|
get_lifetime_object_from_workspace(Dynamic_Workspace *workspace){
|
||
|
Lifetime_Object *result = 0;
|
||
|
switch (workspace->user_type){
|
||
|
case DynamicWorkspace_Unassociated:
|
||
|
{
|
||
|
result = (Lifetime_Object*)workspace->user_back_ptr;
|
||
|
}break;
|
||
|
case DynamicWorkspace_Buffer:
|
||
|
{
|
||
|
Editing_File *file = (Editing_File*)workspace->user_back_ptr;
|
||
|
result = file->lifetime_object;
|
||
|
}break;
|
||
|
case DynamicWorkspace_View:
|
||
|
{
|
||
|
View *vptr = (View*)workspace->user_back_ptr;
|
||
|
result = vptr->lifetime_object;
|
||
|
}break;
|
||
|
default:
|
||
|
{
|
||
|
InvalidPath;
|
||
|
}break;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Scope
|
||
|
get_managed_scope_with_multiple_dependencies(Application_Links *app, Managed_Scope *scopes, i32 count)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Lifetime_Allocator *lifetime_allocator = &models->lifetime_allocator;
|
||
|
|
||
|
Scratch_Block scratch(app);
|
||
|
|
||
|
// TODO(allen): revisit this
|
||
|
struct Node_Ptr{
|
||
|
Node_Ptr *next;
|
||
|
Lifetime_Object *object_ptr;
|
||
|
};
|
||
|
|
||
|
Node_Ptr *first = 0;
|
||
|
Node_Ptr *last = 0;
|
||
|
i32 member_count = 0;
|
||
|
|
||
|
b32 filled_array = true;
|
||
|
for (i32 i = 0; i < count; i += 1){
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, scopes[i]);
|
||
|
if (workspace == 0){
|
||
|
filled_array = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (workspace->user_type){
|
||
|
case DynamicWorkspace_Global:
|
||
|
{
|
||
|
// NOTE(allen): (global_scope INTERSECT X) == X for all X, therefore we emit nothing when a global group is in the key list.
|
||
|
}break;
|
||
|
|
||
|
case DynamicWorkspace_Unassociated:
|
||
|
case DynamicWorkspace_Buffer:
|
||
|
case DynamicWorkspace_View:
|
||
|
{
|
||
|
Lifetime_Object *object = get_lifetime_object_from_workspace(workspace);
|
||
|
Assert(object != 0);
|
||
|
Node_Ptr *new_node = push_array(scratch, Node_Ptr, 1);
|
||
|
sll_queue_push(first, last, new_node);
|
||
|
new_node->object_ptr = object;
|
||
|
member_count += 1;
|
||
|
}break;
|
||
|
|
||
|
case DynamicWorkspace_Intersected:
|
||
|
{
|
||
|
Lifetime_Key *key = (Lifetime_Key*)workspace->user_back_ptr;
|
||
|
if (lifetime_key_check(lifetime_allocator, key)){
|
||
|
i32 key_member_count = key->count;
|
||
|
Lifetime_Object **key_member_ptr = key->members;
|
||
|
for (i32 j = 0; j < key_member_count; j += 1, key_member_ptr += 1){
|
||
|
Node_Ptr *new_node = push_array(scratch, Node_Ptr, 1);
|
||
|
sll_queue_push(first, last, new_node);
|
||
|
new_node->object_ptr = *key_member_ptr;
|
||
|
member_count += 1;
|
||
|
}
|
||
|
}
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
InvalidPath;
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Managed_Scope result = 0;
|
||
|
if (filled_array){
|
||
|
Lifetime_Object **object_ptr_array = push_array(scratch, Lifetime_Object*, member_count);
|
||
|
i32 index = 0;
|
||
|
for (Node_Ptr *node = first;
|
||
|
node != 0;
|
||
|
node = node->next){
|
||
|
object_ptr_array[index] = node->object_ptr;
|
||
|
index += 1;
|
||
|
}
|
||
|
member_count = lifetime_sort_and_dedup_object_set(object_ptr_array, member_count);
|
||
|
Lifetime_Key *key = lifetime_get_or_create_intersection_key(lifetime_allocator, object_ptr_array, member_count);
|
||
|
result = (Managed_Scope)key->dynamic_workspace.scope_id;
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
managed_scope_clear_contents(Application_Links *app, Managed_Scope scope)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, scope);
|
||
|
b32 result = false;
|
||
|
if (workspace != 0){
|
||
|
dynamic_workspace_clear_contents(workspace);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
managed_scope_clear_self_all_dependent_scopes(Application_Links *app, Managed_Scope scope)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, scope);
|
||
|
b32 result = false;
|
||
|
if (workspace != 0 && workspace->user_type != DynamicWorkspace_Global && workspace->user_type != DynamicWorkspace_Intersected){
|
||
|
Lifetime_Object *object = get_lifetime_object_from_workspace(workspace);
|
||
|
Assert(object != 0);
|
||
|
lifetime_object_reset(&models->lifetime_allocator, object);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Base_Allocator*
|
||
|
managed_scope_allocator(Application_Links *app, Managed_Scope scope)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, scope);
|
||
|
Base_Allocator *result = 0;
|
||
|
if (workspace != 0){
|
||
|
result = &workspace->heap_wrapper;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function u64
|
||
|
managed_id_group_highest_id(Application_Links *app, String_Const_u8 group){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_ID_Set *set = &models->managed_id_set;
|
||
|
return(managed_ids_group_highest_id(set, group));
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_ID
|
||
|
managed_id_declare(Application_Links *app, String_Const_u8 group, String_Const_u8 name)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_ID_Set *set = &models->managed_id_set;
|
||
|
return(managed_ids_declare(set, group, name));
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_ID
|
||
|
managed_id_get(Application_Links *app, String_Const_u8 group, String_Const_u8 name){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_ID_Set *set = &models->managed_id_set;
|
||
|
return(managed_ids_get(set, group, name));
|
||
|
}
|
||
|
|
||
|
api(custom) function void*
|
||
|
managed_scope_get_attachment(Application_Links *app, Managed_Scope scope, Managed_ID id, u64 size){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, scope);
|
||
|
void *result = 0;
|
||
|
if (workspace != 0){
|
||
|
Dynamic_Variable_Block *var_block = &workspace->var_block;
|
||
|
String_Const_u8 data = dynamic_variable_get(var_block, id, size);
|
||
|
if (data.size >= size){
|
||
|
result = data.str;
|
||
|
}
|
||
|
else{
|
||
|
#define M \
|
||
|
"ERROR: scope attachment already exists with a size smaller than the requested size; no attachment pointer can be returned."
|
||
|
print_message(app, string_u8_litexpr(M));
|
||
|
#undef M
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
managed_scope_attachment_erase(Application_Links *app, Managed_Scope scope, Managed_ID id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, scope);
|
||
|
b32 result = false;
|
||
|
if (workspace != 0){
|
||
|
Dynamic_Variable_Block *var_block = &workspace->var_block;
|
||
|
dynamic_variable_erase(var_block, id);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Object
|
||
|
alloc_managed_memory_in_scope(Application_Links *app, Managed_Scope scope, i32 item_size, i32 count)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, scope);
|
||
|
Managed_Object result = 0;
|
||
|
if (workspace != 0){
|
||
|
result = managed_object_alloc_managed_memory(workspace, item_size, count, 0);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Object
|
||
|
alloc_buffer_markers_on_buffer(Application_Links *app, Buffer_ID buffer_id, i32 count, Managed_Scope *optional_extra_scope)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Managed_Scope markers_scope = file_get_managed_scope(file);
|
||
|
if (optional_extra_scope != 0){
|
||
|
Managed_Object scope_array[2];
|
||
|
scope_array[0] = markers_scope;
|
||
|
scope_array[1] = *optional_extra_scope;
|
||
|
markers_scope = get_managed_scope_with_multiple_dependencies(app, scope_array, 2);
|
||
|
}
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, markers_scope);
|
||
|
Managed_Object result = 0;
|
||
|
if (workspace != 0){
|
||
|
result = managed_object_alloc_buffer_markers(workspace, buffer_id, count, 0);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
function Managed_Object_Ptr_And_Workspace
|
||
|
get_dynamic_object_ptrs(Models *models, Managed_Object object){
|
||
|
Managed_Object_Ptr_And_Workspace result = {};
|
||
|
u32 hi_id = (object >> 32)&max_u32;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, hi_id);
|
||
|
if (workspace != 0){
|
||
|
u32 lo_id = object&max_u32;
|
||
|
Managed_Object_Standard_Header *header = (Managed_Object_Standard_Header*)dynamic_workspace_get_pointer(workspace, lo_id);
|
||
|
if (header != 0){
|
||
|
result.workspace = workspace;
|
||
|
result.header = header;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function u32
|
||
|
managed_object_get_item_size(Application_Links *app, Managed_Object object)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_Object_Ptr_And_Workspace object_ptrs = get_dynamic_object_ptrs(models, object);
|
||
|
u32 result = 0;
|
||
|
if (object_ptrs.header != 0){
|
||
|
result = object_ptrs.header->item_size;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function u32
|
||
|
managed_object_get_item_count(Application_Links *app, Managed_Object object)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_Object_Ptr_And_Workspace object_ptrs = get_dynamic_object_ptrs(models, object);
|
||
|
u32 result = 0;
|
||
|
if (object_ptrs.header != 0){
|
||
|
result = object_ptrs.header->count;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function void*
|
||
|
managed_object_get_pointer(Application_Links *app, Managed_Object object)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_Object_Ptr_And_Workspace object_ptrs = get_dynamic_object_ptrs(models, object);
|
||
|
return(get_dynamic_object_memory_ptr(object_ptrs.header));
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Object_Type
|
||
|
managed_object_get_type(Application_Links *app, Managed_Object object)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_Object_Ptr_And_Workspace object_ptrs = get_dynamic_object_ptrs(models, object);
|
||
|
if (object_ptrs.header != 0){
|
||
|
Managed_Object_Type type = object_ptrs.header->type;
|
||
|
if (type < 0 || ManagedObjectType_COUNT <= type){
|
||
|
type = ManagedObjectType_Error;
|
||
|
}
|
||
|
return(type);
|
||
|
}
|
||
|
return(ManagedObjectType_Error);
|
||
|
}
|
||
|
|
||
|
api(custom) function Managed_Scope
|
||
|
managed_object_get_containing_scope(Application_Links *app, Managed_Object object)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
u32 hi_id = (object >> 32)&max_u32;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, hi_id);
|
||
|
if (workspace != 0){
|
||
|
return((Managed_Scope)hi_id);
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
managed_object_free(Application_Links *app, Managed_Object object)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
u32 hi_id = (object >> 32)&max_u32;
|
||
|
Dynamic_Workspace *workspace = get_dynamic_workspace(models, hi_id);
|
||
|
b32 result = false;
|
||
|
if (workspace != 0){
|
||
|
result = managed_object_free(workspace, object);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
// TODO(allen): ELIMINATE STORE & LOAD
|
||
|
api(custom) function b32
|
||
|
managed_object_store_data(Application_Links *app, Managed_Object object, u32 first_index, u32 count, void *mem)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_Object_Ptr_And_Workspace object_ptrs = get_dynamic_object_ptrs(models, object);
|
||
|
u8 *ptr = get_dynamic_object_memory_ptr(object_ptrs.header);
|
||
|
b32 result = false;
|
||
|
if (ptr != 0){
|
||
|
u32 item_count = object_ptrs.header->count;
|
||
|
if (0 <= first_index && first_index + count <= item_count){
|
||
|
u32 item_size = object_ptrs.header->item_size;
|
||
|
block_copy(ptr + first_index*item_size, mem, count*item_size);
|
||
|
heap_assert_good(&object_ptrs.workspace->heap);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
managed_object_load_data(Application_Links *app, Managed_Object object, u32 first_index, u32 count, void *mem_out)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Managed_Object_Ptr_And_Workspace object_ptrs = get_dynamic_object_ptrs(models, object);
|
||
|
u8 *ptr = get_dynamic_object_memory_ptr(object_ptrs.header);
|
||
|
b32 result = false;
|
||
|
if (ptr != 0){
|
||
|
u32 item_count = object_ptrs.header->count;
|
||
|
if (0 <= first_index && first_index + count <= item_count){
|
||
|
u32 item_size = object_ptrs.header->item_size;
|
||
|
block_copy(mem_out, ptr + first_index*item_size, count*item_size);
|
||
|
heap_assert_good(&object_ptrs.workspace->heap);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function User_Input
|
||
|
get_next_input_raw(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Thread_Context_Extra_Info *tctx_info = (Thread_Context_Extra_Info*)tctx->user_data;
|
||
|
User_Input result = {};
|
||
|
if (tctx_info->coroutine != 0){
|
||
|
Coroutine *coroutine = (Coroutine*)tctx_info->coroutine;
|
||
|
Co_Out *out = (Co_Out*)coroutine->out;
|
||
|
out->request = CoRequest_None;
|
||
|
coroutine_yield(coroutine);
|
||
|
Co_In *in = (Co_In*)coroutine->in;
|
||
|
result = in->user_input;
|
||
|
}
|
||
|
else{
|
||
|
#define M "ERROR: get_next_input called in a hook that may not make calls to blocking APIs"
|
||
|
print_message(app, string_u8_litexpr(M));
|
||
|
#undef M
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function i64
|
||
|
get_current_input_sequence_number(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(models->current_input_sequence_number);
|
||
|
}
|
||
|
|
||
|
api(custom) function User_Input
|
||
|
get_current_input(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(models->current_input);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
set_current_input(Application_Links *app, User_Input *input)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
block_copy_struct(&models->current_input, input);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
leave_current_input_unhandled(Application_Links *app){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
models->current_input_unhandled = true;
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
set_custom_hook(Application_Links *app, Hook_ID hook_id, Void_Func *func_ptr){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
switch (hook_id){
|
||
|
case HookID_BufferViewerUpdate:
|
||
|
{
|
||
|
models->buffer_viewer_update = (Hook_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_DeltaRule:
|
||
|
{
|
||
|
models->delta_rule = (Delta_Rule_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_ViewEventHandler:
|
||
|
{
|
||
|
models->view_event_handler = (Custom_Command_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_Tick:
|
||
|
{
|
||
|
models->tick = (Tick_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_RenderCaller:
|
||
|
{
|
||
|
models->render_caller = (Render_Caller_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_WholeScreenRenderCaller:
|
||
|
{
|
||
|
models->whole_screen_render_caller = (Whole_Screen_Render_Caller_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_BufferNameResolver:
|
||
|
{
|
||
|
models->buffer_name_resolver = (Buffer_Name_Resolver_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_BeginBuffer:
|
||
|
{
|
||
|
models->begin_buffer = (Buffer_Hook_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_EndBuffer:
|
||
|
{
|
||
|
models->end_buffer = (Buffer_Hook_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_NewFile:
|
||
|
{
|
||
|
models->new_file = (Buffer_Hook_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_SaveFile:
|
||
|
{
|
||
|
models->save_file = (Buffer_Hook_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_BufferEditRange:
|
||
|
{
|
||
|
models->buffer_edit_range = (Buffer_Edit_Range_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_BufferRegion:
|
||
|
{
|
||
|
models->buffer_region = (Buffer_Region_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_Layout:
|
||
|
{
|
||
|
models->layout_func = (Layout_Function*)func_ptr;
|
||
|
}break;
|
||
|
case HookID_ViewChangeBuffer:
|
||
|
{
|
||
|
models->view_change_buffer = (View_Change_Buffer_Function*)func_ptr;
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function Void_Func*
|
||
|
get_custom_hook(Application_Links *app, Hook_ID hook_id){
|
||
|
Void_Func *result = 0;
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
switch (hook_id){
|
||
|
case HookID_BufferViewerUpdate:
|
||
|
{
|
||
|
result = (Void_Func*)models->buffer_viewer_update;
|
||
|
}break;
|
||
|
case HookID_DeltaRule:
|
||
|
{
|
||
|
result = (Void_Func*)models->delta_rule;
|
||
|
}break;
|
||
|
case HookID_ViewEventHandler:
|
||
|
{
|
||
|
result = (Void_Func*)models->view_event_handler;
|
||
|
}break;
|
||
|
case HookID_Tick:
|
||
|
{
|
||
|
result = (Void_Func*)models->tick;
|
||
|
}break;
|
||
|
case HookID_RenderCaller:
|
||
|
{
|
||
|
result = (Void_Func*)models->render_caller;
|
||
|
}break;
|
||
|
case HookID_WholeScreenRenderCaller:
|
||
|
{
|
||
|
result = (Void_Func*)models->whole_screen_render_caller;
|
||
|
}break;
|
||
|
case HookID_BufferNameResolver:
|
||
|
{
|
||
|
result = (Void_Func*)models->buffer_name_resolver;
|
||
|
}break;
|
||
|
case HookID_BeginBuffer:
|
||
|
{
|
||
|
result = (Void_Func*)models->begin_buffer;
|
||
|
}break;
|
||
|
case HookID_EndBuffer:
|
||
|
{
|
||
|
result = (Void_Func*)models->end_buffer;
|
||
|
}break;
|
||
|
case HookID_NewFile:
|
||
|
{
|
||
|
result = (Void_Func*)models->new_file;
|
||
|
}break;
|
||
|
case HookID_SaveFile:
|
||
|
{
|
||
|
result = (Void_Func*)models->save_file;
|
||
|
}break;
|
||
|
case HookID_BufferEditRange:
|
||
|
{
|
||
|
result = (Void_Func*)models->buffer_edit_range;
|
||
|
}break;
|
||
|
case HookID_BufferRegion:
|
||
|
{
|
||
|
result = (Void_Func*)models->buffer_region;
|
||
|
}break;
|
||
|
case HookID_Layout:
|
||
|
{
|
||
|
result = (Void_Func*)models->layout_func;
|
||
|
}break;
|
||
|
case HookID_ViewChangeBuffer:
|
||
|
{
|
||
|
result = (Void_Func*)models->view_change_buffer;
|
||
|
}break;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
set_custom_hook_memory_size(Application_Links *app, Hook_ID hook_id, u64 size){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
b32 result = true;
|
||
|
switch (hook_id){
|
||
|
case HookID_DeltaRule:
|
||
|
{
|
||
|
models->delta_rule_memory_size = size;
|
||
|
}break;
|
||
|
default:
|
||
|
{
|
||
|
result = false;
|
||
|
}break;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Mouse_State
|
||
|
get_mouse_state(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(models->input->mouse);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
get_active_query_bars(Application_Links *app, View_ID view_id, i32 max_result_count, Query_Bar_Ptr_Array *array_out)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_view(view)){
|
||
|
i32 count = 0;
|
||
|
Query_Bar **ptrs = array_out->ptrs;
|
||
|
for (Query_Slot *slot = view->query_set.used_slot;
|
||
|
slot != 0 && (count < max_result_count);
|
||
|
slot = slot->next){
|
||
|
if (slot->query_bar != 0){
|
||
|
ptrs[count++] = slot->query_bar;
|
||
|
}
|
||
|
}
|
||
|
array_out->count = count;
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
start_query_bar(Application_Links *app, Query_Bar *bar, u32 flags)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Panel *active_panel = layout_get_active_panel(&models->layout);
|
||
|
View *active_view = active_panel->view;
|
||
|
Query_Slot *slot = alloc_query_slot(&active_view->query_set);
|
||
|
b32 result = (slot != 0);
|
||
|
if (result){
|
||
|
slot->query_bar = bar;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
end_query_bar(Application_Links *app, Query_Bar *bar, u32 flags)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Panel *active_panel = layout_get_active_panel(&models->layout);
|
||
|
View *active_view = active_panel->view;
|
||
|
free_query_slot(&active_view->query_set, bar);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
clear_all_query_bars(Application_Links *app, View_ID view_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
View *view = imp_get_view(models, view_id);
|
||
|
if (api_check_view(view)){
|
||
|
free_all_queries(&view->query_set);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
print_message(Application_Links *app, String_Const_u8 message)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = models->message_buffer;
|
||
|
if (file != 0){
|
||
|
output_file_append(app->tctx, models, file, message);
|
||
|
file_cursor_to_end(app->tctx, models, file);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
log_string(Application_Links *app, String_Const_u8 str){
|
||
|
return(log_string(str));
|
||
|
}
|
||
|
|
||
|
api(custom) function Face_ID
|
||
|
get_largest_face_id(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(font_set_get_largest_id(&models->font_set));
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
set_global_face(Application_Links *app, Face_ID id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
b32 result = false;
|
||
|
Face *face = font_set_face_from_id(&models->font_set, id);
|
||
|
if (face != 0){
|
||
|
models->global_face_id = face->id;
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function History_Record_Index
|
||
|
buffer_history_get_max_record_index(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
History_Record_Index result = 0;
|
||
|
if (api_check_buffer(file) && history_is_activated(&file->state.history)){
|
||
|
result = history_get_record_count(&file->state.history);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
function void
|
||
|
buffer_history__fill_record_info(Record *record, Record_Info *out){
|
||
|
out->kind = record->kind;
|
||
|
out->pos_before_edit = record->pos_before_edit;
|
||
|
out->edit_number = record->edit_number;
|
||
|
switch (out->kind){
|
||
|
case RecordKind_Single:
|
||
|
{
|
||
|
out->single_string_forward = record->single.forward_text ;
|
||
|
out->single_string_backward = record->single.backward_text;
|
||
|
out->single_first = record->single.first;
|
||
|
}break;
|
||
|
case RecordKind_Group:
|
||
|
{
|
||
|
out->group_count = record->group.count;
|
||
|
}break;
|
||
|
default:
|
||
|
{
|
||
|
InvalidPath;
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function Record_Info
|
||
|
buffer_history_get_record_info(Application_Links *app, Buffer_ID buffer_id, History_Record_Index index){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Record_Info result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
History *history = &file->state.history;
|
||
|
if (history_is_activated(history)){
|
||
|
i32 max_index = history_get_record_count(history);
|
||
|
if (0 <= index && index <= max_index){
|
||
|
if (0 < index){
|
||
|
Record *record = history_get_record(history, index);
|
||
|
buffer_history__fill_record_info(record, &result);
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_InitialStateDummyRecord;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_IndexOutOfBounds;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_NoHistoryAttached;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_InvalidBuffer;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Record_Info
|
||
|
buffer_history_get_group_sub_record(Application_Links *app, Buffer_ID buffer_id, History_Record_Index index, i32 sub_index){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Record_Info result = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
History *history = &file->state.history;
|
||
|
if (history_is_activated(history)){
|
||
|
i32 max_index = history_get_record_count(history);
|
||
|
if (0 <= index && index <= max_index){
|
||
|
if (0 < index){
|
||
|
Record *record = history_get_record(history, index);
|
||
|
if (record->kind == RecordKind_Group){
|
||
|
record = history_get_sub_record(record, sub_index + 1);
|
||
|
if (record != 0){
|
||
|
buffer_history__fill_record_info(record, &result);
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_SubIndexOutOfBounds;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_WrongRecordTypeAtIndex;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_InitialStateDummyRecord;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_IndexOutOfBounds;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_NoHistoryAttached;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result.error = RecordError_InvalidBuffer;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function History_Record_Index
|
||
|
buffer_history_get_current_state_index(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
History_Record_Index result = 0;
|
||
|
if (api_check_buffer(file) && history_is_activated(&file->state.history)){
|
||
|
result = file_get_current_record_index(file);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_history_set_current_state_index(Application_Links *app, Buffer_ID buffer_id, History_Record_Index index){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file) && history_is_activated(&file->state.history)){
|
||
|
i32 max_index = history_get_record_count(&file->state.history);
|
||
|
if (0 <= index && index <= max_index){
|
||
|
edit_change_current_history_state(app->tctx, models, file, index);
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_history_merge_record_range(Application_Links *app, Buffer_ID buffer_id, History_Record_Index first_index, History_Record_Index last_index, Record_Merge_Flag flags){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
result = edit_merge_history_range(app->tctx, models, file, first_index, last_index, flags);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_history_clear_after_current_state(Application_Links *app, Buffer_ID buffer_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
b32 result = false;
|
||
|
if (api_check_buffer(file) && history_is_activated(&file->state.history)){
|
||
|
history_dump_records_after_index(&file->state.history, file->state.current_record_index);
|
||
|
result = true;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
global_history_edit_group_begin(Application_Links *app){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
global_history_adjust_edit_grouping_counter(&models->global_history, 1);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
global_history_edit_group_end(Application_Links *app){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
global_history_adjust_edit_grouping_counter(&models->global_history, -1);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
buffer_set_face(Application_Links *app, Buffer_ID buffer_id, Face_ID id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
|
||
|
b32 did_change = false;
|
||
|
if (api_check_buffer(file)){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, id);
|
||
|
if (face != 0){
|
||
|
file->settings.face_id = face->id;
|
||
|
did_change = true;
|
||
|
}
|
||
|
}
|
||
|
return(did_change);
|
||
|
}
|
||
|
|
||
|
api(custom) function Face_Description
|
||
|
get_face_description(Application_Links *app, Face_ID face_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Face_Description description = {};
|
||
|
if (face_id != 0){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
description = face->description;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
description.parameters.aa_mode = FaceAntialiasingMode_8BitMono;
|
||
|
description.parameters.pt_size = models->settings.font_size;
|
||
|
description.parameters.hinting = models->settings.use_hinting;
|
||
|
}
|
||
|
return(description);
|
||
|
}
|
||
|
|
||
|
api(custom) function Face_Metrics
|
||
|
get_face_metrics(Application_Links *app, Face_ID face_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Face_Metrics result = {};
|
||
|
if (face_id != 0){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
result = face->metrics;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Face_Advance_Map
|
||
|
get_face_advance_map(Application_Links *app, Face_ID face_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Face_Advance_Map result = {};
|
||
|
if (face_id != 0){
|
||
|
Face *face = font_set_face_from_id(&models->font_set, face_id);
|
||
|
if (face != 0){
|
||
|
result = face->advance_map;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Face_ID
|
||
|
get_face_id(Application_Links *app, Buffer_ID buffer_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Face_ID result = 0;
|
||
|
if (buffer_id != 0){
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
if (api_check_buffer(file)){
|
||
|
result = file->settings.face_id;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
result = models->global_face_id;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Face_ID
|
||
|
try_create_new_face(Application_Links *app, Face_Description *description)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Thread_Context_Extra_Info *tctx_info = (Thread_Context_Extra_Info*)tctx->user_data;
|
||
|
Face_ID result = 0;
|
||
|
if (tctx_info != 0 && tctx_info->coroutine != 0){
|
||
|
Coroutine *coroutine = (Coroutine*)tctx_info->coroutine;
|
||
|
Assert(coroutine != 0);
|
||
|
Co_Out *out = (Co_Out*)coroutine->out;
|
||
|
out->request = CoRequest_NewFontFace;
|
||
|
out->face_description = description;
|
||
|
coroutine_yield(coroutine);
|
||
|
Co_In *in = (Co_In*)coroutine->in;
|
||
|
result = in->face_id;
|
||
|
}
|
||
|
else if (tctx_info != 0){
|
||
|
// This API does nothing when called from an async thread.
|
||
|
}
|
||
|
else{
|
||
|
Face *new_face = font_set_new_face(&models->font_set, description);
|
||
|
if (new_face != 0){
|
||
|
result = new_face->id;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
try_modify_face(Application_Links *app, Face_ID id, Face_Description *description)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Thread_Context_Extra_Info *tctx_info = (Thread_Context_Extra_Info*)tctx->user_data;
|
||
|
b32 result = false;
|
||
|
if (tctx_info != 0 && tctx_info->coroutine != 0){
|
||
|
Coroutine *coroutine = (Coroutine*)tctx_info->coroutine;
|
||
|
Assert(coroutine != 0);
|
||
|
Co_Out *out = (Co_Out*)coroutine->out;
|
||
|
out->request = CoRequest_ModifyFace;
|
||
|
out->face_description = description;
|
||
|
out->face_id = id;
|
||
|
coroutine_yield(coroutine);
|
||
|
Co_In *in = (Co_In*)coroutine->in;
|
||
|
result = in->success;
|
||
|
}
|
||
|
else if (tctx_info != 0){
|
||
|
// This API does nothing when called from an async thread.
|
||
|
}
|
||
|
else{
|
||
|
result = font_set_modify_face(&models->font_set, id, description);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
try_release_face(Application_Links *app, Face_ID id, Face_ID replacement_id)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Font_Set *font_set = &models->font_set;
|
||
|
Face *face = font_set_face_from_id(font_set, id);
|
||
|
Face *replacement = font_set_face_from_id(font_set, replacement_id);
|
||
|
return(release_font_and_update(models, face, replacement));
|
||
|
}
|
||
|
|
||
|
api(custom) function String_Const_u8
|
||
|
push_hot_directory(Application_Links *app, Arena *arena)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Hot_Directory *hot = &models->hot_directory;
|
||
|
hot_directory_clean_end(hot);
|
||
|
return(push_string_copy(arena, hot->string));
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
set_hot_directory(Application_Links *app, String_Const_u8 string)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Hot_Directory *hot = &models->hot_directory;
|
||
|
hot_directory_set(hot, string);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
send_exit_signal(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
models->keep_playing = false;
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
hard_exit(Application_Links *app)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
models->hard_exit = true;
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
set_window_title(Application_Links *app, String_Const_u8 title)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
models->has_new_title = true;
|
||
|
u64 cap_before_null = (u64)(models->title_capacity - 1);
|
||
|
u64 copy_size = clamp_top(title.size, cap_before_null);
|
||
|
block_copy(models->title_space, title.str, copy_size);
|
||
|
models->title_space[copy_size] = 0;
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
acquire_global_frame_mutex(Application_Links *app){
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Thread_Context_Extra_Info *tctx_info = (Thread_Context_Extra_Info*)tctx->user_data;
|
||
|
if (tctx_info != 0 && tctx_info->coroutine != 0){
|
||
|
Coroutine *coroutine = (Coroutine*)tctx_info->coroutine;
|
||
|
Assert(coroutine != 0);
|
||
|
Co_Out *out = (Co_Out*)coroutine->out;
|
||
|
out->request = CoRequest_AcquireGlobalFrameMutex;
|
||
|
coroutine_yield(coroutine);
|
||
|
}
|
||
|
else{
|
||
|
system_acquire_global_frame_mutex(tctx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
release_global_frame_mutex(Application_Links *app){
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Thread_Context_Extra_Info *tctx_info = (Thread_Context_Extra_Info*)tctx->user_data;
|
||
|
if (tctx_info != 0 && tctx_info->coroutine != 0){
|
||
|
Coroutine *coroutine = (Coroutine*)tctx_info->coroutine;
|
||
|
Assert(coroutine != 0);
|
||
|
Co_Out *out = (Co_Out*)coroutine->out;
|
||
|
out->request = CoRequest_ReleaseGlobalFrameMutex;
|
||
|
coroutine_yield(coroutine);
|
||
|
}
|
||
|
else{
|
||
|
system_release_global_frame_mutex(tctx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
api(custom) function Vec2_f32
|
||
|
draw_string_oriented(Application_Links *app, Face_ID font_id, ARGB_Color color,
|
||
|
String_Const_u8 str, Vec2_f32 point, u32 flags, Vec2_f32 delta)
|
||
|
{
|
||
|
Vec2_f32 result = point;
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Face *face = font_set_face_from_id(&models->font_set, font_id);
|
||
|
if (models->target == 0){
|
||
|
f32 width = font_string_width(models->target, face, str);
|
||
|
result += delta*width;
|
||
|
}
|
||
|
else{
|
||
|
f32 width = draw_string(models->target, face, str, point, color, flags, delta);
|
||
|
result += delta*width;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function f32
|
||
|
get_string_advance(Application_Links *app, Face_ID font_id, String_Const_u8 str)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Face *face = font_set_face_from_id(&models->font_set, font_id);
|
||
|
return(font_string_width(models->target, face, str));
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
draw_rectangle(Application_Links *app, Rect_f32 rect, f32 roundness, ARGB_Color color){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
if (models->in_render_mode){
|
||
|
draw_rectangle(models->target, rect, roundness, color);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
draw_rectangle_outline(Application_Links *app, Rect_f32 rect, f32 roundness, f32 thickness, ARGB_Color color){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
if (models->in_render_mode){
|
||
|
draw_rectangle_outline(models->target, rect, roundness, thickness, color);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
draw_set_clip(Application_Links *app, Rect_f32 new_clip){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(draw_set_clip(models->target, new_clip));
|
||
|
}
|
||
|
|
||
|
api(custom) function Text_Layout_ID
|
||
|
text_layout_create(Application_Links *app, Buffer_ID buffer_id, Rect_f32 rect, Buffer_Point buffer_point){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer_id);
|
||
|
Text_Layout_ID result = 0;
|
||
|
if (api_check_buffer(file)){
|
||
|
Thread_Context *tctx = app->tctx;
|
||
|
Face *face = file_get_face(models, file);
|
||
|
|
||
|
Gap_Buffer *buffer = &file->state.buffer;
|
||
|
|
||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||
|
|
||
|
Vec2_f32 dim = rect_dim(rect);
|
||
|
|
||
|
i64 line_count = buffer_line_count(buffer);
|
||
|
i64 line_number = buffer_point.line_number;
|
||
|
f32 y = -buffer_point.pixel_shift.y;
|
||
|
for (;line_number <= line_count;
|
||
|
line_number += 1){
|
||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file,
|
||
|
layout_func, dim.x, face,
|
||
|
line_number);
|
||
|
f32 next_y = y + line.height;
|
||
|
if (next_y >= dim.y){
|
||
|
break;
|
||
|
}
|
||
|
y = next_y;
|
||
|
}
|
||
|
|
||
|
Range_i64 visible_line_number_range = Ii64(buffer_point.line_number, line_number);
|
||
|
Range_i64 visible_range = Ii64(buffer_get_first_pos_from_line_number(buffer, visible_line_number_range.min),
|
||
|
buffer_get_last_pos_from_line_number(buffer, visible_line_number_range.max));
|
||
|
|
||
|
i64 item_count = range_size_inclusive(visible_range);
|
||
|
|
||
|
Arena arena = make_arena_system();
|
||
|
Arena *arena_ptr = push_array_zero(&arena, Arena, 1);
|
||
|
*arena_ptr = arena;
|
||
|
ARGB_Color *colors_array = push_array_zero(arena_ptr, ARGB_Color, item_count);
|
||
|
result = text_layout_new(&models->text_layouts, arena_ptr, buffer_id, buffer_point,
|
||
|
visible_range, visible_line_number_range, rect, colors_array,
|
||
|
layout_func);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
text_layout_region(Application_Links *app, Text_Layout_ID text_layout_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Rect_f32 result = {};
|
||
|
Text_Layout *layout = text_layout_get(&models->text_layouts, text_layout_id);
|
||
|
if (layout != 0){
|
||
|
result = layout->rect;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Buffer_ID
|
||
|
text_layout_get_buffer(Application_Links *app, Text_Layout_ID text_layout_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Buffer_ID result = 0;
|
||
|
Text_Layout *layout = text_layout_get(&models->text_layouts, text_layout_id);
|
||
|
if (layout != 0){
|
||
|
result = layout->buffer_id;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Range_i64
|
||
|
text_layout_get_visible_range(Application_Links *app, Text_Layout_ID text_layout_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Range_i64 result = {};
|
||
|
Text_Layout *layout = text_layout_get(&models->text_layouts, text_layout_id);
|
||
|
if (layout != 0){
|
||
|
result = layout->visible_range;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Range_f32
|
||
|
text_layout_line_on_screen(Application_Links *app, Text_Layout_ID layout_id, i64 line_number){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Range_f32 result = {};
|
||
|
Text_Layout *layout = text_layout_get(&models->text_layouts, layout_id);
|
||
|
if (layout == 0){
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
Layout_Function *layout_func = layout->layout_func;
|
||
|
|
||
|
Rect_f32 rect = layout->rect;
|
||
|
if (range_contains_inclusive(layout->visible_line_number_range, line_number)){
|
||
|
Editing_File *file = imp_get_file(models, layout->buffer_id);
|
||
|
if (api_check_buffer(file)){
|
||
|
f32 width = rect_width(rect);
|
||
|
Face *face = file_get_face(models, file);
|
||
|
|
||
|
for (i64 line_number_it = layout->visible_line_number_range.first;;
|
||
|
line_number_it += 1){
|
||
|
Layout_Item_List line = file_get_line_layout(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
line_number_it);
|
||
|
result.max += line.height;
|
||
|
if (line_number_it == line_number){
|
||
|
break;
|
||
|
}
|
||
|
result.min = result.max;
|
||
|
}
|
||
|
|
||
|
result += rect.y0 - layout->point.pixel_shift.y;
|
||
|
}
|
||
|
}
|
||
|
else if (line_number < layout->visible_line_number_range.min){
|
||
|
result = If32(rect.y0, rect.y0);
|
||
|
}
|
||
|
else if (line_number > layout->visible_line_number_range.max){
|
||
|
result = If32(rect.y1, rect.y1);
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function Rect_f32
|
||
|
text_layout_character_on_screen(Application_Links *app, Text_Layout_ID layout_id, i64 pos){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Rect_f32 result = {};
|
||
|
Text_Layout *layout = text_layout_get(&models->text_layouts, layout_id);
|
||
|
if (layout != 0 && range_contains_inclusive(layout->visible_range, pos)){
|
||
|
Editing_File *file = imp_get_file(models, layout->buffer_id);
|
||
|
if (api_check_buffer(file)){
|
||
|
Gap_Buffer *buffer = &file->state.buffer;
|
||
|
i64 line_number = buffer_get_line_index(buffer, pos) + 1;
|
||
|
|
||
|
if (range_contains_inclusive(layout->visible_line_number_range, line_number)){
|
||
|
Rect_f32 rect = layout->rect;
|
||
|
f32 width = rect_width(rect);
|
||
|
Face *face = file_get_face(models, file);
|
||
|
|
||
|
Layout_Function *layout_func = layout->layout_func;
|
||
|
|
||
|
f32 y = 0.f;
|
||
|
Layout_Item_List line = {};
|
||
|
for (i64 line_number_it = layout->visible_line_number_range.first;;
|
||
|
line_number_it += 1){
|
||
|
line = file_get_line_layout(app->tctx, models, file,
|
||
|
layout_func, width, face,
|
||
|
line_number_it);
|
||
|
if (line_number_it == line_number){
|
||
|
break;
|
||
|
}
|
||
|
y += line.height;
|
||
|
}
|
||
|
|
||
|
// TODO(allen): optimization: This is some fairly heavy computation. We really
|
||
|
// need to accelerate the (pos -> item) lookup within a single
|
||
|
// Buffer_Layout_Item_List.
|
||
|
b32 is_first_item = true;
|
||
|
result = Rf32_negative_infinity;
|
||
|
for (Layout_Item_Block *block = line.first;
|
||
|
block != 0;
|
||
|
block = block->next){
|
||
|
i64 count = block->item_count;
|
||
|
Layout_Item *item_ptr = block->items;
|
||
|
for (i32 i = 0; i < count; i += 1, item_ptr += 1){
|
||
|
if (HasFlag(item_ptr->flags, LayoutItemFlag_Ghost_Character)){
|
||
|
continue;
|
||
|
}
|
||
|
i64 index = item_ptr->index;
|
||
|
if (index == pos){
|
||
|
result = rect_union(result, item_ptr->rect);
|
||
|
}
|
||
|
else if (index > pos){
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Vec2_f32 shift = V2f32(rect.x0, rect.y0 + y) - layout->point.pixel_shift;
|
||
|
result.p0 += shift;
|
||
|
result.p1 += shift;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
paint_text_color(Application_Links *app, Text_Layout_ID layout_id, Range_i64 range, ARGB_Color color){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Rect_f32 result = {};
|
||
|
Text_Layout *layout = text_layout_get(&models->text_layouts, layout_id);
|
||
|
if (layout != 0){
|
||
|
range.min = clamp_bot(layout->visible_range.min, range.min);
|
||
|
range.max = clamp_top(range.max, layout->visible_range.max);
|
||
|
range.min -= layout->visible_range.min;
|
||
|
range.max -= layout->visible_range.min;
|
||
|
ARGB_Color *color_ptr = layout->item_colors + range.min;
|
||
|
for (i64 i = range.min; i < range.max; i += 1, color_ptr += 1){
|
||
|
*color_ptr = color;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
paint_text_color_blend(Application_Links *app, Text_Layout_ID layout_id, Range_i64 range, ARGB_Color color, f32 blend){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Rect_f32 result = {};
|
||
|
Text_Layout *layout = text_layout_get(&models->text_layouts, layout_id);
|
||
|
if (layout != 0){
|
||
|
range.min = clamp_bot(layout->visible_range.min, range.min);
|
||
|
range.max = clamp_top(range.max, layout->visible_range.max);
|
||
|
range.min -= layout->visible_range.min;
|
||
|
range.max -= layout->visible_range.min;
|
||
|
Vec4_f32 color_v4f32 = unpack_color(color);
|
||
|
Vec4_f32 color_pm_v4f32 = color_v4f32*blend;
|
||
|
f32 neg_blend = 1.f - blend;
|
||
|
ARGB_Color *color_ptr = layout->item_colors + range.min;
|
||
|
for (i64 i = range.min; i < range.max; i += 1, color_ptr += 1){
|
||
|
Vec4_f32 color_ptr_v4f32 = unpack_color(*color_ptr);
|
||
|
Vec4_f32 blended_v4f32 = color_ptr_v4f32*neg_blend + color_pm_v4f32;
|
||
|
*color_ptr = pack_color(blended_v4f32);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function b32
|
||
|
text_layout_free(Application_Links *app, Text_Layout_ID text_layout_id){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(text_layout_erase(app->tctx, models, &models->text_layouts, text_layout_id));
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
draw_text_layout(Application_Links *app, Text_Layout_ID layout_id, ARGB_Color special_color, ARGB_Color ghost_color){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Text_Layout *layout = text_layout_get(&models->text_layouts, layout_id);
|
||
|
if (layout != 0 && models->target != 0){
|
||
|
text_layout_render(app->tctx, models, layout, special_color, ghost_color);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
open_color_picker(Application_Links *app, Color_Picker *picker)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
if (picker->finished != 0){
|
||
|
*picker->finished = false;
|
||
|
}
|
||
|
system_open_color_picker(picker);
|
||
|
}
|
||
|
|
||
|
api(custom) function void
|
||
|
animate_in_n_milliseconds(Application_Links *app, u32 n)
|
||
|
{
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
if (n == 0){
|
||
|
models->animate_next_frame = true;
|
||
|
}
|
||
|
else{
|
||
|
models->next_animate_delay = Min(models->next_animate_delay, n);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api(custom) function String_Match_List
|
||
|
buffer_find_all_matches(Application_Links *app, Arena *arena, Buffer_ID buffer,
|
||
|
i32 string_id, Range_i64 range, String_Const_u8 needle,
|
||
|
Character_Predicate *predicate, Scan_Direction direction){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
Editing_File *file = imp_get_file(models, buffer);
|
||
|
String_Match_List list = {};
|
||
|
if (api_check_buffer(file)){
|
||
|
if (needle.size > 0){
|
||
|
Scratch_Block scratch(app, arena);
|
||
|
List_String_Const_u8 chunks = buffer_get_chunks(scratch, &file->state.buffer);
|
||
|
buffer_chunks_clamp(&chunks, range);
|
||
|
if (chunks.node_count > 0){
|
||
|
u64_Array jump_table = string_compute_needle_jump_table(arena, needle, direction);
|
||
|
Character_Predicate dummy = {};
|
||
|
if (predicate == 0){
|
||
|
predicate = &dummy;
|
||
|
}
|
||
|
list = find_all_matches(arena, max_i32,
|
||
|
chunks, needle, jump_table, predicate,
|
||
|
direction, range.min, buffer, string_id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(list);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
api(custom) function Profile_Global_List*
|
||
|
get_core_profile_list(Application_Links *app){
|
||
|
Models *models = (Models*)app->cmd_context;
|
||
|
return(&models->profile_list);
|
||
|
}
|
||
|
|
||
|
api(custom) function Doc_Cluster*
|
||
|
get_custom_layer_boundary_docs(Application_Links *app, Arena *arena){
|
||
|
API_Definition *api_def = custom_api_construct(arena);
|
||
|
return(doc_custom_api(arena, api_def));
|
||
|
}
|
||
|
|
||
|
// BOTTOM
|