Example #1
0
void
move_curitem(int direction)
{
        list_item tmp;

        if(curitem < 0 || curitem > last_item())
                return;

	tmp = item_create();
	item_copy(tmp, db_item_get(curitem));

	switch(direction) {
		case MOVE_ITEM_UP:
			if( curitem < 1 )
				goto out_move;
			item_copy(db_item_get(curitem),
					db_item_get(curitem - 1));
			item_copy(db_item_get(curitem-1), tmp);
			scroll_up();
			break;

		case MOVE_ITEM_DOWN:
			if(curitem >= last_item())
				goto out_move;
			item_copy(db_item_get(curitem),
					db_item_get(curitem + 1));
			item_copy(db_item_get(curitem + 1), tmp);
			scroll_down();
			break;
	}

out_move:
	item_free(&tmp);
}
Example #2
0
void
page_down()
{
	if(curitem > db_n_items() - 2)
		return;

	if(curitem == LAST_LIST_ITEM) {
		if((curitem += LIST_LINES) > last_item())
			curitem = last_item();
	} else {
		curitem = min(LAST_LIST_ITEM, last_item());
	}

	refresh_list();
}
Example #3
0
bool
edit_interface_rep::complete_try () {
  tree st= subtree (et, path_up (tp));
  if (is_compound (st)) return false;
  string s= st->label, ss;
  int end= last_item (tp);
  array<string> a;
  if (inside (LABEL) || inside (REFERENCE) || inside (PAGEREF)) {
    if (end != N(s)) return false;
    ss= copy (s);
    tree t= get_labels ();
    int i, n= N(t);
    for (i=0; i<n; i++)
      if (is_atomic (t[i]) && starts (t[i]->label, s))
	a << string (t[i]->label (N(s), N(t[i]->label)));
  }
  else {
    if ((end==0) || (!is_iso_alpha (s[end-1])) ||
	((end!=N(s)) && is_iso_alpha (s[end]))) return false;
    int start= end-1;
    while ((start>0) && is_iso_alpha (s[start-1])) start--;
    ss= s (start, end);
    a= find_completions (drd, et, ss);
  }
  if (N(a) == 0) return false;
  complete_start (ss, a);
  return true;
}
Example #4
0
bool
edit_interface_rep::complete_keypress (string key) {
  set_message ("", "");
  if (key == "space") key= " ";
  if ((key != "tab") && (key != "S-tab")) {
    set_input_normal (); return false; }
  tree st= subtree (et, path_up (tp));
  if (is_compound (st)) {
    set_input_normal (); return false; }
  string s= st->label;
  int end= last_item (tp);
  string old_s= completions [completion_pos];
  string test= completion_prefix * old_s;
  if ((end<N(test)) || (s (end-N(test), end) != test)) {
    set_input_normal (); return false; }

  if (key == "tab") completion_pos++;
  else completion_pos--;
  if (completion_pos < 0) completion_pos= N(completions)-1;
  if (completion_pos >= N(completions)) completion_pos= 0;
  string new_s= completions [completion_pos];
  remove (path_up (tp) * (end-N(old_s)), N(old_s));
  insert (path_up (tp) * (end-N(old_s)), new_s);
  complete_message ();
  return true;
}
Example #5
0
void
edit_interface_rep::complete_start (string prefix, array<string> compls) {
  // check consistency
  tree st= subtree (et, path_up (tp));
  if (is_compound (st)) return;
  string s= st->label;
  int end= last_item (tp);
  if ((end<N(prefix)) || (s (end-N(prefix), end) != prefix)) return;

  // perform first completion and switch to completion mode if necessary
  if (N (compls) == 1) {
    string s= compls[0];
    if (ends (s, "()")) // temporary fix for Pari
      insert_tree (s, path (N(s)-1));
    else insert_tree (s);
    completions= array<string> ();
  }
  else {
    completion_prefix= prefix;
    completions      = close_completions (compls);
    completion_pos   = 0;
    insert_tree (completions[0]);
    complete_message ();
    beep ();
    set_input_mode (INPUT_COMPLETE);
  }
}
Example #6
0
void
goto_end()
{
	if(db_n_items() > 0)
		curitem = last_item();

	refresh_list();
}
Example #7
0
void
edit_select_rep::select_enlarge_text () {
  path p= common (start_p, end_p);
  if (start_p == end_p) p= path_up (p);
  tree st= subtree (et, p);
  ASSERT (is_atomic (st), "non textual tree");
  string s= st->label;
  string mode= get_env_string (MODE);
  int i1= last_item (start_p), j1= i1;
  int i2= last_item (end_p), j2= i2;
  path q= path_up (p);

  if (mode == "text" || mode == "src") {
    int i, f= 4;
    if (i1 > 0) {
      i= i1; tm_char_backwards (s, i);
      f= min (f, breaking_force (s[i]));
    }
    if (i2 < N(s))
      f= min (f, breaking_force (s[i2]));

    while (i1 > 0) {
      i= i1; tm_char_backwards (s, i);
      if (breaking_force (s[i]) > f) break;
      i1= i;
    }
    while (i2 < N(s)) {
      if (breaking_force (s[i2]) > f) break;
      tm_char_forwards (s, i2);
    }

    if (i1 < i2 && (i1 != j1 || i2 != j2)) {
      if (is_concat (subtree (et, q)) && i1 == 0 && i2 == N(s))
	select (q * 0, q * 1);
      else select (p * i1, p * i2);
      return;
    }
  }

  if (is_concat (subtree (et, q)) || (i1 == 0 && i2 == N(s)))
    select (q * 0, q * 1);
  else select (p * 0, p * N(s));
}
Example #8
0
path
box_rep::find_box_path (path p, bool& found) {
  // cout << "Find box path " << box (this) << ", " << p
  //      << "; " << reverse (ip)
  //      << ", " << reverse (find_lip ())
  //      << " -- " << reverse (find_rip ()) << "\n";
  found= (!is_nil(p)) && is_accessible (ip);
  if (last_item (p) == 0) return path (0);
  else return path (1);
}
Example #9
0
void
notify_insert_node (typesetter ttt, path p, tree t) {
  // cout << "Insert node " << p << ", " << t << "\n";
  int i, pos= last_item (p), n= N(t);
  tree r (t, n+1);
  for (i=0; i<pos; i++) r[i]= t[i];
  r[pos]= subtree (ttt->br->st, path_up (p));
  for (i=pos; i<n; i++) r[i+1]= t[i];
  if (is_nil (path_up (p))) ttt->br= make_bridge (ttt, r, ttt->br->ip);
  else ttt->br->notify_assign (path_up (p), r);
}
Example #10
0
bool
admissible_selection (gr_selection sel) {
  if (sel->type != "box" || N(sel->cp) != 1) return true;
  if (last_item (sel->cp[0]) < 0 || N(sel->cp[0]) <= 2) return true;
  path p= path_up (sel->cp[0]);
  if (!has_subtree (the_et, p)) return true;
  tree st= subtree (the_et, p);
  if (is_compound (st, "anim-edit")) return false;
  tree pt= subtree (the_et, path_up (p));
  if (is_func (st, WITH) && is_compound (pt, "anim-edit")) return false;
  return true;
}
Example #11
0
path
text_box_rep::find_box_path (path p, bool& found) {
  // cout << "Find box path " << box (this) << ", " << p
  //      << "; " << reverse (ip)
  //      << ", " << reverse (find_lip ())
  //      << " -- " << reverse (find_rip ()) << "\n";
  found= (!is_nil(p)) && is_accessible (ip);
  if (found) {
    int i= last_item (p) - pos;
    if (i < 0) return path (0);
    else if (i > N(str)) return N(str);
    else return path (i);
  }
  else return path (0);
}
Example #12
0
tree
edit_interface_rep::compute_text_footer (tree st) {
  string r;
  language lan= get_env_language ();
  int end  = last_item (tp);
  int start= end;
  tm_char_backwards (st->label, start);
  r= st->label (start, end);
  if (r == "") r= "start";
  if (r == " ") r= "space";
  if (r == "space" && get_env_string (MODE) == "math") r= "apply";
  if (starts (r, "<") && !starts (r, "<#"))
    if (cork_to_utf8 (r) != r)
      r= r * " (" * r(1, N(r)-1) * ")";
  return r;
}
Example #13
0
void
edit_graphics_rep::back_in_text_at (tree t, path p, bool forward) {
  (void) forward;
  int i= last_item (p);
  if ((i == 0) && is_empty (t[0])) {
    p= path_up (p);
    if (is_func (subtree (et, path_up (p)), WITH)) p= path_up (p);
    tree st= subtree (et, path_up (p));
    if (is_func (st, GRAPHICS)) {
      if (N(st) == 1) assign (p, "");
      else {
        remove (p, 1);
        go_to_border (path_up (p) * 0, true);
      }
    }
  }
}
Example #14
0
/* Compress the custkey in orders. */
bool pageManage::compressCustkey() {
  /* External sort. */
  if (!externalSort())
    return false;

  /* Open the relevant files. */
  FILE* cfileAfterSort = fopen(PATH_OF_CUSTKEY_AFTER_SORT, "rb"),
  *cfileAfterCompress = fopen(PATH_OF_CUSTKEY_AFTER_COMPRESS, "wb");
  if (!cfileAfterSort || !cfileAfterCompress)
    return false;
  int ckeyAfterSort[MAX_ITEM], count = 0, length = 1, pre_ckey = -1;
  item ckeyAfterCompress[MAX_ITEM];

  /* Compress custkey sort. */
  while(!feof(cfileAfterSort)) {
    int num = fread(ckeyAfterSort, sizeof(int), MAX_ITEM, cfileAfterSort);
    for (int i = 0; i < num; ++i) {
      if (ckeyAfterSort[i] == pre_ckey) {
        ++length;
      } else if (pre_ckey != -1) {
        if (count == MAX_ITEM) {
          fwrite(ckeyAfterCompress, sizeof(item), count, cfileAfterCompress);
          count = 0;
        }
        ckeyAfterCompress[count++] = item(pre_ckey, length);
        length = 1;
      }
      pre_ckey = ckeyAfterSort[i];
    }
  }
  if (count != 0)
    fwrite(ckeyAfterCompress, sizeof(item), count, cfileAfterCompress);
  item last_item(pre_ckey, length);
  fwrite(&last_item, sizeof(item), 1, cfileAfterCompress);

  /* Close the relevant files and remove the temporary files. */
  fclose(cfileAfterSort);
  fclose(cfileAfterCompress);
  remove(PATH_OF_CUSTKEY_AFTER_SORT);
  return true;
}
Example #15
0
path
edit_select_rep::focus_search (path p, bool skip_flag, bool up_flag) {
  //cout << "Search focus " << p << ", " << skip_flag << ", " << up_flag << "\n";
  if (!(rp < p)) return rp;
  tree st= subtree (et, p);
  if (!skip_flag) return p;
  if (none_accessible (st) && p == path_up (tp) && last_item (tp) != 0)
    return p;
  if (is_atomic (st) ||
      is_func (st, DOCUMENT) ||
      is_func (st, CONCAT) ||
      is_func (st, TFORMAT) ||
      is_func (st, TABLE) ||
      is_func (st, ROW) ||
      is_func (st, CELL) ||
      is_compound (st, "shown") ||
      is_func (st, HIDDEN) ||
      up_flag)
    return focus_search (path_up (p), skip_flag, false);
  return p;
}
Example #16
0
int
duplicate_item()
{
	list_item item;
	
	if(curitem < 0)
		return 1;

	item = item_create();
	item_duplicate(item, db_item_get(curitem));
	if(add_item2database(item)) {
		item_free(&item);
		return 1;
	}
	item_free(&item);

	curitem = last_item();
	refresh_list();

	return 0;
}
int main(void) 
{
  link *t, *head;
  struct node *d = NULL;
  int max, not_last = 1;

  t = new_list();
  print_list(t);
  head = t;
  max = t->item;
  t = t->next;
  while (t != NULL)
    {
      if (t->item > max)
	max = t->item;
      t = t->next;
    }
  t = head;
  if (max != last_item(t))
    {
      while (t->next != NULL)
	{
	  if (t->next->item == max)
	    {
	      d = t->next;
	      not_last = 0;
	      t->next = d->next;
	      d->next = NULL;
	    }
	  t = t->next;
	}
      t->next = d;
    }
  print_list(head);
  return 0;
}
Example #18
0
tree
edit_interface_rep::compute_compound_footer (tree t, path p) {
  if (!(rp < p)) return "";
  tree up= compute_compound_footer (t, path_up (p));
  if (N(focus_get (false))+1 < N(p)) return up;
  tree st= subtree (t, path_up (p));
  int  l = last_item (p);
  switch (L (st)) {
  case DOCUMENT:
  case PARA:
    return up;
  case SURROUND:
    if (l == 0) return concat (up, "left surrounding ");
    if (l == 1) return concat (up, "right surrounding ");
    return up;
  case CONCAT:
    return up;
  case MOVE:
    if (l == 0) return concat (up, "move ");
    else return up;
  case SHIFT:
    if (l==0) return concat (up, "shift ");
    else return up;
  case RESIZE:
    if (l==0) return concat (up, "resize ");
    else return up;
  case CLIPPED:
    if (l==0) return concat (up, "clipped ");
    else return up;
  case _FLOAT:
    if (N(st) >= 1 && is_atomic (st[0]))
      return concat (up, st[0]->label * " ");
    else return concat (up, "float ");
  case LONG_ARROW:
    if (l == 1) return concat (up, "above ");
    else if (l == 2) return concat (up, "below ");
    else return up;
  case BELOW:
    if (l==0) return concat (up, "body ");
    else return concat (up, "script below ");
  case ABOVE:
    if (l==0) return concat (up, "body ");
    else return concat (up, "script above ");
  case FRAC:
    if (l==0) return concat (up, "numerator ");
    else return concat (up, "denominator ");
  case SQRT:
    if (N(st)==1) return concat (up, "square root ");
    if (l==0) return concat (up, "root ");
    else return concat (up, "index ");
  case WIDE:
    if (N(st) >= 1)
      return concat (up, get_accent_type (as_string (st[1])) * " ");
    else
      return concat (up, "wide ");
  case VAR_WIDE:
    if (N(st) >= 1)
      return concat (up, "under " * get_accent_type (as_string (st[1])) * " ");
    else
      return concat (up, "var-wide ");
  case TREE:
    if (l==0) return concat (up, "root ");
    else return concat (up, "branch(" * as_string (l) * ") ");
  case TFORMAT:
    return up;
  case TABLE:
    return concat (up, "(" * as_string (l+1) * ",");
  case ROW:
    return concat (up, as_string (l+1) * ") ");
  case CELL:
    return up;
  case WITH:
    return concat (up, get_with_text (st), " ");
  case DRD_PROPS:
    if (l == 0)
      return concat (up, "drd property(variable) ");
    if ((l&1) == 1)
      return concat (up, "drd property(" * as_string (l/2+1) * ") ");
    return concat (up, "value(" * as_string (l/2) * ") ");
  case COMPOUND:
    if (N(st) >= 1 && is_atomic (st[0]))
      return concat (up, as_string (st[0]) * " ");
    else
      return concat (up, "compound ");
  case HLINK:
    if (N(st) >= 2)
      return concat (up, "hyperlink(" * as_string (st[1]) * ") ");
    else
      return concat (up, "hyperlink ");
  case TUPLE:
    return concat (up, "tuple(" * as_string (l+1) * ") ");
  case ATTR:
    if ((l&1) == 0)
      return concat (up, "variable(" * as_string (l/2+1) * ") ");
    else
      return concat (up, "value(" * as_string (l/2+1) * ") ");
  case SPECIFIC:
    return concat (up, "texmacs ");
  case ANIM_CONSTANT:
    if (l == 0)
      return concat (up, "anim-constant ");
    else return up;
  case ANIM_TRANSLATE:
    if (l == 0)
      return concat (up, "anim-translate ");
    else return up;
  case ANIM_PROGRESSIVE:
    if (l == 0)
      return concat (up, "anim-progressive ");
    else return up;
  default:
    return concat (up, drd->get_name (L(st)) * " ");
  }
}
Example #19
0
tree
edit_interface_rep::compute_operation_footer (tree st) {
  tree r= "";
  if (N(st) >= 2) {
    switch (L (st)) {
    case VAR_WIDE:
      r= concat ("under ", get_accent_type (as_string (st[1]))); break;
    default: ;
    }
  }
  if (r == "" && N(st) >= 1) {
    switch (L (st)) {
    case HSPACE:
      r= concat ("space"); break;
    case VAR_VSPACE:
      r= concat ("vertical space before");
      break;
    case VSPACE:
      r= concat ("vertical space"); break;
    case SPACE:
      r= concat ("space"); break;
    case _FLOAT:
      r= (is_atomic (st[0])? st[0]->label: string ("float")); break;
    case MID:
      r= concat ("middle ", as_symbol (st[0])); break;
    case RIGHT:
      r= concat ("close ", as_symbol (st[0])); break;
    case BIG:
      r= concat ("big ", as_symbol (st[0])); break;
    case LPRIME:
      r= concat ("left prime ", as_string (st[0])); break;
    case LONG_ARROW:
      r= concat ("long arrow ", as_string (st[0])); break;
    case RPRIME:
      r= concat ("prime ", as_string (st[0])); break;
    case SQRT:
      r= tree ((char*) ((N(st)==1)? "square root": "n-th root")); break;
    case WIDE:
      r= tree (get_accent_type (as_string (st[1]))); break;
    case ASSIGN:
      r= concat ("assign ", as_string (st[0])); break;
    case WITH:
      r= concat ("with ", get_with_text (st)); break;
    case PROVIDES:
      r= concat ("provides ", as_string (st[0])); break;
    case VALUE:
      r= concat ("value ", as_string (st[0])); break;
    case QUOTE_VALUE:
      r= concat ("quoted value ", as_string (st[0])); break;
    case ARG:
      r= concat ("argument ", as_string (st[0])); break;
    case QUOTE_ARG:
      r= concat ("quoted argument ", as_string (st[0])); break;
    case COMPOUND:
      if (is_atomic (st[0])) r= as_string (st[0]);
      else r= "compound";
      break;
    case VAR_INCLUDE:
    case INCLUDE:
      r= concat ("include ", as_string (st[0])); break;
    case INACTIVE:
      r= concat ("inactive ", drd->get_name (L(st[0]))); break;
    case VAR_INACTIVE:
      r= concat ("inactive ", drd->get_name (L(st[0]))); break;
    case LABEL:
      r= concat ("label: ", as_string (st[0])); break;
    case REFERENCE:
      r= concat ("reference: ", as_string (st[0])); break;
    case PAGEREF:
      r= concat ("page reference: ", as_string (st[0])); break;
    case GET_ATTACHMENT:
      r= concat ("get attachment: ", as_string (st[0])); break;
    case WRITE:
      r= concat ("write to ", as_string (st[0])); break;
    case TOC_NOTIFY:
      r= concat ("toc notify: ", as_string (st[1])); break;
    case SPECIFIC:
      r= concat ("specific ", as_string (st[0])); break;
    case IMAGE:
      r= concat ("image"); break;
    default: ;
    }
  }
  if (r == "") {
    switch (L (st)) {
    case IMAGE: r= "image"; break;
    default: r= drd->get_name (L(st));
    }
  }
  if (last_item (tp) == 0) r= concat ("before ", r);
  return r;
}
Example #20
0
static int handle_input(int ch)
{
	switch (ch) 
	{
		case 'q':
			quit_mode = quit_mode ? 0 : 1;
			return 1;

		case 0x1b:
			quit_mode = 0;
			print_help = 0;
			return 1;

		case 'y':
			if (quit_mode)
				exit(0);
			break;

		case 'a':
			next_attr();
			return 1;

		case 'n':
			if (quit_mode)
				quit_mode = 0;
			else
				new_graph();
			return 1;

		case 'x':
			del_graph();
			return 1;

		case 'f':
			fold();
			return 1;

		case 12:
		case KEY_CLEAR:
#ifdef HAVE_REDRAWWIN
			redrawwin(stdscr);
#endif
			clear();
			return 1;

		case 'c':
			c_combined_node_list = c_combined_node_list ? 0 : 1;
			return 1;

		case 'S':
			clear();
			set_graph_unit(X_SEC);
			return 1;

		case 'M':
			clear();
			set_graph_unit(X_MIN);
			return 1;

		case 'H':
			clear();
			set_graph_unit(X_HOUR);
			return 1;

		case 'D':
			clear();
			set_graph_unit(X_DAY);
			return 1;

		case 'R':
			clear();
			set_graph_unit(X_READ);
			return 1;

			case '?':
			clear();
			print_help = print_help ? 0 : 1;
			return 1;

		case 'g':
			c_graphical_in_list = c_graphical_in_list ? 0 : 1;
			return 1;

		case 'd':
			c_detailed_in_list = c_detailed_in_list ? 0 : 1;
			return 1;

		case 'l':
			c_list_in_list = c_list_in_list ? 0 : 1;
			return 1;

		case KEY_PPAGE:
			if (print_help)
				help_page = help_page ? 0 : 1;
			else
				prev_node();
			return 1;

		case KEY_NPAGE:
			if (print_help)
				help_page = help_page ? 0 : 1;
			else
				next_node();
			return 1;

		case KEY_DOWN:
			if (next_item() == END_OF_LIST) {
				if (next_node() != END_OF_LIST)
					first_item();
			}
			return 1;

		case KEY_UP:
			if (prev_item() == END_OF_LIST) {
				if (prev_node() != END_OF_LIST)
					last_item();
			}
			return 1;

		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			goto_item(ch - 48);
			return 1;

		case '>':
			next_graph();
			return 1;

		case '<':
			prev_graph();
			return 1;
			
		default:
			if (get_current_item())
				if (handle_bindings(ch, get_current_item()->i_name))
					return 1;
			break;
	}

	return 0;
}
Example #21
0
void
edit_select_rep::raw_cut (path p1, path p2) {
  if (p2 == p1) return;
  path p = common (p1, p2);
  tree t = subtree (et, p);
  int  n = N(p);
  int  i1= p1[n];
  int  i2= p2[n];

  if (is_document (t) || is_concat (t)) {
    path q1= copy (p); q1 << path (i1, end (t[i1]));
    path q2= copy (p); q2 << path (i2, start (t[i2]));
    raw_cut (q2, p2);
    if (i2>i1+1) remove (p * (i1+1), i2-i1-1);
    raw_cut (p1, q1);
    if (is_concat (t)) correct_concat (p);
    else remove_return (p * i1);
    return;
  }

  if (is_func (t, TFORMAT) || is_func (t, TABLE) || is_func (t, ROW)) {
    path fp= ::table_search_format (et, p);
    tree st= subtree (et, fp);
    int row1, col1, row2, col2;
    table_search_coordinates (st, tail (p1, N(fp)), row1, col1);
    table_search_coordinates (st, tail (p2, N(fp)), row2, col2);
    if (row1>row2) { int tmp= row1; row1= row2; row2= tmp; }
    if (col1>col2) { int tmp= col1; col1= col2; col2= tmp; }

    int i, j;
    for (i=row1; i<=row2; i++)
      for (j=col1; j<=col2; j++) {
        path cp= fp * ::table_search_cell (st, i, j);
        if (is_func (subtree (et, cp), CELL, 1)) cp= cp * 0;
        assign (cp, "");
      }
    path cp= fp * ::table_search_cell (st, row1, col1);
    go_to (cp * path (0, 0));

    if (is_func (st, TFORMAT))
      table_del_format (fp, row1+1, col1+1, row2+1, col2+1, "");
    return;
  }

  if (is_compound (t) && (!is_format (t))) {
    assign (p, "");
    return;
  }

  if ((N(p1) != (N(p)+1)) || (N(p2) != (N(p)+1))) {
    cerr << "t = " << t << "\n";
    cerr << "p = " << p << "\n";
    cerr << "p1= " << p1 << "\n";
    cerr << "p2= " << p2 << "\n";
    FAILED ("invalid cut");
  }

  if (is_atomic (t)) {
    int pos= last_item (p1);
    int nr = last_item (p2)-pos;
    if (nr>0) remove (p1, nr);
  }
  else {
    if ((last_item (p1) != 0) || (last_item (p2) != 1)) {
      cerr << "t = " << t << "\n";
      cerr << "p = " << p << "\n";
      cerr << "p1= " << p1 << "\n";
      cerr << "p2= " << p2 << "\n";
      FAILED ("invalid object cut");
    }
    assign (p, "");
  }
}
Example #22
0
/*
 * This is an alternate interface to 'buildlist' which allows the application
 * to read the list item states back directly without putting them in the
 * output buffer.
 */
