Index: display/d.legend/main.c
===================================================================
--- display/d.legend/main.c	(revision 68670)
+++ display/d.legend/main.c	(working copy)
@@ -48,8 +48,9 @@
     int i, j, k;
     int thin, lines, steps;
     int fp;
+    int label_indent;
     double t, b, l, r;
-    int hide_catnum, hide_catstr, hide_nodata, do_smooth;
+    int hide_catnum, hide_catstr, show_ticks, show_bg, hide_nodata, do_smooth;
     char *cstr;
     double x_box[5], y_box[5];
     struct Categories cats;
@@ -57,8 +58,10 @@
     struct GModule *module;
     struct Option *opt_rast2d, *opt_rast3d, *opt_color, *opt_lines,
 		  *opt_thin, *opt_labelnum, *opt_at, *opt_use, *opt_range,
-		  *opt_font, *opt_path, *opt_charset, *opt_fontsize;
-    struct Flag *hidestr, *hidenum, *hidenodata, *smooth, *flipit, *histo;
+          *opt_font, *opt_path, *opt_charset, *opt_fontsize, *opt_title,
+          *opt_ticks, *opt_tstep, *brdcolor;
+    struct Flag *hidestr, *hidenum, *hidenodata, *smooth, *flipit, *histo,
+            *showtick, *showbg;
     struct Range range;
     struct FPRange fprange, render_range;
     CELL min_ind, max_ind;
@@ -73,11 +76,14 @@
     int flip, horiz, UserRange;
     double UserRangeMin, UserRangeMax, UserRangeTemp;
     double *catlist, maxCat;
-    int catlistCount, use_catlist;
+    int catlistCount, use_catlist, ticksCount;
     double fontsize;
     char *units;
+    char *title;
+    double *tick_values;
+    double t_start, t_step;
+    int colorb;
 
-
     /* Initialize the GIS calls */
     G_gisinit(argv[0]);
 
@@ -99,6 +105,12 @@
     opt_rast3d->required = NO;
     opt_rast3d->guisection = _("Input");
 
+    opt_title = G_define_option();
+    opt_title->key = "title";
+    opt_title->type = TYPE_STRING;
+    opt_title->required = NO;
+    opt_title->description = _("Legend title");
+
     opt_lines = G_define_option();
     opt_lines->key = "lines";
     opt_lines->type = TYPE_INTEGER;
@@ -121,12 +133,31 @@
     opt_labelnum = G_define_option();
     opt_labelnum->key = "labelnum";
     opt_labelnum->type = TYPE_INTEGER;
-    opt_labelnum->answer = "5";
     opt_labelnum->options = "2-100";
     opt_labelnum->description =
 	_("Number of text labels for smooth gradient legend");
     opt_labelnum->guisection = _("Gradient");
 
+    opt_ticks = G_define_option();
+    opt_ticks->key = "label_value";
+    opt_ticks->type = TYPE_DOUBLE;
+    opt_ticks->required = NO;
+    opt_ticks->description =
+    _("Specific values to draw ticks");
+    opt_ticks->required = NO;
+    opt_ticks->multiple = YES;
+    opt_ticks->guisection = _("Gradient");
+
+    opt_tstep = G_define_option();
+    opt_tstep->key = "label_step";
+    opt_tstep->type = TYPE_DOUBLE;
+    opt_tstep->required = NO;
+    opt_tstep->description =
+    _("Display label every step");
+    opt_tstep->required = NO;
+    opt_tstep->guisection = _("Gradient");
+
+
     opt_at = G_define_option();
     opt_at->key = "at";
     opt_at->key_desc = "bottom,top,left,right";
@@ -192,6 +223,12 @@
 	_("Text encoding (only applicable to TrueType fonts)");
     opt_charset->guisection = _("Font settings");
 
+    brdcolor = G_define_standard_option(G_OPT_CN);
+    brdcolor->key = "brdcolor";
+    brdcolor->answer = "black";
+    brdcolor->label = _("Border color");
+    brdcolor->guisection = _("Color");
+
     hidestr = G_define_flag();
     hidestr->key = 'v';
     hidestr->description = _("Do not show category labels");
@@ -202,6 +239,11 @@
     hidenum->description = _("Do not show category numbers");
     hidenum->guisection = _("Advanced");
 
+    showtick = G_define_flag();
+    showtick->key = 't';
+    showtick->description = _("Draw legend ticks for labels");
+    showtick->guisection = _("Advanced");
+
     hidenodata = G_define_flag();
     hidenodata->key = 'n';
     hidenodata->description = _("Skip categories with no label");
