deal with all TeXmacs-related stuff when back (march).
Post by Norbert NemecHi there,
following the bugfix two days ago, I have now finally sat down and tried
to clean out the annoyingly non-standard behavior of TeXmacs in terms of
selection handling and cut-and-paste. Resulting are the three attached
patches that handle mostly independent issues. Each patch has an
explanation included in the header.
To begin with: I am used to the Windows/KDE/Gnome standard behavior
which is very different from that of Emacs. I believe the patches in
their current form will work in Emacs mode as well, but I would ask some
of the true Emacs users to try these patches.
The first patch should be pretty much invisible if you don't use
X11-style mouse-select-and-middle-button-paste. If you do use this, you
should find the new behavior identical to that in other common editors.
If you have xclipboard, klipper or any similar clipboard manager
running, please check how it might interfere with the inter-program
cut-and-paste.
The second patch makes selections generally non-persistent (i.e. text is
unselected as soon as cursor is moved). This is the standard set by
Windows and adopted by freedesktop.org (i.e. KDE, Gnome, etc.). If
anybody has a good reason for supporting persistent selections, please
bring it forward.
The third patch does away with the annoying habit of TeXmacs overwriting
the clipboard when you press <delete> on a selection. I know that some
Emacs users prefer to have their <delete> key bound to perform a <cut>
operation. If anybody out there thinks this kind of behavior should be
configurable (or even default in Emacs mode), could you please contact
me, so we could try to figure out at which place I should put a switch.
Please, test these patches thoroughly and give your feedback. This is
something that will affect every user every day, so it should be polished.
Greeting,
Norbert
Make copy-and-paste compliant with freedesktop standards
From: <>
XA_CLIPBOARD: Windows like via Ctrl-C and Ctrl-V (XA_CLIPBOARD)
XA_PRIMARY: mouse-shortcut by marking with mouse and pressing middle button in some other place
Formerly, both methods were mapped to TeXmacs "primary" buffer, causing some strange behavior.
Now, the mouse selection is copied to a new "mouse" buffer and middle-click pastes from
this special buffer. The regular "primary" buffer is unaffected by mouse selection. Of
course the mouse-selection can also be copied to the "primary" buffer as well.
The naming is a bit confusing to avoid changing too much code
* The "primary" buffer is mapped to XA_CLIPBOARD
* The "mouse" buffer is mapped to XA_PRIMARY
The handling of the "primary" is encapsulated by QClipboard. The "mouse"
cut-and-paste should work TeXmacs internally. No idea how to get it to work
properly with other applications.
All other GUIs should work as before with the "primary" selection and should work
following the freedesktop standard within TeXmacs. Whether a inter-application
mechanism exists and can be used for mouse copy-paste depends on the system.
============
* added function 'bool contains(list<T>,T)' to list.hpp
* created symbols XA_CLIPBOARD and XA_TARGETS in x_gui initialized at startup to standard XAtom values.
* changed edit_interface_rep::mouse_select to select into "mouse" instead of "primary" buffer
* changed edit_interface_rep::mouse_paste to
a) go_to position of mouse click
b) paste from "mouse" instead of "primary" buffer
* removed x_gui_rep::selection variable - do conversion from selection_s on the fly in
x_loop instead
* changed x_gui_rep::set_selection to handle "mouse" (pointing to XA_PRIMARY) and "primary" (pointing to XA_CLIPBOARD)
* completely restructured x_gui_rep::get_selection to achieve the same here
* handle SelectionRequestEvent and SelectionClearEvent for either XA_PRIMARY or XA_CLIPBOARD
---
src/src/Edit/Interface/edit_mouse.cpp | 10 +++--
src/src/Edit/Replace/edit_select.cpp | 2 +
src/src/Kernel/Containers/list.cpp | 5 +++
src/src/Kernel/Containers/list.hpp | 1 +
src/src/Plugins/X11/x_drawable.hpp | 2 +
src/src/Plugins/X11/x_gui.cpp | 62 ++++++++++++++++++++-------------
src/src/Plugins/X11/x_gui.hpp | 4 ++
src/src/Plugins/X11/x_init.cpp | 9 +++--
src/src/Plugins/X11/x_loop.cpp | 32 +++++++++++------
src/src/Plugins/X11/x_window.hpp | 2 +
10 files changed, 80 insertions(+), 49 deletions(-)
diff --git a/src/src/Edit/Interface/edit_mouse.cpp b/src/src/Edit/Interface/edit_mouse.cpp
index bf6eebc..3af1018 100644
--- a/src/src/Edit/Interface/edit_mouse.cpp
+++ b/src/src/Edit/Interface/edit_mouse.cpp
@@ -56,7 +56,7 @@ edit_interface_rep::mouse_any (string type, SI x, SI y, int mods, time_t t) {
dragging= true;
}
else if (dragging && (type == "move"))
- type2= "dragging";
+ type2= "dragging";
if (dragging && (type == "release-left"))
type2= "end-drag";
@@ -66,7 +66,7 @@ edit_interface_rep::mouse_any (string type, SI x, SI y, int mods, time_t t) {
right_dragging= true;
}
else if (right_dragging && (type == "move"))
- type2= "right-dragging";
+ type2= "right-dragging";
if (right_dragging && (type == "release-right"))
type2= "end-right-drag";
@@ -180,14 +180,14 @@ edit_interface_rep::mouse_select (SI x, SI y, int mods) {
eval ("(graphics-reset-context 'exit)");
}
if (selection_active_any ())
- selection_set ("primary", selection_get (), true);
+ selection_set ("mouse", selection_get (), true);
}
void
edit_interface_rep::mouse_paste (SI x, SI y) { (void) x; (void) y;
if (eb->action ("paste", x, y, 0) != "") return;
- selection_copy ();
- selection_paste ();
+ go_to (x, y);
+ selection_paste ("mouse");
}
void
diff --git a/src/src/Edit/Replace/edit_select.cpp b/src/src/Edit/Replace/edit_select.cpp
index 7eb32ee..9b0fd6f 100644
--- a/src/src/Edit/Replace/edit_select.cpp
+++ b/src/src/Edit/Replace/edit_select.cpp
@@ -557,7 +557,7 @@ edit_select_rep::selection_set (string key, tree t, bool persistant) {
nicely copying graphics into text, text into graphics, etc.
*/
string s;
- if (key == "primary") {
+ if (key == "primary" || key == "mouse") {
if (selection_export == "verbatim") t= exec_texmacs (t, tp);
if (selection_export == "html") t= exec_html (t, tp);
if (selection_export == "latex") t= exec_latex (t, tp);
diff --git a/src/src/Kernel/Containers/list.cpp b/src/src/Kernel/Containers/list.cpp
index 4efd8c4..faa1bcf 100644
--- a/src/src/Kernel/Containers/list.cpp
+++ b/src/src/Kernel/Containers/list.cpp
@@ -194,4 +194,9 @@ remove (list<T> l, T what) {
else return list<T> (l->item, remove (l->next, what));
}
+template<class T> bool
+contains (list<T> l, T what) {
+ return (!is_nil(l) && (l->item == what || contains(l->next, what)));
+}
+
#endif // defined LIST_CC
diff --git a/src/src/Kernel/Containers/list.hpp b/src/src/Kernel/Containers/list.hpp
index 2a82d01..1df1dff 100644
--- a/src/src/Kernel/Containers/list.hpp
+++ b/src/src/Kernel/Containers/list.hpp
@@ -70,6 +70,7 @@ TMPL T& access_last (list<T>& l);
TMPL list<T>& suppress_last (list<T>& l);
TMPL list<T> reverse (list<T> l);
TMPL list<T> remove (list<T> l, T what);
+TMPL bool contains (list<T> l, T what);
TMPL ostream& operator << (ostream& out, list<T> l);
TMPL list<T>& operator << (list<T>& l, T item);
diff --git a/src/src/Plugins/X11/x_drawable.hpp b/src/src/Plugins/X11/x_drawable.hpp
index a877a6f..088a3b5 100644
--- a/src/src/Plugins/X11/x_drawable.hpp
+++ b/src/src/Plugins/X11/x_drawable.hpp
friend class x_gui_rep;
friend class x_window_rep;
- friend Bool my_predicate (Display* dpy, XEvent* ev, XPointer arg);
+ friend Bool my_selnotify_predicate (Display* dpy, XEvent* ev, XPointer arg);
};
#endif // defined X_DRAWABLE_H
diff --git a/src/src/Plugins/X11/x_gui.cpp b/src/src/Plugins/X11/x_gui.cpp
index e99254f..13ceb7a 100644
--- a/src/src/Plugins/X11/x_gui.cpp
+++ b/src/src/Plugins/X11/x_gui.cpp
@@ -188,7 +188,7 @@ x_gui_rep::has_mouse_grab (widget w) {
******************************************************************************/
Bool
-my_predicate (Display* dpy, XEvent* ev, XPointer arg) { (void) dpy;
+my_selnotify_predicate (Display* dpy, XEvent* ev, XPointer arg) { (void) dpy;
x_window win= (x_window) arg;
return (win->win==ev->xany.window) && (ev->type==SelectionNotify);
}
@@ -212,29 +212,41 @@ bool
x_gui_rep::get_selection (string key, tree& t, string& s) {
t= "none";
s= "";
+ bool res=false;
+
if (selection_t->contains (key)) {
t= copy (selection_t [key]);
s= copy (selection_s [key]);
- return true;
+ res=true;
}
- if (key != "primary") return false;
- if (XGetSelectionOwner (dpy, XA_PRIMARY) == None) return false;
- if (is_nil (windows_l)) return false;
+ Atom xsel;
+ if(key == "primary")
+ xsel = XA_CLIPBOARD;
+ else if (key == "mouse")
+ xsel = XA_PRIMARY;
+ else
+ return res;
+
+ Window selown = XGetSelectionOwner(dpy, xsel);
+ if (selown == None
+ || is_nil (windows_l)
+ || contains(windows_l,selown)) return res;
+
Window win= windows_l->item;
x_window x_win= (x_window) Window_to_window[win];
- Atom data= XInternAtom (dpy, "MY_STRING_SELECTION", false);
- XConvertSelection (dpy, XA_PRIMARY, XA_STRING, data, win, CurrentTime);
+ Atom prop= XInternAtom (dpy, "MY_STRING_SELECTION", false);
+ XConvertSelection (dpy, xsel, XA_STRING, prop, win, CurrentTime);
int i;
XEvent ev;
for (i=0; i<1000000; i++)
- if (XCheckIfEvent (dpy, &ev, my_predicate, (XPointer) x_win))
+ if (XCheckIfEvent (dpy, &ev, my_selnotify_predicate, (XPointer) x_win))
break;
- if (i==1000000) return false;
+ if (i==1000000) return res;
XSelectionEvent& sel= ev.xselection;
- if (sel.property!=None) {
+ if (sel.property==prop) {
long offset=0;
Atom type;
int fm;
@@ -249,23 +261,27 @@ x_gui_rep::get_selection (string key, tree& t, string& s) {
offset += (n >> 2);
XFree (ret);
} while (remains>0);
- }
- t= tuple ("extern", s);
- return true;
+ t= tuple ("extern", s);
+ return true;
+ } else
+ return res;
}
bool
x_gui_rep::set_selection (string key, tree t, string s) {
selection_t (key)= copy (t);
selection_s (key)= copy (s);
- if (key == "primary") {
- if (is_nil (windows_l)) return false;
- Window win= windows_l->item;
- if (selection!=NULL) tm_delete_array (selection);
- XSetSelectionOwner (dpy, XA_PRIMARY, win, CurrentTime);
- if (XGetSelectionOwner(dpy, XA_PRIMARY)==None) return false;
- selection= as_charp (s);
- }
+ Atom xsel;
+ if(key == "primary") {
+ xsel = XA_CLIPBOARD;
+ } else if (key == "mouse") {
+ xsel = XA_PRIMARY;
+ } else
+ return true;
+ if (is_nil (windows_l)) return false;
+ Window win= windows_l->item;
+ XSetSelectionOwner (dpy, xsel, win, CurrentTime);
+ if (XGetSelectionOwner(dpy, xsel)==None) return false;
return true;
}
@@ -273,10 +289,6 @@ void
x_gui_rep::clear_selection (string key) {
selection_t->reset (key);
selection_s->reset (key);
- if ((key == "primary") && (selection != NULL)) {
- tm_delete_array (selection);
- selection= NULL;
- }
}
bool
diff --git a/src/src/Plugins/X11/x_gui.hpp b/src/src/Plugins/X11/x_gui.hpp
index c1abfd6..24840fa 100644
--- a/src/src/Plugins/X11/x_gui.hpp
+++ b/src/src/Plugins/X11/x_gui.hpp
hashmap<int,string> upper_key;
list<Window> windows_l;
- char* selection;
hashmap<string,tree> selection_t;
hashmap<string,string> selection_s;
+ Atom XA_CLIPBOARD;
+ Atom XA_TARGETS;
+
x_gui_rep (int argc, char** argv);
~x_gui_rep ();
diff --git a/src/src/Plugins/X11/x_init.cpp b/src/src/Plugins/X11/x_init.cpp
index adae260..0294337 100644
--- a/src/src/Plugins/X11/x_init.cpp
+++ b/src/src/Plugins/X11/x_init.cpp
@@ -443,9 +443,9 @@ x_gui_rep::initialize_keyboard_pointer () {
Map (XK_Cyrillic_E, "\xdd");
Map (XK_Cyrillic_YU, "\xde");
Map (XK_Cyrillic_YA, "\xdf");
-
+
//Ukrainian letters in T2A encoding
- Map (XK_Ukrainian_i, "i"); // Fall back!
+ Map (XK_Ukrainian_i, "i"); // Fall back!
Map (XK_Ukrainian_I, "I"); // Fall back!
Map (XK_Ukrainian_yi, "\xa8");
Map (XK_Ukrainian_YI, "\x88");
character_bitmap (NULL), character_pixmap ((pointer) 0),
xpm_bitmap (0), xpm_pixmap (0),
lower_key (""), upper_key (""),
- selection (NULL), selection_t ("none"), selection_s ("")
+ selection_t ("none"), selection_s ("")
{
the_gui= this;
if ((dpy= XOpenDisplay (NULL)) == NULL)
interrupted = false;
interrupt_time = texmacs_time ();
+ XA_CLIPBOARD = XInternAtom (dpy, "CLIPBOARD", false);
+ XA_TARGETS = XInternAtom (dpy, "TARGETS", false);
+
if (XMatchVisualInfo (dpy, scr, depth, TrueColor, &visual) != 0) {
if (visual.red_mask == (255 << 16) &&
visual.green_mask == (255 << 8) &&
diff --git a/src/src/Plugins/X11/x_loop.cpp b/src/src/Plugins/X11/x_loop.cpp
index 17919e3..def6a40 100644
--- a/src/src/Plugins/X11/x_loop.cpp
+++ b/src/src/Plugins/X11/x_loop.cpp
@@ -273,8 +273,7 @@ x_gui_rep::process_event (x_window win, XEvent* ev) {
if (N(key)>0) win->key_event (key);
break;
}
- {
+ case SelectionRequest: {
XSelectionRequestEvent& req= ev->xselectionrequest;
XSelectionEvent sel;
sel.type = SelectionNotify;
@@ -282,10 +281,14 @@ x_gui_rep::process_event (x_window win, XEvent* ev) {
sel.selection = req.selection;
sel.target = req.target;
sel.time = req.time;
- Atom XA_TARGETS = XInternAtom(dpy, "TARGETS", False);
- if (selection==NULL)
+ string key = "none";
+ if(req.selection == XA_PRIMARY) {
+ key = "mouse";
+ } else if(req.selection == XA_CLIPBOARD)
+ key = "primary";
+ if (!selection_s->contains(key)) {
sel.property = None;
- else if (req.target==XA_TARGETS) {
+ } else if (req.target==XA_TARGETS) {
Atom targets[2];
targets[0] = XA_TARGETS;
targets[1] = XA_STRING;
@@ -294,19 +297,24 @@ x_gui_rep::process_event (x_window win, XEvent* ev) {
(unsigned char*)&targets[0],2);
sel.property = req.property;
} else if ((req.target==AnyPropertyType) || (req.target==XA_STRING)) {
+ char *txt = as_charp (selection_s(key));
XChangeProperty (dpy, req.requestor, req.property, XA_STRING,
8, PropModeReplace,
- (unsigned char*) selection,
- strlen (selection));
+ (unsigned char*) txt,
+ strlen (txt));
+ tm_delete_array (txt);
sel.property = req.property;
} else
sel.property = None;
XSendEvent (dpy, sel.requestor, false, 0, (XEvent*) &sel);
- break;
- }
- clear_selection ("primary");
- break;
+ } break;
+ case SelectionClear: {
+ XSelectionClearEvent& req= ev->xselectionclear;
+ if(req.selection == XA_PRIMARY) {
+ clear_selection("mouse");
+ } else if(req.selection == XA_CLIPBOARD)
+ clear_selection ("primary");
+ } break;
{
Atom wm_protocols = XInternAtom(win->dpy, "WM_PROTOCOLS", 1);
diff --git a/src/src/Plugins/X11/x_window.hpp b/src/src/Plugins/X11/x_window.hpp
index a4631e9..56ad01b 100644
--- a/src/src/Plugins/X11/x_window.hpp
+++ b/src/src/Plugins/X11/x_window.hpp
friend class x_gui_rep;
friend class x_drawable_rep;
- friend Bool my_predicate (Display* dpy, XEvent* ev, XPointer arg);
+ friend Bool my_selnotify_predicate (Display* dpy, XEvent* ev, XPointer arg);
friend int get_identifier (window w);
};
Makes selections non-persistent following Windows (KDE etc.) standard.
From: <>
The selection is deactivated as soon as the cursor is moved by keys or mouse click.
This solves the annoying problem that a selection might still exist somewhere
off-screen, being replaced unnoticed when new text is entered. Now a selection may only
exist at the position of the cursor. (Clipboard and mouse-copy-paste buffer are
preserved beyond the life of the selection itself.)
---
src/src/Edit/Interface/edit_mouse.cpp | 11 ++++++++++-
src/src/Edit/Replace/edit_select.cpp | 34 +++++++++++++++++++++------------
2 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/src/src/Edit/Interface/edit_mouse.cpp b/src/src/Edit/Interface/edit_mouse.cpp
index 3af1018..5639f19 100644
--- a/src/src/Edit/Interface/edit_mouse.cpp
+++ b/src/src/Edit/Interface/edit_mouse.cpp
@@ -132,6 +132,8 @@ edit_interface_rep::mouse_extra_click (SI x, SI y) {
get_selection (p1, p2);
if ((p1==p2) || path_less (tp, p1) || path_less (p2, tp)) select (tp, tp);
select_enlarge ();
+ if (selection_active_any ())
+ selection_set ("mouse", selection_get (), true);
return false;
}
@@ -139,6 +141,7 @@ void
edit_interface_rep::mouse_drag (SI x, SI y) {
if (inside_graphics ()) return;
if (eb->action ("drag", x, y, 0) != "") return;
+ go_to (x, y);
end_x = x;
end_y = y;
selection_visible ();
@@ -150,8 +153,8 @@ edit_interface_rep::mouse_drag (SI x, SI y) {
p1= p2;
p2= temp;
}
- set_selection (p1, p2);
if ((p1 == p2) && start_drag) return;
+ set_selection (p1, p2);
start_drag= start_right_drag= false;
notify_change (THE_SELECTION);
}
@@ -179,6 +182,12 @@ edit_interface_rep::mouse_select (SI x, SI y, int mods) {
invalidate_graphical_object ();
eval ("(graphics-reset-context 'exit)");
}
+ if(start_drag) {
+ path sp= find_innermost_scroll (eb, tp);
+ path p0= tree_path (sp, x, y, 0);
+ set_selection (p0, p0);
+ notify_change (THE_SELECTION);
+ }
if (selection_active_any ())
selection_set ("mouse", selection_get (), true);
}
diff --git a/src/src/Edit/Replace/edit_select.cpp b/src/src/Edit/Replace/edit_select.cpp
index 9b0fd6f..c99a2fb 100644
--- a/src/src/Edit/Replace/edit_select.cpp
+++ b/src/src/Edit/Replace/edit_select.cpp
@@ -98,21 +98,31 @@ edit_select_rep::select_line () {
void
edit_select_rep::select_from_cursor () {
- if (!(selecting || shift_selecting)) return;
- if (path_less (mid_p, tp)) {
- start_p= copy (mid_p);
- end_p = copy (tp);
- }
- else {
- start_p= copy (tp);
- end_p = copy (mid_p);
+ if (selecting) {
+ if (path_less (mid_p, tp)) {
+ start_p= copy (mid_p);
+ end_p = copy (tp);
+ } else {
+ start_p= copy (tp);
+ end_p = copy (mid_p);
+ }
+ notify_change (THE_SELECTION);
+ if (shift_selecting) selecting = false;
}
- notify_change (THE_SELECTION);
}
void
edit_select_rep::select_from_cursor_if_active () {
- if (selecting) select_from_cursor ();
+ if (selecting) {
+ if (path_less (mid_p, tp)) {
+ start_p= copy (mid_p);
+ end_p = copy (tp);
+ } else {
+ start_p= copy (tp);
+ end_p = copy (mid_p);
+ }
+ notify_change (THE_SELECTION);
+ } else selection_cancel ();
}
void
@@ -128,10 +138,10 @@ edit_select_rep::select_from_shift_keyboard () {
if ((!shift_selecting) || (end_p == start_p) ||
((tp != start_p) && (tp != end_p)))
{
- selecting= false;
- shift_selecting= true;
mid_p= copy (tp);
}
+ selecting= true;
+ shift_selecting= true;
}
/******************************************************************************
Make delete act without touching the clipboard.
From: <>
Formerly, pressing the <delete> key with an active selection would perform a "cut" operation,
placing the deleted text in the clipboard.
Now, <delete> does actually only delete the selected text following Windows (KDE etc.) standards.
---
src/TeXmacs/progs/generic/generic-edit.scm | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/src/TeXmacs/progs/generic/generic-edit.scm b/src/TeXmacs/progs/generic/generic-edit.scm
index 8dc9d92..c5d5d70 100644
--- a/src/TeXmacs/progs/generic/generic-edit.scm
+++ b/src/TeXmacs/progs/generic/generic-edit.scm
@@ -41,7 +41,8 @@
(tm-define (kbd-remove forward?) (remove-text forward?))
(tm-define (kbd-remove forward?)
(:mode with-active-selection?)
- (clipboard-cut "primary"))
+ (clipboard-cut "nowhere")
+ (clipboard-clear "nowhere"))
(tm-define (kbd-tab)
(if (not (complete-try?))
_______________________________________________
Texmacs-dev mailing list
http://lists.gnu.org/mailman/listinfo/texmacs-dev