Logo Search packages:      
Sourcecode: hnb version File versions  Download package

ui_draw.c

/*
 * ui_draw.c -- drawing of a nodes and trees using curses
 *
 * Copyright (C) 2001-2003 Øyvind Kolås <pippin@users.sourceforge.net>
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include "tree.h"
#include "tree_todo.h"
#include <string.h>
#include <unistd.h>
#include "curses.h"
#include "stdio.h"
#include "prefs.h"
#include "ui_overlay.h"
#define UI_C
#include "ui.h"
#include "ui_draw.h"
#include "cli.h"
#include <stdlib.h>
#include <ctype.h>

#define KEEPLINES 5

int nodes_above;
int active_line;
int nodes_below;

static Node *up (Node *sel, Node *node)
{
      if (node_up (node) && node_getflag( node_up (node), F_expanded)) {
            node = node_up (node);
            while (node_right (node) && node_getflag(node,F_expanded)) {
                  node = node_right (node);
                  node = node_bottom (node);
            }
            return (node);
      } else {
            if (node_up (node))
                  return (node_up (node));
            else
                  return (node_left (node));
      }
      return node_left (node);
}

static Node *down (Node *sel, Node *node)
{
      if (node_getflag(node,F_expanded)) {
            return node_recurse (node);
      } else {
            if (node_down (node)) {
                  return (node_down (node));
            } else {
                  while (node != 0) {
                        node = node_left (node);
                        if (node_down (node))
                              return (node_down (node));
                  }
            }
      }
      return NULL;
}

int startlevel = 0;

int hnb_edit_posup = 0;             /*contains the cursor pos for up/down */
int hnb_edit_posdown = 0;           /*from here when in editing mode */

enum {
      drawmode_test = 0,
      drawmode_normal,
      drawmode_selected,
      drawmode_edit,
      drawmode_completion
};

/* draws the actual node data with word wrapping, should be reengineered into a general
 * linewrapping function.
 *
 *
 */
