diff --git a/src/include/clipboard.h b/src/include/clipboard.h index e720a9f..8bd3b4a 100644 --- a/src/include/clipboard.h +++ b/src/include/clipboard.h @@ -89,12 +89,14 @@ G_GNUC_INTERNAL const char * v3270_update_selected_text(GtkWidget *widget, gboolean cut); G_GNUC_INTERNAL GList * v3270_getColumns_from_selection(v3270 * terminal); - - /// @brief Get formatted contents as single text. + /// @brief Get contents. G_GNUC_INTERNAL gchar * v3270_get_copy_as_text(v3270 * terminal); G_GNUC_INTERNAL gchar * v3270_get_copy_as_html(v3270 * terminal); G_GNUC_INTERNAL gchar * v3270_get_copy_as_table(v3270 * terminal, const gchar *delimiter); G_GNUC_INTERNAL gchar * v3270_get_copy_as_data_block(v3270 * terminal); + /// @brief Set contents. + G_GNUC_INTERNAL gboolean v3270_set_from_data_block(v3270 * terminal, const struct SelectionHeader *selection); + #endif // V3270_CLIPBOARD_H_INCLUDED diff --git a/src/include/terminal.h b/src/include/terminal.h index 1fb47bc..d0f7fc2 100644 --- a/src/include/terminal.h +++ b/src/include/terminal.h @@ -64,6 +64,9 @@ G_BEGIN_DECLS // Cursors GdkCursor * cursors[LIB3270_POINTER_COUNT]; + // Atoms + GdkAtom clipboard_formatted; ///< @brief Format for internal clipboard cut & paste. + // Signals void (*activate)(GtkWidget *widget); void (*toggle_changed)(v3270 *widget,LIB3270_TOGGLE toggle_id,gboolean toggle_state,const gchar *toggle_name); diff --git a/src/selection/copy.c b/src/selection/copy.c index f31304a..d94a1a7 100644 --- a/src/selection/copy.c +++ b/src/selection/copy.c @@ -53,14 +53,6 @@ terminal->selection.format = format; do_copy(terminal,cut); -#ifdef DEBUG - { - // DEBUG DATA BLOCK - g_free(v3270_get_copy_as_data_block(terminal)); - } - -#endif // DEBUG - v3270_update_system_clipboard(widget); } diff --git a/src/selection/datablock.c b/src/selection/datablock.c index 5f02750..ba28bb3 100644 --- a/src/selection/datablock.c +++ b/src/selection/datablock.c @@ -71,7 +71,7 @@ static GList * getUnprotected(H3270 *hSession, const lib3270_selection *selectio struct SelectionFieldHeader * field = (struct SelectionFieldHeader *) g_malloc0(sizeof(struct SelectionFieldHeader) + length); - field->baddr = lib3270_translate_to_address(hSession,row + selection->bounds.row,start + selection->bounds.col); + field->baddr = lib3270_translate_to_address(hSession,row + selection->bounds.row + 1,start + selection->bounds.col + 1); field->length = length; // Copy string @@ -125,6 +125,7 @@ gchar * v3270_get_copy_as_data_block(v3270 * terminal) } // Setup block header + debug("Creating block at offset %u", header->length); struct SelectionBlockHeader * blockheader = (struct SelectionBlockHeader *) (((unsigned char *) header) + header->length); header->length += sizeof(* blockheader); @@ -149,6 +150,7 @@ gchar * v3270_get_copy_as_data_block(v3270 * terminal) memcpy((ptr+header->length), value->data, length); header->length += length; + blockheader->records++; } @@ -158,3 +160,87 @@ gchar * v3270_get_copy_as_data_block(v3270 * terminal) return (gchar *) g_realloc((gpointer) header, header->length+1); } + +gboolean v3270_set_from_data_block(v3270 * terminal, const struct SelectionHeader *selection) +{ + const unsigned char * raw_data = (const unsigned char *) selection; + unsigned int raw_pos = sizeof(struct SelectionHeader); + unsigned int column; + unsigned int record; + + while(raw_pos < selection->length) + { + const struct SelectionBlockHeader * block = (const struct SelectionBlockHeader *) (raw_data + raw_pos); + debug("Processing block at offset %u with %u elements", raw_pos, block->records); + + raw_pos += sizeof(struct SelectionBlockHeader); + gboolean found = TRUE; + + for(record = 0; record < block->records; record++) + { + const struct SelectionFieldHeader * field = (struct SelectionFieldHeader *) (raw_data + raw_pos); + + debug("Analizing field at %u: addr=%u length=%u", + raw_pos, + field->baddr, + field->length + ); + + raw_pos += (sizeof(struct SelectionFieldHeader) + field->length); + for(column = 0; column < field->length; column++) + { + if(lib3270_is_protected(terminal->host,field->baddr+column)) + { + debug("Column %d is protected",column); + found = FALSE; + break; + } + } + } + + if(found && block->records) + { + // The current datablock is valid, paste it! + raw_data = (const unsigned char *) (block+1); + raw_pos = 0; + + debug("Found valid screen with %u elements", block->records); + + for(record = 0; record < block->records; record++) + { + const struct SelectionFieldHeader * field = (struct SelectionFieldHeader *) (raw_data + raw_pos); + + debug("Processing field at %u: addr=%u length=%u", + raw_pos, + field->baddr, + field->length + ); + + raw_pos += (sizeof(struct SelectionFieldHeader) + field->length); + + debug( + "Pasting record %u baddr=%u length=%u", + record, + field->baddr, + (unsigned int) field->length + ); + + if(lib3270_set_string_at_address(terminal->host, field->baddr,(const unsigned char *) (field+1), field->length) < 0) + { + debug("Can't set string baddr=%u length=%u errno=%d %s", + field->baddr, + field->length, + errno, + strerror(errno) + ); + return FALSE; + } + } + + return TRUE; + } + + } + + return FALSE; +} diff --git a/src/selection/linux/copy.c b/src/selection/linux/copy.c index 35799e6..045ee10 100644 --- a/src/selection/linux/copy.c +++ b/src/selection/linux/copy.c @@ -95,7 +95,7 @@ static void clipboard_get(G_GNUC_UNUSED GtkClipboard *clipboard, GtkSelectionDa g_autofree gchar *data = v3270_get_copy_as_data_block(terminal); gtk_selection_data_set( selection, - gdk_atom_intern_static_string("application/x-v3270-unprotected"), + GTK_V3270_GET_CLASS(obj)->clipboard_formatted, 8, (guchar *) data, ((struct SelectionHeader *) data)->length diff --git a/src/selection/linux/paste.c b/src/selection/linux/paste.c index b007552..324907e 100644 --- a/src/selection/linux/paste.c +++ b/src/selection/linux/paste.c @@ -36,30 +36,139 @@ static void text_received(G_GNUC_UNUSED GtkClipboard *clipboard, const gchar *t v3270_input_text(widget,text,"UTF-8"); } -static gboolean has_target(const gchar *name, const GdkAtom *atoms, const gint n_atoms) +static gboolean has_target(const GdkAtom atom, const GdkAtom *atoms, const gint n_atoms) { gint ix; for(ix = 0; ix < n_atoms; ix++) { - if(!g_ascii_strcasecmp(name,gdk_atom_name(atoms[ix]))) + if(atom == atoms[ix]) return TRUE; - } return FALSE; } +static void formatted_received(GtkClipboard *clipboard, GtkSelectionData *selection_data, GtkWidget *widget) +{ + const struct SelectionHeader *selection = (const struct SelectionHeader *) gtk_selection_data_get_data(selection_data); + + v3270 * terminal = GTK_V3270(widget); + + debug( + "Received formatted data with %u bytes: Build=%u rows=%u cols=%u", + selection->length, + selection->build, + selection->rows, + selection->cols + ); + + if(selection->cols != lib3270_get_width(terminal->host) || selection->rows != lib3270_get_height(terminal->host)) + { + GtkWidget * dialog = + gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_toplevel(widget)), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_NONE, + _("Not the same terminal type") + ); + + + gtk_window_set_title(GTK_WINDOW(dialog),_("Can't paste")); + + gtk_dialog_add_buttons( + GTK_DIALOG (dialog), + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Paste as text"), GTK_RESPONSE_APPLY, + NULL + ); + + gtk_dialog_set_default_response(GTK_DIALOG (dialog),GTK_RESPONSE_APPLY); + + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + + gtk_widget_destroy(dialog); + + if(response == GTK_RESPONSE_APPLY) + { + gtk_clipboard_request_text( + clipboard, + (GtkClipboardTextReceivedFunc) text_received, + (gpointer) widget + ); + } + + return; + + } + + if(!v3270_set_from_data_block(terminal, selection)) + { + GtkWidget * dialog = + gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_toplevel(widget)), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_NONE, + _("Unable to paste formatted data") + ); + + + gtk_window_set_title(GTK_WINDOW(dialog),_("Can't paste")); + + gtk_dialog_add_buttons( + GTK_DIALOG (dialog), + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Paste as text"), GTK_RESPONSE_APPLY, + NULL + ); + + gtk_dialog_set_default_response(GTK_DIALOG (dialog),GTK_RESPONSE_APPLY); + + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + + gtk_widget_destroy(dialog); + + if(response == GTK_RESPONSE_APPLY) + { + gtk_clipboard_request_text( + clipboard, + (GtkClipboardTextReceivedFunc) text_received, + (gpointer) widget + ); + } + + return; + + + } + + +} + static void targets_received(GtkClipboard *clipboard, GdkAtom *atoms, gint n_atoms, GtkWidget *widget) { - if(has_target("application/x-" PACKAGE_NAME, atoms, n_atoms)) + if(has_target(GTK_V3270_GET_CLASS(widget)->clipboard_formatted,atoms,n_atoms)) { - debug("Clipboard as TN3270 \"%s\" data",PACKAGE_NAME); + debug("Clipboard as TN3270 \"%s\" data",gdk_atom_name(GTK_V3270_GET_CLASS(widget)->clipboard_formatted)); + + gtk_clipboard_request_contents( + clipboard, + GTK_V3270_GET_CLASS(widget)->clipboard_formatted, + (GtkClipboardReceivedFunc) formatted_received, + (gpointer) widget + ); + return; } // No special format available, request it as text. - gtk_clipboard_request_text(clipboard, (GtkClipboardTextReceivedFunc) text_received, (gpointer) widget); + gtk_clipboard_request_text( + clipboard, + (GtkClipboardTextReceivedFunc) text_received, + (gpointer) widget + ); } diff --git a/src/terminal/widget.c b/src/terminal/widget.c index 9a32bfd..a8f3b64 100644 --- a/src/terminal/widget.c +++ b/src/terminal/widget.c @@ -258,6 +258,9 @@ static void v3270_class_init(v3270Class *klass) // Object methods gobject_class->finalize = finalize; + // Atoms + klass->clipboard_formatted = gdk_atom_intern_static_string("application/x-v3270-unprotected"); + // Widget methods widget_class->realize = v3270_realize; widget_class->size_allocate = v3270_size_allocate; -- libgit2 0.21.2