style-border.c 22.6 KB
Newer Older
1
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
Jody Goldberg's avatar
Jody Goldberg committed
2

3
/*
Jody Goldberg's avatar
Jody Goldberg committed
4 5
 * border.c: Managing drawing and printing cell borders
 *
6
 * Copyright (C) 1999-2001 Jody Goldberg (jody@gnome.org)
Jody Goldberg's avatar
Jody Goldberg committed
7 8 9 10 11
 *
 * 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 of the
 * License, or (at your option) any later version.
12
 *
Jody Goldberg's avatar
Jody Goldberg committed
13 14 15 16
 * 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.
17
 *
Jody Goldberg's avatar
Jody Goldberg committed
18 19
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
J.H.M. Dassen (Ray)'s avatar
J.H.M. Dassen (Ray) committed
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
Jody Goldberg's avatar
Jody Goldberg committed
21
 * USA
22
 */
23 24
#include <gnumeric-config.h>
#include "gnumeric.h"
25
#include "style-border.h"
26

27
#include "style-color.h"
28
#include "style.h"
29
#include "sheet-style.h"
30
#include "sheet.h"
31
#include <gdk/gdk.h>
32 33

struct LineDotPattern {
34 35 36
	const gint elements;
	const gint8 *const pattern;
	const double *const pattern_d;
37 38
};

39 40 41
static const gint8 dashed_pattern[] = { 3, 1 };
static const double dashed_pattern_d[] = { 3., 1. };
static const struct LineDotPattern dashed_line =
42
{ sizeof (dashed_pattern), dashed_pattern, dashed_pattern_d };
43

44 45 46
static const gint8 med_dashed_pattern[] = { 9, 3 };
static const double med_dashed_pattern_d[] = { 9., 3. };
static const struct LineDotPattern med_dashed_line =
47
{ sizeof (med_dashed_pattern), med_dashed_pattern, med_dashed_pattern_d };
48

49 50 51
static const gint8 dotted_pattern[] = { 2, 2 };
static const double dotted_pattern_d[] = { 2., 2. };
static const struct LineDotPattern dotted_line =
52
{ sizeof (dotted_pattern), dotted_pattern, dotted_pattern_d };
53

54 55 56
static const gint8 hair_pattern[] = { 1, 1 };
static const double hair_pattern_d[] = { 1., 1. };
static const struct LineDotPattern hair_line =
57
{ sizeof (hair_pattern), hair_pattern, hair_pattern_d };
58

59 60 61
static const gint8 dash_dot_pattern[] = { 8, 3, 3, 3 };
static const double dash_dot_pattern_d[] = { 8., 3., 3., 3. };
static const struct LineDotPattern dash_dot_line =
62
{ sizeof (dash_dot_pattern), dash_dot_pattern, dash_dot_pattern_d };
63

64 65 66
static const gint8 med_dash_dot_pattern[] = { 9, 3, 3, 3 };
static const double med_dash_dot_pattern_d[] = { 9., 3., 3., 3. };
static const struct LineDotPattern med_dash_dot_line =
67
{ sizeof (med_dash_dot_pattern), med_dash_dot_pattern, med_dash_dot_pattern_d };
68

69 70 71
static const gint8 dash_dot_dot_pattern[] = { 3, 3, 9, 3, 3, 3 };
static const double dash_dot_dot_pattern_d[] = { 3., 3., 9., 3., 3., 3. };
static const struct LineDotPattern dash_dot_dot_line =
72
{ sizeof (dash_dot_dot_pattern), dash_dot_dot_pattern, dash_dot_dot_pattern_d };
73

74 75 76
static const gint8 med_dash_dot_dot_pattern[] = { 3, 3, 3, 3, 9, 3 };
static const double med_dash_dot_dot_pattern_d[] = { 3., 3., 3., 3., 9., 3. };
static const struct LineDotPattern med_dash_dot_dot_line =
77
{ sizeof (med_dash_dot_dot_pattern), med_dash_dot_dot_pattern, med_dash_dot_dot_pattern_d };
78

79 80 81
static const gint8 slant_pattern[] = { 11, 1, 5, 1 };
static const double slant_pattern_d[] = { 11., 1., 5., 1. };
static const struct LineDotPattern slant_line =
82
{ sizeof (slant_pattern), slant_pattern, slant_pattern_d };
83 84

