Line data Source code
1 : /* File: gui_sketch_card_painter.c; Copyright and License: see below */
2 :
3 : #include "sketch/gui_sketch_card_painter.h"
4 : #include "u8/u8_trace.h"
5 : #include "gui_gtk.h"
6 : #include <assert.h>
7 :
8 0 : void gui_sketch_card_painter_init( gui_sketch_card_painter_t *this_,
9 : gui_resources_t *resources,
10 : gui_sketch_texture_t *texture_downloader )
11 : {
12 0 : U8_TRACE_BEGIN();
13 0 : assert( resources != NULL );
14 0 : assert( texture_downloader != NULL );
15 :
16 0 : gui_sketch_style_init( &((*this_).sketch_style) );
17 0 : (*this_).resources = resources;
18 0 : (*this_).texture_downloader = texture_downloader;
19 :
20 0 : U8_TRACE_END();
21 0 : }
22 :
23 0 : void gui_sketch_card_painter_destroy( gui_sketch_card_painter_t *this_ )
24 : {
25 0 : U8_TRACE_BEGIN();
26 :
27 0 : (*this_).resources = NULL;
28 0 : (*this_).texture_downloader = NULL;
29 0 : gui_sketch_style_destroy( &((*this_).sketch_style) );
30 :
31 0 : U8_TRACE_END();
32 0 : }
33 :
34 0 : void gui_sketch_card_painter_draw_overlay( gui_sketch_card_painter_t *this_,
35 : gui_tool_t selected_tool,
36 : const gui_sketch_drag_state_t *drag_state,
37 : const gui_sketch_card_t *card_under_mouse,
38 : gui_marked_set_t *marked_objects,
39 : cairo_t *cr )
40 : {
41 0 : U8_TRACE_BEGIN();
42 0 : assert( NULL != drag_state );
43 0 : assert( NULL != cr );
44 :
45 0 : switch ( selected_tool )
46 : {
47 0 : case GUI_TOOL_NAVIGATE:
48 : {
49 0 : gui_sketch_card_painter_private_draw_nav_mode( this_, drag_state, card_under_mouse, cr );
50 : }
51 0 : break;
52 :
53 0 : case GUI_TOOL_EDIT:
54 : {
55 0 : gui_sketch_card_painter_private_draw_edit_mode( this_, drag_state, card_under_mouse, cr );
56 : }
57 0 : break;
58 :
59 0 : case GUI_TOOL_CREATE:
60 : {
61 0 : const data_id_t highlighted = gui_marked_set_get_highlighted( marked_objects );
62 0 : const layout_subelement_kind_t highlighted_kind = gui_marked_set_get_highlighted_kind( marked_objects );
63 0 : gui_sketch_card_painter_private_draw_create_mode( this_,
64 : drag_state,
65 : card_under_mouse,
66 : data_id_get_table(&highlighted),
67 : highlighted_kind,
68 : cr
69 : );
70 : }
71 0 : break;
72 :
73 0 : case GUI_TOOL_SEARCH:
74 : {
75 : }
76 0 : break;
77 :
78 0 : default:
79 : {
80 0 : U8_LOG_ERROR("selected_tool is out of range");
81 : }
82 0 : break;
83 : }
84 :
85 0 : U8_TRACE_END();
86 0 : }
87 :
88 0 : void gui_sketch_card_painter_private_draw_nav_mode( gui_sketch_card_painter_t *this_,
89 : const gui_sketch_drag_state_t *drag_state,
90 : const gui_sketch_card_t *card_under_mouse,
91 : cairo_t *cr )
92 : {
93 0 : U8_TRACE_BEGIN();
94 0 : assert( NULL != drag_state );
95 0 : assert( NULL != cr );
96 :
97 0 : if ( gui_sketch_drag_state_is_waiting_for_move ( drag_state ) )
98 : {
99 0 : const data_full_id_t *const dragged_object = gui_sketch_drag_state_get_dragged_object_const( drag_state );
100 0 : const data_id_t *const dragged_primary = data_full_id_get_primary_id_const( dragged_object );
101 0 : const bool dragging_diagram = ( DATA_TABLE_DIAGRAM == data_id_get_table( dragged_primary ) );
102 0 : if ( dragging_diagram )
103 : {
104 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
105 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
106 0 : GdkTexture *icon = gui_resources_get_sketch_move_v( (*this_).resources );
107 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
108 : }
109 : }
110 :
111 0 : U8_TRACE_END();
112 0 : }
113 :
114 0 : void gui_sketch_card_painter_private_draw_edit_mode( gui_sketch_card_painter_t *this_,
115 : const gui_sketch_drag_state_t *drag_state,
116 : const gui_sketch_card_t *card_under_mouse,
117 : cairo_t *cr )
118 : {
119 0 : U8_TRACE_BEGIN();
120 0 : assert( NULL != drag_state );
121 0 : assert( NULL != cr );
122 :
123 0 : if ( gui_sketch_drag_state_is_dragging ( drag_state ) )
124 : {
125 0 : const data_full_id_t *const dragged_object = gui_sketch_drag_state_get_dragged_object_const( drag_state );
126 0 : const data_id_t *const dragged_primary = data_full_id_get_primary_id_const( dragged_object );
127 0 : const bool dragging_classifier = ( DATA_TABLE_DIAGRAMELEMENT == data_id_get_table( dragged_primary ) );
128 :
129 0 : if ( NULL != card_under_mouse )
130 : {
131 0 : const data_diagram_t *diag = gui_sketch_card_get_diagram_const ( card_under_mouse );
132 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type( diag );
133 0 : const bool draw_2d_diagram
134 : = (( diag_type != DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM )
135 0 : && ( diag_type != DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM )
136 0 : && ( diag_type != DATA_DIAGRAM_TYPE_LIST ));
137 :
138 0 : if ( draw_2d_diagram && dragging_classifier )
139 : {
140 : #if 0
141 : /* draw grid line crossings */
142 : gui_sketch_card_painter_private_draw_grid( this_, card_under_mouse, cr );
143 : #endif
144 :
145 : /* draw new grid lines that visualize the order of classifiers */
146 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
147 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
148 0 : gui_sketch_card_painter_private_visualize_order( this_, card_under_mouse, to_x, to_y, cr );
149 : }
150 : }
151 : }
152 :
153 0 : if ( gui_sketch_drag_state_is_waiting_for_move ( drag_state ) )
154 : {
155 0 : const data_full_id_t *const dragged_object = gui_sketch_drag_state_get_dragged_object_const( drag_state );
156 0 : const data_id_t *const dragged_primary = data_full_id_get_primary_id_const( dragged_object );
157 0 : const bool dragging_classifier = ( DATA_TABLE_DIAGRAMELEMENT == data_id_get_table( dragged_primary ) );
158 0 : const bool dragging_feature = ( DATA_TABLE_FEATURE == data_id_get_table( dragged_primary ) );
159 0 : const bool dragging_relationship = ( DATA_TABLE_RELATIONSHIP == data_id_get_table( dragged_primary ) );
160 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
161 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
162 0 : if ( dragging_classifier || dragging_feature )
163 0 : {
164 0 : GdkTexture *icon = gui_resources_get_sketch_move_2d( (*this_).resources );
165 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
166 : }
167 0 : else if ( dragging_relationship )
168 : {
169 0 : const data_diagram_t *diag = gui_sketch_card_get_diagram_const ( card_under_mouse );
170 0 : if ( NULL != card_under_mouse )
171 : {
172 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type( diag );
173 0 : if ( diag_type == DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM )
174 : {
175 0 : GdkTexture *icon = gui_resources_get_sketch_move_v( (*this_).resources );
176 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
177 : }
178 0 : else if ( diag_type == DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM )
179 : {
180 0 : GdkTexture *icon = gui_resources_get_sketch_move_h( (*this_).resources );
181 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
182 : }
183 : }
184 : }
185 : }
186 :
187 0 : U8_TRACE_END();
188 0 : }
189 :
190 0 : void gui_sketch_card_painter_private_draw_create_mode( gui_sketch_card_painter_t *this_,
191 : const gui_sketch_drag_state_t *drag_state,
192 : const gui_sketch_card_t *card_under_mouse,
193 : data_table_t highlighted_table,
194 : layout_subelement_kind_t highlighted_kind,
195 : cairo_t *cr )
196 : {
197 0 : U8_TRACE_BEGIN();
198 0 : assert( NULL != drag_state );
199 0 : assert( NULL != cr );
200 :
201 0 : if ( gui_sketch_drag_state_is_dragging ( drag_state ) )
202 : {
203 : /* draw a line */
204 0 : const int32_t from_x = gui_sketch_drag_state_get_from_x ( drag_state );
205 0 : const int32_t from_y = gui_sketch_drag_state_get_from_y ( drag_state );
206 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
207 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
208 0 : gui_sketch_card_painter_private_draw_arrow( this_, from_x, from_y, to_x, to_y, cr );
209 : }
210 : else /* ! gui_sketch_drag_state_is_dragging() */
211 : {
212 0 : if ( NULL != card_under_mouse )
213 : {
214 0 : const bool draw_new_classifier
215 0 : = (( highlighted_table == DATA_TABLE_CLASSIFIER )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_SPACE ))
216 0 : || (( highlighted_table == DATA_TABLE_DIAGRAMELEMENT )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_SPACE ))
217 0 : || (( highlighted_table == DATA_TABLE_DIAGRAM )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_SPACE ));
218 0 : const bool draw_new_feature
219 0 : = (( highlighted_table == DATA_TABLE_CLASSIFIER )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_OUTLINE ))
220 0 : || (( highlighted_table == DATA_TABLE_DIAGRAMELEMENT )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_OUTLINE ));
221 0 : const bool draw_new_relationship
222 0 : = (( highlighted_table == DATA_TABLE_CLASSIFIER )&&( highlighted_kind != LAYOUT_SUBELEMENT_KIND_SPACE ))
223 0 : || (( highlighted_table == DATA_TABLE_DIAGRAMELEMENT )&&( highlighted_kind != LAYOUT_SUBELEMENT_KIND_SPACE ))
224 0 : || ( highlighted_table == DATA_TABLE_FEATURE );
225 :
226 0 : const data_diagram_t *diag = gui_sketch_card_get_diagram_const ( card_under_mouse );
227 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type( diag );
228 0 : const bool draw_2d_diagram
229 : = (( diag_type != DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM )
230 0 : && ( diag_type != DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM )
231 0 : && ( diag_type != DATA_DIAGRAM_TYPE_LIST ));
232 :
233 : /* get coordinates */
234 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
235 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
236 :
237 : /* draw grid */
238 0 : if (( draw_new_classifier )&&( draw_2d_diagram ))
239 : {
240 : #if 0
241 : /* draw grid line crossings */
242 : gui_sketch_card_painter_private_draw_grid( this_, card_under_mouse, cr );
243 : #endif
244 : /* draw new grid lines that visualize the order of classifiers */
245 0 : gui_sketch_card_painter_private_visualize_order( this_, card_under_mouse, to_x, to_y, cr );
246 : }
247 :
248 : /* draw boxes and arrow icons */
249 0 : if ( draw_new_feature )
250 : {
251 0 : assert( draw_new_relationship );
252 0 : assert( ! draw_new_classifier );
253 0 : GdkTexture *icon = gui_resources_get_sketch_refine( (*this_).resources );
254 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
255 : }
256 0 : else if ( draw_new_relationship )
257 : {
258 0 : assert( ! draw_new_classifier );
259 0 : GdkTexture *icon = gui_resources_get_sketch_relate( (*this_).resources );
260 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
261 : }
262 0 : else if ( draw_new_classifier )
263 : {
264 0 : GdkTexture *icon = gui_resources_get_sketch_create( (*this_).resources );
265 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
266 : }
267 : else
268 : {
269 : /* draw nothing */
270 : }
271 : }
272 : }
273 :
274 0 : U8_TRACE_END();
275 0 : }
276 :
277 0 : void gui_sketch_card_painter_private_draw_arrow( gui_sketch_card_painter_t *this_,
278 : int32_t from_x,
279 : int32_t from_y,
280 : int32_t to_x,
281 : int32_t to_y,
282 : cairo_t *cr )
283 : {
284 0 : U8_TRACE_BEGIN();
285 0 : assert( NULL != cr );
286 :
287 0 : const GdkRGBA high_color = gui_sketch_style_get_highlight_color( &((*this_).sketch_style) );
288 0 : cairo_set_source_rgba( cr, high_color.red, high_color.green, high_color.blue, high_color.alpha );
289 0 : cairo_move_to ( cr, from_x, from_y );
290 0 : cairo_line_to ( cr, to_x, to_y );
291 0 : cairo_stroke (cr);
292 :
293 : /* draw the arrow tip */
294 : int clock_direction; /* the gdk coordinate system if bottom up - and out clock is also mirrored! */
295 0 : const int32_t x_dist = to_x - from_x;
296 0 : const int32_t y_dist = to_y - from_y;
297 0 : if ( x_dist == 0 )
298 : {
299 0 : clock_direction = ( y_dist < 0 ) ? 6 : 0;
300 : }
301 : else
302 : {
303 0 : int32_t y_x_deci_ratio = ( y_dist * 10 ) / x_dist;
304 0 : if ( y_x_deci_ratio < 3 /* = 0.26 = tan(15 deg) */ )
305 : {
306 0 : if ( y_x_deci_ratio < -10 /* = -1.00 = tan(-45 deg) */ )
307 : {
308 0 : if ( y_x_deci_ratio < -37 /* = -3.73 = tan(-75 deg) */ )
309 : {
310 0 : clock_direction = 6;
311 : }
312 : else
313 : {
314 0 : clock_direction = 5;
315 : }
316 : }
317 : else
318 : {
319 0 : if ( y_x_deci_ratio < -2 /* = -0.26 = tan(-15 deg) */ )
320 : {
321 0 : clock_direction = 4;
322 : }
323 : else
324 : {
325 0 : clock_direction = 3;
326 : }
327 : }
328 : }
329 : else
330 : {
331 0 : if ( y_x_deci_ratio < 10 /* = 1.00 = tan(45 deg) */ )
332 : {
333 0 : clock_direction = 2;
334 : }
335 : else
336 : {
337 0 : if ( y_x_deci_ratio < 38 /* = 3.73 = tan(75 deg) */ )
338 : {
339 0 : clock_direction = 1;
340 : }
341 : else
342 : {
343 0 : clock_direction = 0;
344 : }
345 : }
346 : }
347 0 : if ( x_dist < 0 )
348 : {
349 0 : clock_direction = (clock_direction+6)%12;
350 : }
351 : }
352 : const static int32_t DX[12][2]={{-6,+6},{-9, 0},{-11,-4},{-9,-9},{-4,-11},{ 0,-9},{+6,-6},{+9, 0},{+11,+4},{+9,+9},{+4,+11},{ 0,+9}};
353 : const static int32_t DY[12][2]={{-9,-9},{-4,-11},{ 0,-9},{+6,-6},{+9, 0},{+11,+4},{+9,+9},{+4,+11},{ 0,+9},{+6,-6},{-9, 0},{-11,-4}};
354 0 : cairo_move_to ( cr, to_x + DX[clock_direction][0], to_y + DY[clock_direction][0] );
355 0 : cairo_line_to ( cr, to_x, to_y );
356 0 : cairo_line_to ( cr, to_x + DX[clock_direction][1], to_y + DY[clock_direction][1] );
357 0 : cairo_stroke (cr);
358 :
359 0 : U8_TRACE_END();
360 0 : }
361 :
362 : #if 0
363 : void gui_sketch_card_painter_private_draw_grid( gui_sketch_card_painter_t *this_,
364 : const gui_sketch_card_t *card_under_mouse,
365 : cairo_t *cr )
366 : {
367 : U8_TRACE_BEGIN();
368 : assert( NULL != card_under_mouse );
369 : assert( NULL != cr );
370 :
371 : const GdkRGBA high_color = gui_sketch_style_get_highlight_color( &((*this_).sketch_style) );
372 : cairo_set_source_rgba( cr, high_color.red, high_color.green, high_color.blue, high_color.alpha );
373 :
374 : /* draw grid */
375 : shape_int_rectangle_t bounds;
376 : uint32_t x_count;
377 : uint32_t y_count;
378 : gui_sketch_card_get_grid_area( card_under_mouse, &bounds, &x_count, &y_count );
379 : assert( x_count >= 2 );
380 : assert( y_count >= 2 );
381 : const int32_t left = shape_int_rectangle_get_left(&bounds);
382 : const int32_t top = shape_int_rectangle_get_top(&bounds);
383 : const int32_t width = shape_int_rectangle_get_width(&bounds);
384 : const int32_t height = shape_int_rectangle_get_height(&bounds);
385 : for ( uint32_t x_idx = 1; (x_idx+1) < x_count; x_idx ++ )
386 : {
387 : for ( uint32_t y_idx = 1; (y_idx+1) < y_count; y_idx ++ )
388 : {
389 : const int32_t x = left + (( width * x_idx )/( x_count-1 ));
390 : const int32_t y = top + (( height * y_idx )/( y_count-1 ));
391 : static const int32_t HALF_LINE = 16;
392 : cairo_rectangle ( cr, x, y-HALF_LINE, 1, 2*HALF_LINE );
393 : cairo_fill (cr);
394 : cairo_rectangle ( cr, x-HALF_LINE, y, 2*HALF_LINE, 1 );
395 : cairo_fill (cr);
396 : }
397 : }
398 :
399 : U8_TRACE_END();
400 : }
401 : #endif
402 :
403 0 : void gui_sketch_card_painter_private_visualize_order( gui_sketch_card_painter_t *this_,
404 : const gui_sketch_card_t *card_under_mouse,
405 : int32_t x,
406 : int32_t y,
407 : cairo_t *cr )
408 : {
409 0 : U8_TRACE_BEGIN();
410 0 : assert( NULL != card_under_mouse );
411 0 : assert( NULL != cr );
412 :
413 : /* fetch input data: subwidget */
414 0 : const shape_int_rectangle_t bounds = gui_sketch_card_get_bounds( card_under_mouse );
415 0 : const int32_t left = shape_int_rectangle_get_left(&bounds);
416 0 : const int32_t top = shape_int_rectangle_get_top(&bounds);
417 0 : const int32_t right = shape_int_rectangle_get_right(&bounds);
418 0 : const int32_t bottom = shape_int_rectangle_get_bottom(&bounds);
419 :
420 : /* fetch input data: grid */
421 0 : const geometry_grid_t *const grid = gui_sketch_card_get_grid_const( card_under_mouse);
422 0 : const geometry_grid_kind_t grid_kind = geometry_grid_get_kind( grid );
423 0 : const double snap_interval = gui_sketch_style_get_snap_to_grid( &((*this_).sketch_style) );
424 0 : const geometry_non_linear_scale_t *const x_scale = geometry_grid_get_x_scale_const( grid );
425 0 : const int32_t order_at_x = geometry_non_linear_scale_get_order( x_scale, (double) x, snap_interval );
426 0 : const geometry_non_linear_scale_t *const y_scale = geometry_grid_get_y_scale_const( grid );
427 0 : const int32_t order_at_y = geometry_non_linear_scale_get_order( y_scale, (double) y, snap_interval );
428 :
429 : /* define own distances */
430 0 : const double gap = 3.0;
431 0 : const double curve_space = 3.0 * gap;
432 :
433 : /* fetch input data: visible set */
434 0 : const layout_visible_set_t *const layout_data = gui_sketch_card_get_visible_set( card_under_mouse );
435 0 : const uint32_t classifiers = layout_visible_set_get_visible_classifier_count( layout_data );
436 :
437 : /* prepare draw */
438 0 : const GdkRGBA high_color = gui_sketch_style_get_highlight_color( &((*this_).sketch_style) );
439 0 : cairo_set_source_rgba( cr, high_color.red, high_color.green, high_color.blue, high_color.alpha );
440 :
441 : /* to draw x, follow y from top to bottom */
442 0 : if (( grid_kind == GEOMETRY_GRID_KIND_X )||( grid_kind == GEOMETRY_GRID_KIND_XY ))
443 : {
444 : /* order visible set by top y coordinates of classifiers */
445 : universal_array_index_sorter_t sorted;
446 0 : universal_array_index_sorter_init( &sorted );
447 0 : for ( uint32_t idx = 0; idx < classifiers; idx ++ )
448 : {
449 : const layout_visible_classifier_t *const classifier
450 0 : = layout_visible_set_get_visible_classifier_const( layout_data, idx );
451 :
452 0 : const geometry_rectangle_t *const envelope = layout_visible_classifier_get_envelope_box_const( classifier );
453 0 : const int64_t weight = geometry_rectangle_get_top( envelope );
454 0 : const u8_error_t insert_error = universal_array_index_sorter_insert( &sorted, idx, weight );
455 0 : if ( U8_ERROR_NONE != insert_error )
456 : {
457 0 : U8_LOG_WARNING( "not all rectangles are considered for the top-to-bottom line" );
458 : }
459 : }
460 :
461 : /* process rectangles top to bottom */
462 : /* local variables represent 3 points: */
463 : /* current_pos_x / current_pos_y : The position where the already drawn line ends */
464 : /* planned_pos_x / planned_pos_y : The position that would be the next unless the next loop finds an earlier position */
465 : /* draw_to_pos_x / draw_to_pos_y : While drawing, this is a temporary value storing the next current_pos position */
466 0 : double current_pos_x = x;
467 0 : double current_pos_y = top;
468 0 : double planned_pos_x = x;
469 0 : double planned_pos_y = top; /* the planned next y - may be superseded by the next classifier */
470 0 : cairo_move_to( cr, x, top );
471 0 : const uint32_t count_sorted = universal_array_index_sorter_get_count( &sorted );
472 0 : for ( uint32_t idx_of_idx = 0; idx_of_idx < count_sorted; idx_of_idx ++ )
473 : {
474 0 : const uint32_t idx = universal_array_index_sorter_get_array_index( &sorted, idx_of_idx );
475 : const layout_visible_classifier_t *const classifier
476 0 : = layout_visible_set_get_visible_classifier_const( layout_data, idx );
477 :
478 : /* skip classifiers which contain others, these are not positioned according to their order value */
479 0 : const uint32_t children = layout_visible_set_count_descendants( layout_data, classifier );
480 0 : if ( children == 0 )
481 : {
482 0 : const data_visible_classifier_t *const vis_data = layout_visible_classifier_get_data_const( classifier );
483 0 : const data_classifier_t *const c_data = data_visible_classifier_get_classifier_const( vis_data );
484 0 : const int32_t c_x_order = data_classifier_get_x_order( c_data );
485 0 : const geometry_rectangle_t *const envelope = layout_visible_classifier_get_envelope_box_const( classifier );
486 0 : const double envelope_left = geometry_rectangle_get_left( envelope );
487 0 : const double envelope_right = geometry_rectangle_get_right( envelope );
488 0 : const double envelope_top = geometry_rectangle_get_top( envelope );
489 0 : const double envelope_bottom = geometry_rectangle_get_bottom( envelope );
490 : /* if line passes through the classifier, center on symbol, not on envelope */
491 0 : const geometry_rectangle_t *const symbol = layout_visible_classifier_get_symbol_box_const( classifier );
492 0 : const double symbol_center_x = geometry_rectangle_get_center_x( symbol );
493 :
494 0 : const bool pass_through_center_x = ( c_x_order == order_at_x );
495 0 : const bool pass_on_left = ( c_x_order > order_at_x ) && ( envelope_left < x );
496 0 : const bool pass_on_right = ( c_x_order < order_at_x ) && ( envelope_right > x );
497 0 : const bool ignored = !( pass_through_center_x || pass_on_left || pass_on_right );
498 0 : const double draw_to_pos_y = envelope_top - gap;
499 0 : double draw_to_pos_x = x;
500 0 : if ( pass_through_center_x )
501 : {
502 0 : draw_to_pos_x = symbol_center_x;
503 : }
504 0 : if ( pass_on_left )
505 : {
506 0 : draw_to_pos_x = envelope_left - gap;
507 : }
508 0 : if ( pass_on_right )
509 : {
510 0 : draw_to_pos_x = envelope_right + gap;
511 : }
512 0 : if ( ! ignored )
513 : {
514 : /* if planned_pos_x not at x and enough space to draw */
515 : /* 1) a return to x and 2) a comeback to new draw_to_pos_x */
516 0 : if ( (( planned_pos_x < ( x - 0.001 ) )||( planned_pos_x > ( x + 0.001 ) ))
517 0 : && ( planned_pos_y + ( 2 * curve_space ) < draw_to_pos_y ) )
518 : {
519 0 : cairo_line_to( cr, current_pos_x, planned_pos_y );
520 0 : cairo_curve_to( cr,
521 : current_pos_x,
522 : planned_pos_y + curve_space,
523 : x,
524 : planned_pos_y,
525 : x,
526 : planned_pos_y + curve_space
527 : );
528 0 : current_pos_x = x;
529 0 : current_pos_y = planned_pos_y + curve_space;
530 : }
531 :
532 : /* if there is enough space to draw a line before drawing a curve to draw_to_pos_x */
533 0 : if ( ( current_pos_y + curve_space ) < draw_to_pos_y )
534 : {
535 0 : cairo_line_to( cr, current_pos_x, draw_to_pos_y - curve_space );
536 0 : current_pos_y = draw_to_pos_y - curve_space;
537 : }
538 :
539 : /* draw begin of current pass-by */
540 0 : cairo_curve_to( cr,
541 : current_pos_x,
542 : draw_to_pos_y,
543 : draw_to_pos_x,
544 : current_pos_y,
545 : draw_to_pos_x,
546 : draw_to_pos_y
547 : );
548 : /* update variables for next loop */
549 0 : current_pos_x = draw_to_pos_x;
550 0 : current_pos_y = draw_to_pos_y;
551 0 : planned_pos_x = current_pos_x;
552 0 : planned_pos_y = envelope_bottom + gap;
553 : }
554 : }
555 : }
556 : /* draw end of last pass-by (if any) */
557 0 : if ( (( planned_pos_x < ( x - 0.001 ) )||( planned_pos_x > ( x + 0.001 ) ))
558 0 : && ( planned_pos_y + ( 1 * curve_space ) < bottom ) )
559 : {
560 0 : cairo_line_to( cr, current_pos_x, planned_pos_y );
561 0 : cairo_curve_to( cr,
562 : current_pos_x,
563 : planned_pos_y + curve_space,
564 : x,
565 : planned_pos_y,
566 : x,
567 : planned_pos_y + curve_space
568 : );
569 0 : current_pos_x = x;
570 0 : current_pos_y = planned_pos_y + curve_space;
571 : }
572 : /* draw to end point and stroke the drawn path */
573 0 : cairo_line_to( cr, current_pos_x, bottom );
574 0 : if ( ! geometry_non_linear_scale_is_order_on_grid( x_scale, order_at_x ) )
575 : {
576 : double dashes[2];
577 0 : dashes[0] = 2.0 * gap; /* on segment */
578 0 : dashes[1] = 4.0 * gap; /* off segment */
579 0 : cairo_set_dash ( cr, dashes, 2, 0.0 );
580 : }
581 0 : cairo_stroke( cr );
582 0 : cairo_set_dash( cr, NULL, 0, 0.0 );
583 : }
584 :
585 : /* to draw y, follow x from left to right */
586 0 : if (( grid_kind == GEOMETRY_GRID_KIND_Y )||( grid_kind == GEOMETRY_GRID_KIND_XY ))
587 : {
588 : /* order visible set by left x coordinates of classifiers */
589 : universal_array_index_sorter_t sorted;
590 0 : universal_array_index_sorter_init( &sorted );
591 0 : for ( uint32_t idx = 0; idx < classifiers; idx ++ )
592 : {
593 : const layout_visible_classifier_t *const classifier
594 0 : = layout_visible_set_get_visible_classifier_const( layout_data, idx );
595 :
596 0 : const geometry_rectangle_t *const envelope = layout_visible_classifier_get_envelope_box_const( classifier );
597 0 : const int64_t weight = geometry_rectangle_get_left( envelope );
598 0 : const u8_error_t insert_error = universal_array_index_sorter_insert( &sorted, idx, weight );
599 0 : if ( U8_ERROR_NONE != insert_error )
600 : {
601 0 : U8_LOG_WARNING( "not all rectangles are considered for the left-to-right line" );
602 : }
603 : }
604 :
605 : /* process rectangles left to right */
606 : /* local variables represent 3 points: */
607 : /* current_pos_x / current_pos_y : The position where the already drawn line ends */
608 : /* planned_pos_x / planned_pos_y : The position that would be the next unless the next loop finds an earlier position */
609 : /* draw_to_pos_x / draw_to_pos_y : While drawing, this is a temporary value storing the next current_pos position */
610 0 : double current_pos_x = left;
611 0 : double current_pos_y = y;
612 0 : double planned_pos_x = left; /* the planned next x - may be superseded by the next classifier */
613 0 : double planned_pos_y = y;
614 0 : cairo_move_to( cr, left, y );
615 0 : const uint32_t count_sorted = universal_array_index_sorter_get_count( &sorted );
616 0 : for ( uint32_t idx_of_idx = 0; idx_of_idx < count_sorted; idx_of_idx ++ )
617 : {
618 0 : const uint32_t idx = universal_array_index_sorter_get_array_index( &sorted, idx_of_idx );
619 : const layout_visible_classifier_t *const classifier
620 0 : = layout_visible_set_get_visible_classifier_const( layout_data, idx );
621 :
622 : /* skip classifiers which contain others, these are not positioned according to their order value */
623 0 : const uint32_t children = layout_visible_set_count_descendants( layout_data, classifier );
624 0 : if ( children == 0 )
625 : {
626 0 : const data_visible_classifier_t *const vis_data = layout_visible_classifier_get_data_const( classifier );
627 0 : const data_classifier_t *const c_data = data_visible_classifier_get_classifier_const( vis_data );
628 0 : const int32_t c_y_order = data_classifier_get_y_order( c_data );
629 0 : const geometry_rectangle_t *const envelope = layout_visible_classifier_get_envelope_box_const( classifier );
630 0 : const double envelope_top = geometry_rectangle_get_top( envelope );
631 0 : const double envelope_bottom = geometry_rectangle_get_bottom( envelope );
632 0 : const double envelope_left = geometry_rectangle_get_left( envelope );
633 0 : const double envelope_right = geometry_rectangle_get_right( envelope );
634 : /* if line passes through the classifier, center on symbol, not on envelope */
635 0 : const geometry_rectangle_t *const symbol = layout_visible_classifier_get_symbol_box_const( classifier );
636 0 : const double symbol_center_y = geometry_rectangle_get_center_y( symbol );
637 :
638 0 : const bool pass_through_center_y = ( c_y_order == order_at_y );
639 0 : const bool pass_on_top = ( c_y_order > order_at_y ) && ( envelope_top < y );
640 0 : const bool pass_on_bottom = ( c_y_order < order_at_y ) && ( envelope_bottom > y );
641 0 : const bool ignored = !( pass_through_center_y || pass_on_top || pass_on_bottom );
642 0 : const double draw_to_pos_x = envelope_left - gap;
643 0 : double draw_to_pos_y = y;
644 0 : if ( pass_through_center_y )
645 : {
646 0 : draw_to_pos_y = symbol_center_y;
647 : }
648 0 : if ( pass_on_top )
649 : {
650 0 : draw_to_pos_y = envelope_top - gap;
651 : }
652 0 : if ( pass_on_bottom )
653 : {
654 0 : draw_to_pos_y = envelope_bottom + gap;
655 : }
656 0 : if ( ! ignored )
657 : {
658 : /* if planned_pos_y not at y and enough space to draw */
659 : /* 1) a return to y and 2) a comeback to new draw_to_pos_y */
660 0 : if ( (( planned_pos_y < ( y - 0.001 ) )||( planned_pos_y > ( y + 0.001 ) ))
661 0 : && ( planned_pos_x + ( 2 * curve_space ) < draw_to_pos_x ) )
662 : {
663 0 : cairo_line_to( cr, planned_pos_x, current_pos_y );
664 0 : cairo_curve_to( cr,
665 : planned_pos_x + curve_space,
666 : current_pos_y,
667 : planned_pos_x,
668 : y,
669 : planned_pos_x + curve_space,
670 : y
671 : );
672 0 : current_pos_x = planned_pos_x + curve_space;
673 0 : current_pos_y = y;
674 : }
675 :
676 : /* if there is enough space to draw a line before drawing a curve to draw_to_pos_x */
677 0 : if ( ( current_pos_x + curve_space ) < draw_to_pos_x )
678 : {
679 0 : cairo_line_to( cr, draw_to_pos_x - curve_space, current_pos_y );
680 0 : current_pos_x = draw_to_pos_x - curve_space;
681 : }
682 :
683 : /* draw begin of current pass-by */
684 0 : cairo_curve_to( cr,
685 : draw_to_pos_x,
686 : current_pos_y,
687 : current_pos_x,
688 : draw_to_pos_y,
689 : draw_to_pos_x,
690 : draw_to_pos_y
691 : );
692 : /* update variables for next loop */
693 0 : current_pos_x = draw_to_pos_x;
694 0 : current_pos_y = draw_to_pos_y;
695 0 : planned_pos_x = envelope_right + gap;
696 0 : planned_pos_y = current_pos_y;
697 : }
698 : }
699 : }
700 : /* draw end of last pass-by (if any) */
701 0 : if ( (( planned_pos_y < ( y - 0.001 ) )||( planned_pos_y > ( y + 0.001 ) ))
702 0 : && ( planned_pos_x + ( 1 * curve_space ) < right ) )
703 : {
704 0 : cairo_line_to( cr, planned_pos_x, current_pos_y );
705 0 : cairo_curve_to( cr,
706 : planned_pos_x + curve_space,
707 : current_pos_y,
708 : planned_pos_x,
709 : y,
710 : planned_pos_x + curve_space,
711 : y
712 : );
713 0 : current_pos_x = planned_pos_x + curve_space;
714 0 : current_pos_y = y;
715 : }
716 : /* draw to end point and stroke the drawn path */
717 0 : cairo_line_to( cr, right, current_pos_y );
718 0 : if ( ! geometry_non_linear_scale_is_order_on_grid( y_scale, order_at_y ) )
719 : {
720 : double dashes[2];
721 0 : dashes[0] = 2.0 * gap; /* on segment */
722 0 : dashes[1] = 4.0 * gap; /* off segment */
723 0 : cairo_set_dash ( cr, dashes, 2, 0.0 );
724 : }
725 0 : cairo_stroke( cr );
726 0 : cairo_set_dash( cr, NULL, 0, 0.0 );
727 : }
728 :
729 0 : U8_TRACE_END();
730 0 : }
731 :
732 0 : void gui_sketch_card_painter_private_draw_element_space( const gui_sketch_card_painter_t *this_,
733 : const layout_subelement_id_t *subelement,
734 : const layout_visible_set_t *layouted_set,
735 : cairo_t *cr )
736 : {
737 0 : assert( NULL != subelement );
738 0 : assert( NULL != layouted_set );
739 0 : assert( NULL != cr );
740 0 : const layout_diagram_t *const layout_diag = layout_visible_set_get_diagram_const( layouted_set );
741 0 : const double label_border = 2.0; /* additional visible border that just draws a box nicely, without effect for selecting */
742 0 : const double connector_border = (double) gui_sketch_style_get_snap_to_relationship( &((*this_).sketch_style) );
743 :
744 : geometry_rectangle_t highlight;
745 0 : geometry_rectangle_init_empty( &highlight );
746 : data_id_t search_id;
747 0 : data_id_copy( &search_id, data_full_id_get_primary_id_const( layout_subelement_id_get_id_const( subelement ) ) );
748 0 : const layout_subelement_kind_t kind = layout_subelement_id_get_kind( subelement );
749 :
750 : /* check diagram */
751 0 : const data_diagram_t *const diag_data = layout_diagram_get_data_const( layout_diag );
752 0 : const data_id_t diag_id = data_diagram_get_data_id( diag_data );
753 0 : if ( data_id_equals( &search_id, &diag_id ) )
754 : {
755 0 : switch ( kind )
756 : {
757 0 : case LAYOUT_SUBELEMENT_KIND_LABEL:
758 : {
759 0 : geometry_rectangle_replace( &highlight, layout_diagram_get_label_box_const( layout_diag ) );
760 0 : geometry_rectangle_expand_4dir( &highlight, 4.0 /* delta_width */, 4.0 /* delta_height */ );
761 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
762 : }
763 0 : break;
764 :
765 0 : case LAYOUT_SUBELEMENT_KIND_OUTLINE:
766 : {
767 : geometry_rectangle_t empty_space;
768 0 : geometry_rectangle_replace( &highlight, layout_diagram_get_bounds_const( layout_diag ) );
769 0 : geometry_rectangle_copy( &empty_space, layout_diagram_get_draw_area_const( layout_diag ) ); /* no highlight in space */
770 0 : gui_sketch_card_painter_private_draw_border( this_, &highlight, &empty_space, cr );
771 : }
772 0 : break;
773 :
774 0 : default:
775 : case LAYOUT_SUBELEMENT_KIND_SPACE:
776 : {
777 0 : geometry_rectangle_replace( &highlight, layout_diagram_get_draw_area_const( layout_diag ) );
778 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
779 : }
780 0 : break;
781 : }
782 : }
783 :
784 : /* iterate over all classifiers */
785 0 : const uint32_t c_count = layout_visible_set_get_visible_classifier_count( layouted_set );
786 0 : for ( uint32_t c_index = 0; c_index < c_count; c_index ++ )
787 : {
788 : const layout_visible_classifier_t *const visible_classifier
789 0 : = layout_visible_set_get_visible_classifier_const ( layouted_set, c_index );
790 0 : const data_diagramelement_t *diagele = layout_visible_classifier_get_diagramelement_const( visible_classifier );
791 0 : const data_id_t diagele_id = data_diagramelement_get_data_id( diagele );
792 0 : if ( data_id_equals( &search_id, &diagele_id ) )
793 : {
794 0 : switch ( kind )
795 : {
796 0 : case LAYOUT_SUBELEMENT_KIND_LABEL:
797 : {
798 0 : geometry_rectangle_replace( &highlight, layout_visible_classifier_get_label_box_const( visible_classifier ) );
799 0 : geometry_rectangle_expand_4dir( &highlight, 4.0 /* delta_width */, 4.0 /* delta_height */ );
800 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
801 : }
802 0 : break;
803 :
804 0 : case LAYOUT_SUBELEMENT_KIND_OUTLINE:
805 : {
806 : geometry_rectangle_t empty_space;
807 0 : geometry_rectangle_replace( &highlight, layout_visible_classifier_get_symbol_box_const( visible_classifier ) );
808 0 : geometry_rectangle_copy( &empty_space, layout_visible_classifier_get_space_const( visible_classifier ) ); /* no highlight in space */
809 0 : gui_sketch_card_painter_private_draw_border( this_, &highlight, &empty_space, cr );
810 : }
811 0 : break;
812 :
813 0 : default:
814 : case LAYOUT_SUBELEMENT_KIND_SPACE:
815 : {
816 0 : geometry_rectangle_replace( &highlight, layout_visible_classifier_get_space_const( visible_classifier ) );
817 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
818 : }
819 0 : break;
820 : }
821 : }
822 : }
823 :
824 : /* check all contained features */
825 0 : const uint32_t f_count = layout_visible_set_get_feature_count( layouted_set );
826 0 : for ( uint32_t f_index = 0; f_index < f_count; f_index ++ )
827 : {
828 : const layout_feature_t *const the_feature
829 0 : = layout_visible_set_get_feature_const ( layouted_set, f_index );
830 0 : const data_feature_t *feature = layout_feature_get_data_const( the_feature );
831 0 : const data_id_t feature_id = data_feature_get_data_id( feature );
832 0 : if ( data_id_equals( &search_id, &feature_id ) )
833 : {
834 0 : switch ( kind )
835 : {
836 0 : case LAYOUT_SUBELEMENT_KIND_LABEL:
837 : {
838 0 : geometry_rectangle_replace( &highlight, layout_feature_get_label_box_const( the_feature ) );
839 0 : geometry_rectangle_expand_4dir( &highlight, 2.0 * label_border, 2.0 * label_border );
840 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
841 : }
842 0 : break;
843 :
844 0 : default:
845 : case LAYOUT_SUBELEMENT_KIND_OUTLINE:
846 : {
847 0 : geometry_rectangle_replace( &highlight, layout_feature_get_symbol_box_const( the_feature ) );
848 0 : geometry_rectangle_expand_4dir( &highlight, 2.0 * label_border, 2.0 * label_border );
849 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
850 : }
851 0 : break;
852 : }
853 : }
854 : }
855 :
856 0 : const uint32_t count_relations = layout_visible_set_get_relationship_count ( layouted_set );
857 0 : for ( uint32_t rel_index = 0; rel_index < count_relations; rel_index ++ )
858 : {
859 : const layout_relationship_t *const the_relationship
860 0 : = layout_visible_set_get_relationship_const( layouted_set, rel_index );
861 0 : const data_relationship_t *const relationship = layout_relationship_get_data_const( the_relationship );
862 0 : const data_id_t relationship_id = data_relationship_get_data_id( relationship );
863 0 : if ( data_id_equals( &search_id, &relationship_id ) )
864 : {
865 0 : switch ( kind )
866 : {
867 0 : case LAYOUT_SUBELEMENT_KIND_LABEL:
868 : {
869 0 : geometry_rectangle_replace( &highlight, layout_relationship_get_label_box_const( the_relationship ) );
870 0 : geometry_rectangle_expand_4dir( &highlight, 2.0 * label_border, 2.0 * label_border );
871 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
872 : }
873 0 : break;
874 :
875 0 : default:
876 : case LAYOUT_SUBELEMENT_KIND_OUTLINE:
877 : {
878 : const geometry_connector_t *const relationship_shape
879 0 : = layout_relationship_get_shape_const( the_relationship );
880 : {
881 : geometry_rectangle_t segment_src;
882 0 : segment_src = geometry_connector_get_segment_bounds( relationship_shape,
883 : GEOMETRY_CONNECTOR_SEGMENT_SOURCE
884 : );
885 0 : geometry_rectangle_expand_4dir( &segment_src, 2.0 * connector_border, 2.0 * connector_border );
886 0 : gui_sketch_card_painter_private_draw_rect( this_, &segment_src, cr );
887 : }
888 : {
889 : geometry_rectangle_t segment_dst;
890 0 : segment_dst = geometry_connector_get_segment_bounds( relationship_shape,
891 : GEOMETRY_CONNECTOR_SEGMENT_DESTINATION
892 : );
893 0 : geometry_rectangle_expand_4dir( &segment_dst, 2.0 * connector_border, 2.0 * connector_border );
894 0 : gui_sketch_card_painter_private_draw_rect( this_, &segment_dst, cr );
895 : }
896 : {
897 : geometry_rectangle_t segment_main;
898 0 : segment_main = geometry_connector_get_segment_bounds( relationship_shape,
899 : GEOMETRY_CONNECTOR_SEGMENT_MAIN
900 : );
901 0 : geometry_rectangle_expand_4dir( &segment_main, 2.0 * connector_border, 2.0 * connector_border );
902 0 : gui_sketch_card_painter_private_draw_rect( this_, &segment_main, cr );
903 : }
904 : }
905 0 : break;
906 : }
907 : }
908 : }
909 0 : }
910 :
911 0 : void gui_sketch_card_painter_draw_paper( gui_sketch_card_painter_t *this_,
912 : gui_tool_t selected_tool,
913 : const gui_sketch_drag_state_t *drag_state,
914 : const gui_sketch_card_t *card,
915 : cairo_t *cr )
916 : {
917 0 : U8_TRACE_BEGIN();
918 0 : assert( NULL != drag_state );
919 0 : assert( NULL != card );
920 0 : assert( NULL != cr );
921 :
922 0 : if ( gui_sketch_card_is_visible(card) )
923 : {
924 0 : const bool create_tool = ( selected_tool == GUI_TOOL_CREATE );
925 :
926 0 : shape_int_rectangle_t bounds = gui_sketch_card_get_bounds( card );
927 0 : const int32_t left = shape_int_rectangle_get_left( &bounds );
928 0 : const int32_t top = shape_int_rectangle_get_top( &bounds );
929 0 : const uint32_t width = shape_int_rectangle_get_width( &bounds );
930 0 : const uint32_t height = shape_int_rectangle_get_height( &bounds );
931 :
932 : /* draw paper */
933 0 : if ( create_tool )
934 : {
935 : static const double GRAY_R = 0.875;
936 : static const double GRAY_G = 0.875;
937 : static const double GRAY_B = 0.875;
938 : static const double GRAY_A = 1.0;
939 0 : cairo_set_source_rgba( cr, GRAY_R, GRAY_G, GRAY_B, GRAY_A );
940 : }
941 : else
942 : {
943 : static const double WHITE_R = 1.0;
944 : static const double WHITE_G = 1.0;
945 : static const double WHITE_B = 1.0;
946 : static const double WHITE_A = 1.0;
947 0 : cairo_set_source_rgba( cr, WHITE_R, WHITE_G, WHITE_B, WHITE_A );
948 : }
949 0 : cairo_rectangle ( cr, left, top, width, height );
950 0 : cairo_fill (cr);
951 :
952 0 : const layout_visible_set_t *const layout = gui_sketch_card_get_visible_set( card );
953 0 : if ( layout_visible_set_is_valid( layout ) && create_tool )
954 : {
955 0 : const int32_t mouse_x = gui_sketch_drag_state_get_to_x( drag_state );
956 0 : const int32_t mouse_y = gui_sketch_drag_state_get_to_y( drag_state );
957 :
958 : const layout_subelement_id_t subelement_to_highlight
959 0 : = gui_sketch_card_get_element_at_pos( card, mouse_x, mouse_y, !create_tool /* filter_lifelines */ );
960 :
961 0 : gui_sketch_card_painter_private_draw_element_space( this_, &subelement_to_highlight, layout, cr );
962 : }
963 : }
964 :
965 0 : U8_TRACE_END();
966 0 : }
967 :
968 :
969 : /*
970 : Copyright 2017-2025 Andreas Warnke
971 :
972 : Licensed under the Apache License, Version 2.0 (the "License");
973 : you may not use this file except in compliance with the License.
974 : You may obtain a copy of the License at
975 :
976 : http://www.apache.org/licenses/LICENSE-2.0
977 :
978 : Unless required by applicable law or agreed to in writing, software
979 : distributed under the License is distributed on an "AS IS" BASIS,
980 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
981 : See the License for the specific language governing permissions and
982 : limitations under the License.
983 : */
|