static int draw_textblock (int line_start, int col_start, int width,
                                       int cursor_pos, Node *node, int drawmode)
{
      int col_end = col_start + width;
      unsigned char word[200];      /* current word being rendered */
      int wpos = 0;                       /* position in current word */
      int dpos = 0;                       /* position in data */
      int col;                            /* current column */

      int lines_used = 1;

      int cursor_state = 0;
      int cx = 0, cy = 0;                 /* coordinates to draw cursor at */

      unsigned char *data =
            (unsigned char *) fixnullstring (node_get (node, TEXT));

      col = col_start;

      word[0] = 0;
      if (drawmode == drawmode_edit) {
            hnb_edit_posup = 0;
            hnb_edit_posdown = strlen ((char *) data);
      }

      switch (drawmode) {
            case drawmode_test:
                  break;
            case drawmode_completion:
                  if (node_right (node)) {
                        ui_style (ui_style_parentnode);
                  } else {
                        ui_style (ui_style_node);
                  }
                  break;
            case drawmode_selected:
                  if (node_right (node)) {
                        ui_style (ui_style_parentselected);
                  } else {
                        ui_style (ui_style_selected);
                  }
                  break;
            case drawmode_normal:
            case drawmode_edit:
            default:
                  if (node_right (node)) {
                        ui_style (ui_style_parentnode);
                  } else {
                        ui_style (ui_style_node);
                  }
                  break;
      }

      while ((dpos == 0) || data[dpos - 1]) {   /* loop through data + \0 */
            switch (data[dpos]) {
                  case '\0':              /* \0 as well,.. to print last word */
                  case ' ':
                  case '\t':
                  case '\n':
                  case '\r':              /* all whitespace is treated as spaces */
                        if (col + wpos + 1 >= col_end) {    /* reached margin */
                              if (drawmode == drawmode_edit) {
                                    if (cursor_state == 0)
                                          hnb_edit_posup = cursor_pos - (col - col_start);
                                    if (cursor_state == 1) {
                                          hnb_edit_posdown = cursor_pos + (col - col_start);
                                          cursor_state = 2;
                                    }
                              }

                              col = col_start;
                              lines_used++;
                              if (lines_used + line_start >= LINES)
                                    return lines_used;
                        }
                        if (drawmode != drawmode_test) {
                              if (line_start + lines_used - 1 >= 0) {
                                    move (line_start + lines_used - 1, col);

                                    /* must break the word in two due to differnt text formatting */
                                    if (drawmode == drawmode_completion
                                          && cursor_state == 0 && dpos >= cursor_pos) {
                                          int i;

                                          for (i = 0; i < wpos - (dpos - cursor_pos); i++)
                                                addch (word[i]);
                                          if (node_right (node)) {
                                                ui_style (ui_style_parentselected);
                                          } else {
                                                ui_style (ui_style_selected);
                                          }
                                          for (i = wpos - (dpos - cursor_pos); i < wpos;
                                                 i++)
                                                addch (word[i]);
                                    } else {

                                          addstr ((char *) word);

                                    }
                                    if (data[dpos])
                                          addch (' ');
                              }
                        }

                        switch (drawmode) {
                              case drawmode_edit:
                                    if (cursor_state == 0 && dpos >= cursor_pos) {
                                          cy = line_start + lines_used - 1;
                                          cx = col - (dpos - cursor_pos) + wpos;
                                          cursor_state = 1;
                                    }
                                    break;
                              case drawmode_completion:
                                    if (cursor_state == 0 && dpos >= cursor_pos) {
                                          if (node_right (node)) {
                                                ui_style (ui_style_parentselected);
                                          } else {
                                                ui_style (ui_style_selected);
                                          }
                                          cursor_state = 1;
                                    }
                              default:
                                    break;
                        }

                        col += wpos + 1;
                        word[wpos = 0] = 0;
                        break;
                  default:
                        if (wpos < 198) {
                              word[wpos++] = data[dpos];
                              word[wpos] = 0;
                        }
                        break;
            }
            dpos++;
      }
      /* draw the cursor */
      if (drawmode == drawmode_edit) {
            move (cy, cx);
            if (node_right (node)) {
                  ui_style (ui_style_parentselected);
            } else {
                  ui_style (ui_style_selected);
            }
            addch (data[cursor_pos]);
            if (node_right (node)) {
                  ui_style (ui_style_parentnode);
            } else {
                  ui_style (ui_style_node);
            }
      }
      return lines_used;
}



static int draw_dummy (int line, int col, int width, Node *node, int drawmode)
{
      if (width == 0)
            width = 1;
      if (drawmode != drawmode_test) {
            int j;

            move (line, col);
            ui_style (ui_style_bullet);
            for (j = 0; j < width; j++) {
                  addch ('X');
            }
      }
      return width;
}

static int draw_spacing (int line, int col, int width, Node *node,
                                     int drawmode)
{
      if (width == 0)
            width = 1;
      if (drawmode != drawmode_test) {
            int j;

            move (line, col);
            ui_style (ui_style_background);
            for (j = 0; j < width; j++) {
                  addch (' ');
            }
      }
      return width;
}

static char bullet_leaf[4] = "  ·";
static char bullet_parent[4] = "  +";
static char bullet_parent_expanded[4] = "  -";



static int draw_bullet (int line, int col, int width, Node *node,
                                    int drawmode)
{
      int asize;
      int perc;

/*    if(width==0)*/
      width = 3;

      perc = calc_percentage_size (node, &asize);
      {
            ui_style (ui_style_bullet);

            move (line, col);
            switch (perc) {
                  case -1:
                        if (drawmode != drawmode_test)
                              addstr ((node_right (node)) ? node_getflag(node,F_expanded)
                                          ? bullet_parent_expanded : bullet_parent
                                          : bullet_leaf);
                        break;
                  case 0:
                        if (drawmode != drawmode_test)
                              addstr ("[ ]");
                        break;
                  case 2000:
                        if (drawmode != drawmode_test)
                              addstr ("[X]");
                        break;
                  default:{
                        char str[100];

                        snprintf (str, 4, "%2i%%", perc);
                        if (drawmode != drawmode_test)
                              addstr (str);
                  }
            }
      }

      return width;
}