struct {
85 86 87 88
	gint width;
	gint offset;
	struct LineDotPattern const * pattern;
} static const style_border_data[] = {
89 90 91 92 93 94 95 96
	/* 0x0 : GNM_STYLE_BORDER_NONE */			{ 0, 0, NULL },
	/* 0x1 : GNM_STYLE_BORDER_THIN */			{ 0, 0, NULL },
	/* 0x2 : GNM_STYLE_BORDER_MEDIUM */			{ 2, 0, NULL },
	/* 0x3 : GNM_STYLE_BORDER_DASHED */			{ 1, 0, &dashed_line },
	/* 0x4 : GNM_STYLE_BORDER_DOTTED */			{ 1, 0, &dotted_line },
	/* 0x5 : GNM_STYLE_BORDER_THICK */			{ 3, 0, NULL },
	/* 0x6 : GNM_STYLE_BORDER_DOUBLE */			{ 0, 0, NULL },
	/* 0x7 : GNM_STYLE_BORDER_HAIR */			{ 1, 0, &hair_line },
Morten Welinder's avatar
Morten Welinder committed
97 98 99 100 101 102 103
	/* 0x8 : GNM_STYLE_BORDER_MEDIUM_DASH */		{ 2, 9, &med_dashed_line },
	/* 0x9 : GNM_STYLE_BORDER_DASH_DOT */		{ 1, 0, &dash_dot_line },
	/* 0xa : GNM_STYLE_BORDER_MEDIUM_DASH_DOT */	{ 2, 17,&med_dash_dot_line },
	/* 0xb : GNM_STYLE_BORDER_DASH_DOT_DOT */		{ 1, 0, &dash_dot_dot_line },
	/* 0xc : GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT */	{ 2, 21,&med_dash_dot_dot_line },
	/* 0xd : GNM_STYLE_BORDER_SLANTED_DASH_DOT */	{ 2, 6, &slant_line },/* How to slant */
	/* 0xe : GNM_STYLE_BORDER_INCONSISTENT */		{ 3, 0, &hair_line },
104 105
};

Michael Meeks's avatar
Michael Meeks committed
106
static GHashTable *border_hash = NULL;
107 108

static gint
Michael Meeks's avatar
Michael Meeks committed
109
style_border_equal (gconstpointer v1, gconstpointer v2)
110
{
Morten Welinder's avatar
Morten Welinder committed
111 112
	GnmBorder const *k1 = (GnmBorder const *) v1;
	GnmBorder const *k2 = (GnmBorder const *) v2;
113

Morten Welinder's avatar
Morten Welinder committed
114 115
	/*
	 * ->color is a pointer, but the comparison is safe because
116
	 * all colours are cached, see style_color_new_i16.
117 118
	 */
	return	(k1->color == k2->color) &&
119 120 121 122
		(k1->line_type == k2->line_type);
}

static guint
Michael Meeks's avatar
Michael Meeks committed
123
style_border_hash (gconstpointer v)
124
{
Morten Welinder's avatar
Morten Welinder committed
125
	GnmBorder const *b = (GnmBorder const *) v;
126

Morten Welinder's avatar
Morten Welinder committed
127 128 129 130
	/*
	 * HACK ALERT!
	 *
	 * ->color is a pointer, but the comparison is safe because
131
	 * all colours are cached, see style_color_new_i16.
Morten Welinder's avatar
Morten Welinder committed
132
	 *
133
	 */
134
	return (GPOINTER_TO_UINT(b->color) ^ b->line_type);
135 136
}

Morten Welinder's avatar
Morten Welinder committed
137
GnmBorder *
Morten Welinder's avatar
Morten Welinder committed
138
gnm_style_border_none (void)
139
{
Morten Welinder's avatar
Morten Welinder committed
140
	static GnmBorder * none = NULL;
141
	if (none == NULL) {
Morten Welinder's avatar
Morten Welinder committed
142
		none = g_new0 (GnmBorder, 1);
Morten Welinder's avatar
Morten Welinder committed
143
		none->line_type = GNM_STYLE_BORDER_NONE;
144
		none->color = style_color_grid ();
145
		none->begin_margin = none->end_margin = none->width = 0;
146 147 148 149 150 151 152 153
		none->ref_count = 1;
	}

	g_return_val_if_fail (none != NULL, NULL);

	return none;
}

154
/**
Morten Welinder's avatar
Morten Welinder committed
155
 * gnm_style_border_none_set_color:
156 157
 * @color :
 *
Morten Welinder's avatar
Morten Welinder committed
158 159
 * This function updates the color of gnm_style_border_none when the wanted grid
 * color is known. gnm_style_border_none tells how to render the grid. Because
160 161 162 163 164 165
 * the grid color may be different for different sheets, the functions which
 * render the grid call this function first.  The rule for selecting the
 * grid color, which is the same as in Excel, is: - if the auto pattern
 * color is default (which is black), the grid color is gray, as returned by
 * style_color_grid ().  - otherwise, the auto pattern color is used for the
 * grid.
166
 * NOTE : Absorbs a reference to @color.
167 168
 */