int
dlg_buildlist(const char *title,
	      const char *cprompt,
	      int height,
	      int width,
	      int list_height,
	      int item_no,
	      DIALOG_LISTITEM * items,
	      const char *states,
	      int order_mode,
	      int *current_item)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	HELPKEY_BINDINGS,
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
	DLG_KEYS_DATA( DLGK_ITEM_FIRST, KEY_HOME ),
	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_END ),
	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_LL ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	'+' ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  CHR_NEXT ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,	'-' ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,	KEY_UP ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,  CHR_PREVIOUS ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE) ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE+KEY_MAX) ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE) ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE+KEY_MAX) ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,	KEY_LEFTCOL ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT,	KEY_RIGHTCOL ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    ALL_DATA all;
    MY_DATA *data = all.list;
    int i, j, k, key2, found, x, y, cur_x, cur_y;
    int key = 0, fkey;
    bool save_visit = dialog_state.visit_items;
    int button;
    int cur_item;
    int was_mouse;
    int name_width, text_width, full_width, list_width;
    int result = DLG_EXIT_UNKNOWN;
    int num_states;
    bool first = TRUE;
    WINDOW *dialog;
    char *prompt = dlg_strclone(cprompt);
    const char **buttons = dlg_ok_labels();
    const char *widget_name = "buildlist";

    (void) order_mode;

    /*
     * Unlike other uses of --visit-items, we have two windows to visit.
     */
    if (dialog_state.visit_cols)
	dialog_state.visit_cols = 2;

    memset(&all, 0, sizeof(all));
    all.items = items;
    all.item_no = item_no;

    if (dialog_vars.default_item != 0) {
	cur_item = dlg_default_listitem(items);
    } else {
	if ((cur_item = first_item(&all, 0)) < 0)
	    cur_item = first_item(&all, 1);
    }
    button = (dialog_state.visit_items
	      ? (items[cur_item].state ? sRIGHT : sLEFT)
	      : dlg_default_button());

    dlg_does_output();
    dlg_tab_correct_str(prompt);

