519b67fddf8f55f05dcd050913f864fae225e8f0
[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 defined(__osf__)
376                 if (fs != NULL)
377 #else
378                 if (fs)
379 #endif /* __osf__ */
380 #if defined(SC3) || defined (__osf__)
381                     (ia->*fs)();
382 #else
383                     (((IcccmAgent*)f_real_object)->*fs)();
384 #endif
385         }
386         else {
387 #if defined(__osf__)
388             if (ff != NULL)
389 #else
390             if (ff)
391 #endif /* __osf__ */
392 #if defined(SC3) || defined (__osf__)
393                 (ia->*ff)();
394 #else
395                 (((IcccmAgent*)f_real_object)->*ff)();
396 #endif
397         }
398     }
399
400     f_getting_selection = False;
401 }
402
403 Boolean
404 IcccmAgent::convertCB(Widget w, Atom* selection, Atom* target,
405                 Atom* type_return, XtPointer* value_return,
406                 unsigned long* length_return, int* format_return)
407 {
408     assert( f_selection_owner );
409     return f_selection_owner->
410                 convert_handler(w, *selection, *target, *type_return,
411                                 *value_return, *length_return, *format_return);
412 }
413
414 // convert multibyte string to compound text, returns number of bytes in
415 // compound text
416 // NOTE: This routine allocates some space on heap, it's caller's
417 //       responsibility to free it.
418 static int
419 mbstoct(const char* mbs, const char*& ct)
420 {
421     if (mbs == NULL) {
422         ct = NULL;
423         return -1;
424     }
425
426     XTextProperty prop;
427     if (XmbTextListToTextProperty(window_system().display(), (char**)&mbs, 1,
428                                         XCompoundTextStyle, &prop) != Success) {
429         ct = NULL;
430         return -2;
431     }
432
433     assert( prop.encoding == XA_COMPOUND_TEXT(window_system().display()) );
434     ct = (char*)prop.value;
435     return ((prop.nitems + 1) * prop.format / 8);
436 }
437
438 // extract latin1 characters from a multibyte string
439 // NOTE: This routine allocates some space on heap, it's caller's
440 //       responsibility to free it using XtFree().
441 static const char*
442 extract_latin1(const char* mbs)
443 {
444     if (mbs == NULL)
445         return NULL;
446     char* latin1s = XtMalloc((strlen(mbs) + 1) * sizeof(char));
447
448     const char* src = mbs;
449     char* dest = latin1s;
450     while (*src) {
451         int mb_len = mblen(src, MB_CUR_MAX);
452         assert( mb_len > 0 );
453         if (mb_len == 1)
454             *dest++ = *src++;
455         else
456             src += mb_len;
457     }
458     *dest = '\0';
459     return latin1s;
460 }
461
462 Boolean
463 IcccmAgent::convert_handler(Widget w, Atom selection, Atom target,
464                 Atom& type_return, XtPointer& value_return,
465                 unsigned long& length_return, int& format_return)
466 {
467   Atom* target_list;
468   int* length;
469
470   XSelectionRequestEvent* req = XtGetSelectionRequest(w, selection, NULL);
471
472   if (target == XA_TARGETS(XtDisplay(w))) {
473     ON_DEBUG( cout << "selection requested as compound targets" << '\n' << flush; );
474     Atom* xmu_targets;
475     unsigned long xmu_length;
476
477     XmuConvertStandardSelection(w, req->time, &selection, &target, &type_return,
478                                 (caddr_t*)(void*)&xmu_targets, &xmu_length,
479                                 &format_return);
480     assert( type_return == XA_ATOM );
481     assert( format_return == sizeof(Atom) * 8 );
482     // put our targets in the list
483 #define OLIAS_SUPPORT_TARGETS   4
484     length_return = xmu_length + OLIAS_SUPPORT_TARGETS;
485     target_list = (Atom*)XtMalloc(sizeof(Atom) * length_return);
486     target_list[0] = XA_COMPOUND_TEXT(XtDisplay(w));
487     target_list[1] = XA_STRING;
488     target_list[2] = XA_TEXT(XtDisplay(w));
489     target_list[3] = XA_LENGTH(XtDisplay(w));
490     for (unsigned int i = OLIAS_SUPPORT_TARGETS; i < length_return; i++)
491         target_list[i] = xmu_targets[i - OLIAS_SUPPORT_TARGETS];
492     XtFree((char*)xmu_targets);
493     xmu_targets = NULL;
494     value_return = (XtPointer)target_list;
495     return True;                                
496   }
497
498   if (target == XA_COMPOUND_TEXT(XtDisplay(w))) {
499     ON_DEBUG( cout << "selection requested as compound text" << '\n' << flush; );
500     const char* ct;
501 #if 0
502     if ((length_return = mbstoct(selection_text(), ct)) <= 0)
503 #else
504     if ((length_return = mbstoct((((IcccmAgent*)f_real_object)->*f_string_exporter)(), ct)) <= 0)
505 #endif
506         return False;
507     type_return = XA_COMPOUND_TEXT(XtDisplay(w));
508     value_return = (XtPointer)ct;
509     format_return = 8;
510     return True;
511   }
512
513 #ifdef Internationalize
514   if (target == XA_STRING) {
515     ON_DEBUG( cout << "selection requested as string" << '\n' << flush; );
516     type_return = XA_STRING;
517 #if 0
518     value_return = selection_text();
519 #else
520     value_return = (XtPointer)
521                         (((IcccmAgent*)f_real_object)->*f_string_exporter)();
522 #endif
523     const char* ct = extract_latin1((char*)&value_return);
524     XtFree((char*)&value_return);
525     value_return = (XtPointer)ct;
526     length_return = strlen(ct);
527     format_return = 8;
528     return True;
529   }
530
531   if (target == XA_TEXT(XtDisplay(w))) {
532     ON_DEBUG( cout << "selection requested as text" << '\n' << flush; );
533     type_return = XA_TEXT(XtDisplay(w));
534 #if 0
535     length_return = selection_text((char*)*value_return);
536 #else
537     value_return = (XtPointer)
538                         (((IcccmAgent*)f_real_object)->*f_string_exporter)();
539     length_return = strlen((char*)value_return);
540 #endif
541     format_return = 8;
542     return True;
543   }
544 #else
545   if (target == XA_STRING ||
546       target == XA_TEXT (XtDisplay (w))) {
547     ON_DEBUG( cout << "selection requested as string or text" << '\n' << flush; );
548     type_return = XA_STRING;
549 #if 0
550     length_return = selection_text((char*)*value_return);
551 #else
552     //
553     //  SWM 950427 -- selection_text is undefined. Looking at the
554     //  above code, looks like strlen will do the job.
555     //
556     length_return = strlen ((char *) value_return);
557 #endif
558     format_return = 8;
559     return True;
560   }
561 #endif
562
563   if (target == XA_LENGTH(XtDisplay(w))) {
564     ON_DEBUG( cout << "selection requested as length" << '\n' << flush; );
565
566     const char* ct;
567     length = (int *)XtMalloc(sizeof(int)); 
568     // we should return the length of compound text
569     *length = mbstoct((((IcccmAgent*)f_real_object)->*f_string_exporter)(), ct);
570     XtFree((char*)ct);
571
572     type_return = XA_INTEGER;
573     value_return = (XtPointer)length; 
574     length_return = 1;
575     format_return = sizeof(int) * 8;
576     return True;
577   }
578
579   ON_DEBUG( cout << "selection requested as others..." << '\n' << flush; );
580
581   // We couldn't do it, maybe Xmu can
582   return(XmuConvertStandardSelection(w, req->time, &selection, &target,
583                         &type_return, (caddr_t*)value_return, &length_return,
584                         &format_return));
585 }
586
587 // relinquish the selection voluntarily
588 void
589 IcccmAgent::disown_selection(Boolean call_app_handler)
590 {
591     ON_DEBUG(cout << "***** disown_selection *****" << endl;);
592     f_voluntary_disown = True; // guard not to execute loseCB
593
594     if (f_selection_owner == this) {
595 #ifdef DEBUG
596         cout << "(DEBUG) disown_selection: call XtDisownSelection" << endl;
597 #endif
598         XtDisownSelection(f_owning_widget, f_owning_selection, CurrentTime);
599
600         if (call_app_handler == True) // application specific handler
601             f_selection_owner->IcccmAgent::lose_selection(f_owning_selection);
602
603         reset_selection_info();
604     }
605     f_voluntary_disown = False; // reset the guard
606 }
607
608 void
609 IcccmAgent::reset_selection_info()
610 {
611     f_selection_owner = NULL;
612     f_owning_selection = None;
613     f_owning_widget = NULL;
614 }
615
616 // dispatch post processing function according to who is getting the selection
617 void
618 IcccmAgent::lose_selection(Atom atom)
619 {
620     if (ia_getting_selection() == True)
621         turn_over_selection(atom);
622     else {
623         // NOTE: lose_selection is virtual, if derived does not provide
624         //       lose_selection, it ends in infinite loops!
625         lose_selection(atom);
626     }
627 }
628
629 // True if some IcccmAgent is about to own selection
630 Boolean
631 IcccmAgent::ia_getting_selection()
632 {
633     return (Boolean)f_getting_selection;
634 }
635
636 // loseCB is a callback that is called by XtDisownSelection
637 void
638 IcccmAgent::loseCB(Widget, Atom*)
639 {
640     ON_DEBUG(cout << "(DEBUG) ***** loseCB *****" << endl;);
641
642     // if this XtDisownSelection is voluntary, leave things to disown_selection
643     if (f_voluntary_disown == True)
644         return;
645
646     if (f_selection_owner) {
647         // perform application specific post processing
648         f_selection_owner->IcccmAgent::lose_selection(f_owning_selection);
649
650         f_selection_owner->reset_selection_info();
651     }
652     else {
653 #ifdef DEBUG
654         cerr << "(WARNING) loseCB: no selection owner exists" << endl;
655 #endif
656     }
657 }
658
659 Boolean
660 IcccmAgent::is_selection_owner()
661 {
662     return (f_selection_owner == this);
663 }