void
Morten Welinder's avatar
Morten Welinder committed
169
gnm_style_border_none_set_color (GnmColor *color)
170
{
Morten Welinder's avatar
Morten Welinder committed
171
	GnmBorder *none = gnm_style_border_none ();
172
	GnmColor *nc;
173

Morten Welinder's avatar
Morten Welinder committed
174
	if (color == none->color) {
Morten Welinder's avatar
Morten Welinder committed
175
		style_color_unref (color);
176
		return;
Morten Welinder's avatar
Morten Welinder committed
177
	}
Jody Goldberg's avatar
Jody Goldberg committed
178

179 180 181 182 183
	nc = none->color;
	none->color = color;
	style_color_unref (nc);
}

184
/**
Morten Welinder's avatar
Morten Welinder committed
185
 * gnm_style_border_fetch :
186
 *
187 188 189 190
 * @line_type : dash style
 * @color : colour
 * @orientation : Not currently used.
 *
Morten Welinder's avatar
Morten Welinder committed
191
 * Fetches a GnmBorder from the cache, creating one if necessary.  Absorbs
192 193
 * the colour reference.  In the future we may have different dash styles for
 * the same pattern depending on whether this is a horizontal or vertical line.
194
 */
Morten Welinder's avatar
Morten Welinder committed
195
GnmBorder *
196 197
gnm_style_border_fetch (GnmStyleBorderType		 line_type,
			GnmColor			*color,
Morten Welinder's avatar
Morten Welinder committed
198
			GnmStyleBorderOrientation	 orientation)
199
{
Morten Welinder's avatar
Morten Welinder committed
200 201
	GnmBorder *border;
	GnmBorder key;
202

Morten Welinder's avatar
Morten Welinder committed
203
	if (line_type < GNM_STYLE_BORDER_NONE || line_type > GNM_STYLE_BORDER_MAX) {
204
		g_warning ("Invalid border type: %d", line_type);
Morten Welinder's avatar
Morten Welinder committed
205
		line_type = GNM_STYLE_BORDER_NONE;
206
	}
207

Morten Welinder's avatar
Morten Welinder committed
208
	if (line_type == GNM_STYLE_BORDER_NONE) {
209 210
		if (color)
			style_color_unref (color);
Morten Welinder's avatar
Morten Welinder committed
211
		return gnm_style_border_ref (gnm_style_border_none ());
212
	}
213

214
	g_return_val_if_fail (color != NULL, NULL);
215 216 217
	key.line_type = line_type;
	key.color = color;

Michael Meeks's avatar
Michael Meeks committed
218 219
	if (border_hash) {
		border = g_hash_table_lookup (border_hash, &key);
220 221 222
		if (border != NULL) {
			if (color)
				style_color_unref (color);
Morten Welinder's avatar
Morten Welinder committed
223
			return gnm_style_border_ref (border);
224
		}
225
	} else
Michael Meeks's avatar
Michael Meeks committed
226 227
		border_hash = g_hash_table_new (style_border_hash,
						style_border_equal);
228

Morten Welinder's avatar
Morten Welinder committed
229
	border = g_new0 (GnmBorder, 1);
230
	*border = key;
Michael Meeks's avatar
Michael Meeks committed
231
	g_hash_table_insert (border_hash, border, border);
232
	border->ref_count = 1;
Morten Welinder's avatar
Morten Welinder committed
233 234
	border->width = gnm_style_border_get_width (line_type);
	if (border->line_type == GNM_STYLE_BORDER_DOUBLE) {
235 236 237 238 239 240
		border->begin_margin = 1;
		border->end_margin = 1;
	} else {
		border->begin_margin = (border->width) > 1 ? 1 : 0;
		border->end_margin = (border->width) > 2 ? 1 : 0;
	}
241 242 243 244

	return border;
}

245
gboolean
Morten Welinder's avatar
Morten Welinder committed
246
gnm_style_border_visible_in_blank (GnmBorder const *border)
247 248 249
{
	g_return_val_if_fail (border != NULL, FALSE);

Morten Welinder's avatar
Morten Welinder committed
250
	return border->line_type != GNM_STYLE_BORDER_NONE;
251 252
}

253
gint
Morten Welinder's avatar
Morten Welinder committed
254
gnm_style_border_get_width (GnmStyleBorderType const line_type)
255
{
Morten Welinder's avatar
Morten Welinder committed
256 257
	g_return_val_if_fail (line_type >= GNM_STYLE_BORDER_NONE, 0);
	g_return_val_if_fail (line_type < GNM_STYLE_BORDER_MAX, 0);
258

Morten Welinder's avatar
Morten Welinder committed
259
	if (line_type == GNM_STYLE_BORDER_NONE)
260 261
		return 0;

262 263 264
	return style_border_data [line_type].width;
}

