wip: add clipboard support
This commit is contained in:
@@ -194,6 +194,27 @@ static void wp_sync_paragraph_count(WordProcessorState* wp) {
|
||||
}
|
||||
}
|
||||
|
||||
static int wp_count_newline_chars(const char* text, int len) {
|
||||
if (len < 0) return -1;
|
||||
if (len > 0 && text == nullptr) return -1;
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (text[i] == '\0') return -1;
|
||||
if (text[i] == '\n') count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int wp_count_newlines_in_range(WordProcessorState* wp, int start, int end) {
|
||||
int count = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
if (wp_char_at(wp, i) == '\n')
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void wp_on_insert_newline(WordProcessorState* wp, int abs_pos) {
|
||||
if (wp->paragraph_count >= WP_MAX_PARAGRAPHS) return;
|
||||
int para = wp_find_paragraph_at(wp, abs_pos);
|
||||
@@ -1115,6 +1136,46 @@ void wp_delete_selection(WordProcessorState* wp) {
|
||||
wp_clear_selection(wp);
|
||||
}
|
||||
|
||||
char* wp_selection_plain_text_alloc(WordProcessorState* wp, int* out_len) {
|
||||
if (out_len) *out_len = 0;
|
||||
if (wp == nullptr || !wp->has_selection) return nullptr;
|
||||
|
||||
int sel_s, sel_e;
|
||||
wp_sel_range(wp, &sel_s, &sel_e);
|
||||
int len = sel_e - sel_s;
|
||||
if (len <= 0) return nullptr;
|
||||
|
||||
char* text = (char*)montauk::malloc(len + 1);
|
||||
if (text == nullptr) return nullptr;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
text[i] = wp_char_at(wp, sel_s + i);
|
||||
text[len] = '\0';
|
||||
|
||||
if (out_len) *out_len = len;
|
||||
return text;
|
||||
}
|
||||
|
||||
bool wp_insert_text(WordProcessorState* wp, const char* text, int len) {
|
||||
int newline_count = wp_count_newline_chars(text, len);
|
||||
if (wp == nullptr || newline_count < 0) return false;
|
||||
if (len == 0) return true;
|
||||
if (wp->cursor_run < 0 || wp->cursor_run >= wp->run_count) return false;
|
||||
if (wp->total_text_len + len > WP_MAX_TEXT - 1) return false;
|
||||
if (wp->paragraph_count + newline_count > WP_MAX_PARAGRAPHS) return false;
|
||||
|
||||
StyledRun* cur = &wp->runs[wp->cursor_run];
|
||||
int extra_runs = 0;
|
||||
if (!wp_same_style(cur, wp->cur_font_id, wp->cur_size, wp->cur_flags)) {
|
||||
extra_runs = (wp->cursor_offset == 0 || wp->cursor_offset == cur->len) ? 1 : 2;
|
||||
}
|
||||
if (wp->run_count + extra_runs > WP_MAX_RUNS) return false;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
wp_insert_char(wp, text[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
void wp_recompute_wrap(WordProcessorState* wp, int content_w, int layout_dpi) {
|
||||
int wrap_width = content_w - WP_MARGIN * 2 - WP_SCROLLBAR_W;
|
||||
if (wrap_width < 50) wrap_width = 50;
|
||||
@@ -1664,6 +1725,38 @@ static bool wp_restore_snapshot(WordProcessorState* wp, const UndoSnapshot* snap
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wp_replace_selection_with_text(WordProcessorState* wp, const char* text, int len) {
|
||||
int newline_count = wp_count_newline_chars(text, len);
|
||||
if (wp == nullptr || newline_count < 0) return false;
|
||||
|
||||
int sel_len = 0;
|
||||
int sel_newlines = 0;
|
||||
if (wp->has_selection) {
|
||||
int sel_s, sel_e;
|
||||
wp_sel_range(wp, &sel_s, &sel_e);
|
||||
sel_len = sel_e - sel_s;
|
||||
sel_newlines = wp_count_newlines_in_range(wp, sel_s, sel_e);
|
||||
}
|
||||
|
||||
if (wp->total_text_len - sel_len + len > WP_MAX_TEXT - 1)
|
||||
return false;
|
||||
if (wp->paragraph_count - sel_newlines + newline_count > WP_MAX_PARAGRAPHS)
|
||||
return false;
|
||||
|
||||
UndoSnapshot snap = {};
|
||||
if (!wp_capture_snapshot(wp, &snap))
|
||||
return false;
|
||||
|
||||
if (wp->has_selection)
|
||||
wp_delete_selection(wp);
|
||||
|
||||
bool ok = wp_insert_text(wp, text, len);
|
||||
if (!ok)
|
||||
wp_restore_snapshot(wp, &snap);
|
||||
wp_free_snapshot(&snap);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void wp_save_file(WordProcessorState* wp) {
|
||||
if (wp->filepath[0] == '\0') {
|
||||
wp_open_save_pathbar(wp);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "wordprocessor.hpp"
|
||||
#include <gui/dialogs.hpp>
|
||||
#include <gui/clipboard.hpp>
|
||||
|
||||
static void wp_set_status(WordProcessorState* wp, const char* msg) {
|
||||
if (!msg) msg = "";
|
||||
@@ -13,6 +14,57 @@ static void wp_set_status(WordProcessorState* wp, const char* msg) {
|
||||
wp->status_msg[sizeof(wp->status_msg) - 1] = '\0';
|
||||
}
|
||||
|
||||
static bool wp_copy_selection_to_clipboard(WordProcessorState* wp) {
|
||||
int len = 0;
|
||||
char* text = wp_selection_plain_text_alloc(wp, &len);
|
||||
if (text == nullptr || len <= 0) {
|
||||
if (text) montauk::mfree(text);
|
||||
wp_set_status(wp, "Nothing selected");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = gui::clipboard_set_text(text, len);
|
||||
montauk::mfree(text);
|
||||
if (!ok) {
|
||||
wp_set_status(wp, "Copy failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
wp_set_status(wp, "Copied selection");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool wp_cut_selection_to_clipboard(WordProcessorState* wp) {
|
||||
if (!wp_copy_selection_to_clipboard(wp))
|
||||
return false;
|
||||
|
||||
wp_delete_selection(wp);
|
||||
wp_history_checkpoint(wp);
|
||||
wp_set_status(wp, "Cut selection");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool wp_paste_from_clipboard(WordProcessorState* wp) {
|
||||
int len = 0;
|
||||
char* text = gui::clipboard_get_text_alloc(&len);
|
||||
if (text == nullptr || len <= 0) {
|
||||
if (text) montauk::mfree(text);
|
||||
wp_set_status(wp, "Clipboard is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = wp_replace_selection_with_text(wp, text, len);
|
||||
montauk::mfree(text);
|
||||
if (!ok) {
|
||||
wp_set_status(wp, "Clipboard text could not be inserted");
|
||||
return false;
|
||||
}
|
||||
|
||||
wp_history_checkpoint(wp);
|
||||
wp_set_status(wp, "Pasted clipboard");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void wp_open_pathbar_for_open(WordProcessorState* wp) {
|
||||
char path[256] = {};
|
||||
char msg[160] = {};
|
||||
@@ -584,6 +636,19 @@ void wp_handle_key(const Montauk::KeyEvent& key) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.ctrl && !key.alt && (key.ascii == 'c' || key.ascii == 'C')) {
|
||||
wp_copy_selection_to_clipboard(wp);
|
||||
return;
|
||||
}
|
||||
if (key.ctrl && !key.alt && (key.ascii == 'x' || key.ascii == 'X')) {
|
||||
wp_cut_selection_to_clipboard(wp);
|
||||
return;
|
||||
}
|
||||
if (key.ctrl && !key.alt && (key.ascii == 'v' || key.ascii == 'V')) {
|
||||
wp_paste_from_clipboard(wp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.scancode == 0x48) {
|
||||
if (key.shift) wp_start_selection(wp);
|
||||
else wp_clear_selection(wp);
|
||||
|
||||
@@ -424,6 +424,9 @@ void wp_set_line_spacing(WordProcessorState* wp, int value);
|
||||
void wp_toggle_list(WordProcessorState* wp, uint8_t list_type);
|
||||
void wp_insert_divider(WordProcessorState* wp, uint8_t divider_type);
|
||||
void wp_delete_selection(WordProcessorState* wp);
|
||||
char* wp_selection_plain_text_alloc(WordProcessorState* wp, int* out_len);
|
||||
bool wp_insert_text(WordProcessorState* wp, const char* text, int len);
|
||||
bool wp_replace_selection_with_text(WordProcessorState* wp, const char* text, int len);
|
||||
|
||||
void wp_recompute_wrap(WordProcessorState* wp, int content_w, int layout_dpi);
|
||||
int wp_find_wrap_line(WordProcessorState* wp, int abs_pos);
|
||||
|
||||
Reference in New Issue
Block a user