@@ -222,7 +264,17 @@
     histo->description = _("Add histogram to smoothed legend");
     histo->guisection = _("Gradient");
 
+    showbg = G_define_flag();
+    showbg->key = 'b';
+    showbg->description = _("Show background");
+    showbg->guisection = _("Color");
 
+    G_option_exclusive(hidenum, opt_labelnum, NULL);
+    G_option_exclusive(hidenum, opt_ticks, NULL);
+    G_option_exclusive(hidenum, opt_tstep, NULL);
+    G_option_exclusive(opt_labelnum, opt_tstep, NULL);
+    G_option_exclusive(opt_labelnum, opt_ticks, NULL);
+
     /* Check command line */
     if (G_parser(argc, argv))
 	exit(EXIT_FAILURE);
@@ -241,12 +293,26 @@
 	maptype = MAP_TYPE_RASTER3D;
     }
 
+    if (opt_title->answer)
+        title = opt_title->answer;
+    else
+        title = "";
+
     hide_catstr = hidestr->answer;	/* note hide_catstr gets changed and re-read below */
     hide_catnum = hidenum->answer;
+    show_ticks = showtick->answer;
     hide_nodata = hidenodata->answer;
     do_smooth = smooth->answer;
     flip = flipit->answer;
+    show_bg = showbg->answer;
 
+    if (showtick->answer){
+        show_ticks = showtick->answer;
+        label_indent = 12;
+    }
+    else
+        label_indent = 6;
+
     color = D_parse_color(opt_color->answer, TRUE);
 
     if (opt_lines->answer != NULL)
@@ -258,9 +324,29 @@
     if (!thin)
 	thin = 1;
 
+    steps = 0;
+    if ((opt_tstep->answer == NULL) && (opt_ticks->answer == NULL))
+        steps = 5;
+
+
     if (opt_labelnum->answer != NULL)
 	sscanf(opt_labelnum->answer, "%d", &steps);
 