#ifdef KEY_RESIZE
  retry:
#endif

    all.use_height = list_height;
    all.use_width = (2 * (dlg_calc_list_width(item_no, items)
			  + 4
			  + 2 * MARGIN)
		     + 1);
    all.use_width = MAX(26, all.use_width);
    if (all.use_height == 0) {
	/* calculate height without items (4) */
	dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, all.use_width);
	dlg_calc_listh(&height, &all.use_height, item_no);
    } else {
	dlg_auto_size(title, prompt,
		      &height, &width,
		      MIN_HIGH + all.use_height, all.use_width);
    }
    dlg_button_layout(buttons, &width);
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    /* we need at least two states */
    if (states == 0 || strlen(states) < 2)
	states = " *";
    num_states = (int) strlen(states);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

    dialog = dlg_new_window(height, width, y, x);
    dlg_register_window(dialog, widget_name, binding);
    dlg_register_buttons(dialog, widget_name, buttons);

    dlg_mouse_setbase(all.base_x = x, all.base_y = y);

    dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
    dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
    dlg_draw_title(dialog, title);

    (void) wattrset(dialog, dialog_attr);
    dlg_print_autowrap(dialog, prompt, height, width);

    list_width = (width - 6 * MARGIN - 2) / 2;
    getyx(dialog, cur_y, cur_x);
    data[0].box_y = cur_y + 1;
    data[0].box_x = MARGIN + 1;
    data[1].box_y = cur_y + 1;
    data[1].box_x = data[0].box_x + 1 + 2 * MARGIN + list_width;

    /*
     * After displaying the prompt, we know how much space we really have.
     * Limit the list to avoid overwriting the ok-button.
     */
    if (all.use_height + MIN_HIGH > height - cur_y)
	all.use_height = height - MIN_HIGH - cur_y;
    if (all.use_height <= 0)
	all.use_height = 1;

    for (k = 0; k < 2; ++k) {
	/* create new window for the list */
	data[k].win = dlg_sub_window(dialog, all.use_height, list_width,
				     y + data[k].box_y + 1,
				     x + data[k].box_x + 1);

	/* draw a box around the list items */
	dlg_draw_box(dialog, data[k].box_y, data[k].box_x,
		     all.use_height + 2 * MARGIN,
		     list_width + 2 * MARGIN,
		     menubox_border_attr, menubox_border2_attr);
    }

    text_width = 0;
    name_width = 0;
    /* Find length of longest item to center buildlist */
    for (i = 0; i < item_no; i++) {
	text_width = MAX(text_width, dlg_count_columns(items[i].text));
	name_width = MAX(name_width, dlg_count_columns(items[i].name));
    }

    /* If the name+text is wider than the list is allowed, then truncate
     * one or both of them.  If the name is no wider than 1/4 of the list,
     * leave it intact.
     */
    all.use_width = (list_width - 6 * MARGIN);
    if (dialog_vars.no_tags && !dialog_vars.no_items) {
	full_width = MIN(all.use_width, text_width);
    } else if (dialog_vars.no_items) {
	full_width = MIN(all.use_width, name_width);
    } else {
	if (text_width >= 0
	    && name_width >= 0
	    && all.use_width > 0
	    && text_width + name_width > all.use_width) {
	    int need = (int) (0.25 * all.use_width);
	    if (name_width > need) {
		int want = (int) (all.use_width * ((double) name_width) /
				  (text_width + name_width));
		name_width = (want > need) ? want : need;
	    }
	    text_width = all.use_width - name_width;
	}
	full_width = text_width + name_width;
    }

    all.check_x = (all.use_width - full_width) / 2;
    all.item_x = ((dialog_vars.no_tags
		   ? 0
		   : (dialog_vars.no_items
		      ? 0
		      : (name_width + 2)))
		  + all.check_x);

    /* ensure we are scrolled to show the current choice */
    j = MIN(all.use_height, item_no);
    for (i = 0; i < 2; ++i) {
	int top_item = 0;
	if ((items[cur_item].state != 0) == i) {
	    top_item = cur_item - j + 1;
	    if (top_item < 0)
		top_item = 0;
	    set_top_item(&all, top_item, i);
	} else {
	    set_top_item(&all, 0, i);
	}
    }

    /* register the new window, along with its borders */
    for (i = 0; i < 2; ++i) {
	dlg_mouse_mkbigregion(data[i].box_y + 1,
			      data[i].box_x,
			      all.use_height,
			      list_width + 2,
			      2 * KEY_MAX + (i * (1 + all.use_height)),
			      1, 1, 1 /* by lines */ );
    }

    dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);

    while (result == DLG_EXIT_UNKNOWN) {
	int which = (items[cur_item].state != 0);
	MY_DATA *moi = data + which;
	int at_top = index2row(&all, moi->top_index, which);
	int at_end = index2row(&all, -1, which);
	int at_bot = skip_rows(&all, at_top, all.use_height, which);

	dlg_trace_msg("\t** state %d:%d top %d (%d:%d:%d) %d\n",
		      cur_item, item_no - 1,
		      moi->top_index,
		      at_top, at_bot, at_end,
		      which);

	if (first) {
	    print_both(&all, cur_item);
	    dlg_trace_win(dialog);
	    first = FALSE;
	}

	if (button < 0) {	/* --visit-items */
	    int cur_row = index2row(&all, cur_item, which);
	    cur_y = (data[which].box_y
		     + cur_row
		     + 1);
	    if (at_top > 0)
		cur_y -= at_top;
	    cur_x = (data[which].box_x
		     + all.check_x + 1);
	    dlg_trace_msg("\t...visit row %d (%d,%d)\n", cur_row, cur_y, cur_x);
	    wmove(dialog, cur_y, cur_x);
	}

	key = dlg_mouse_wgetch(dialog, &fkey);
	if (dlg_result_key(key, fkey, &result))
	    break;

	was_mouse = (fkey && is_DLGK_MOUSE(key));
	if (was_mouse)
	    key -= M_EVENT;

	if (!was_mouse) {
	    ;
	} else if (key >= 2 * KEY_MAX) {
	    i = (key - 2 * KEY_MAX) % (1 + all.use_height);
	    j = (key - 2 * KEY_MAX) / (1 + all.use_height);
	    k = row2index(&all, i + at_top, j);
	    dlg_trace_msg("MOUSE column %d, row %d ->item %d\n", j, i, k);
	    if (k >= 0 && j < 2) {
		if (j != which) {
		    /*
		     * Mouse click was in the other column.
		     */
		    moi = data + j;
		    fix_top_item(&all, k, j);
		}
		which = j;
		at_top = index2row(&all, moi->top_index, which);
		at_bot = skip_rows(&all, at_top, all.use_height, which);
		cur_item = k;
		print_both(&all, cur_item);
		key = KEY_TOGGLE;	/* force the selected item to toggle */
	    } else {
		beep();
		continue;
	    }
	    fkey = FALSE;
	} else if (key >= KEY_MIN) {
	    if (key > KEY_MAX) {
		if (which == 0) {
		    key = KEY_RIGHTCOL;		/* switch to right-column */
		    fkey = FALSE;
		} else {
		    key -= KEY_MAX;
		}
	    } else {
		if (which == 1) {
		    key = KEY_LEFTCOL;	/* switch to left-column */
		    fkey = FALSE;
		}
	    }
	    key = dlg_lookup_key(dialog, key, &fkey);
	}

	/*
	 * A space toggles the item status.  Normally we put the cursor on
	 * the next available item in the same column.  But if there are no
	 * more items in the column, move the cursor to the other column.
	 */
	if (key == KEY_TOGGLE) {
	    int new_choice;
	    int new_state = items[cur_item].state + 1;

	    if ((new_choice = next_item(&all, cur_item, which)) == cur_item) {
		new_choice = prev_item(&all, cur_item, which);
	    }
	    dlg_trace_msg("cur_item %d, new_choice:%d\n", cur_item, new_choice);
	    if (new_state >= num_states)
		new_state = 0;

	    items[cur_item].state = new_state;
	    if (cur_item == moi->top_index) {
		set_top_item(&all, new_choice, which);
	    }

	    if (new_choice >= 0) {
		fix_top_item(&all, cur_item, !which);
		cur_item = new_choice;
	    }
	    print_both(&all, cur_item);
	    dlg_trace_win(dialog);
	    continue;		/* wait for another key press */
	}

	/*
	 * Check if key pressed matches first character of any item tag in
	 * list.  If there is more than one match, we will cycle through
	 * each one as the same key is pressed repeatedly.
	 */
	found = FALSE;
	if (!fkey) {
	    if (button < 0 || !dialog_state.visit_items) {
		for (j = cur_item + 1; j < item_no; j++) {
		    if (check_hotkey(items, j, which)) {
			found = TRUE;
			i = j;
			break;
		    }
		}
		if (!found) {
		    for (j = 0; j <= cur_item; j++) {
			if (check_hotkey(items, j, which)) {
			    found = TRUE;
			    i = j;
			    break;
			}
		    }
		}
		if (found)
		    dlg_flush_getc();
	    } else if ((j = dlg_char_to_button(key, buttons)) >= 0) {
		button = j;
		ungetch('\n');
		continue;
	    }
	}

	/*
	 * A single digit (1-9) positions the selection to that line in the
	 * current screen.
	 */
	if (!found
	    && (key <= '9')
	    && (key > '0')
	    && (key - '1' < at_bot)) {
	    found = TRUE;
	    i = key - '1';
	}

	if (!found && fkey) {
	    switch (key) {
	    case DLGK_FIELD_PREV:
		if ((button == sRIGHT) && dialog_state.visit_items) {
		    key = DLGK_GRID_LEFT;
		    button = sLEFT;
		} else {
		    button = dlg_prev_button(buttons, button);
		    dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
				     FALSE, width);
		    if (button == sRIGHT) {
			key = DLGK_GRID_RIGHT;
		    } else {
			continue;
		    }
		}
		break;
	    case DLGK_FIELD_NEXT:
		if ((button == sLEFT) && dialog_state.visit_items) {
		    key = DLGK_GRID_RIGHT;
		    button = sRIGHT;
		} else {
		    button = dlg_next_button(buttons, button);
		    dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
				     FALSE, width);
		    if (button == sLEFT) {
			key = DLGK_GRID_LEFT;
		    } else {
			continue;
		    }
		}
		break;
	    }
	}

	if (!found && fkey) {
	    i = cur_item;
	    found = TRUE;
	    switch (key) {
	    case DLGK_GRID_LEFT:
		i = closest_item(&all, cur_item, 0);
		fix_top_item(&all, i, 0);
		break;
	    case DLGK_GRID_RIGHT:
		i = closest_item(&all, cur_item, 1);
		fix_top_item(&all, i, 1);
		break;
	    case DLGK_PAGE_PREV:
		if (cur_item > moi->top_index) {
		    i = moi->top_index;
		} else if (moi->top_index != 0) {
		    int temp = at_top;
		    if ((temp -= all.use_height) < 0)
			temp = 0;
		    i = row2index(&all, temp, which);
		}
		break;
	    case DLGK_PAGE_NEXT:
		if ((at_end - at_bot) < all.use_height) {
		    i = next_item(&all,
				  row2index(&all, at_end, which),
				  which);
		} else {
		    i = next_item(&all,
				  row2index(&all, at_bot, which),
				  which);
		    at_top = at_bot;
		    set_top_item(&all,
				 next_item(&all,
					   row2index(&all, at_top, which),
					   which),
				 which);
		    at_bot = skip_rows(&all, at_top, all.use_height, which);
		    at_bot = MIN(at_bot, at_end);
		}
		break;
	    case DLGK_ITEM_FIRST:
		i = first_item(&all, which);
		break;
	    case DLGK_ITEM_LAST:
		i = last_item(&all, which);
		break;
	    case DLGK_ITEM_PREV:
		i = prev_item(&all, cur_item, which);
		if (stop_prev(&all, cur_item, which))
		    continue;
		break;
	    case DLGK_ITEM_NEXT:
		i = next_item(&all, cur_item, which);
		break;
	    default:
		found = FALSE;
		break;
	    }
	}

	if (found) {
	    if (i != cur_item) {
		int now_at = index2row(&all, i, which);
		int oops = item_no;
		int old_item;

		dlg_trace_msg("<--CHOICE %d\n", i);
		dlg_trace_msg("<--topITM %d\n", moi->top_index);
		dlg_trace_msg("<--now_at %d\n", now_at);
		dlg_trace_msg("<--at_top %d\n", at_top);
		dlg_trace_msg("<--at_bot %d\n", at_bot);

		if (now_at >= at_bot) {
		    while (now_at >= at_bot) {
			if ((at_bot - at_top) >= all.use_height) {
			    set_top_item(&all,
					 next_item(&all, moi->top_index, which),
					 which);
			}
			at_top = index2row(&all, moi->top_index, which);
			at_bot = skip_rows(&all, at_top, all.use_height, which);

			dlg_trace_msg("...at_bot %d (now %d vs %d)\n",
				      at_bot, now_at, at_end);
			dlg_trace_msg("...topITM %d\n", moi->top_index);
			dlg_trace_msg("...at_top %d (diff %d)\n", at_top,
				      at_bot - at_top);

			if (at_bot >= at_end) {
			    /*
			     * If we bumped into the end, move the top-item
			     * down by one line so that we can display the
			     * last item in the list.
			     */
			    if ((at_bot - at_top) > all.use_height) {
				set_top_item(&all,
					     next_item(&all, moi->top_index, which),
					     which);
			    } else if (at_top > 0 &&
				       (at_bot - at_top) >= all.use_height) {
				set_top_item(&all,
					     next_item(&all, moi->top_index, which),
					     which);
			    }
			    break;
			}
			if (--oops < 0) {
			    dlg_trace_msg("OOPS-forward\n");
			    break;
			}
		    }
		} else if (now_at < at_top) {
		    while (now_at < at_top) {
			old_item = moi->top_index;
			set_top_item(&all,
				     prev_item(&all, moi->top_index, which),
				     which);
			at_top = index2row(&all, moi->top_index, which);

			dlg_trace_msg("...at_top %d (now %d)\n", at_top, now_at);
			dlg_trace_msg("...topITM %d\n", moi->top_index);

			if (moi->top_index >= old_item)
			    break;
			if (at_top <= now_at)
			    break;
			if (--oops < 0) {
			    dlg_trace_msg("OOPS-backward\n");
			    break;
			}
		    }
		}
		dlg_trace_msg("-->now_at %d\n", now_at);
		cur_item = i;
		print_both(&all, cur_item);
	    }
	    dlg_trace_win(dialog);
	    continue;		/* wait for another key press */
	}

	if (fkey) {
	    switch (key) {
	    case DLGK_ENTER:
		result = dlg_enter_buttoncode(button);
		break;
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		/* reset data */
		height = old_height;
		width = old_width;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
#endif
	    default:
		if (was_mouse) {
		    if ((key2 = dlg_ok_buttoncode(key)) >= 0) {
			result = key2;
			break;
		    }
		    beep();
		}
	    }
	} else {
	    beep();
	}
    }

    dialog_state.visit_cols = save_visit;
    dlg_del_window(dialog);
    dlg_mouse_free_regions();
    free(prompt);
    *current_item = cur_item;
    return result;
}
tree
texmacs_invarianted_merge (tree t, string src,
                           tree org, tree u, hashmap<tree,path> h) {
  if (is_atomic (t)) return t;
  else {
    if (true) {
      int i, n= N(t);
      tree r (t, n);
      for (i=0; i<n; i++)
        r[i]= texmacs_invarianted_merge (t[i], src, org, u, h);
      t= r;
    }
    if (is_concat (t) || is_document (t)) {
      int i, n= N(t);
      tree r (L(t));
      for (i=0; i<n; i++) {
        if (is_document (t) && is_compound (t[i], "ilx", 1))
          t[i]= compound ("ilx", texmacs_invarianted_extend (t[i][0], src));

        if (N(r) > 0 &&
            is_compound (r[N(r)-1], "ilx", 1) &&
            is_compound (t[i], "ilx", 1)) {
          int b1, e1, b2, e2;
          bool ok = get_range (r[N(r)-1][0], b1, e1, src);
          ok = get_range (t[i][0], b2, e2, src) || ok;
          if (ok && e1 <= b2) {
            skip_latex_spaces (src, e1);
            if (e1 >= b2) {
              string id= as_string (b1) * ":" * as_string (e2);
              r[N(r)-1][0]= id;
              continue;
            }
          }
        }

        int j= i;
        while (j<n && !is_compound (t[j], "ilx", 1)) j++;
        if (j < n && j > i && N(r) > 0 && is_compound (r[N(r)-1], "ilx", 1)) {
          // NOTE: this special treatment allows for the recognition of
          // pieces which may be invarianted even in case of missing markers
          int b1, e1, b2, e2;
          bool ok1= get_range (r[N(r)-1][0], b1, e1, src);
          bool ok2= get_range (t[j][0], b2, e2, src);
          if (ok1 && ok2 && e1 <= b2 && i-1 < N(org)) {
            path p= h [org[i-1]];
            if (p != path (-1)) {
              tree pt= subtree (u, path_up (p));
              int k, k2= last_item (p);
              for (k=i-1; k<=j && k2<N(pt); k++, k2++)
                if (org[k] != pt[k2]) {
                  //cout << "  <<< " << org[k] << LF
                  //     << "  >>> " << pt[k2] << LF;
                  break;
                }
              if (k > j) {
                string id= as_string (b1) * ":" * as_string (e2);
                r[N(r)-1][0]= id;
                i= j;
                continue;
              }
            }
          }
        }

        r << t[i];
      }
      if (is_concat (r) && N(r) == 1) r= r[0];
      return r;
    }
    return t;
  }
}