4b7f662116e9b91b06aa14eaff42791ef1b0a1ff
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / Agents / IcccmAgent.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 // $TOG: IcccmAgent.C /main/8 1998/04/17 11:33:22 mgreess $
24 /*      Copyright (c) 1994,1995,1996 FUJITSU LIMITED    */
25 /*      All Rights Reserved                             */
26
27 #define C_TOC_Element
28 #define L_Basic
29
30 #define C_IcccmAgent
31 #define L_Agents
32
33 #define C_WindowSystem
34 #define L_Other
35
36 #define C_MessageMgr
37 #define L_Managers
38
39 #include <X11/Xmu/Atoms.h>
40 #include <X11/Xmu/StdSel.h>
41
42 #include <Prelude.h>
43
44 #include "Other/XmStringLocalized.hh"
45 #include "Managers/CatMgr.hh"
46
47 #include "Registration.hh"
48
49 Time            IcccmAgent::f_paste_activated_time;
50 IcccmAgent*     IcccmAgent::f_selection_owner;
51 Atom            IcccmAgent::f_owning_selection;
52 Widget          IcccmAgent::f_owning_widget;
53 int             IcccmAgent::f_getting_selection,
54                 IcccmAgent::f_voluntary_disown;
55
56 IcccmAgent::IcccmAgent(void* real_object,
57                 data_handler_t string_handler, data_exporter_t string_exporter)
58         : f_real_object(real_object),
59           f_string_handler(string_handler), f_string_exporter(string_exporter)
60 {
61     ON_DEBUG(cout << "***** IcccmAgent *****" << endl;);
62 }
63
64 IcccmAgent::IcccmAgent(void* real_object, data_handler_t string_handler)
65         : f_real_object(real_object),
66           f_string_handler(string_handler),
67           f_string_exporter((data_exporter_t) NULL)
68 {
69     ON_DEBUG(cout << "***** IcccmAgent *****" << endl;);
70 }
71
72 IcccmAgent::IcccmAgent(void* real_object, data_exporter_t string_exporter)
73         : f_real_object(real_object),
74           f_string_handler((data_handler_t) NULL),
75           f_string_exporter(string_exporter)
76 {
77     ON_DEBUG(cout << "***** IcccmAgent *****" << endl;);
78 }
79
80 IcccmAgent::~IcccmAgent()
81 {
82     ON_DEBUG(cout << "***** ~IcccmAgent *****" << endl;);
83     // if I am the owner, relinquishes the selection
84     if (f_selection_owner == this) {
85             assert( selection_owner() );
86             disown_selection();
87             assert( selection_owner() == NULL );
88     }
89 }
90
91 IcccmAgent*
92 IcccmAgent::selection_owner()
93 {
94 #ifndef NDEBUG
95     if (f_selection_owner)
96         assert( f_owning_selection != None && f_owning_widget != NULL );
97     else
98         assert( f_owning_selection == None && f_owning_widget == NULL );
99 #endif
100     return f_selection_owner;
101 }
102
103 Atom
104 IcccmAgent::value_handler(Widget w, XtPointer ia, Atom *selection,
105         Atom *type, XtPointer value, unsigned long *length, int *format)
106 {
107     if (*type == XT_CONVERT_FAIL) {
108         ON_DEBUG( cerr << "(ERROR) valueCB: conversion failed" << endl; );
109         return None;
110     }
111
112     if ((*type == None) || (*length == 0)) {
113 #ifdef DEBUG
114         cerr << "(ERROR) valueCB: this guy contradicted himself!" << endl;
115 #endif
116         return None;
117     }
118
119     if (*type == XA_TEXT(XtDisplay(w)) || *type == XA_STRING) {
120 #ifdef DEBUG   
121         if (*type == XA_STRING)
122             cout << "(DEBUG) valueCB: import data as XA_STRING" << endl;
123         else
124             cout << "(DEBUG) valueCB: import data as XA_TEXT" << endl;
125 #endif
126 #ifdef Internationalize
127         if (*type == XA_STRING) { // Latin1
128             const char* p = extract_ascii((char*)value);
129             XtFree((char*)value);
130             value = (XtPointer)p;
131         }
132         else { // XA_TEXT
133             char* mbs = (char *)value;
134             wchar_t *wcs = (wchar_t*)malloc(strlen(mbs) + 1);
135             // check if value string can be valid in current locale
136             if ((long)mbstowcs(wcs, mbs, strlen(mbs) + 1) < 0) { // invalid
137                 const char* p = extract_ascii((char*)value);
138                 XtFree((char*)value);
139                 value = (XtPointer)p;
140             }
141             free(wcs);
142         }
143 #endif
144         (((IcccmAgent*)ia)->*f_string_handler)((char*)value, *length);
145         XtFree((char*)value);
146     }
147     else if (*type == XA_COMPOUND_TEXT(XtDisplay(w))) {
148 #ifdef DEBUG
149         cerr << "(DEBUG) valueCB: import data as XA_COMPOUND_TEXT" << endl;
150 #endif
151         XTextProperty prop;
152         prop.value = (unsigned char*)value;
153         prop.encoding = XA_COMPOUND_TEXT(XtDisplay(w));
154         prop.format = 8;
155         prop.nitems = *length;
156         char** string_list;
157         int count, len, slen;
158         XmbTextPropertyToTextList(XtDisplay(w), &prop, &string_list, &count);
159         char *mbs = strdup("");
160         for (int i=0; i<count; i++) {
161             slen = strlen(mbs);
162             len = strlen(string_list[i]);
163             mbs = (char*)realloc(mbs, slen + len + 1);
164             *((char *) memcpy(mbs, string_list[i], len) + len) = '\0';
165         }
166         XwcFreeStringList((wchar_t**)string_list);
167         (((IcccmAgent*)ia)->*f_string_handler)(mbs, strlen(mbs));
168         free((void*)mbs);
169     }
170     else {
171 #ifdef DEBUG
172         cerr << "(ERROR) valueCB: import data as unsupported Atom" << endl;
173 #endif
174         return None;
175     }
176     return *type;
177 }
178
179 Atom
180 IcccmAgent::target_handler(Widget w, XtPointer cdata, Atom *selection,
181         Atom *type, XtPointer value, unsigned long *length, int *format)
182 {
183 #define targetCB_return         { nest--; return best_target; }
184
185     static int nest = 0;
186     nest++;
187
188     Atom best_target = None;
189
190     if (*type == XT_CONVERT_FAIL) {
191         if (nest < 5) { // retry
192             ON_DEBUG( cerr << "(ERROR) targetCB: failed, retry" << endl; );
193             XtGetSelectionValue(w, *selection, XA_TARGETS(XtDisplay(w)),
194                                 targetCB, cdata, f_paste_activated_time);
195         }
196         ON_DEBUG( cerr << "(ERROR) targetCB: failed" << endl; );
197         targetCB_return
198     }
199
200     // There is no owner or the owner doesn't understand XA_TARGETS!
201     // Hopefully, XA_TEXT will work.
202     if ((*type == None) || (*length == 0)) {
203 #ifdef DEBUG
204         cerr << "(WARNING) targetCB: "
205              << "this guy doesn't understand XA_TARGETS, "
206              << "just try XA_TEXT" << endl;
207 #endif
208         best_target = XA_TEXT(XtDisplay(w));
209     }
210     else
211         best_target = pickup_target(w, (Atom *)value, *length);
212
213     if (best_target == None)
214         targetCB_return
215     else
216         XtGetSelectionValue(w, *selection, best_target, valueCB,
217                             cdata, f_paste_activated_time);
218
219     targetCB_return
220
221 #undef targetCB_return
222 }
223
224 void
225 IcccmAgent::get_selection_value(Widget w, Atom selection,
226                                 IcccmAgent* ia_this, Time as_of)
227 {
228     f_paste_activated_time = as_of;
229
230     XtGetSelectionValue(w, selection, XA_TARGETS(XtDisplay(w)),
231                                 targetCB, (XtPointer)ia_this, as_of);
232 }
233
234 void
235 IcccmAgent::targetCB(Widget w, XtPointer ia, Atom *selection,
236         Atom *type, XtPointer value, unsigned long *length, int *format)
237 {
238     Atom atom;
239     atom = ((IcccmAgent*)ia)->target_handler(w, ia, selection, type, value,
240                                                         length, format);
241     if (atom == None) // failed to resolve the best atom
242         message_mgr().info_dialog ((char*)
243                 UAS_String(CATGETS(Set_Messages, 4,
244                                 "There is no text selected.\n"
245                                 "Select some text, then try again.")));
246 }
247
248 void
249 IcccmAgent::valueCB(Widget w, XtPointer ia, Atom *selection,
250         Atom *type, XtPointer value, unsigned long *length, int *format)
251 {
252     Atom atom;
253     atom = ((IcccmAgent*)ia)->value_handler(w, ia, selection, type, value,
254                                                         length, format);
255
256     if (atom == None) // failed to resolve the best atom
257         message_mgr().info_dialog ((char*)
258                 UAS_String(CATGETS(Set_Messages, 4,
259                                 "There is no text selected.\n"
260                                 "Select some text, then try again.")));
261 }
262
263 Atom
264 IcccmAgent::pickup_target(Widget w, Atom *targets, unsigned long length)
265 {
266     Atom best = None;
267     Display* display = XtDisplay(w);
268
269     Atom *iter;
270     // search XA_COMPOUND_TEXT
271     for (iter=targets; iter<targets+length; iter++) {
272         if (*iter == XA_COMPOUND_TEXT(display)) {
273             best = *iter;
274             ON_DEBUG( cout << "(DEBUG) pickup XA_COMPOUND_TEXT" << endl; );
275             break;
276         }
277     }
278     if (best != None)
279         return best;
280
281     for (iter=targets; iter<targets+length; iter++) {
282         if (*iter == XA_TEXT(display)) {
283             best = *iter;
284             ON_DEBUG( cout << "(DEBUG) pickup XA_TEXT" << endl; );
285             break;
286         }
287     }
288     if (best != None)
289         return best;
290
291     for (iter=targets; iter<targets+length; iter++) {
292         if (*iter == XA_STRING) {
293             best = *iter;
294             ON_DEBUG( cout << "(DEBUG) pickup XA_STRING" << endl; );
295             break;
296         }
297         else
298             continue;
299     }
300 #ifdef DEBUG
301     if (best == None)
302         cout << "(DEBUG) no proper Atoms found" << endl;
303 #endif
304
305     return best;
306 }
307
308 // set new string_handler, returns old one
309 data_handler_t
310 IcccmAgent::set_string_handler(data_handler_t handler)
311 {
312     data_handler_t old_handler = f_string_handler;
313     f_string_handler = handler;
314     return old_handler;
315 }
316
317 // set new string_exporter, returns old one
318 data_exporter_t
319 IcccmAgent::set_string_exporter(data_exporter_t exporter)
320 {
321     data_exporter_t old_exporter = f_string_exporter;
322     f_string_exporter = exporter;
323     return old_exporter;
324 }
325
326 const char*
327 IcccmAgent::extract_ascii(const char* mbs)
328 {
329     if (mbs == NULL)
330         return NULL;
331     unsigned char* asciis = (unsigned char*)
332                                 XtMalloc((strlen(mbs) + 1) * sizeof(char));
333
334     const unsigned char* src = (const unsigned char*)mbs;
335     unsigned char* dest = asciis;
336     for (; *src; src++) {
337         if ((*src & 0x80) == 0) // msb off
338             *dest++ = *src;
339     }
340     *dest = '\0';
341     return (char*)asciis;
342 }
343
344 void
345 IcccmAgent::own_selection(Widget w, Atom selection, IcccmAgent* ia,
346                         Time as_of, own_success_CB_t fs, own_fail_CB_t ff)
347 {
348     ON_DEBUG(cout << "***** IcccmAgent::own_selection *****" << endl;);
349     f_getting_selection = True;
350
351 #ifdef DEBUG
352     if (f_selection_owner && f_selection_owner != this)
353         cout << "(DEBUG) own_selection: "
354              << "Another IcccmAgent already has selection" << endl;
355 #endif
356     // same owner, same widget, same selection
357     if (f_selection_owner == this &&
358         f_owning_selection == selection &&
359         f_owning_widget == w) {
360 #ifdef DEBUG
361         cout << "(DEBUG) own_selection: same combination, do nothing" << endl;
362 #endif
363 #if 0
364         (((IcccmAgent*)f_real_object)->*fs)();
365 #endif
366     }
367     else {
368     // DoneProc not supported yet
369         if (XtOwnSelection(w, selection, as_of,
370                                         convertCB, loseCB, NULL) == True) {
371                 assert( selection_owner() == NULL );
372                 f_selection_owner = this;
373                 f_owning_selection = selection;
374                 f_owning_widget = w;
375                 if (fs)
376 #if defined(SC3)
377                     (ia->*fs)();
378 #else
379                     (((IcccmAgent*)f_real_object)->*fs)();
380 #endif
381         }
382         else {
383             if (ff)
384 #if defined(SC3)
385                 (ia->*ff)();
386 #else
387                 (((IcccmAgent*)f_real_object)->*ff)();
388 #endif
389         }
390     }
391
392     f_getting_selection = False;
393 }
394
395 Boolean
396 IcccmAgent::convertCB(Widget w, Atom* selection, Atom* target,
397                 Atom* type_return, XtPointer* value_return,
398                 unsigned long* length_return, int* format_return)
399 {
400     assert( f_selection_owner );
401     return f_selection_owner->
402                 convert_handler(w, *selection, *target, *type_return,
403                                 *value_return, *length_return, *format_return);
404 }
405
406 // convert multibyte string to compound text, returns number of bytes in
407 // compound text
408 // NOTE: This routine allocates some space on heap, it's caller's
409 //       responsibility to free it.
410 static int
411 mbstoct(const char* mbs, const char*& ct)
412 {
413     if (mbs == NULL) {
414         ct = NULL;
415         return -1;
416     }
417
418     XTextProperty prop;
419     if (XmbTextListToTextProperty(window_system().display(), (char**)&mbs, 1,
420                                         XCompoundTextStyle, &prop) != Success) {
421         ct = NULL;
422         return -2;
423     }
424
425     assert( prop.encoding == XA_COMPOUND_TEXT(window_system().display()) );
426     ct = (char*)prop.value;
427     return ((prop.nitems + 1) * prop.format / 8);
428 }
429
430 // extract latin1 characters from a multibyte string
431 // NOTE: This routine allocates some space on heap, it's caller's
432 //       responsibility to free it using XtFree().
433 static const char*
434 extract_latin1(const char* mbs)
435 {
436     if (mbs == NULL)
437         return NULL;
438     char* latin1s = XtMalloc((strlen(mbs) + 1) * sizeof(char));
439
440     const char* src = mbs;
441     char* dest = latin1s;
442     while (*src) {
443         int mb_len = mblen(src, MB_CUR_MAX);
444         assert( mb_len > 0 );
445         if (mb_len == 1)
446             *dest++ = *src++;
447         else
448             src += mb_len;
449     }
450     *dest = '\0';
451     return latin1s;
452 }
453
454 Boolean
455 IcccmAgent::convert_handler(Widget w, Atom selection, Atom target,
456                 Atom& type_return, XtPointer& value_return,
457                 unsigned long& length_return, int& format_return)
458 {
459   Atom* target_list;
460   int* length;
461
462   XSelectionRequestEvent* req = XtGetSelectionRequest(w, selection, NULL);
463
464   if (target == XA_TARGETS(XtDisplay(w))) {
465     ON_DEBUG( cout << "selection requested as compound targets" << '\n' << flush; );
466     Atom* xmu_targets;
467     unsigned long xmu_length;
468
469     XmuConvertStandardSelection(w, req->time, &selection, &target, &type_return,
470                                 (caddr_t*)(void*)&xmu_targets, &xmu_length,
471                                 &format_return);
472     assert( type_return == XA_ATOM );
473     assert( format_return == sizeof(Atom) * 8 );
474     // put our targets in the list
475 #define OLIAS_SUPPORT_TARGETS   4
476     length_return = xmu_length + OLIAS_SUPPORT_TARGETS;
477     target_list = (Atom*)XtMalloc(sizeof(Atom) * length_return);
478     target_list[0] = XA_COMPOUND_TEXT(XtDisplay(w));
479     target_list[1] = XA_STRING;
480     target_list[2] = XA_TEXT(XtDisplay(w));
481     target_list[3] = XA_LENGTH(XtDisplay(w));
482     for (unsigned int i = OLIAS_SUPPORT_TARGETS; i < length_return; i++)
483         target_list[i] = xmu_targets[i - OLIAS_SUPPORT_TARGETS];
484     XtFree((char*)xmu_targets);
485     xmu_targets = NULL;
486     value_return = (XtPointer)target_list;
487     return True;                                
488   }
489
490   if (target == XA_COMPOUND_TEXT(XtDisplay(w))) {
491     ON_DEBUG( cout << "selection requested as compound text" << '\n' << flush; );
492     const char* ct;
493 #if 0
494     if ((length_return = mbstoct(selection_text(), ct)) <= 0)
495 #else
496     if ((length_return = mbstoct((((IcccmAgent*)f_real_object)->*f_string_exporter)(), ct)) <= 0)
497 #endif
498         return False;
499     type_return = XA_COMPOUND_TEXT(XtDisplay(w));
500     value_return = (XtPointer)ct;
501     format_return = 8;
502     return True;
503   }
504
505 #ifdef Internationalize
506   if (target == XA_STRING) {
507     ON_DEBUG( cout << "selection requested as string" << '\n' << flush; );
508     type_return = XA_STRING;
509 #if 0
510     value_return = selection_text();
511 #else
512     value_return = (XtPointer)
513                         (((IcccmAgent*)f_real_object)->*f_string_exporter)();
514 #endif
515     const char* ct = extract_latin1((char*)&value_return);
516     XtFree((char*)&value_return);
517     value_return = (XtPointer)ct;
518     length_return = strlen(ct);
519     format_return = 8;
520     return True;
521   }
522
523   if (target == XA_TEXT(XtDisplay(w))) {
524     ON_DEBUG( cout << "selection requested as text" << '\n' << flush; );
525     type_return = XA_TEXT(XtDisplay(w));
526 #if 0
527     length_return = selection_text((char*)*value_return);
528 #else
529     value_return = (XtPointer)
530                         (((IcccmAgent*)f_real_object)->*f_string_exporter)();
531     length_return = strlen((char*)value_return);
532 #endif
533     format_return = 8;
534     return True;
535   }
536 #else
537   if (target == XA_STRING ||
538       target == XA_TEXT (XtDisplay (w))) {
539     ON_DEBUG( cout << "selection requested as string or text" << '\n' << flush; );
540     type_return = XA_STRING;
541 #if 0
542     length_return = selection_text((char*)*value_return);
543 #else
544     //
545     //  SWM 950427 -- selection_text is undefined. Looking at the
546     //  above code, looks like strlen will do the job.
547     //
548     length_return = strlen ((char *) value_return);
549 #endif
550     format_return = 8;
551     return True;
552   }
553 #endif
554
555   if (target == XA_LENGTH(XtDisplay(w))) {
556     ON_DEBUG( cout << "selection requested as length" << '\n' << flush; );
557
558     const char* ct;
559     length = (int *)XtMalloc(sizeof(int)); 
560     // we should return the length of compound text
561     *length = mbstoct((((IcccmAgent*)f_real_object)->*f_string_exporter)(), ct);
562     XtFree((char*)ct);
563
564     type_return = XA_INTEGER;
565     value_return = (XtPointer)length; 
566     length_return = 1;
567     format_return = sizeof(int) * 8;
568     return True;
569   }
570
571   ON_DEBUG( cout << "selection requested as others..." << '\n' << flush; );
572
573   // We couldn't do it, maybe Xmu can
574   return(XmuConvertStandardSelection(w, req->time, &selection, &target,
575                         &type_return, (caddr_t*)value_return, &length_return,
576                         &format_return));
577 }
578
579 // relinquish the selection voluntarily
580 void
581 IcccmAgent::disown_selection(Boolean call_app_handler)
582 {
583     ON_DEBUG(cout << "***** disown_selection *****" << endl;);
584     f_voluntary_disown = True; // guard not to execute loseCB
585
586     if (f_selection_owner == this) {
587 #ifdef DEBUG
588         cout << "(DEBUG) disown_selection: call XtDisownSelection" << endl;
589 #endif
590         XtDisownSelection(f_owning_widget, f_owning_selection, CurrentTime);
591
592         if (call_app_handler == True) // application specific handler
593             f_selection_owner->IcccmAgent::lose_selection(f_owning_selection);
594
595         reset_selection_info();
596     }
597     f_voluntary_disown = False; // reset the guard
598 }
599
600 void
601 IcccmAgent::reset_selection_info()
602 {
603     f_selection_owner = NULL;
604     f_owning_selection = None;
605     f_owning_widget = NULL;
606 }
607
608 // dispatch post processing function according to who is getting the selection
609 void
610 IcccmAgent::lose_selection(Atom atom)
611 {
612     if (ia_getting_selection() == True)
613         turn_over_selection(atom);
614     else {
615         // NOTE: lose_selection is virtual, if derived does not provide
616         //       lose_selection, it ends in infinite loops!
617         lose_selection(atom);
618     }
619 }
620
621 // True if some IcccmAgent is about to own selection
622 Boolean
623 IcccmAgent::ia_getting_selection()
624 {
625     return (Boolean)f_getting_selection;
626 }
627
628 // loseCB is a callback that is called by XtDisownSelection
629 void
630 IcccmAgent::loseCB(Widget, Atom*)
631 {
632     ON_DEBUG(cout << "(DEBUG) ***** loseCB *****" << endl;);
633
634     // if this XtDisownSelection is voluntary, leave things to disown_selection
635     if (f_voluntary_disown == True)
636         return;
637
638     if (f_selection_owner) {
639         // perform application specific post processing
640         f_selection_owner->IcccmAgent::lose_selection(f_owning_selection);
641
642         f_selection_owner->reset_selection_info();
643     }
644     else {
645 #ifdef DEBUG
646         cerr << "(WARNING) loseCB: no selection owner exists" << endl;
647 #endif
648     }
649 }
650
651 Boolean
652 IcccmAgent::is_selection_owner()
653 {
654     return (f_selection_owner == this);
655 }