1 module pango.utils;
2 
3 import glib;
4 import gobject;
5 
6 import std.traits;
7 import std.conv;
8 import std..string;
9 import std.range;
10 import core.memory;
11 
12 
13 package enum Transfer {
14     Full,
15     None,
16 }
17 
18 
19 private GQuark gobjAssocQuark;
20 
21 static this() {
22     gobjAssocQuark = g_quark_from_string(toStringz("D_GObject"));
23 }
24 
25 
26 
27 DObjT getDObject(DObjT, GObjT)(GObjT * obj, Transfer transfer)
28 {
29     static if ( is(DObjT : D_GObject) )
30     {
31         if (!obj) return null;
32 
33         auto p = g_object_get_qdata(cast(GObject*)obj, gobjAssocQuark);
34         if (p) return cast(DObjT)(cast(D_GObject)p);
35         return new DObjT(obj, transfer);
36     }
37     else static if ( is(DObjT == class) )
38     {
39         if (!obj) return null;
40 
41         return new DObjT(obj, transfer);
42     }
43     else
44     {
45         if (!obj) throw new Exception("tentative to construct obj from null ptr");
46 
47         return DObjT(obj, transfer);
48     }
49 }
50 
51 
52 D_GObject getExistingDObject(GObjT, bool ThrowErrorIfNot=true) (GObjT * obj, Transfer transfer)
53 {
54     if (!obj) return null;
55 
56     auto p = g_object_get_qdata(cast(GObject*)obj, gobjAssocQuark);
57     if (!p) {
58         static if (ThrowErrorIfNot) throw new Error("could not retrieve \"existing\" object");
59         else return null;
60     }
61     return cast(D_GObject)p;
62     
63 }
64 
65 
66 abstract class D_GObject
67 {
68     private GObject *gobjPtr_;
69     private bool isGCProtected;
70 
71     protected inout(GObject)* gobjPtr() inout { return gobjPtr_; }
72 
73 
74     protected this(GObject* ptr, Transfer transfer) {
75         gobjPtr_ = ptr;
76         if (transfer == Transfer.None) g_object_ref(gobjPtr_);
77         
78         g_object_set_qdata_full(gobjPtr_, gobjAssocQuark,
79                                cast(gpointer)this, &destroyNotification);
80         g_object_add_toggle_ref(gobjPtr_, &toggleNotification, cast(gpointer)this);
81 
82         if (gobjPtr_.ref_count > 1) protectFromGC();
83     }
84 
85 
86     ~this() {
87         g_object_steal_qdata(gobjPtr_, gobjAssocQuark);
88         exposeToGC();
89         g_object_unref(gobjPtr_);
90     }
91 
92     invariant() {
93         assert(gobjPtr_ !is null);
94     }
95 
96 
97     private
98     {
99 
100         void protectFromGC() {
101             if (!isGCProtected) {
102                 GC.addRoot(cast(void*)this);
103                 GC.setAttr(cast(void*)this, GC.BlkAttr.NO_MOVE);
104                 isGCProtected = true;
105             }
106         }
107 
108         void exposeToGC() {
109             if (isGCProtected) {
110                 GC.removeRoot(cast(void*)this);
111                 GC.clrAttr(cast(void*)this, GC.BlkAttr.NO_MOVE);
112                 isGCProtected = false;
113             }
114         }
115     }
116 }
117 
118 
119 mixin template GObjectHolder(GDerivedT)
120 {
121     alias NativePtrT = GDerivedT*;
122 
123     @property inout(GDerivedT)* nativePtr() inout { return cast(inout(GDerivedT)*) gobjPtr; }
124 }
125 
126 
127 private {
128 
129     extern (C)
130     void toggleNotification(gpointer data,
131                             GObject *object,
132                             gboolean is_last_ref)
133     {
134         D_GObject obj = cast(D_GObject)data;
135         if (is_last_ref) {
136             obj.exposeToGC();
137         }
138         else {
139             obj.protectFromGC();
140         }
141     }
142 
143 
144     extern (C)
145         void destroyNotification(gpointer data) {
146             D_GObject obj = cast(D_GObject)data;
147             obj.exposeToGC();
148     }
149 
150 }
151 
152 
153 
154 mixin template NativePtrHolder(PangoCType, alias FreeFunc)
155 {
156     private {
157         PangoCType *nativePtr_;
158         Transfer transfer_;
159         
160         void initialize(PangoCType *ptr, Transfer transfer) {
161             nativePtr_ = ptr;
162             transfer_ = transfer;
163         }
164 
165         invariant() {
166             assert(nativePtr_ !is null);
167         }
168     }
169 
170     alias NativePtrT = PangoCType*;
171 
172     ~this() {
173         if (transfer_ == Transfer.Full) FreeFunc(nativePtr_);
174     }
175 
176     pure @property inout(PangoCType)* nativePtr() inout { return nativePtr_; }
177 
178 }
179 
180 
181 mixin template RefCountThat(PangoCType, alias FreeFunc)
182 {
183     import std.typecons;
184 
185     private {
186 
187         struct Payload
188         {
189             PangoCType *_payload;
190 
191             this(PangoCType *ptr)
192             {
193                 _payload = ptr;
194             }
195             ~this()
196             {
197                 if(_payload)
198                 {
199                     FreeFunc(_payload);
200                     _payload = null;
201                 }
202             }
203 
204             @disable this(this);
205         }
206 
207         alias Data = RefCounted!Payload;
208         Data data_;
209 
210         void initialize(PangoCType *ptr, Transfer transfer) {
211             data_ = Data(ptr);
212             assert(transfer == Transfer.Full);
213         }
214     }
215 
216     public alias NativePtrT = PangoCType*;
217 
218     public @property inout(PangoCType) *nativePtr() inout {
219         return data_.payload_;
220     }
221 }
222 
223 
224 
225 
226 mixin template RefCountedGObj(GObjT, string prefix)
227 {
228     alias NativePtrT = GObjT*;
229 
230     private GObjT* nativePtr_;
231 
232 
233     private void initialize(GObjT* ptr, Transfer transfer) {
234         nativePtr_ = ptr;
235         if (transfer != Transfer.Full) {
236             reference();
237         }
238     }
239     
240     pure @property inout(GObjT)* nativePtr() inout { return nativePtr_; }
241 
242 
243     private void reference() {
244         mixin(prefix ~ "_ref(nativePtr_);");
245     }
246     
247     private void unreference() {
248         mixin(prefix ~ "_unref(nativePtr_);");
249     }
250 
251     @disable this();
252 
253 
254     this(this) {
255         reference();
256     }
257 
258     ~this() {
259         unreference();
260     }
261 
262     invariant() {
263         assert(nativePtr_ !is null);
264     }
265 }
266 
267 
268 
269 template listGObjects(ObjT, alias ListFun) // if (__traits(hasMember, ObjT, "nativePtr_"))
270 {
271     ObjT[] listGObjects(CollecT)(CollecT* ptr, Transfer transfer)
272     {
273         ObjT.NativePtrT *arr;
274         int n;
275         ListFun(ptr, &arr, &n);
276         scope(exit) g_free(arr);
277 
278         if (!n) return [];
279 
280         ObjT[] res;
281         foreach(i; 0 .. n) {
282             res ~= getDObject!ObjT(arr[i], transfer);
283         }
284 
285         return res;
286     }
287 }
288 
289 
290 template listValues(T, alias ListFun)
291 {
292     T[] listValues(CollecT)(CollecT* ptr)
293     {
294         T *arr;
295         int n;
296         ListFun(ptr, &arr, &n);
297         scope(exit) g_free(arr);
298 
299         if (!n) return [];
300 
301         return arr[0 .. n].dup;
302     }
303 }
304 
305 
306 
307 GList *objsToGList(ObjT)(ObjT[] objs) if (is(ObjT == class) || isPointer!ObjT ||
308                                           (ObjT.sizeof == gpointer.sizeof && isIntegral!ObjT))
309 {
310     GList *res;
311     foreach (obj; objs) {
312         res = g_list_append(res, cast(gpointer)obj);
313     }
314     return res;
315 }
316 
317 
318 ObjT[] objsFromGList(ObjT)(GList *list) if (is(ObjT == class) || isPointer!ObjT ||
319                                             (ObjT.sizeof == gpointer.sizeof && isIntegral!ObjT))
320 {
321     ObjT[] res;
322     while (list !is null) {
323         res ~= cast(ObjT)list.data;
324         list = list.next;
325     }
326     return res;
327 }
328 
329 GSList *objsToGSList(ObjT)(ObjT[] objs) if (is(ObjT == class) || isPointer!ObjT ||
330                                           (ObjT.sizeof == gpointer.sizeof && isIntegral!ObjT))
331 {
332     GSList *res;
333     foreach (obj; objs) {
334         res = g_slist_append(res, cast(gpointer)obj);
335     }
336     return res;
337 }
338 
339 
340 ObjT[] objsFromGSList(ObjT)(GSList *list) if (is(ObjT == class) || isPointer!ObjT ||
341                                             (ObjT.sizeof == gpointer.sizeof && isIntegral!ObjT))
342 {
343     ObjT[] res;
344     while (list !is null) {
345         res ~= cast(ObjT)list.data;
346         list = list.next;
347     }
348     return res;
349 }
350 
351 
352 
353 DObjT[] dobjsFromGList(DObjT)(GList *list, Transfer transfer) if (__traits(hasMember, DObjT, "nativePtr"))
354 {
355     DObjT[] res;
356     while (list !is null) {
357         res ~= getDObject!DObjT(cast(DObjT.NativePtrT)list.data, transfer);
358         list = list.next;
359     }
360     return res;
361 }
362 
363 
364 
365 GList *dobjsToGList(DObjT)(DObjT[] dobjs) if (__traits(hasMember, DObjT, "nativePtr"))
366 {
367     GList *res;
368     foreach (dobj; dobjs) {
369         res = g_list_append(res, cast(gpointer)dobj.nativePtr);
370     }
371     return res;
372 }
373 
374 
375 
376 DObjT[] dobjsFromGSList(DObjT)(GSList *list, Transfer transfer) if (__traits(hasMember, DObjT, "nativePtr"))
377 {
378     DObjT[] res;
379     while (list !is null) {
380         res ~= getDObject!DObjT(cast(DObjT.NativePtrT)list.data, transfer);
381         list = list.next;
382     }
383     return res;
384 }
385 
386 
387 
388 GList *dobjsToGSList(DObjT)(DObjT[] dobjs) if (__traits(hasMember, DObjT, "nativePtr"))
389 {
390     GSList *res;
391     foreach (dobj; dobjs) {
392         res = g_slist_append(res, cast(gpointer)dobj.nativePtr);
393     }
394     return res;
395 }
396 
397 
398 
399 
400 // FIXME: finish impl
401 //struct GListGObjProxy(ObjT)
402 //{
403 //    private alias GObjPtrT = ObjT.NativePtrT;
404 //
405 //    private GList *listPtr_ =null;
406 //
407 //    package this (GList *ptr) {
408 //        listPtr_ = ptr;
409 //    }
410 //
411 //    @property inout(GList)* listPtr() inout { return listPtr_; }
412 //
413 //
414 //    void free() {
415 //        g_list_free(listPtr_);
416 //    }
417 //    
418 //    void free1() {
419 //        g_list_free_1(listPtr_);
420 //    }
421 //
422 //    //void freeFull() if (is (ObjT : D_GObject))
423 //    //{
424 //    //    g_list_free_full(list, &g_object_unref);
425 //    //}
426 //
427 //    void freeFull(GDestroyNotify freeFunc) {
428 //        g_list_free_full(list, freeFunc);
429 //    }
430 //
431 //    void append(ObjT obj) {
432 //        listPtr_ = g_list_append(listPtr_, obj.nativePtr);
433 //    }
434 //
435 //    void prepend(ObjT obj) {
436 //        listPtr_ = g_list_prepend(listPtr_, obj.nativePtr);
437 //    }
438 //
439 //    void insert(ObjT obj, int position) {
440 //        listPtr_ = g_list_insert(listPtr_, obj.nativePtr, position);
441 //    }
442 //
443 //
444 //    //GList*   g_list_insert_sorted           (GList            *list,
445 //    //                                         gpointer          data,
446 //    //                                         GCompareFunc      func);
447 //    //
448 //    //GList*   g_list_insert_sorted_with_data (GList            *list,
449 //    //                                         gpointer          data,
450 //    //                                         GCompareDataFunc  func,
451 //    //                                         gpointer          user_data);
452 //    //
453 //    //GList*   g_list_insert_before           (GList            *list,
454 //    //                                         GList            *sibling,
455 //    //                                         gpointer          data);
456 //    //
457 //    //GList*   g_list_concat                  (GList            *list1,
458 //    //                                         GList            *list2);
459 //    //
460 //    //GList*   g_list_remove                  (GList            *list,
461 //    //                                         gconstpointer     data);
462 //    //
463 //    //GList*   g_list_remove_all              (GList            *list,
464 //    //                                         gconstpointer     data);
465 //    //
466 //    //GList*   g_list_remove_link             (GList            *list,
467 //    //                                         GList            *llink);
468 //    //
469 //    //GList*   g_list_delete_link             (GList            *list,
470 //    //                                         GList            *link_);
471 //    //
472 //    //GList*   g_list_reverse                 (GList            *list);
473 //    //
474 //    //GList*   g_list_copy                    (GList            *list);
475 //    //
476 //    //
477 //    //GList*   g_list_copy_deep               (GList            *list,
478 //    //                                         GCopyFunc         func,
479 //    //                                         gpointer          user_data);
480 //    //
481 //    //
482 //    //GList*   g_list_nth                     (GList            *list,
483 //    //                                         guint             n);
484 //    //
485 //    //GList*   g_list_nth_prev                (GList            *list,
486 //    //                                         guint             n);
487 //    //
488 //    //GList*   g_list_find                    (GList            *list,
489 //    //                                         gconstpointer     data);
490 //    //
491 //    //GList*   g_list_find_custom             (GList            *list,
492 //    //                                         gconstpointer     data,
493 //    //                                         GCompareFunc      func);
494 //    //
495 //    //gint     g_list_position                (GList            *list,
496 //    //                                         GList            *llink);
497 //    //
498 //    //gint     g_list_index                   (GList            *list,
499 //    //                                         gconstpointer     data);
500 //    //
501 //    //GList*   g_list_last                    (GList            *list);
502 //    //
503 //    //GList*   g_list_first                   (GList            *list);
504 //    //
505 //    //guint    g_list_length                  (GList            *list);
506 //    
507 //    //int opApply(int delegate(ref ObjT obj) dg) {
508 //    //    ApplyFuncData d;
509 //    //    d.dg = dg;
510 //    //    g_list_foreach(listPtr_, &applyFunc, &d);
511 //    //    return d.res;
512 //    //}
513 //    //
514 //    //private {
515 //    //    struct ApplyFuncData {
516 //    //        int res;
517 //    //        int delegate(ref ObjT obj) dg;
518 //    //    }
519 //    //    extern(C) static void applyFunc(gpointer data, gointer userData) {
520 //    //        ApplyFuncData *d = cast(ApplyFuncData*)userData;
521 //    //        if (d.res) return;
522 //    //
523 //    //        GObjPtrT gobj = cast(GObjPtrT)data;
524 //    //        if (!gobj) return;
525 //    //        GObjT obj = getDObject!GObjT(gobj);
526 //    //        d.res = d.dg(obj);
527 //    //    }
528 //    //}
529 //
530 //    //void     g_list_foreach                 (GList            *list,
531 //    //                                         GFunc             func,
532 //    //                                         gpointer          user_data);
533 //    //
534 //    //GList*   g_list_sort                    (GList            *list,
535 //    //                                         GCompareFunc      compare_func);
536 //    //
537 //    //GList*   g_list_sort_with_data          (GList            *list,
538 //    //                                         GCompareDataFunc  compare_func,
539 //    //                                         gpointer          user_data);
540 //    //
541 //    //gpointer g_list_nth_data                (GList            *list,
542 //    //                                         guint             n);
543 //
544 //}