add existing code (build system, libs, initial mizucp code)
[mizunara.git] / ui / motif / tree.c
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright 2014 Olaf Wintermann. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above copyright
13  *      notice, this list of conditions and the following disclaimer in the
14  *      documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <inttypes.h>
33
34 #include "tree.h"
35
36 #include "container.h"
37 #include "../common/object.h"
38 #include "../common/context.h"
39 #include <ucx/utils.h>
40
41 UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb) {
42     // TODO: check if modelinfo is complete
43     
44     Arg args[32];
45     int n = 0;
46     
47     // create scrolled window
48     UiContainer *ct = uic_get_current_container(obj);
49     Widget parent = ct->prepare(ct, args, &n, TRUE);
50     
51     XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC);
52     n++;
53     XtSetArg(args[n], XmNshadowThickness, 0);
54     n++;
55     Widget scrollw = XmCreateScrolledWindow(parent, "scroll_win", args, n);
56     ct->add(ct, scrollw);
57     XtManageChild(scrollw);
58     
59     // create table headers
60     XmStringTable header = (XmStringTable)XtMalloc(
61             model->columns * sizeof(XmString));
62     for(int i=0;i<model->columns;i++) {
63         header[i] = XmStringCreateLocalized(model->titles[i]);
64     }
65     n = 0;
66     XtSetArg(args[n], XmNdetailColumnHeading, header);
67     n++;
68     XtSetArg(args[n], XmNdetailColumnHeadingCount, model->columns);
69     n++;
70     
71     // set res
72     XtSetArg(args[n], XmNlayoutType, XmDETAIL);
73     n++;
74     XtSetArg(args[n], XmNentryViewType, XmSMALL_ICON);
75     n++;
76     XtSetArg(args[n], XmNselectionPolicy, XmSINGLE_SELECT);
77     n++;
78     XtSetArg(args[n], XmNwidth, 600);
79     n++;
80     
81     // create widget
82     //UiContainer *ct = uic_get_current_container(obj);
83     //Widget parent = ct->add(ct, args, &n);
84     
85     Widget container = XmCreateContainer(scrollw, "table", args, n);
86     XtManageChild(container);
87     
88     // add callbacks
89     UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData));
90     event->obj = obj;
91     event->activate = cb.activate;
92     event->selection = cb.selection;
93     event->userdata = cb.userdata;
94     event->last_selection = NULL;
95     if(cb.selection) {
96         XtAddCallback(
97                 container,
98                 XmNselectionCallback,
99                 (XtCallbackProc)ui_table_select_callback,
100                 event);
101     }
102     if(cb.activate) {
103         XtAddCallback(
104                 container,
105                 XmNdefaultActionCallback,
106                 (XtCallbackProc)ui_table_action_callback,
107                 event);
108     }
109     
110     // add initial data
111     UiList *list = var->value;
112     void *data = list->first(list);
113     int width = 0;
114     while(data) {
115         int w = ui_add_icon_gadget(container, model, data);
116         if(w > width) {
117             width = w;
118         }
119         data = list->next(list);
120     }
121     
122     UiTableView *tableview = ucx_mempool_malloc(obj->ctx->mempool, sizeof(UiTableView));
123     tableview->widget = container;
124     tableview->var = var;
125     tableview->model = model;
126     
127     // set new XmContainer width
128     XtVaSetValues(container, XmNwidth, width, NULL);
129     
130     // cleanup
131     for(int i=0;i<model->columns;i++) {
132         XmStringFree(header[i]);
133     }
134     XtFree((char*)header);
135     
136     return scrollw;
137 }
138
139 UIWIDGET ui_table(UiObject *obj, UiList *data, UiModel *model, UiListCallbacks cb) {
140     UiVar *var = malloc(sizeof(UiVar));
141     var->value = data;
142     var->type = UI_VAR_SPECIAL;
143     return ui_table_var(obj, var, model, cb);
144 }
145
146 void ui_table_update(UiEvent *event, UiTableView *view) {
147     // clear container
148     Widget *children;
149     int nc;
150
151     XtVaGetValues(
152             view->widget,
153             XmNchildren,
154             &children,
155             XmNnumChildren,
156             &nc,
157             NULL);
158
159     for(int i=0;i<nc;i++) {
160         XtDestroyWidget(children[i]);
161     }
162     
163     UiList *list = view->var->value;
164     
165     void *data = list->first(list);
166     int width = 0;
167     while(data) {
168         int w = ui_add_icon_gadget(view->widget, view->model, data);
169         if(w > width) {
170             width = w;
171         }
172         data = list->next(list);
173     }
174     
175 }
176
177 #define UI_COL_CHAR_WIDTH 12
178
179 int ui_add_icon_gadget(Widget container, UiModel *model, void *data) {
180     int width = 50;
181     
182     if(model->columns == 0) {
183         return width;
184     }
185     
186     XmString label = NULL;
187     Arg args[8];
188     Boolean f;
189     // first column
190     if(model->types[0] != 12345678) { // TODO: icon/label type
191         char *str = ui_type_to_string(
192                 model->types[0],
193                 model->getvalue(data, 0),
194                 &f);
195         
196         // column width
197         width += strlen(str) * UI_COL_CHAR_WIDTH;
198         
199         
200         XmString label = XmStringCreateLocalized(str);
201         XtSetArg(args[0], XmNlabelString, label);
202         if(f) {
203             free(str);
204         }
205     } else {
206         // TODO
207     }
208             
209     // remaining columns are the icon gadget details
210     XmStringTable details = (XmStringTable)XtMalloc(
211             (model->columns - 1) * sizeof(XmString));
212     for(int i=1;i<model->columns;i++) {
213         char *str = ui_type_to_string(
214                 model->types[i],
215                 model->getvalue(data, i),
216                 &f);
217         
218         // column width
219         width += strlen(str) * UI_COL_CHAR_WIDTH;
220         
221         details[i - 1] = XmStringCreateLocalized(str);
222         if(f) {
223             free(str);
224         }
225     }
226     XtSetArg(args[1], XmNdetail, details);
227     XtSetArg(args[2], XmNdetailCount, model->columns - 1);
228     XtSetArg(args[3], XmNshadowThickness, 0); 
229     // create widget
230     Widget item = XmCreateIconGadget(container, "table_item", args, 4);
231     XtManageChild(item);
232     
233     // cleanup
234     XmStringFree(label);
235     for(int i=0;i<model->columns-1;i++) {
236         XmStringFree(details[i]);
237     }
238     XtFree((char*)details);
239     
240     return width;
241 }
242
243 char* ui_type_to_string(UiModelType type, void *data, Boolean *free) {
244     switch(type) {
245         case UI_STRING: *free = FALSE; return data;
246         case UI_INTEGER: {
247             *free = TRUE;
248             int *val = data;
249             sstr_t str = ucx_asprintf(ucx_default_allocator(), "%d", *val);
250             return str.ptr;
251         }
252         default: break;
253     }
254     *free = FALSE;
255     return NULL;
256 }
257
258 void ui_table_action_callback(
259         Widget widget,
260         UiTreeEventData *event,
261         XmContainerSelectCallbackStruct *sel)
262
263     UiListSelection *selection = ui_list_selection(sel);
264     
265     UiEvent e;
266     e.obj = event->obj;
267     e.window = event->obj->window;
268     e.document = event->obj->ctx->document;
269     e.eventdata = selection;
270     e.intval = selection->count > 0 ? selection->rows[0] : -1;
271     event->activate(&e, event->userdata);
272     
273     free(event->last_selection->rows);
274     free(event->last_selection);
275     event->last_selection = selection;
276 }
277
278 void ui_table_select_callback(
279         Widget widget,
280         UiTreeEventData *event,
281         XmContainerSelectCallbackStruct *sel)
282
283     UiListSelection *selection = ui_list_selection(sel);
284     if(!ui_compare_list_selection(selection, event->last_selection)) {
285         UiEvent e;
286         e.obj = event->obj;
287         e.window = event->obj->window;
288         e.document = event->obj->ctx->document;
289         e.eventdata = selection;
290         e.intval = selection->count > 0 ? selection->rows[0] : -1;
291         event->selection(&e, event->userdata);
292     }
293     if(event->last_selection) {
294         free(event->last_selection->rows);
295         free(event->last_selection);
296     }
297     event->last_selection = selection;
298 }
299
300 UiListSelection* ui_list_selection(XmContainerSelectCallbackStruct *xs) {
301     UiListSelection *selection = malloc(sizeof(UiListSelection));
302     selection->count = xs->selected_item_count;
303     selection->rows = calloc(selection->count, sizeof(int));
304     for(int i=0;i<selection->count;i++) {
305         int index;
306         XtVaGetValues(xs->selected_items[i], XmNpositionIndex, &index, NULL);
307         selection->rows[i] = index;
308     }
309     return selection;
310 }
311
312 Boolean ui_compare_list_selection(UiListSelection *s1, UiListSelection *s2) {
313     if(!s1 || !s2) {
314         return FALSE;
315     } 
316     if(s1->count != s2->count) {
317         return FALSE;
318     }
319     for(int i=0;i<s1->count;i++) {
320         if(s1->rows[i] != s2->rows[i]) {
321             return FALSE;
322         }
323     }
324     return TRUE;
325 }