about summary refs log tree commit diff stats
path: root/draw.c
blob: 178770b27bf022f18c4295c459225342f0e7b6b0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/* (C)opyright MMIV-MMVII Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */
#include "dwm.h"
#include <stdio.h>
#include <string.h>

/* static */

static Bool
isoccupied(unsigned int t)
{
	Client *c;
	for(c = clients; c; c = c->next)
		if(c->tags[t])
			return True;
	return False;
}

static unsigned int
textnw(const char *text, unsigned int len) {
	XRectangle r;

	if(dc.font.set) {
		XmbTextExtents(dc.font.set, text, len, NULL, &r);
		return r.width;
	}
	return XTextWidth(dc.font.xfont, text, len);
}

static void
drawtext(const char *text, unsigned long col[ColLast], Bool filledsquare, Bool emptysquare) {
	int x, y, w, h;
	static char buf[256];
	unsigned int len, olen;
	XGCValues gcv;
	XRectangle r = { dc.x, dc.y, dc.w, dc.h };
	XPoint pt[5];

	XSetForeground(dpy, dc.gc, col[ColBG]);
	XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
	if(!text)
		return;
	w = 0;
	olen = len = strlen(text);
	if(len >= sizeof buf)
		len = sizeof buf - 1;
	memcpy(buf, text, len);
	buf[len] = 0;
	h = dc.font.ascent + dc.font.descent;
	y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
	x = dc.x + (h / 2);
	/* shorten text if necessary */
	while(len && (w = textnw(buf, len)) > dc.w - h)
		buf[--len] = 0;
	if(len < olen) {
		if(len > 1)
			buf[len - 1] = '.';
		if(len > 2)
			buf[len - 2] = '.';
		if(len > 3)
			buf[len - 3] = '.';
	}
	if(w > dc.w)
		return; /* too long */
	gcv.foreground = col[ColFG];
	if(dc.font.set) {
		XChangeGC(dpy, dc.gc, GCForeground, &gcv);
		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
	}
	else {
		gcv.font = dc.font.xfont->fid;
		XChangeGC(dpy, dc.gc, GCForeground | GCFont, &gcv);
		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
	}
	x = (h + 2) / 4;
	if(filledsquare) {
		r.x = dc.x + 1;
		r.y = dc.y + 1;
		r.width = r.height = x + 1;
		XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
	}
	else if(emptysquare) {
		pt[0].x = dc.x + 1;
		pt[0].y = dc.y + 1;
		pt[1].x = x;
		pt[1].y = 0;
		pt[2].x = 0;
		pt[2].y = x;
		pt[3].x = -x;
		pt[3].y = 0;
		pt[4].x = 0;
		pt[4].y = -x;
		XDrawLines(dpy, dc.drawable, dc.gc, pt, 5, CoordModePrevious);
	}
}

/* extern */

void
drawstatus(void) {
	int i, x;

	dc.x = dc.y = 0;
	for(i = 0; i < ntags; i++) {
		dc.w = textw(tags[i]);
		if(seltag[i])
			drawtext(tags[i], dc.sel, sel && sel->tags[i], isoccupied(i));
		else
			drawtext(tags[i], dc.norm, sel && sel->tags[i], isoccupied(i));
		dc.x += dc.w;
	}
	dc.w = bmw;
	drawtext(arrange == dofloat ? FLOATSYMBOL : TILESYMBOL, dc.norm, False, False);
	x = dc.x + dc.w;
	dc.w = textw(stext);
	dc.x = sw - dc.w;
	if(dc.x < x) {
		dc.x = x;
		dc.w = sw - x;
	}
	drawtext(stext, dc.norm, False, False);
	if((dc.w = dc.x - x) > bh) {
		dc.x = x;
		drawtext(sel ? sel->name : NULL, sel ? dc.sel : dc.norm, False, False);
	}
	XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
	XSync(dpy, False);
}

unsigned long
getcolor(const char *colstr) {
	Colormap cmap = DefaultColormap(dpy, screen);
	XColor color;

	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
		eprint("error, cannot allocate color '%s'\n", colstr);
	return color.pixel;
}

void
setfont(const char *fontstr) {
	char *def, **missing;
	int i, n;

	missing = NULL;
	if(dc.font.set)
		XFreeFontSet(dpy, dc.font.set);
	dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
	if(missing) {
		while(n--)
			fprintf(stderr, "missing fontset: %s\n", missing[n]);
		XFreeStringList(missing);
	}
	if(dc.font.set) {
		XFontSetExtents *font_extents;
		XFontStruct **xfonts;
		char **font_names;
		dc.font.ascent = dc.font.descent = 0;
		font_extents = XExtentsOfFontSet(dc.font.set);
		n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
		for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
			if(dc.font.ascent < (*xfonts)->ascent)
				dc.font.ascent = (*xfonts)->ascent;
			if(dc.font.descent < (*xfonts)->descent)
				dc.font.descent = (*xfonts)->descent;
			xfonts++;
		}
	}
	else {
		if(dc.font.xfont)
			XFreeFont(dpy, dc.font.xfont);
		dc.font.xfont = NULL;
		if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)))
			eprint("error, cannot load font: '%s'\n", fontstr);
		dc.font.ascent = dc.font.xfont->ascent;
		dc.font.descent = dc.font.xfont->descent;
	}
	dc.font.height = dc.font.ascent + dc.font.descent;
}

