summaryrefslogtreecommitdiff
path: root/frontends/riscos/textselection.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/riscos/textselection.c')
-rw-r--r--frontends/riscos/textselection.c657
1 files changed, 657 insertions, 0 deletions
diff --git a/frontends/riscos/textselection.c b/frontends/riscos/textselection.c
new file mode 100644
index 000000000..718171db0
--- /dev/null
+++ b/frontends/riscos/textselection.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Text selection code (platform-dependent implementation)
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <oslib/osfile.h>
+#include <oslib/wimp.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "content/hlcache.h"
+#include "desktop/gui_clipboard.h"
+#include "desktop/gui_window.h"
+#include "desktop/textinput.h"
+#include "desktop/browser.h"
+
+#include "riscos/gui.h"
+#include "riscos/menus.h"
+#include "riscos/message.h"
+#include "riscos/mouse.h"
+#include "riscos/save.h"
+#include "riscos/textselection.h"
+#include "riscos/ucstables.h"
+
+
+#ifndef wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX
+#define wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX ((wimp_drag_claim_flags) 0x2u)
+#endif
+
+
+/** Receive of Dragging message has claimed it */
+static bool dragging_claimed = false;
+static wimp_t dragging_claimant;
+static os_box dragging_box = { -34, -34, 34, 34 }; /* \todo - size properly */
+static wimp_drag_claim_flags last_claim_flags = 0;
+static struct gui_window *last_start_window;
+
+static bool drag_claimed = false;
+
+static bool owns_clipboard = false;
+static bool owns_caret_and_selection = false;
+
+/* Current clipboard contents if we own the clipboard
+ * Current paste buffer if we don't
+ */
+static char *clipboard = NULL;
+static size_t clip_length = 0;
+
+/* Paste context */
+static ro_gui_selection_prepare_paste_cb paste_cb = NULL;
+static void *paste_cb_pw = NULL;
+static int paste_prev_message = 0;
+
+static void ro_gui_selection_drag_end(wimp_dragged *drag, void *g);
+static void ro_gui_discard_clipboard_contents(void);
+static void ro_gui_dragging_bounced(wimp_message *message);
+
+
+/**
+ * Start drag-selecting text within a browser window (RO-dependent part)
+ *
+ * \param g gui window
+ */
+
+void gui_start_selection(struct gui_window *g)
+{
+ wimp_full_message_claim_entity msg;
+ wimp_auto_scroll_info scroll;
+ wimp_window_state state;
+ wimp_drag drag;
+ os_error *error;
+
+ LOG("starting text_selection drag");
+
+ state.w = g->window;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* claim caret and selection */
+ msg.size = sizeof(msg);
+ msg.your_ref = 0;
+ msg.action = message_CLAIM_ENTITY;
+ msg.flags = wimp_CLAIM_CARET_OR_SELECTION;
+
+ error = xwimp_send_message(wimp_USER_MESSAGE,
+ (wimp_message*)&msg, wimp_BROADCAST);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ owns_caret_and_selection = true;
+
+ scroll.w = g->window;
+ scroll.pause_zone_sizes.x0 = 80;
+ scroll.pause_zone_sizes.y0 = 80;
+ scroll.pause_zone_sizes.x1 = 80;
+ scroll.pause_zone_sizes.y1 = 80;
+ scroll.pause_duration = 0;
+ scroll.state_change = (void *)0;
+ error = xwimp_auto_scroll(wimp_AUTO_SCROLL_ENABLE_VERTICAL |
+ wimp_AUTO_SCROLL_ENABLE_HORIZONTAL,
+ &scroll, 0);
+ if (error)
+ LOG("xwimp_auto_scroll: 0x%x: %s", error->errnum, error->errmess);
+
+ ro_mouse_drag_start(ro_gui_selection_drag_end, ro_gui_window_mouse_at,
+ NULL, g);
+
+ drag.type = wimp_DRAG_USER_POINT;
+ /* Don't constrain mouse pointer during drags */
+ drag.bbox.x0 = -16384;
+ drag.bbox.y0 = -16384;
+ drag.bbox.x1 = 16384;
+ drag.bbox.y1 = 16384;
+
+ error = xwimp_drag_box(&drag);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ last_start_window = g;
+}
+
+
+/**
+ * End of text selection drag operation
+ *
+ * \param *drag position of pointer at conclusion of drag
+ * \param *data gui window pointer.
+ */
+
+static void ro_gui_selection_drag_end(wimp_dragged *drag, void *data)
+{
+ wimp_auto_scroll_info scroll;
+ wimp_pointer pointer;
+ os_error *error;
+ os_coord pos;
+ struct gui_window *g = (struct gui_window *) data;
+
+ scroll.w = g->window;
+ error = xwimp_auto_scroll(0, &scroll, 0);
+ if (error)
+ LOG("xwimp_auto_scroll: 0x%x: %s", error->errnum, error->errmess);
+
+ error = xwimp_drag_box((wimp_drag*)-1);
+ if (error) {
+ LOG("xwimp_drag_box: 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+
+ error = xwimp_get_pointer_info(&pointer);
+ if (error) {
+ LOG("xwimp_get_pointer_info 0x%x : %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (ro_gui_window_to_window_pos(g, drag->final.x0, drag->final.y0, &pos))
+ browser_window_mouse_track(g->bw, 0, pos.x, pos.y);
+}
+
+/**
+ * Core tells front end to put given text in clipboard
+ *
+ * \param buffer UTF-8 text, owned by core
+ * \param length Byte length of UTF-8 text in buffer
+ * \param styles Array of styles given to text runs, owned by core, or NULL
+ * \param n_styles Number of text run styles in array
+ */
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ char *new_cb;
+
+ if (length == 0)
+ return;
+
+ new_cb = malloc(length);
+ if (new_cb == NULL)
+ return;
+
+ memcpy(new_cb, buffer, length);
+
+ /* Replace existing clipboard contents */
+ free(clipboard);
+ clipboard = new_cb;
+ clip_length = length;
+
+ if (!owns_clipboard) {
+ /* Tell RO we now own clipboard */
+ wimp_full_message_claim_entity msg;
+ os_error *error;
+
+ LOG("claiming clipboard");
+
+ msg.size = sizeof(msg);
+ msg.your_ref = 0;
+ msg.action = message_CLAIM_ENTITY;
+ msg.flags = wimp_CLAIM_CLIPBOARD;
+
+ error = xwimp_send_message(wimp_USER_MESSAGE,
+ (wimp_message*)&msg, wimp_BROADCAST);
+ if (error) {
+ LOG("xwimp_send_message: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ owns_clipboard = true;
+ }
+
+ LOG("clipboard now holds %zd bytes", clip_length);
+}
+
+
+/**
+ * Core asks front end for clipboard contents.
+ *
+ * \param buffer UTF-8 text, allocated by front end, ownership yielded to core
+ * \param length Byte length of UTF-8 text in buffer
+ */
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ *buffer = NULL;
+ *length = 0;
+
+ if (clip_length > 0) {
+ char *cb = malloc(clip_length);
+ if (cb != NULL) {
+ memcpy(cb, clipboard, clip_length);
+ *buffer = cb;
+ *length = clip_length;
+ }
+ }
+}
+
+
+/**
+ * Discard the current contents of the clipboard, if any, releasing the
+ * memory it uses.
+ */
+
+void ro_gui_discard_clipboard_contents(void)
+{
+ free(clipboard);
+ clipboard = NULL;
+ clip_length = 0;
+}
+
+
+static void ro_gui_selection_prepare_paste_complete(void)
+{
+ ro_gui_selection_prepare_paste_cb cb = paste_cb;
+ void *pw = paste_cb_pw;
+
+ paste_cb = NULL;
+ paste_cb_pw = NULL;
+ paste_prev_message = 0;
+
+ cb(pw);
+}
+
+static void ro_gui_selection_prepare_paste_bounced(wimp_message *message)
+{
+ ro_gui_selection_prepare_paste_complete();
+}
+
+/**
+ * Prepare to paste data from another application
+ *
+ * \param w Window being pasted into
+ * \param cb Callback to call once preparation is complete
+ * \param pw Private data for callback
+ */
+
+void ro_gui_selection_prepare_paste(wimp_w w,
+ ro_gui_selection_prepare_paste_cb cb, void *pw)
+{
+ if (owns_clipboard) {
+ /* We own the clipboard: we're already prepared */
+ cb(pw);
+ } else {
+ /* Someone else owns the clipboard: request its contents */
+ wimp_full_message_data_request msg;
+ bool success;
+
+ ro_gui_discard_clipboard_contents();
+
+ msg.size = 48; /* There's only one filetype listed. */
+ msg.your_ref = 0;
+ msg.action = message_DATA_REQUEST;
+ msg.w = w;
+ msg.i = -1;
+ msg.pos.x = 0;
+ msg.pos.y = 0;
+ msg.flags = wimp_DATA_REQUEST_CLIPBOARD;
+ msg.file_types[0] = osfile_TYPE_TEXT;
+ msg.file_types[1] = ~0;
+
+ success = ro_message_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message *) &msg, wimp_BROADCAST,
+ ro_gui_selection_prepare_paste_bounced);
+ if (success == false) {
+ /* Ensure key is handled, anyway */
+ cb(pw);
+ } else {
+ /* Set up paste context */
+ paste_cb = cb;
+ paste_cb_pw = pw;
+ paste_prev_message = msg.my_ref;
+ }
+ }
+}
+
+/**
+ * Prepare to paste data from another application (step 2)
+ *
+ * \param dataxfer DataSave message
+ * \return True if message was handled, false otherwise
+ */
+bool ro_gui_selection_prepare_paste_datasave(
+ wimp_full_message_data_xfer *dataxfer)
+{
+ bool success;
+
+ /* Ignore messages that aren't for us */
+ if (dataxfer->your_ref == 0 || dataxfer->your_ref != paste_prev_message)
+ return false;
+
+ /* We're done if the paste data isn't text */
+ if (dataxfer->file_type != osfile_TYPE_TEXT) {
+ ro_gui_selection_prepare_paste_complete();
+ return true;
+ }
+
+ /* Generate and send DataSaveAck */
+ dataxfer->your_ref = dataxfer->my_ref;
+ dataxfer->size = offsetof(wimp_full_message_data_xfer, file_name) + 16;
+ dataxfer->action = message_DATA_SAVE_ACK;
+ dataxfer->est_size = -1;
+ memcpy(dataxfer->file_name, "<Wimp$Scrap>", SLEN("<Wimp$Scrap>") + 1);
+
+ success = ro_message_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message *) dataxfer, dataxfer->sender,
+ ro_gui_selection_prepare_paste_bounced);
+ if (success == false) {
+ ro_gui_selection_prepare_paste_complete();
+ } else {
+ paste_prev_message = dataxfer->my_ref;
+ }
+
+ return true;
+}
+
+
+/**
+ * Prepare to paste data from another application (step 3)
+ *
+ * \param dataxfer DataLoad message
+ * \return True if message was handled, false otherwise
+ */
+bool ro_gui_selection_prepare_paste_dataload(
+ wimp_full_message_data_xfer *dataxfer)
+{
+ FILE *fp;
+
+ /* Ignore messages that aren't for us */
+ if (dataxfer->your_ref == 0 || dataxfer->your_ref != paste_prev_message)
+ return false;
+
+ fp = fopen(dataxfer->file_name, "r");
+ if (fp != NULL) {
+ long size;
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ if (size > 0) {
+ char *local_cb = malloc(size);
+ if (local_cb != NULL) {
+ nserror ret;
+ fread(local_cb, 1, size, fp);
+
+ ret = utf8_from_local_encoding(local_cb, size,
+ &clipboard);
+ if (ret == NSERROR_OK) {
+ clip_length = strlen(clipboard);
+ }
+
+ free(local_cb);
+ }
+ }
+
+ fclose(fp);
+ }
+
+ /* Send DataLoadAck */
+ dataxfer->action = message_DATA_LOAD_ACK;
+ dataxfer->your_ref = dataxfer->my_ref;
+ ro_message_send_message(wimp_USER_MESSAGE,
+ (wimp_message *) dataxfer, dataxfer->sender, NULL);
+
+ ro_gui_selection_prepare_paste_complete();
+ return true;
+}
+
+
+/**
+ * Responds to CLAIM_ENTITY message notifying us that the caret
+ * and selection or clipboard have been claimed by another application.
+ *
+ * \param claim CLAIM_ENTITY message
+ */
+
+void ro_gui_selection_claim_entity(wimp_full_message_claim_entity *claim)
+{
+ /* ignore our own broadcasts! */
+ if (claim->sender != task_handle) {
+
+ LOG("%x", claim->flags);
+
+ if (claim->flags & wimp_CLAIM_CARET_OR_SELECTION) {
+ owns_caret_and_selection = false;
+ }
+
+ if (claim->flags & wimp_CLAIM_CLIPBOARD) {
+ ro_gui_discard_clipboard_contents();
+ owns_clipboard = false;
+ }
+ }
+}
+
+
+/**
+ * Responds to DATA_REQUEST message, returning information about the
+ * clipboard contents if we own the clipboard.
+ *
+ * \param req DATA_REQUEST message
+ */
+
+void ro_gui_selection_data_request(wimp_full_message_data_request *req)
+{
+ if (owns_clipboard && clip_length > 0 &&
+ (req->flags & wimp_DATA_REQUEST_CLIPBOARD)) {
+ wimp_full_message_data_xfer message;
+ int size;
+// int i;
+
+// for(i = 0; i < NOF_ELEMENTS(req->file_types); i++) {
+// bits ftype = req->file_types[i];
+// if (ftype == ~0U) break; /* list terminator */
+//
+// LOG("type %x", ftype);
+// i++;
+// }
+
+ /* we can only supply text at the moment, so that's what you're getting! */
+ size = offsetof(wimp_full_message_data_xfer, file_name) + 9;
+ message.size = (size + 3) & ~3;
+ message.your_ref = req->my_ref;
+ message.action = message_DATA_SAVE;
+ message.w = req->w;
+ message.i = req->i;
+ message.pos = req->pos;
+ message.file_type = osfile_TYPE_TEXT;
+ message.est_size = clip_length;
+ memcpy(message.file_name, "TextFile", 9);
+
+ ro_gui_send_datasave(GUI_SAVE_CLIPBOARD_CONTENTS,
+ &message, req->sender);
+ }
+}
+
+
+/**
+ * Save the clipboard contents to a file.
+ *
+ * \param path the pathname of the file
+ * \return true iff success, otherwise reporting the error before returning false
+ */
+
+bool ro_gui_save_clipboard(const char *path)
+{
+ char *local_cb;
+ nserror ret;
+ os_error *error;
+
+ assert(clip_length > 0 && clipboard);
+
+ ret = utf8_to_local_encoding(clipboard, clip_length, &local_cb);
+ if (ret != NSERROR_OK) {
+ ro_warn_user("SaveError", "Could not convert");
+ return false;
+ }
+
+ error = xosfile_save_stamped(path, osfile_TYPE_TEXT,
+ (byte*) local_cb,
+ (byte*) local_cb + strlen(local_cb));
+
+ free(local_cb);
+
+ if (error) {
+ LOG("xosfile_save_stamped: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Handler for Message_Dragging, used to implement auto-scrolling and
+ * ghost caret when a drag is in progress.
+ */
+
+void ro_gui_selection_dragging(wimp_message *message)
+{
+ wimp_full_message_dragging *drag = (wimp_full_message_dragging*)message;
+ struct gui_window *g;
+ os_coord pos;
+
+ /* with autoscrolling, we will probably need to remember the
+ * gui_window and override the drag->w window handle which
+ * could be any window on the desktop */
+ g = ro_gui_window_lookup(drag->w);
+
+ if ((drag->flags & wimp_DRAGGING_TERMINATE_DRAG) || !g) {
+
+ drag_claimed = false;
+ return;
+ }
+
+ if (!ro_gui_window_to_window_pos(g, drag->pos.x, drag->pos.y, &pos))
+ return;
+
+ drag_claimed = false;
+}
+
+
+
+/**
+ * Reset drag-and-drop state when drag completes (DataSave received)
+ */
+
+void ro_gui_selection_drag_reset(void)
+{
+ drag_claimed = false;
+}
+
+
+/**
+ *
+ */
+
+void ro_gui_selection_drag_claim(wimp_message *message)
+{
+ wimp_full_message_drag_claim *claim = (wimp_full_message_drag_claim*)message;
+
+ dragging_claimant = message->sender;
+ dragging_claimed = true;
+
+ /* have we been asked to remove the drag box/sprite? */
+ if (claim->flags & wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX) {
+ ro_gui_drag_box_cancel();
+ }
+ else {
+ /* \todo - restore it here? */
+ }
+
+ /* do we need to restore the default pointer shape? */
+ if ((last_claim_flags & wimp_DRAG_CLAIM_POINTER_CHANGED) &&
+ !(claim->flags & wimp_DRAG_CLAIM_POINTER_CHANGED)) {
+ gui_window_set_pointer(last_start_window, GUI_POINTER_DEFAULT);
+ }
+
+ last_claim_flags = claim->flags;
+}
+
+
+void ro_gui_selection_send_dragging(wimp_pointer *pointer)
+{
+ wimp_full_message_dragging dragmsg;
+
+ LOG("sending DRAGGING to %p, %d", pointer->w, pointer->i);
+
+ dragmsg.size = offsetof(wimp_full_message_dragging, file_types) + 8;
+ dragmsg.your_ref = 0;
+ dragmsg.action = message_DRAGGING;
+ dragmsg.w = pointer->w;
+ dragmsg.i = pointer->i;
+ dragmsg.pos = pointer->pos;
+/* \todo - this is interesting because it depends upon not just the state of the
+ shift key, but also whether it /can/ be deleted, ie. from text area/input
+ rather than page contents */
+ dragmsg.flags = wimp_DRAGGING_FROM_SELECTION;
+ dragmsg.box = dragging_box;
+ dragmsg.file_types[0] = osfile_TYPE_TEXT;
+ dragmsg.file_types[1] = ~0;
+
+ /* if the message_dragmsg messages have been claimed we must address them
+ to the claimant task, which is not necessarily the task that owns whatever
+ window happens to be under the pointer */
+
+ if (dragging_claimed) {
+ ro_message_send_message(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message*)&dragmsg, dragging_claimant, ro_gui_dragging_bounced);
+ }
+ else {
+ ro_message_send_message_to_window(wimp_USER_MESSAGE_RECORDED,
+ (wimp_message*)&dragmsg, pointer->w, pointer->i,
+ ro_gui_dragging_bounced, &dragging_claimant);
+ }
+}
+
+
+/**
+ * Our message_DRAGGING message was bounced, ie. the intended recipient does not
+ * support the drag-and-drop protocol or cannot receive the data at the pointer
+ * position.
+ */
+
+void ro_gui_dragging_bounced(wimp_message *message)
+{
+ dragging_claimed = false;
+}
+
+static struct gui_clipboard_table clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+struct gui_clipboard_table *riscos_clipboard_table = &clipboard_table;