static char *node2no_path (Node *node)
{
      static char path[512];
      int pos = 0;
      int levels = nodes_left (node);
      int cnt;

      path[0] = 0;

      for (cnt = levels; cnt >= 0; cnt--) {
            int cnt2;
            Node *tnode = node;

            for (cnt2 = 0; cnt2 < cnt; cnt2++)
                  tnode = node_left (tnode);

            sprintf (&path[pos], "%i", nodes_up (tnode) + 1);
            pos = strlen (path);
            path[pos] = '.';
            path[++pos] = 0;
      }

      path[--pos] = 0;

      return (path);
}

static int draw_node_no (int line, int col, int width, Node *node,
                                     int drawmode)
{
      char str[100] = "";

      if (width == 0)
            width = 4;

      ui_style (ui_style_bullet);
      move (line, col);
      snprintf (str, 5, "%4i", node_no (node));
      if (drawmode != drawmode_test) {
            addstr (str);
      }

      return width;
}


static int draw_nr (int line, int col, int width, Node *node, int drawmode)
{
      char str[100] = "";

      if (width == 0)
            width = 3;

      ui_style (ui_style_bullet);
      move (line, col);
      snprintf (str, 5, "%3i", nodes_up (node) + 1);
      if (drawmode != drawmode_test) {
            addstr (str);
      }

      return width;
}



static int draw_anr (int line, int col, int width, Node *node, int drawmode)
{
      char str[100] = "";
      char fstr[20];

      if (width == 0)
            width = 8;

      ui_style (ui_style_bullet);
      move (line, col);
      snprintf (fstr, 8, "%%%is", width);
      snprintf (str, width + 2, fstr, node2no_path (node));
      if (drawmode != drawmode_test) {
            addstr (str);
      }

      return width;
}

static int node_getval(Node *node, char *name){
      char *got=node_get(node,name);
      if(!got)return -1;
      return(atoi(got));
}


static int draw_debug (int line, int col, int width, Node *node, int drawmode)
{
      int asize;
      int size = node_getval (node,"size");
      int perc;

      width = 40;

      if (drawmode != drawmode_test) {
            ui_style (ui_style_background);
            move (line, col);
            perc = calc_percentage_size (node, &asize);

            {
                  char str[64];

                  sprintf (str, "(%i/%i) ",
                               (perc == 2000 ? 100 : perc * asize) / 100, asize);
                  if (drawmode != drawmode_test)
                        addstr (str);
            }

            {
                  perc = calc_percentage_size (node, &asize);
                  attrset (A_NORMAL);
                  {
                        char str[256];

                        sprintf (str, "size:%i a_size:%i %i%% ", node_getval (node,"size"),
                                     size, perc);
                        addstr (str);
                  }
            }

            if (node_calc_size (node) != -1) {
                  char str[10];

                  sprintf (str, "%4.1f ", (float) node_calc_size (node) / 10.0);
                  addstr (str);
            }
      }

      return width;
}

#define MAX_COLUMNS 20

static int draw_indent (int line, int col, int width, Node *node,
                                    int drawmode)
{
      if (width == 0)
            width = 4;

      return width * nodes_left (node);
}


enum {
      col_spacing = 0,
      col_indent,
      col_nr,
      col_anr,
      col_bullet,
      col_data,
      col_debug,
      col_percentage,
      col_node_no,
      col_dummy,
      col_terminate
};


static int (*col_fun[col_terminate + 1]) (int line, int col, int width,
                                                              Node *node, int drawmode) = {
draw_spacing, draw_indent, draw_nr, draw_anr, draw_bullet, draw_spacing,
            draw_debug, draw_spacing, draw_node_no, draw_dummy, draw_dummy};

static struct {
      int type;
      int width;
} col_def[MAX_COLUMNS] = {
      {
      col_indent, 4}, {
      col_spacing, 1}, {
      col_bullet, 3}, {
      col_spacing, 1}, {
      col_data, 0}, {
      col_spacing, 1}, {
      col_dummy, 10}, {
      col_spacing, 2}, {
      col_dummy, 10}, {
      col_spacing, 1}, {
      col_terminate, 0}
};