unsigned int
textw(const char *text) {
	return textnw(text, strlen(text)) + dc.font.height;
}
n> begin t := tree; if t = nil then begin result := newRope(s); if countCacheMisses then inc(misses); exit end; t := splay(s, t, cmp); if cmp = 0 then begin // We get here if it's already in the Tree // Don't add it again result := t; if countCacheMisses then inc(hits); end else begin if countCacheMisses then inc(misses); result := newRope(s); if cmp < 0 then begin result.left := t.left; result.right := t; t.left := nil end else begin // i > t.item: result.right := t.right; result.left := t; t.right := nil end end end; function RopeInvariant(r: PRope): Boolean; begin if r = nil then result := true else begin result := true (* if r.data <> snil then result := true else begin result := (r.left <> nil) and (r.right <> nil); if result then result := ropeInvariant(r.left); if result then result := ropeInvariant(r.right); end *) end end; function toRope(const s: string): PRope; begin if s = '' then result := nil else if cacheLeafs then begin result := insertInCache(s, cache); cache := result; end else result := newRope(s); assert(RopeInvariant(result)); end; // ------------------------------------------------------------------ procedure RopeSeqInsert(var rs: TRopeSeq; r: PRope; at: Natural); var len, i: int; begin len := length(rs); if at > len then SetLength(rs, at+1) else SetLength(rs, len+1); // move old rope elements: for i := len downto at+1 do rs[i] := rs[i-1]; // this is correct, I used pen and paper to validate it rs[at] := r end; function con(a, b: PRope): PRope; overload; begin assert(RopeInvariant(a)); assert(RopeInvariant(b)); if a = nil then // len is valid for every cord not only for leafs result := b else if b = nil then result := a else begin result := newRope(); result.len := a.len + b.len; result.left := a; result.right := b end; assert(RopeInvariant(result)); end; function con(a: PRope; const b: string): PRope; overload; var r: PRope; begin assert(RopeInvariant(a)); if b = '' then result := a else begin r := toRope(b); if a = nil then begin result := r end else begin result := newRope(); result.len := a.len + r.len; result.left := a; result.right := r; end end; assert(RopeInvariant(result)); end; function con(const a: string; b: PRope): PRope; overload; var r: PRope; begin assert(RopeInvariant(b)); if a = '' then result := b else begin r := toRope(a); if b = nil then result := r else begin result := newRope(); result.len := b.len + r.len; result.left := r; result.right := b; end end; assert(RopeInvariant(result)); end; function con(a: array of PRope): PRope; overload; var i: int; begin result := nil; for i := 0 to high(a) do result := con(result, a[i]); assert(RopeInvariant(result)); end; function toRope(i: BiggestInt): PRope; begin result := toRope(ToString(i)) end; function toRopeF(const r: BiggestFloat): PRope; begin result := toRope(toStringF(r)) end; procedure app(var a: PRope; b: PRope); overload; begin a := con(a, b); assert(RopeInvariant(a)); end; procedure app(var a: PRope; const b: string); overload; begin a := con(a, b); assert(RopeInvariant(a)); end; procedure prepend(var a: PRope; b: PRope); begin a := con(b, a); assert(RopeInvariant(a)); end; procedure InitStack(var stack: TRopeSeq); begin {@ignore} setLength(stack, 0); {@emit stack := @[];} end; procedure push(var stack: TRopeSeq; r: PRope); var len: int; begin len := length(stack); setLength(stack, len+1); stack[len] := r; end; function pop(var stack: TRopeSeq): PRope; var len: int; begin len := length(stack); result := stack[len-1]; setLength(stack, len-1); end; procedure WriteRopeRec(var f: TTextFile; c: PRope); begin assert(RopeInvariant(c)); if c = nil then exit; if (c.data <> snil) then begin nimWrite(f, c.data) end else begin writeRopeRec(f, c.left); writeRopeRec(f, c.right) end end; procedure newWriteRopeRec(var f: TTextFile; c: PRope); var stack: TRopeSeq; it: PRope; begin assert(RopeInvariant(c)); initStack(stack); push(stack, c); while length(stack) > 0 do begin it := pop(stack); while it.data = snil do begin push(stack, it.right); it := it.left; assert(it <> nil); end; assert(it.data <> snil); nimWrite(f, it.data); end end; procedure WriteRope(head: PRope; const filename: string); var f: TTextFile; // we use a textfile for automatic buffer handling begin if OpenFile(f, filename, fmWrite) then begin if head <> nil then newWriteRopeRec(f, head); nimCloseFile(f); end else rawMessage(errCannotOpenFile, filename); end; procedure recRopeToStr(var result: string; var resultLen: int; p: PRope); begin if p = nil then exit; // do not add to result if (p.data = snil) then begin recRopeToStr(result, resultLen, p.left); recRopeToStr(result, resultLen, p.right); end else begin CopyMem(@result[resultLen+StrStart], @p.data[strStart], p.len); Inc(resultLen, p.len); assert(resultLen <= length(result)); end end; procedure newRecRopeToStr(var result: string; var resultLen: int; r: PRope); var stack: TRopeSeq; it: PRope; begin initStack(stack); push(stack, r); while length(stack) > 0 do begin it := pop(stack); while it.data = snil do begin push(stack, it.right); it := it.left; end; assert(it.data <> snil); CopyMem(@result[resultLen+StrStart], @it.data[strStart], it.len); Inc(resultLen, it.len); assert(resultLen <= length(result)); end end; function ropeToStr(p: PRope): string; var resultLen: int; begin assert(RopeInvariant(p)); if p = nil then result := '' else begin result := newString(p.len); resultLen := 0; newRecRopeToStr(result, resultLen, p); end end; function ropef(const frmt: TFormatStr; const args: array of PRope): PRope; var i, j, len, start: int; begin i := strStart; len := length(frmt); result := nil; while i <= len + StrStart - 1 do begin if frmt[i] = '$' then begin inc(i); // skip '$' case frmt[i] of '$': begin app(result, '$'+''); inc(i); end; '0'..'9': begin j := 0; repeat j := (j*10) + Ord(frmt[i]) - ord('0'); inc(i); until (i > len + StrStart - 1) or not (frmt[i] in ['0'..'9']); if j > high(args)+1 then internalError('ropes: invalid format string $' + toString(j)); app(result, args[j-1]); end; 'N', 'n': begin app(result, tnl); inc(i); end; else InternalError('ropes: invalid format string $' + frmt[i]); end end; start := i; while (i <= len + StrStart - 1) do if (frmt[i] <> '$') then inc(i) else break; if i-1 >= start then begin app(result, ncopy(frmt, start, i-1)); end end; assert(RopeInvariant(result)); end; procedure appf(var c: PRope; const frmt: TFormatStr; const args: array of PRope); begin app(c, ropef(frmt, args)) end; const bufSize = 1024; // 1 KB is reasonable function auxRopeEqualsFile(r: PRope; var bin: TBinaryFile; buf: Pointer): Boolean; var readBytes: int; begin if (r.data <> snil) then begin if r.len > bufSize then // A token bigger than 1 KB? - This cannot happen in reality. // Well, at least I hope so. 1 KB did happen! internalError('ropes: token too long'); readBytes := readBuffer(bin, buf, r.len); result := (readBytes = r.len) // BUGFIX and equalMem(buf, addr(r.data[strStart]), r.len); end else begin result := auxRopeEqualsFile(r.left, bin, buf); if result then result := auxRopeEqualsFile(r.right, bin, buf); end end; function RopeEqualsFile(r: PRope; const f: string): Boolean; var bin: TBinaryFile; buf: Pointer; begin result := openFile(bin, f); if not result then exit; // not equal if file does not exist buf := alloc(BufSize); result := auxRopeEqualsFile(r, bin, buf); if result then result := readBuffer(bin, buf, bufSize) = 0; // really at the end of file? dealloc(buf); CloseFile(bin); end; function crcFromRopeAux(r: PRope; startVal: TCrc32): TCrc32; var i: int; begin if r.data <> snil then begin result := startVal; for i := strStart to length(r.data)+strStart-1 do result := updateCrc32(r.data[i], result); end else begin result := crcFromRopeAux(r.left, startVal); result := crcFromRopeAux(r.right, result); end end; function newCrcFromRopeAux(r: PRope; startVal: TCrc32): TCrc32; var stack: TRopeSeq; it: PRope; L, i: int; begin initStack(stack); push(stack, r); result := startVal; while length(stack) > 0 do begin it := pop(stack); while it.data = snil do begin push(stack, it.right); it := it.left; end; assert(it.data <> snil); i := strStart; L := length(it.data)+strStart; while i < L do begin result := updateCrc32(it.data[i], result); inc(i); end end end; function crcFromRope(r: PRope): TCrc32; begin result := newCrcFromRopeAux(r, initCrc32) end; function writeRopeIfNotEqual(r: PRope; const filename: string): boolean; // returns true if overwritten var c: TCrc32; begin c := crcFromFile(filename); if c <> crcFromRope(r) then begin writeRope(r, filename); result := true end else result := false end; initialization new(N); // init dummy node for splay algorithm {@ignore} fillChar(N^, sizeof(N^), 0); {@emit} end.