/* jlayout - A Java code generator for GUI layout Copyright (c) 2007-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file jlcheck.c The jlcheck module in the jlayout program. */ /** Inside the jlcheck module. */ #define JLCHECK_C 1 #include "jl.h" $(trace-include) /** Ensure that each object has a class and a name assigned. @param j Jlayout job. @return 1 on success, 0 on error. */ static int all_objects_have_classes_and_names DK_P1(JLJ *,j) { int back = 1; JLO *o; dksto_it_reset(j->o_it); while((o = (JLO *)dksto_it_next(j->o_it)) != NULL) { if(!(o->on)) { back = 0; j->errlineno = o->l_creation; jlmsg_log1(j, DK_LOG_LEVEL_ERROR, 2); } else { if(!jlo_get_classname(o)) { $? "! no class name (2)" back = 0; j->errlineno = o->l_creation; if(o->on) { jlmsg_log3(j, DK_LOG_LEVEL_ERROR, 3, o->on, 4); } else { jlmsg_log1(j, DK_LOG_LEVEL_ERROR, 5); } } } } return back; } /** Enumerate objects (assign a unique number to each object). @param j Jlayout job. */ static void enumerate_objects DK_P1(JLJ *,j) { JLO *jlo; $? "+ enumerate_objects" (j->o_main)->obj_no = 0UL; j->max_obj_no = 0UL; dksto_it_reset(j->o_it); while((jlo = (JLO *)dksto_it_next(j->o_it)) != NULL) { j->max_obj_no += 1UL; jlo->obj_no = j->max_obj_no; $? ". enumerate %lu %s", j->max_obj_no, TR_STR(jlo->on) } $? "- enumerate_objects" } /** Set bit matrix entries for one objects dependencies. @param j Jlayout job. @param o Object to process. @param bm Bit matrix. */ static void create_entries_for_object DK_P3(JLJ *,j, JLO *,o, dk_bitmatrix_t *,bm) { JCN *jcn; JLO *jlo; $? "+ create_entries_for_object \"%s\"", TR_STR(o->on) if(o->menubar) { dkbf_matrix_set(bm, o->obj_no, (o->menubar)->obj_no, 1); } if((o->c_st) && (o->c_it)) { dksto_it_reset(o->c_it); while((jcn = (JCN *)dksto_it_next(o->c_it)) != NULL) { if((jcn->o) && (jcn->t == JCN_OBJECT)) { jlo = jcn->o; dkbf_matrix_set(bm, o->obj_no, jlo->obj_no, 1); $? ". dependency \"%s\" -> \"%s\"", TR_STR(jlo->on), TR_STR(o->on) $? ". dependency %lu -> %lu", jlo->obj_no, o->obj_no } } } $? "- create_entries_for_object" } /** Find levels for objects. @param jlj Jlayout job. @param l Buffer for level numbers. @param sz Number of entries in \a l. @param bm Bit matrix containig object dependencies. @return 1 on success, 0 on error. */ static int find_levels DK_P4(JLJ *,jlj, unsigned long *,l, size_t, sz, dk_bitmatrix_t *,bm) { int back = 1, cc = 0, can_set_level = 0; size_t i, j; unsigned long passno, *ulptr; $? "+ find_levels" for(i = 0; i < sz; i++) { if(dkbf_matrix_get(bm, i, i)) { back = 0; $? "! circular dependencies %lu", (unsigned long)i /* ERROR: Circular dependencies */ jlmsg_log1(jlj, DK_LOG_LEVEL_ERROR, 14); } } if(back) { // initialize array ulptr = l; for(i = 0; i < sz; i++) { *(ulptr++) = 0UL; } // multiple passes to assign a level cc = 1; passno = 1UL; while(cc) { $? ". pass %lu", passno cc = 0; for(i = 0; i < sz; i++) { $? ". check i=%lu", (unsigned long)i if(l[i] == 0UL) { can_set_level = 1; for(j = 0; j < sz; j++) { if(i != j) { $? ". check j=%lu", (unsigned long)j if(dkbf_matrix_get(bm, i, j)) { if(l[j] == 0UL) { $? ". j not yet evaluated" can_set_level = 0; } } } } if(can_set_level) { l[i] = passno; $? ". level i=%lu: %lu", (unsigned long)i, passno jlj->max_level = passno; } else { cc = 1; $? ". another pass necessary" } } } // count passes, we must not have more passes than objects passno++; if(passno >= (unsigned long)sz) { if(cc) { $? "! emergency stop" cc = 0; back = 0; /* Emergency stop, internal error */ jlmsg_log1(jlj, DK_LOG_LEVEL_ERROR, 15); } } } } $? "- find_levels" return back; } /** Write object dependencies into bit matrix. @param j Jlayout job. @param bm Bit matrix. */ static void create_bit_matrix_entries DK_P2(JLJ *,j, dk_bitmatrix_t *,bm) { JLO *jlo; $? "+ create_bit_matrix_entries" create_entries_for_object(j, j->o_main, bm); dksto_it_reset(j->o_it); while((jlo = (JLO *)dksto_it_next(j->o_it)) != NULL) { create_entries_for_object(j, jlo, bm); } $? "- create_bit_matrix_entries" } $!trace-code static $!trace-code void $!trace-code show_dependencies DK_P2(dk_bitmatrix_t *,bm, size_t,sz) $!trace-code { $!trace-code size_t quelle, ziel; $!trace-code if(dktrace_file()) { $!trace-code fprintf(dktrace_file(), ". START DEPENDENCIES\n"); $!trace-code for(ziel = 0; ziel < sz; ziel++) { $!trace-code for(quelle = 0; quelle < sz; quelle++) { $!trace-code if(dkbf_matrix_get(bm, ziel, quelle)) { $!trace-code fprintf( $!trace-code dktrace_file(), $!trace-code ". DEPENDENCY %lu -> %lu\n", $!trace-code (unsigned long)quelle, (unsigned long)ziel $!trace-code ); $!trace-code } $!trace-code } $!trace-code } $!trace-code fprintf(dktrace_file(), ". END DEPENDENCIES\n"); $!trace-code fflush(dktrace_file()); $!trace-code } $!trace-code } /** Levelize objects to find order of construction. @param j Jlayout job. @return 1 on success, 0 on error. */ static int levelize_objects DK_P1(JLJ *,j) { int back = 0; unsigned long number_of_objects; size_t sz; unsigned long *levels = NULL; dk_bitmatrix_t *bm = NULL; JLO *jlo; $? "+ levelize_objects" number_of_objects = j->max_obj_no; number_of_objects++; sz = (size_t)number_of_objects; if((unsigned long)sz == number_of_objects) { levels = dk_new(unsigned long,sz); if(levels) { bm = dkbf_matrix_open(sz, sz); if(bm) { $? ". dynamic memory allocated" create_bit_matrix_entries(j, bm); $!trace-code show_dependencies(bm, sz); dkbf_matrix_expand(bm); $!trace-code show_dependencies(bm, sz); back = find_levels(j, levels, sz, bm); (j->o_main)->obj_lvl = levels[0]; dksto_it_reset(j->o_it); while((jlo = (JLO *)dksto_it_next(j->o_it)) != NULL) { jlo->obj_lvl = levels[jlo->obj_no]; } dkbf_matrix_close(bm); } else { jlmsg_error_memory(j); } dk_delete(levels); } else { jlmsg_error_memory(j); } } else { /* ERROR: Too many objects */ jlmsg_log1(j, DK_LOG_LEVEL_ERROR, 17); } $? "- levelize_objects %d", back return back; } /** Check gridbag layout for one object. @param j Jlayout job. @param jlo Object to check. @return 1 on success, 0 on error. */ static int do_gbl_check DK_P2(JLJ *,j, JLO *,jlo) { int back = 1; JCN *jcn; JLO *o; long x, y, bx, by, l; size_t sx, sy; long *tx = NULL, *ty = NULL; dk_bitfield_t *bfx1 = NULL, *bfy1 = NULL; /* objects start in line/row */ dk_bitfield_t *bfx2 = NULL, *bfy2 = NULL; /* objects end in line/row */ $? "+ do_gbl_check %s", TR_STR(jlo->on) jlo->grid_layout_r = jlo->grid_layout_c = 0L; jlo->max_x = jlo->max_y = 0L; /* pass 1: find number of rows and columns */ dksto_it_reset(jlo->c_it); while((jcn = (JCN *)dksto_it_next(jlo->c_it)) != NULL) { switch(jcn->t) { case JCN_OBJECT: { if(jcn->o) { o = jcn->o; x = o->pos_x + (o->pos_w - 1L); y = o->pos_y + (o->pos_h - 1L); if(x > jlo->max_x) { jlo->max_x = x; } if(y > jlo->max_y) { jlo->max_y = y; } } } break; default: { /* Error: Dont know how to add glue or separator */ } break; } } $? ". max_x=%ld max_y=%ld", jlo->max_x, jlo->max_y bx = jlo->max_x + 2L; by = jlo->max_y + 2L; $? ". bx=%ld by=%ld", bx, by sx = (size_t)bx; sy = (size_t)by; if(((long)sx == bx) && ((long)sy == by)) { jlo->distance_x = dkbf_open(bx); jlo->distance_y = dkbf_open(by); if((jlo->distance_x) && (jlo->distance_y)) { bfx1 = dkbf_open(bx); bfy1 = dkbf_open(by); bfx2 = dkbf_open(bx); bfy2 = dkbf_open(by); tx = dk_new(long,bx); ty = dk_new(long,by); if((bfx1) && (bfy1) && (bfx2) && (bfy2) && (tx) && (ty)) { dksto_it_reset(jlo->c_it); while((jcn = (JCN *)dksto_it_next(jlo->c_it)) != NULL) { switch(jcn->t) { case JCN_OBJECT: { if(jcn->o) { o = jcn->o; dkbf_set(bfx1, o->pos_x, 1); dkbf_set(bfy1, o->pos_y, 1); x = o->pos_x + o->pos_w; y = o->pos_y + o->pos_h; dkbf_set(bfx2, x, 1); dkbf_set(bfy2, y, 1); } } break; } } for(x = 0L; x < bx; x++) { if(dkbf_get(bfx1, x)) { if(dkbf_get(bfx2, x)) { dkbf_set(jlo->distance_x, x, 1); $? ". add dummy column before column %ld", x } } } dkbf_set(jlo->distance_x, 0, 1); dkbf_set(jlo->distance_x, (bx - 1L), 1); for(y = 0L; y < by; y++) { if(dkbf_get(bfy1, y)) { if(dkbf_get(bfy2, y)) { dkbf_set(jlo->distance_y, y, 1); $? ". add dummy row before row %ld", y } } } dkbf_set(jlo->distance_y, 0, 1); dkbf_set(jlo->distance_y, (by - 1L), 1); for(x = 0L; x < bx; x++) { tx[x] = x; } for(y = 0L; y < by; y++) { ty[y] = y; } for(x = 0L; x < bx; x++) { if(dkbf_get(jlo->distance_x, x)) { for(l = x; l < bx; l++) { tx[l] += 1L; } } } for(y = 0L; y < by; y++) { if(dkbf_get(jlo->distance_y, y)) { for(l = y; l < by; l++) { ty[l] += 1L; } } } $? ". transformations" $!trace-code for(x = 0L; x < bx; x++) { $!trace-code if(dktrace_file()) fprintf(dktrace_file(), ". x %ld -> %ld\n", x, tx[x]); $!trace-code } $!trace-code for(y = 0L; y < by; y++) { $!trace-code if(dktrace_file()) fprintf(dktrace_file(), ". y %ld -> %ld\n", y, ty[y]); $!trace-code } dksto_it_reset(jlo->c_it); while((jcn = (JCN *)dksto_it_next(jlo->c_it)) != NULL) { switch(jcn->t) { case JCN_OBJECT: { if(jcn->o) { o = jcn->o; o->cor_x = tx[o->pos_x]; o->cor_y = ty[o->pos_y]; x = tx[o->pos_x + (o->pos_w - 1L)]; y = ty[o->pos_y + (o->pos_h - 1L)]; if(x > jlo->cor_mx) { jlo->cor_mx = x; } if(y > jlo->cor_my) { jlo->cor_my = y; } o->cor_w = 1L + (x - o->cor_x); o->cor_h = 1L + (y - o->cor_y); $? ". pos x=%ld y=%ld w=%ld h=%ld", o->pos_x, o->pos_y, o->pos_w, o->pos_h $? ". cor x=%ld y=%ld w=%ld h=%ld", o->cor_x, o->cor_y, o->cor_w, o->cor_h } } break; } } $? ". cor_mx=%ld cor_my=%ld", jlo->cor_mx, jlo->cor_my } else { back = 0; /* ERROR: Memory */ jlmsg_error_memory(j); } if(bfx1) { dkbf_close(bfx1); bfx1 = NULL; } if(bfy1) { dkbf_close(bfy1); bfy1 = NULL; } if(bfx2) { dkbf_close(bfx2); bfx2 = NULL; } if(bfy2) { dkbf_close(bfy2); bfy2 = NULL; } if(tx) { dk_delete(tx); tx = NULL; } if(ty) { dk_delete(ty); ty = NULL; } } else { back = 0; /* ERROR: Memory */ jlmsg_error_memory(j); } } else { $? "! numeric signed/unsigned overflow" back = 0; /* ERROR: Too many rows or columns */ jlmsg_log1(j, DK_LOG_LEVEL_ERROR, 54); } $? "- do_gbl_check %d", back return back; } /** Check gridbag layout for one object if the object uses gridbag layout. @param j Jlayout job. @param jlo Object to check. @return 1 on success, 0 on error. */ static int gbl_check DK_P2(JLJ *,j, JLO *,jlo) { int back = 1; if(jlo->layout == LAYOUT_GRIDBAG) { back = do_gbl_check(j, jlo); } return back; } /** Check all gridbag layouts. @param j Jlayout job. @return 1 on success, 0 on error. */ static int check_gridbag_layouts DK_P1(JLJ *,j) { int back = 0; JLO *jlo; if(gbl_check(j, j->o_main)) { back = 1; dksto_it_reset(j->o_it); while((jlo = (JLO *)dksto_it_next(j->o_it)) != NULL) { if(!gbl_check(j, jlo)) { back = 0; } } } return back; } /** Check input for consistency. @param j Jlayout job. @return 1 on success, 0 on error. */ int jlcheck DK_P1(JLJ *,j) { int back = 0; char *olderrfilename; $? "+ jlcheck" olderrfilename = j->errfilename; j->errfilename = j->ifname; if(all_objects_have_classes_and_names(j)) { enumerate_objects(j); if(levelize_objects(j)) { if(check_gridbag_layouts(j)) { back = 1; } } } j->errfilename = olderrfilename; $? "- jlcheck %d", back return back; }