Morten Welinder's avatar
Morten Welinder committed
265 266
GnmStyleBorderOrientation
gnm_style_border_get_orientation (GnmStyleBorderLocation type)
267 268
{
	switch (type) {
Morten Welinder's avatar
Morten Welinder committed
269 270 271 272 273 274 275 276
	case GNM_STYLE_BORDER_LEFT:
	case GNM_STYLE_BORDER_RIGHT:
		return GNM_STYLE_BORDER_VERTICAL;
	case GNM_STYLE_BORDER_DIAG:
	case GNM_STYLE_BORDER_REV_DIAG:
		return GNM_STYLE_BORDER_DIAGONAL;
	case GNM_STYLE_BORDER_TOP:
	case GNM_STYLE_BORDER_BOTTOM:
277
	default:
Morten Welinder's avatar
Morten Welinder committed
278
		return GNM_STYLE_BORDER_HORIZONTAL;
279
	}
280 281
}

Morten Welinder's avatar
Morten Welinder committed
282
GnmBorder *
Morten Welinder's avatar
Morten Welinder committed
283
gnm_style_border_ref (GnmBorder *border)
284
{
285
	/* NULL is ok */
Jody Goldberg's avatar
Jody Goldberg committed
286 287
	if (border != NULL)
		++border->ref_count;
288 289 290 291
	return border;
}

void
Morten Welinder's avatar
Morten Welinder committed
292
gnm_style_border_unref (GnmBorder *border)
293
{
Jody Goldberg's avatar
Jody Goldberg committed
294 295 296
	if (border == NULL)
		return;

297 298 299 300 301 302
	g_return_if_fail (border->ref_count > 0);

	border->ref_count--;
	if (border->ref_count != 0)
		return;

303 304 305 306
	/* Just to be on the safe side.
	 * We are allowed to deref the border_none,
	 * but not to free it.
	 */
Morten Welinder's avatar
Morten Welinder committed
307
	g_return_if_fail (border != gnm_style_border_none ());
308

309 310 311
	/* Remove here, before we mess with the hashed fields.  */
	g_hash_table_remove (border_hash, border);

312 313 314 315 316
	if (border->color) {
		style_color_unref (border->color);
		border->color = NULL;
	}

317 318 319
	g_free (border);
}

320
static gboolean
Morten Welinder's avatar
Morten Welinder committed
321
style_border_hmargins (GnmBorder const * const * prev_vert,
322
		       GnmStyleRow const *sr, int col,
323
		       int offsets [2][2], int dir)