/* FIXME: make backup?,.. and make sure data is present,.., make possiblity to write back? */

void* display_format_cmd (int argc, char **argv, void *data)
{
      char *p = argv[1];
      int width;
      int type;
      int col_no = 0;

      if(argc<2){
            return data;
      }

      do {
            width = 0;
            type = col_spacing;
            switch (*p) {
                  case 'i':
                        type = col_indent;
                        if (isdigit (*(p + 1))) {
                              width = atoi (p + 1);
                              while (isdigit ((unsigned char)*(p + 1)))
                                    p++;
                        }
                        break;
                  case 'd':
                        type = col_data;
                        if (isdigit (*(p + 1))) {
                              width = atoi (p + 1);
                              while (isdigit ((unsigned char)*(p + 1)))
                                    p++;
                        }
                        break;
                  case 'D':
                        type = col_debug;
                        if (isdigit (*(p + 1))) {
                              width = atoi (p + 1);
                              while (isdigit ((unsigned char)*(p + 1)))
                                    p++;
                        }
                        break;
                  case 'x':
                        type = col_dummy;
                        if (isdigit (*(p + 1))) {
                              width = atoi (p + 1);
                              while (isdigit ((unsigned char)*(p + 1)))
                                    p++;
                        }
                        break;
                  case '1':
                        type = col_nr;
                        if (*(p + 1) == '.') {
                              type = col_anr;
                              p++;
                        }
                        if (isdigit (*(p + 1))) {
                              width = atoi (p + 1);
                              while (isdigit ((unsigned char)*(p + 1)))
                                    p++;
                        }
                        break;
                  case '-':
                        type = col_bullet;
                        if (isdigit (*(p + 1))) {
                              width = atoi (p + 1);
                              while (isdigit ((unsigned char)*(p + 1)))
                                    p++;
                        }
                        break;
                  case '#':
                        type = col_node_no;
                        if (isdigit (*(p + 1))) {
                              width = atoi (p + 1);
                              while (isdigit ((unsigned char)*(p + 1)))
                                    p++;
                        }
                        break;
                  case ' ':
                        type = col_spacing;
                        while (' ' == ((unsigned char)*(p + 1))) {
                              p++;
                              width++;
                        }
                        break;
                  default:
                        cli_outfunf ("td not_parsed(%c)", *p);
                        break;
            }
            col_def[col_no].type = type;
            col_def[col_no].width = width;
            col_no++;
      } while (*(++p));

      col_def[col_no].type = col_terminate;

      return data;
}





/*
 * @param line_start which line on the display the first line of the draw node is on
 * @param level      the indentation level of this item
 * @param node       the node to draw
 * @param cursor_pos different meanings in different modes, testmode: none
 *                   highlightmode: none, edit_mode: the position in the data
 *                   that should be highlighted,
 *                   completion: the number of matched chars in data
 *                   
 * @param draw_mode  1=draw, 0=test
 *
 * @return number of lines needed to draw item
 **/
static int draw_item (int line_start, int cursor_pos, Node *node,
                                int drawmode)
{
      int col_no = 0;
      int lines_used = 1;

      int col_start = 0;
      int col_end = COLS;

      col_start = 0;
      /* draw columns before col_data */

      while (col_def[col_no].type != col_data
               && col_def[col_no].type != col_terminate) {
            col_start +=
                  col_fun[col_def[col_no].type] (line_start, col_start,
                                                               col_def[col_no].width, node,
                                                               drawmode);
            col_no++;
      }

      /* fastforward to end of col_def */
      while (col_def[col_no].type != col_terminate)
            col_no++;

      col_no--;

      /* draw columns after col_data */
      while (col_no && col_def[col_no].type != col_data) {
            int width =
                  col_fun[col_def[col_no].type] (line_start,
                                                               col_end - col_def[col_no].width,
                                                               col_def[col_no].width, node,
                                                               drawmode_test);
            col_end -=
                  col_fun[col_def[col_no].type] (line_start, col_end - width, width,
                                                               node, drawmode);
            col_no--;
      }

      lines_used =
            draw_textblock (line_start, col_start, (col_end - col_start),
                                    cursor_pos, node, drawmode);

      return lines_used;
}