+    if (opt_tstep->answer != NULL)
+        t_step = atof(opt_tstep->answer);
+
+    ticksCount = 0;
+    if (opt_ticks->answer != NULL) {
+        tick_values = (double *)G_calloc(100+1, sizeof(double));
+        for (i = 0; i < 100; i++)   /* fill with dummy values */
+            tick_values[i] = 1.0 * (i + 1);
+        tick_values[i] = 0;
+
+        for (i = 0; (opt_ticks->answers[i] != NULL) && i < 100; i++)
+            tick_values[i] = atof(opt_ticks->answers[i]);
+        ticksCount = i;
+    }
+
     catlistCount = 0;
     if (opt_use->answer != NULL) {	/* should this be answerS ? */
 	use_catlist = TRUE;
@@ -317,9 +403,13 @@
 
     D_open_driver();
 
-    white = D_translate_color(DEFAULT_FG_COLOR);
-    black = D_translate_color(DEFAULT_BG_COLOR);
+    /* Parse and select background color */
+    colorb = D_parse_color(brdcolor->answer, TRUE);
 
+
+    black = D_translate_color(DEFAULT_FG_COLOR);
+    white = D_translate_color(DEFAULT_BG_COLOR);
+
     if (opt_font->answer)
 	D_font(opt_font->answer);
     else if (opt_path->answer)
@@ -621,8 +711,8 @@
 
     if (do_smooth) {
 	int wleg, lleg, dx, dy;
-	double txsiz;
-	int ppl;
+    double txsiz, coef;
+    double ppl;
 	int tcell;
 	float ScaleFactor = 1.0;
 
@@ -671,23 +761,46 @@
 	if (!fp) {		/* cut down labelnum so they don't repeat */
 	    if (do_cats < steps)
 		steps = do_cats;
-	    if (1 == steps)
+        if ((steps < 2))
 		steps = 2;	/* ward off the ppl floating point exception */
 	}
 
+
+    /* Draw text and ticks */
+    if (!horiz)
+    txsiz = (y1 - y0) / 20;
+    else
+    txsiz = (x1 - x0) / 20;
+
+    /* scale text to fit in window if position not manually set */
+    /* usually not needed, except when frame is really narrow   */
+    if (opt_at->answer == NULL) {	/* ie default scaling */
+    ScaleFactor = ((r - x1) / ((MaxLabelLen + 1) * txsiz * 0.81));	/* ?? txsiz*.81=actual text width. */
+    if (ScaleFactor < 1.0) {
+        txsiz = txsiz * ScaleFactor;
+    }
+    }
+
+    if (opt_fontsize->answer != NULL)
+    txsiz = fontsize;
+
+    if (txsiz < 0)
+    txsiz = 0;	/* keep it sane */
+
+    D_text_size(txsiz, txsiz);
+    D_use_color(color);
+
+
+    if (steps >= 2) {
 	for (k = 0; k < steps; k++) {
 	    if (!fp) {
 		if (!flip)
 		    tcell =
-			min_ind + k * (double)(max_ind - min_ind) / (steps -
-								     1);
+                min_ind + k * (double)(max_ind - min_ind) / (steps - 1);
 		else
 		    tcell =
-			max_ind - k * (double)(max_ind - min_ind) / (steps -
-								     1);
+                max_ind - k * (double)(max_ind - min_ind) / (steps - 1);
 
-		cstr = Rast_get_c_cat(&tcell, &cats);
-
 		if (!cstr[0])	/* no cats found, disable str output */
 		    hide_catstr = 1;
 		else
@@ -713,78 +826,163 @@
 			val = dmax - k * (dmax - dmin) / (steps - 1);
 
 		    sprintf(buff, DispFormat, val);
+
 		}
 	    }
 
-	    /* this probably shouldn't happen mid-loop as text sizes
-	       might not end up being uniform, but it's a start */
+            ppl = (lleg) / (steps * 1.0 - 1);
+
+            if (!hide_catnum) {
+                if (!horiz) {
+                    D_pos_abs(x1 + label_indent, y0 + ppl * k + txsiz / 2);
+                    if (show_ticks)
+                        D_line_abs(x1, y0 + ppl * k, x1 + 6, y0 + ppl * k);
+                }
+                else {
+                /* text width is 0.81 of text height? so even though we set width
+                   to txsiz with D_text_size(), we still have to reduce.. hmmm */
+                    D_pos_abs(x0 + ppl * k - (strlen(buff) * txsiz * .81 / 2),
+                              y1 + label_indent + txsiz);
+                    if (show_ticks)
+                        D_line_abs(x0 + ppl * k, y1, x0 + ppl * k, y1 + 6);
+                }
+
 	    if (strlen(buff) > MaxLabelLen)
 		MaxLabelLen = strlen(buff);
 
-	    /* Draw text */
-	    if (!horiz)
-		txsiz = (y1 - y0) / 20;
-	    else
-		txsiz = (x1 - x0) / 20;
+                if (color)
+                D_text(buff);
+            }
 
-	    /* scale text to fit in window if position not manually set */
-	    /* usually not needed, except when frame is really narrow   */
-	    if (opt_at->answer == NULL) {	/* ie default scaling */
-		ScaleFactor = ((r - x1) / ((MaxLabelLen + 1) * txsiz * 0.81));	/* ?? txsiz*.81=actual text width. */
-		if (ScaleFactor < 1.0) {
-		    txsiz = txsiz * ScaleFactor;
+        }	/* for */
 		}
+
+    lleg = y1 - y0;
+    wleg = x1 - x0;
+
+    /* draw tick_values labels and ticks */
+    if (ticksCount > 0) {
+        for (i = 0; i < ticksCount; i++) {
+            if ((tick_values[i] < dmin) || (tick_values[i] > dmax)) {
+                G_fatal_error(_("tick_value=%s out of range [%.3f, %.3f]"),
+                      opt_ticks->answers[i], dmin, dmax);
 	    }
+            coef = (tick_values[i] - dmin) / ((dmax - dmin) * 1.0);
+            sprintf(buff, DispFormat, tick_values[i]);
+            if (!flip){
+                if (!horiz) {
+                    if (show_ticks)
+                        D_line_abs(x1, y0 + coef * lleg,
+                                   x1 + 6, y0 + coef * lleg);
+                    D_pos_abs(x1 + label_indent, y0 + coef * lleg + txsiz/2);}
+                else {
+                    if (show_ticks)
+                        D_line_abs(x0 + coef * wleg, y1,
+                                   x0 + coef * wleg, y1 + 6);
+                    D_pos_abs(x0 + coef * wleg - (strlen(buff) * txsiz * .81 / 2),
+                              y1 + label_indent + txsiz);}
+            }
+            else{
+                if (!horiz) {
+                    if (show_ticks)
+                        D_line_abs(x1, y1 - coef * lleg,
+                                   x1 + 6, y1 - coef * lleg);
+                    D_pos_abs(x1 + label_indent, y1 - coef * lleg + txsiz/2);}
+                else {
+                    if (show_ticks)
+                        D_line_abs(x1 - coef * wleg, y1,
+                                   x1 - coef * wleg, y1 + 6);
+                    D_pos_abs(x1 - coef * wleg - (strlen(buff) * txsiz * .81 / 2),
+                              y1 + label_indent + txsiz);}
+            }
+            if (strlen(buff) > MaxLabelLen)
+            MaxLabelLen = strlen(buff);
 
-	    if (opt_fontsize->answer != NULL)
-		txsiz = fontsize;
+            D_text(buff);
+        }
+    }
 
-	    if (txsiz < 0)
-		txsiz = 0;	/* keep it sane */
+    /* draw tick_step */
+    if (opt_tstep->answer) {
+        t_start = ceil(dmin/t_step) * t_step;
+        if (t_start == -0)
+            t_start = 0;
 
-	    D_text_size(txsiz, txsiz);
-	    D_use_color(color);
+        if (!flip){
+            if (!horiz)
+                while (t_start<dmax) {
+                    sprintf(buff, DispFormat, t_start);
+                    coef = (t_start - dmin) / ((dmax - dmin) * 1.0);
+                    if (show_ticks)
+                        D_line_abs(x1, y0 + coef * lleg,
+                                   x1 + 6, y0 + coef * lleg);
+                    D_pos_abs(x1 + label_indent, y0 + coef * lleg + txsiz/2);
 
-	    ppl = (lleg) / (steps - 1);
+                    if (strlen(buff) > MaxLabelLen)
+                    MaxLabelLen = strlen(buff);
 
-	    if (!horiz) {
-		if (!k)		/* first  */
-		    D_pos_abs(x1 + 4, y0 + txsiz);
-		else if (k == steps - 1)	/* last */
-		    D_pos_abs(x1 + 4, y1);
+                    D_text(buff);
+
+                    t_start += t_step;}
 		else
-		    D_pos_abs(x1 + 4, y0 + ppl * k + txsiz / 2);
+                while (t_start<dmax) {
+                    sprintf(buff, DispFormat, t_start);
+                    coef = (t_start - dmin) / ((dmax - dmin) * 1.0);
+                    if (show_ticks)
+                        D_line_abs(x0 + coef * wleg,y1,
+                                   x0 + coef * wleg, y1 + 6);
+                    D_pos_abs(x0 + coef * wleg - (strlen(buff) * txsiz * .81 / 2),
+                              y1 + label_indent + txsiz);
+
+                    if (strlen(buff) > MaxLabelLen)
+                    MaxLabelLen = strlen(buff);
+
+                    D_text(buff);
+
+                    t_start += t_step;}
 	    }
 	    else {
-		/* text width is 0.81 of text height? so even though we set width
-		   to txsiz with D_text_size(), we still have to reduce.. hmmm */
-		if (!k)		/* first  */
-		    D_pos_abs(x0 - (strlen(buff) * txsiz * .81 / 2),
-			      y1 + 4 + txsiz);
-		else if (k == steps - 1)	/* last */
-		    D_pos_abs(x1 - (strlen(buff) * txsiz * .81 / 2),
-			      y1 + 4 + txsiz);
+            if (!horiz)
+                while (t_start<dmax) {
+                    sprintf(buff, DispFormat, t_start);
+                    coef = (t_start - dmin) / ((dmax - dmin) * 1.0);
+                    if (show_ticks)
+                        D_line_abs(x1, y1 - coef * lleg,
+                                   x1 + 6, y1 - coef * lleg);
+                    D_pos_abs(x1 + label_indent, y1 - coef * lleg + txsiz/2);
+
+                    if (strlen(buff) > MaxLabelLen)
+                    MaxLabelLen = strlen(buff);
+
+                    D_text(buff);
+
+                    t_start += t_step;}
 		else
-		    D_pos_abs(x0 + ppl * k -
-			      (strlen(buff) * txsiz * .81 / 2),
-			      y1 + 4 + txsiz);
-	    }
+                while (t_start<dmax) {
+                    sprintf(buff, DispFormat, t_start);
+                    coef = (t_start - dmin) / ((dmax - dmin) * 1.0);
+                    if (show_ticks)
+                        D_line_abs(x1 - coef * wleg, y1,
+                                   x1 - coef * wleg, y1 + 6);
+                    D_pos_abs(x1 - coef * wleg - (strlen(buff) * txsiz * .81 / 2),
+                              y1 + label_indent + txsiz);
 
-	    if (color)
+                    if (strlen(buff) > MaxLabelLen)
+                    MaxLabelLen = strlen(buff);
+
 		D_text(buff);
 
-	}	/* for */
+                    t_start += t_step;}
+        }
+    }
 
-	lleg = y1 - y0;
-	wleg = x1 - x0;
-
 	/* Black box */
 	D_use_color(black);
 	D_begin();
-	D_move_abs(x0 + 1, y0 + 1);
-	D_cont_rel(0, lleg - 2);
-	D_cont_rel(wleg - 2, 0);
-	D_cont_rel(0, 2 - lleg);
+    D_move_abs(x0, y0 );
+    D_cont_rel(0, lleg);
+    D_cont_rel(wleg, 0);
+    D_cont_rel(0, -lleg);
 	D_close();
 	D_end();
 	D_stroke();
@@ -792,15 +990,34 @@
 	/* White box */
 	D_use_color(white);
 	D_begin();
-	D_move_abs(x0, y0);
-	D_cont_rel(0, lleg);
-	D_cont_rel(wleg, 0);
-	D_cont_rel(0, -lleg);
+    D_move_abs(x0 + 2, y0 + 2);
+    D_cont_rel(0, lleg - 4);
+    D_cont_rel(wleg - 4, 0);
+    D_cont_rel(0, -lleg + 4);
 	D_close();
 	D_end();
 	D_stroke();
 
+    if(strlen(title) > 0){
+    /* Display title */
+        double x_tit, y_tit;
 
+        D_use_color(color);
+
+        if (horiz){
+            x_tit = (x0 + x1)/2. - (strlen(title) * txsiz * 0.81)/2;
+            y_tit = y0 - (txsiz);
+        }
+        else{
+            x_tit = x0;
+            y_tit = y0 - (txsiz);
+        }
+
+        D_pos_abs(x_tit, y_tit);
+        D_text(title);
+    }
+    else{
+    /* Display units label */
 	/* print units label, if present */
 	if (maptype == MAP_TYPE_RASTER2D)
 	    units = Rast_read_units(map_name, "");
@@ -821,10 +1038,10 @@
 
 	    if (horiz) {
 		x_pos = (x0 + x1)/2. - (strlen(units) * txsiz * 0.81)/2;
-		y_pos = y1 + (txsiz * 3);
+            y_pos = y1 + (txsiz * 2.75);
 	    }
 	    else {
-		x_pos = x1 - 4;
+            x_pos = x0;
 		if (default_pos)
 		    y_pos = y0 - (txsiz * 1.75);
 		else
@@ -834,6 +1051,7 @@
 	    D_pos_abs(x_pos, y_pos);
 	    D_text(units);
 	}
+    }
 
 
 	/* display sidebar histogram, if requested */
@@ -848,7 +1066,41 @@
 			   maptype, fp, render_range);
 	}
 