324
{
Morten Welinder's avatar
Morten Welinder committed
325 326 327 328 329
	GnmBorder const *border = sr->top [col];
	GnmBorder const *t0 = prev_vert [col];
	GnmBorder const *t1 = prev_vert [col+1];
	GnmBorder const *b0 = sr->vertical [col];
	GnmBorder const *b1 = sr->vertical [col+1];
330

Morten Welinder's avatar
Morten Welinder committed
331
	if (border->line_type == GNM_STYLE_BORDER_DOUBLE) {
332
		/* pull inwards or outwards */
Morten Welinder's avatar
Morten Welinder committed
333 334
		if (!gnm_style_border_is_blank (t0)) {
			if (t0->line_type == GNM_STYLE_BORDER_DOUBLE)
335
				offsets [1][0] =  dir * t0->end_margin;
336
			else
337
				offsets [1][0] = -dir * t0->begin_margin;
Morten Welinder's avatar
Morten Welinder committed
338
		} else if (!gnm_style_border_is_blank (b0))
339
			offsets [1][0] = -dir * b0->begin_margin;
340 341 342
		else
			offsets [1][0] = 0;

Morten Welinder's avatar
Morten Welinder committed
343 344
		if (!gnm_style_border_is_blank (t1)) {
			if (t1->line_type == GNM_STYLE_BORDER_DOUBLE)
345
				offsets [1][1] = -dir * t1->begin_margin;
346
			else
347
				offsets [1][1] =  dir * t1->end_margin;
Morten Welinder's avatar
Morten Welinder committed
348
		} else if (!gnm_style_border_is_blank (b1))
349
			offsets [1][1] =  dir * b1->end_margin;
350 351 352
		else
			offsets [1][1] = 0;

Morten Welinder's avatar
Morten Welinder committed
353 354
		if (!gnm_style_border_is_blank (b0)) {
			if (b0->line_type == GNM_STYLE_BORDER_DOUBLE)
355
				offsets [0][0] =  dir * b0->end_margin;
356
			else
357
				offsets [0][0]= -dir * b0->begin_margin;
Morten Welinder's avatar
Morten Welinder committed
358
		} else if (!gnm_style_border_is_blank (t0))
359
			offsets [0][0]= -dir * t0->begin_margin;
360 361 362
		else
			offsets [0][0]= 0;

Morten Welinder's avatar
Morten Welinder committed
363 364
		if (!gnm_style_border_is_blank (b1)) {
			if (b1->line_type == GNM_STYLE_BORDER_DOUBLE)
365
				offsets [0][1] = -dir * b1->begin_margin;
366
			else
367
				offsets [0][1] =  dir * b1->end_margin;
Morten Welinder's avatar
Morten Welinder committed
368
		} else if (!gnm_style_border_is_blank (t1))
369
			offsets [0][1] =  dir * t1->end_margin;
370 371 372 373
		else
			offsets [0][1] = 0;
		return TRUE;
	}
374

375
	offsets [0][0] = offsets [0][1] = 0;
Morten Welinder's avatar
Morten Welinder committed
376
	if (border->line_type == GNM_STYLE_BORDER_NONE) {
377 378 379
		/* No need to check for show grid.  That is done when the
		 * borders are loaded.  Do not over write background patterns
		 */
Morten Welinder's avatar
Morten Welinder committed
380
		if (!gnm_style_border_is_blank (b0))
381
			offsets [0][0] = dir *(1 + b0->end_margin);
Morten Welinder's avatar
Morten Welinder committed
382
		else if (!gnm_style_border_is_blank (t0))
383
			offsets [0][0] = dir *(1 + t0->end_margin);
384
		else if (sr->top [col-1] == NULL)
385
			offsets [0][0] = dir;
386

Morten Welinder's avatar
Morten Welinder committed
387
		if (!gnm_style_border_is_blank (b1))
388
			offsets [0][1] = -dir * (1 - b1->begin_margin);
Morten Welinder's avatar
Morten Welinder committed
389
		else if (!gnm_style_border_is_blank (t1))
390
			offsets [0][1] = -dir * (1 - t1->begin_margin);
391
		else if (sr->top [col+1] == NULL)
392
			offsets [0][1] = -dir;
393 394
	} else {
		/* pull outwards */
Morten Welinder's avatar
Morten Welinder committed
395
		if (gnm_style_border_is_blank (sr->top [col-1])) {
396
			int offset = 0;
Morten Welinder's avatar
Morten Welinder committed
397
			if (!gnm_style_border_is_blank (b0))
398
				offset = b0->begin_margin;
Morten Welinder's avatar
Morten Welinder committed
399
			if (!gnm_style_border_is_blank (t0)) {
400
				int tmp = t0->begin_margin;
401 402
				if (offset < tmp)
					offset = tmp;
403
			}
404
			offsets [0][0] = -dir * offset;
405
		}
406

Morten Welinder's avatar
Morten Welinder committed
407
		if (gnm_style_border_is_blank (sr->top [col+1])) {
408
			int offset = 0;
Morten Welinder's avatar
Morten Welinder committed
409
			if (!gnm_style_border_is_blank (b1))
410
				offset = b1->end_margin;
Morten Welinder's avatar
Morten Welinder committed
411
			if (!gnm_style_border_is_blank (t1)) {
412
				int tmp = t1->end_margin;
413 414
				if (offset < tmp)
					offset = tmp;
415
			}
416
			offsets [0][1] = dir * offset;
417 418
		}
	}
419
	return FALSE;
420 421
}

422
static gboolean
Morten Welinder's avatar
Morten Welinder committed
423
style_border_vmargins (GnmBorder const * const * prev_vert,
424
		       GnmStyleRow const *sr, int col,
425
		       int offsets [2][2])