extern int hnb_nodes_up;
extern int hnb_nodes_down;

#define MAXLINES 512
static int line_nodeno[MAXLINES] = { 0 };

void ui_draw (Node *node, char *input, int edit_mode)
{
      int lines;

      static struct {
            int self;
            int prev;
      } node_numb = {
      1, 1};

      if (!prefs.fixedfocus) {

            node_numb.prev = node_numb.self;
            node_numb.self = node_no (node);

            if (node_numb.self > node_numb.prev) {
                  active_line++;
            } else if (node_numb.self < node_numb.prev) {
                  active_line--;
            }

            {
                  int i;

                  for (i = 0; i < ((LINES < MAXLINES) ? LINES : MAXLINES); i++)
                        if (line_nodeno[i] == node_numb.self) {
                              active_line = i;
                              break;
                        }
            }

            if (node_numb.self == 1) {    /* jumped to root, always bring nodes to top of screen */
                  active_line = 1;
            }

            {
                  int i;

                  for (i = 0; i < ((LINES < MAXLINES) ? LINES : MAXLINES); i++)
                        line_nodeno[i] = 0;
            }


            {
                  int maxline = LINES - KEEPLINES;

                  if (active_line > maxline)    /*if we overlap with help,.. move up */
                        active_line = maxline;
                  if (active_line < KEEPLINES)
                        active_line = KEEPLINES;
            }
      };

      nodes_above = active_line;
      nodes_below = LINES - active_line;

      {
            hnb_nodes_up = 0;
            hnb_nodes_down = 0;

            erase ();
/* draw nodes above selected node */
            {
                  Node *prev_down = node; /* to aid pgup/pgdn */
                  int line = active_line;
                  Node *tnode = up (node, node);

                  while (tnode) {
                        draw_item (line -=
                                       draw_item (0, 0, tnode, drawmode_test), 0, tnode,
                                       drawmode_normal);

                        line_nodeno[line] = node_no (tnode);

                        if (node_down (tnode) == prev_down) {
                              hnb_nodes_up++;
                              prev_down = tnode;
                        }

                        tnode = up (node, tnode);
                        if (active_line - nodes_above >= line)
                              tnode = 0;
                  }
            }

            /* draw the currently selected item */
            line_nodeno[active_line] = node_no (node);

            if (edit_mode) {
                  lines = draw_item (active_line, strlen(input) - 1,
                                 node, drawmode_edit);
            } else {
                  lines = draw_item (active_line, strlen (input), node,
                                 drawmode_completion);
            }

            /* draw items below current item */
            {
                  Node *prev_up = node;   /* to aid pgup/pgdn */
                  Node *tnode = down (node, node);

                  lines += active_line;
                  if (lines >= LINES)
                        tnode = 0;
                  while (tnode) {
                        line_nodeno[lines] = node_no (tnode);
                        lines += draw_item (lines, 0, tnode, drawmode_normal);

                        if (node_up (tnode) == prev_up) {
                              hnb_nodes_down++;
                              prev_up = tnode;
                        }

                        tnode = down (node, tnode);
                        if (lines >= LINES)
                              tnode = 0;

                  }
            }
      }

      help_draw (ui_current_scope);

      move (LINES - 1, COLS - 1);

/*    refresh ();*/

      hnb_nodes_up++;
      hnb_nodes_down++;
}

/*
!init_ui_draw();
*/
void init_ui_draw ()
{
      cli_add_command ("display_format", display_format_cmd, "<format string>");
      cli_add_help ("display_format", "\
defines how each node is displayed, the display string syntax is \
interpreted as follows: \
spaces turn into real spaces, i means indentation, - means bullet, \
d means the real data of the node, x is a temporary placeholder for \
upcoming columntypes,. (for debugging only) \
i and x can take an argument specifying how many characters wide \
the field should be");
      cli_add_string ("bullet_leaf", bullet_leaf, "");
      cli_add_string ("bullet_parent", bullet_parent, "");
      cli_add_string ("bullet_parent_expanded", bullet_parent_expanded, "");

}

Generated by  Doxygen 1.6.0   Back to index