+
+    /* draw background */
+    if (show_bg){
+        double x0bg, y0bg, x1bg, y1bg;
+
+        if (!horiz){
+            x0bg = x0 - txsiz;
+            x1bg = x0 + wleg + label_indent + (MaxLabelLen * txsiz * 0.81) + txsiz;
+            y1bg = y0 + lleg + txsiz;
+            if (strlen(title) > 0)
+                y0bg = y0 - (2.5 * txsiz);
+            else
+                y0bg = y0 - txsiz;
     }
+        else{
+            x0bg = x0 - (MaxLabelLen * txsiz * 0.81)/2 - txsiz ;
+            x1bg = x0 + wleg + (MaxLabelLen * txsiz * 0.81)/2 + txsiz;
+            y1bg = y0 + lleg + label_indent + 1.5 * txsiz;
+            if (strlen(title) > 0)
+                y0bg = y0 - (2.5 * txsiz);
+            else
+                y0bg = y0 - txsiz;
+        }
+
+        D_use_color(colorb);
+        D_begin();
+        D_move_abs(x0bg, y0bg);
+        D_cont_abs(x0bg, y1bg);
+        D_cont_abs(x1bg, y1bg);
+        D_cont_abs(x1bg, y0bg);
+        D_close();
+        D_end();
+        D_stroke();
+    }
+    }
     else {	/* non FP, no smoothing */
 
 	int true_l, true_r;
@@ -938,9 +1190,9 @@
 
 	    k++;  /* count of actual boxes drawn (hide_nodata option invaidates using j-1) */
 
-	    /* White box */
+        /* Black box */
 	    cur_dot_row += dots_per_line;
-	    D_use_color(white);
+        D_use_color(black);
 	    D_begin();
 	    D_move_abs(l + 2, (cur_dot_row - 1));
 	    D_cont_rel(0, (3 - dots_per_line));
@@ -950,8 +1202,8 @@
 	    D_end();
 	    D_stroke();
 
-	    /* Black box */
-	    D_use_color(black);
+        /* White box */
+        D_use_color(white);
 	    D_begin();
 	    D_move_abs(l + 3, (cur_dot_row - 2));
 	    D_cont_rel(0, (5 - dots_per_line));
Index: .
===================================================================
--- .	(revision 68670)
+++ .	(working copy)