426
{
Morten Welinder's avatar
Morten Welinder committed
427 428 429 430 431
	GnmBorder const *border = sr->vertical [col];
	GnmBorder const *l0 = sr->top [col-1];
	GnmBorder const *r0 = sr->top [col];
	GnmBorder const *l1 = sr->bottom [col-1];
	GnmBorder const *r1 = sr->bottom [col];
432

Morten Welinder's avatar
Morten Welinder committed
433
	if (border->line_type == GNM_STYLE_BORDER_DOUBLE) {
434
		/* pull inwards or outwards */
Morten Welinder's avatar
Morten Welinder committed
435
		if (!gnm_style_border_is_blank (l0))
436
			offsets [1][0] =  l0->end_margin;
Morten Welinder's avatar
Morten Welinder committed
437
		else if (!gnm_style_border_is_blank (r0))
438
			offsets [1][0] = -r0->begin_margin;
439 440 441
		else
			offsets [1][0] = 0;

Morten Welinder's avatar
Morten Welinder committed
442
		if (!gnm_style_border_is_blank (l1))
443
			offsets [1][1] = -l1->begin_margin;
Morten Welinder's avatar
Morten Welinder committed
444
		else if (!gnm_style_border_is_blank (r1))
445
			offsets [1][1] =  r1->end_margin;
446 447 448
		else
			offsets [1][1] = 0;

Morten Welinder's avatar
Morten Welinder committed
449
		if (!gnm_style_border_is_blank (r0))
450
			offsets [0][0] = r0->end_margin;
Morten Welinder's avatar
Morten Welinder committed
451
		else if (!gnm_style_border_is_blank (l0))
452
			offsets [0][0] = -l0->begin_margin;
453 454 455
		else
			offsets [0][0] = 0;

Morten Welinder's avatar
Morten Welinder committed
456
		if (!gnm_style_border_is_blank (r1))
457
			offsets [0][1] = -r1->begin_margin;
Morten Welinder's avatar
Morten Welinder committed
458
		else if (!gnm_style_border_is_blank (l1))
459
			offsets [0][1] =  l1->end_margin;
460 461 462
		else
			offsets [0][1] = 0;
		return TRUE;
Jody Goldberg's avatar
Jody Goldberg committed
463
	}
464 465

	offsets [0][0] = offsets [0][1] = 0;
Morten Welinder's avatar
Morten Welinder committed
466
	if (border->line_type == GNM_STYLE_BORDER_NONE) {
467 468 469
		/* No need to check for show grid.  That is done when the
		 * borders are loaded.
		 */
Morten Welinder's avatar
Morten Welinder committed
470
		if (!gnm_style_border_is_blank (r0))
471
			offsets [0][0] = 1 + r0->end_margin;
Morten Welinder's avatar
Morten Welinder committed
472
		else if (!gnm_style_border_is_blank (l0))
473
			offsets [0][0] = 1 + l0->end_margin;
474 475 476 477
		/* Do not over write background patterns */
		else if (prev_vert [col] == NULL)
			offsets [0][0] = 1;

Morten Welinder's avatar
Morten Welinder committed
478
		if (!gnm_style_border_is_blank (r1))
479
			offsets [0][1] = -1 - r1->begin_margin;
Morten Welinder's avatar
Morten Welinder committed
480
		else if (!gnm_style_border_is_blank (l1))
481
			offsets [0][1] = -1 - l1->begin_margin;
482 483 484 485 486
		/* Do not over write background patterns */
		else if (sr->vertical [col] == NULL)
			offsets [0][1] = -1;
	} else {
		/* pull inwards */
487
		int offset = 0;
Morten Welinder's avatar
Morten Welinder committed
488
		if (!gnm_style_border_is_blank (r0))
489
			offset = 1 + r0->end_margin;
Morten Welinder's avatar
Morten Welinder committed
490
		if (!gnm_style_border_is_blank (l0)) {
491 492 493
			int tmp = 1 + l0->end_margin;
			if (offset < tmp)
				offset = tmp;
494
		}
495 496 497
		offsets [0][0] = offset;

		offset = 0;
Morten Welinder's avatar
Morten Welinder committed
498
		if (!gnm_style_border_is_blank (r1))
499
			offset = 1 + r1->begin_margin;
Morten Welinder's avatar
Morten Welinder committed
500
		if (!gnm_style_border_is_blank (l1)) {
501 502 503
			int tmp = 1 + l1->begin_margin;
			if (offset < tmp)
				offset = tmp;
504
		}
505
		offsets [0][1] = -offset;
506 507 508
	}
	return FALSE;
}
509

510 511
void
gnm_style_border_set_dash (GnmStyleBorderType const i,
512 513 514 515 516 517 518 519 520 521 522 523 524
			   cairo_t *context)
{
	int w;

	g_return_if_fail (context != NULL);
	g_return_if_fail (i >= GNM_STYLE_BORDER_NONE);
	g_return_if_fail (i < GNM_STYLE_BORDER_MAX);

	w = style_border_data[i].width;
	if (w == 0)
		w = 1;
	cairo_set_line_width (context,((double) w));

525 526 527 528 529 530
	if (style_border_data[i].pattern != NULL) {
		struct LineDotPattern const * const pat =
			style_border_data[i].pattern;
		cairo_set_dash (context, pat->pattern_d, pat->elements,
				style_border_data[i].offset);
	} else
531
		cairo_set_dash (context, NULL, 0, 0);
532 533 534 535 536 537 538 539 540
}

static inline gboolean
style_border_set_gtk (GnmBorder const * const border,
		      cairo_t *context)
{
	if (border == NULL)
		return FALSE;

541
	gnm_style_border_set_dash (border->line_type, context);
542 543
	cairo_set_source_rgba (context,
			       GO_COLOR_TO_CAIRO (border->color->go_color));
544 545 546 547 548
	return TRUE;
}

static inline void
print_hline_gtk (cairo_t *context,
549
		 double x1, double x2, double y, int width)
550 551 552 553 554 555 556 557 558 559 560 561
{
	if (width == 0 || width % 2)
		y += .5;

	/* exclude far pixel to match gdk */
	cairo_move_to (context, x1, y);
	cairo_line_to (context, x2, y);
	cairo_stroke (context);
}

static inline void
print_vline_gtk (cairo_t *context,
562
		 double x, double y1, double y2, int width, int dir)
563 564 565 566 567 568 569 570 571 572
{
	if (width == 0 || width % 2)
		x += .5*dir;

	/* exclude far pixel to match gdk */
	cairo_move_to (context, x, y1);
	cairo_line_to (context, x, y2);
	cairo_stroke (context);
}

573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
/**
 * gnm_style_borders_row_draw :
 *
 * TODO : This is not the final resting place for this.
 * It will move into the gui layer eventually.
 */
void
gnm_style_borders_row_draw (GnmBorder const * const * prev_vert,
			    GnmStyleRow const *sr,
			    cairo_t *cr,
			    int x, int y1, int y2,
			    int *colwidths,
			    gboolean draw_vertical, int dir)
{
	int o[2][2];
	int col, next_x = x;
	GnmBorder const *border;

	cairo_save (cr);

	for (col = sr->start_col; col <= sr->end_col ; col++, x = next_x) {

		if (colwidths[col] == -1)
			continue;
		next_x = x + dir * colwidths[col];

		border = sr->top [col];

		if (style_border_set_gtk (border, cr)) {
602
			double y = y1;
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
			if (style_border_hmargins (prev_vert, sr, col, o, dir)) {
				print_hline_gtk (cr, x + o[1][0],
						 next_x + o[1][1] + dir, y1-1.,
						 border->width);
				++y;
			}

			/* See note in gnm_style_border_set_gc_dash about +1 */
			print_hline_gtk (cr, x + o[0][0],
					 next_x + o[0][1] + dir, y, border->width);
		}

		if (!draw_vertical)
			continue;


		border = sr->vertical [col];
		if (style_border_set_gtk (border, cr)) {
621
			double x1 = x;
622 623 624 625 626 627 628 629 630 631 632 633 634
			if (style_border_vmargins (prev_vert, sr, col, o)) {
				print_vline_gtk (cr, x-dir, y1 + o[1][0],
						 y2 + o[1][1] + 1., border->width, dir);
				x1 += dir;
			}
			/* See note in gnm_style_border_set_gc_dash about +1 */
			print_vline_gtk (cr, x1, y1 + o[0][0],
					 y2 + o[0][1] + 1., border->width, dir);
		}
	}
	if (draw_vertical) {
		border = sr->vertical [col];
		if (style_border_set_gtk (border, cr)) {
635
			double x1 = x;
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
			if (style_border_vmargins (prev_vert, sr, col, o)) {
				print_vline_gtk (cr, x-dir, y1 + o[1][0] + 1.,
						 y2 + o[1][1], border->width, dir);
				x1 += dir;
			}
			/* See note in gnm_style_border_set_gc_dash about +1 */
			print_vline_gtk (cr, x1, y1 + o[0][0],
					 y2 + o[0][1] + 1, border->width, dir);
		}
	}

	cairo_restore (cr);
}

void
gnm_style_border_draw_diag (GnmStyle const *style,
			    cairo_t *cr,
			    int x1, int y1, int x2, int y2)
{
	GnmBorder const *diag;

	cairo_save (cr);

	diag = gnm_style_get_border (style, MSTYLE_BORDER_REV_DIAGONAL);
	if (diag != NULL && diag->line_type != GNM_STYLE_BORDER_NONE) {
		style_border_set_gtk (diag, cr);
		if (diag->line_type == GNM_STYLE_BORDER_DOUBLE) {
			cairo_move_to (cr, x1+1.5,  y1+3.);
			cairo_line_to (cr, x2-2.,   y2- .5);
			cairo_stroke (cr);
			cairo_move_to (cr, x1+ 3.,  y1+1.5);
			cairo_line_to (cr, x2-  .5, y2-2.);
		} else {
			cairo_move_to (cr, x1+.5, y1+.5);
			cairo_line_to (cr, x2+.5, y2+.5);
		}
		cairo_stroke (cr);
	}

	diag = gnm_style_get_border (style, MSTYLE_BORDER_DIAGONAL);
	if (diag != NULL && diag->line_type != GNM_STYLE_BORDER_NONE) {
		style_border_set_gtk (diag, cr);
		if (diag->line_type == GNM_STYLE_BORDER_DOUBLE) {
			cairo_move_to (cr, x1+1.5, y2-2.);
			cairo_line_to (cr, x2-2.,  y1+1.5);
			cairo_stroke (cr);
			cairo_move_to (cr, x1+3.,  y2- .5);
			cairo_line_to (cr, x2- .5, y1+3.);
		} else {
			cairo_move_to (cr, x1+.5, y2+.5);
			cairo_line_to (cr, x2+.5, y1+.5);
		}
		cairo_stroke (cr);
	}

	cairo_restore (cr);
}

694 695 696 697
void
gnm_style_borders_row_print_gtk (GnmBorder const * const * prev_vert,
				 GnmStyleRow const *sr,
				 cairo_t *context,
698
				 double x, double y1, double y2,
699 700 701 702
				 Sheet const *sheet,
				 gboolean draw_vertical, int dir)
{
	int o[2][2], col;
703
	double next_x = x;
704
	GnmBorder const *border;
705
	double const hscale = sheet->display_formulas ? 2 : 1;
706 707 708 709 710 711 712 713 714 715

	cairo_save (context);

	for (col = sr->start_col; col <= sr->end_col ; col++, x = next_x) {
		/* TODO : make this sheet agnostic.  Pass in an array of
		 * widths and a flag for whether or not to draw grids.
		 */
		ColRowInfo const *cri = sheet_col_get_info (sheet, col);
		if (!cri->visible)
			continue;
716
		next_x = x + dir * cri->size_pts * hscale;
717 718 719 720

		border = sr->top [col];

		if (style_border_set_gtk (border, context)) {
721
			double y = y1;
722 723 724 725 726 727 728 729 730 731 732 733 734 735
			if (style_border_hmargins (prev_vert, sr, col, o, dir)) {
				print_hline_gtk (context, x + o[1][0],
						 next_x + o[1][1] + dir, y1-1.,
						 border->width);
				++y;
			}

			print_hline_gtk (context, x + o[0][0],
					 next_x + o[0][1] + dir, y, border->width);
		}

		if (!draw_vertical)
			continue;

736

737 738
		border = sr->vertical [col];
		if (style_border_set_gtk (border, context)) {
739
			double x1 = x;
740 741 742 743 744 745 746 747 748 749 750 751
			if (style_border_vmargins (prev_vert, sr, col, o)) {
				print_vline_gtk (context, x-dir, y1 + o[1][0],
						 y2 + o[1][1] + 1., border->width, dir);
				x1 += dir;
			}
			print_vline_gtk (context, x1, y1 + o[0][0],
					 y2 + o[0][1] + 1., border->width, dir);
		}
	}
	if (draw_vertical) {
		border = sr->vertical [col];
		if (style_border_set_gtk (border, context)) {
752
			double x1 = x;
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
			if (style_border_vmargins (prev_vert, sr, col, o)) {
				print_vline_gtk (context, x-dir, y1 + o[1][0] + 1.,
						 y2 + o[1][1], border->width, dir);
				x1 += dir;
			}
			/* See note in gnm_style_border_set_gc_dash about +1 */
			print_vline_gtk (context, x1, y1 + o[0][0],
					 y2 + o[0][1] + 1, border->width, dir);
		}
	}

	cairo_restore (context);
}

void
gnm_style_border_print_diag_gtk (GnmStyle const *style,
769 770
				 cairo_t *context,
				 double x1, double y1, double x2, double y2)
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
{
	GnmBorder const *diag;


	cairo_save (context);

	diag = gnm_style_get_border (style, MSTYLE_BORDER_REV_DIAGONAL);
	if (diag != NULL && diag->line_type != GNM_STYLE_BORDER_NONE) {
		style_border_set_gtk (diag, context);
		if (diag->line_type == GNM_STYLE_BORDER_DOUBLE) {
			cairo_move_to (context, x1+1.5,  y1+3.);
			cairo_line_to (context, x2-2.,   y2- .5);
			cairo_stroke (context);
			cairo_move_to (context, x1+ 3.,  y1+1.5);
			cairo_line_to (context, x2-  .5, y2-2.);
		} else {
			cairo_move_to (context, x1+.5, y1+.5);
			cairo_line_to (context, x2+.5, y2+.5);
		}
		cairo_stroke (context);
	}

	diag = gnm_style_get_border (style, MSTYLE_BORDER_DIAGONAL);
	if (diag != NULL && diag->line_type != GNM_STYLE_BORDER_NONE) {
		style_border_set_gtk (diag, context);
		if (diag->line_type == GNM_STYLE_BORDER_DOUBLE) {
			cairo_move_to (context, x1+1.5, y2-2.);
			cairo_line_to (context, x2-2.,  y1+1.5);
			cairo_stroke (context);
			cairo_move_to (context, x1+3.,  y2- .5);
			cairo_line_to (context, x2- .5, y1+3.);
		} else {
			cairo_move_to (context, x1+.5, y2+.5);
			cairo_line_to (context, x2+.5, y1+.5);
		}
		cairo_stroke (context);
	}
808

809 810
	cairo_restore (context);
}