1 /*
2 * wmslib/src/but/tblock.c, part of wmslib (Library functions)
3 * Copyright (C) 1994-1996 William Shubert.
4 * See "configure.h.in" for more copyright information.
5 */
6
7 #include <configure.h>
8
9 #ifdef X11_DISP
10
11 #ifdef STDC_HEADERS
12 #include <stdlib.h>
13 #include <unistd.h>
14 #endif /* STDC_HEADERS */
15 #include <stdio.h>
16 #include <X11/Xlib.h>
17 #include <X11/Xutil.h>
18 #include <X11/cursorfont.h>
19 #include <X11/Xatom.h>
20 #include <X11/keysym.h>
21 #include <sys/time.h>
22 #ifdef HAVE_SYS_SELECT_H
23 #include <sys/select.h>
24 #endif
25 #include <wms.h>
26 #include <wms/snd.h>
27 #include <but/but.h>
28 #include <but/box.h>
29 #include <but/tblock.h>
30 #include <but/text.h>
31
32
33 /**********************************************************************
34 * Data Types
35 **********************************************************************/
36 typedef struct Word_struct {
37 int start, len, xoff, yoff, w;
38 } Word;
39
40 typedef struct {
41 char *str;
42 int nwords;
43 Word *words;
44 ButTextAlign align;
45 int font;
46 int setup_w;
47 } Tb;
48
49
50 /**********************************************************************
51 * Forward Declarations
52 **********************************************************************/
53 static void draw(But *but, int x,int y, int w,int h);
54 static ButOut destroy(But *but);
55
56
57 /**********************************************************************
58 * Globals
59 **********************************************************************/
60 static const ButAction action = {
61 NULL,NULL,NULL,NULL,
62 NULL,NULL, draw, destroy, but_flags, NULL};
63
64
65 /**********************************************************************
66 * Functions
67 **********************************************************************/
butTblock_create(ButWin * win,int layer,int flags,const char * text,ButTextAlign align)68 But *butTblock_create(ButWin *win, int layer, int flags,
69 const char *text, ButTextAlign align) {
70 But *but;
71 Tb *tb;
72
73 assert(MAGIC(win));
74 assert(MAGIC(&win->butsNoDraw));
75 tb = wms_malloc(sizeof(Tb));
76 but = but_create(win, tb, &action);
77 but->layer = layer;
78 but->flags = flags;
79
80 tb->str = NULL;
81 tb->nwords = 0;
82 tb->words = NULL;
83 tb->align = align;
84 tb->font = 0;
85 tb->setup_w = 0;
86 but_init(but);
87 if (text != NULL)
88 butTblock_setText(but, text);
89 return(but);
90 }
91
92
butTblock_setFont(But * but,int fontnum)93 void butTblock_setFont(But *but, int fontnum) {
94 Tb *tb = but->iPacket;
95
96 assert(but->action == &action);
97 tb->font = fontnum;
98 but_draw(but);
99 }
100
101
butTblock_getH(But * but)102 int butTblock_getH(But *but) {
103 Tb *tb = but->iPacket;
104 int fonth;
105
106 assert(but->action == &action);
107 fonth = but->win->env->fonts[tb->font]->ascent +
108 but->win->env->fonts[tb->font]->descent;
109 if (tb->nwords == 0)
110 return(fonth);
111 else
112 return(tb->words[tb->nwords - 1].yoff + fonth);
113 }
114
115
butTblock_getText(But * but)116 const char *butTblock_getText(But *but) {
117 Tb *tb;
118
119 assert(but->action == &action);
120 tb = but->iPacket;
121 return(tb->str);
122 }
123
124
butTblock_setText(But * but,const char * text)125 void butTblock_setText(But *but, const char *text) {
126 Tb *tb = but->iPacket;
127 ButEnv *env = but->win->env;
128 int w = tb->setup_w = but->w, txtH;
129 int len, numLines, wordNum, totalWidth, i, j;
130 int lastSpaceSeen, spacesAtLastSpace, spacesThisLine;
131 int widthAtLastSpace, widthNonSpace, widthOfCurrentChar;
132 int widthNonSpaceAtLastSpace;
133 bool nonSpaceSeen, prevCharWasSpace;
134 Word *words;
135 int *widths;
136 int wordWMax, wordWMin;
137
138 assert(but->action == &action);
139 txtH = env->fonts[tb->font]->ascent + env->fonts[tb->font]->descent;
140 if (text != NULL) {
141 len = strlen(text);
142 if (tb->str != NULL)
143 wms_free(tb->str);
144 tb->str = wms_malloc((len + 1) * sizeof(char));
145 strcpy(tb->str, text);
146 }
147 if (w == 0)
148 return;
149 text = tb->str;
150 len = strlen(text);
151 if (tb->words != NULL)
152 wms_free(tb->words);
153 if (text == NULL) {
154 tb->words = NULL;
155 return;
156 }
157 tb->nwords = strlen(tb->str);
158 if (tb->nwords == 0)
159 return;
160 tb->words = words = wms_malloc(tb->nwords * sizeof(Word));
161 widths = wms_malloc(len * sizeof(int));
162 words[0].start = 0;
163 words[0].xoff = 0;
164 words[0].yoff = 0;
165 words[0].w = 0;
166 wordNum = totalWidth = widthNonSpace = 0;
167 widthAtLastSpace = 0;
168 spacesAtLastSpace = 0;
169 widthNonSpaceAtLastSpace = 0;
170 wordWMax = wordWMin = 0;
171 numLines = 1;
172 spacesThisLine = lastSpaceSeen = 0;
173 nonSpaceSeen = prevCharWasSpace = FALSE;
174 for (i = 0; i < len; ++i) {
175 widthOfCurrentChar = butEnv_charWidth(env, tb->str+i, tb->font);
176 assert(widthOfCurrentChar > 0);
177 widths[i] = widthOfCurrentChar;
178 if (tb->str[i] == ' ') {
179 if (!prevCharWasSpace) {
180 lastSpaceSeen = i;
181 widthAtLastSpace = totalWidth;
182 widthNonSpaceAtLastSpace = widthNonSpace;
183 spacesAtLastSpace = spacesThisLine;
184 }
185 if (nonSpaceSeen)
186 ++spacesThisLine;
187 else
188 widthNonSpace += widthOfCurrentChar;
189 prevCharWasSpace = TRUE;
190 } else {
191 if ((uchar)tb->str[i] < BUTWRITE_MINPRINT) {
192 ++i;
193 widths[i] = 0;
194 }
195 nonSpaceSeen = TRUE;
196 prevCharWasSpace = FALSE;
197 widthNonSpace += widthOfCurrentChar;
198 }
199 totalWidth += widthOfCurrentChar;
200 if (totalWidth > w) {
201 if (lastSpaceSeen != words[wordNum].start)
202 i = lastSpaceSeen;
203 words[wordNum].len = i - words[wordNum].start;
204 switch(tb->align) {
205 case butText_center:
206 words[wordNum].xoff += (w - widthAtLastSpace) / 2;
207 words[wordNum].w = widthAtLastSpace;
208 break;
209 case butText_right:
210 words[wordNum].xoff += (w - widthAtLastSpace);
211 words[wordNum].w = widthAtLastSpace;
212 break;
213 case butText_just:
214 widthNonSpace = widthNonSpaceAtLastSpace;
215 totalWidth = 0;
216 j = words[wordNum].start;
217 nonSpaceSeen = FALSE;
218 while (spacesAtLastSpace > 0) {
219 assert(j <= i);
220 if ((tb->str[j] == ' ') && nonSpaceSeen) {
221 widthOfCurrentChar = (w - widthNonSpace +
222 spacesAtLastSpace/2) / spacesAtLastSpace;
223 --spacesAtLastSpace;
224 widthNonSpace += widthOfCurrentChar;
225 totalWidth += widthOfCurrentChar;
226 if (tb->str[j - 1] != ' ') {
227 words[wordNum].len = j - words[wordNum].start;
228 assert(words[wordNum].w > 0);
229 assert(words[wordNum].w < w);
230 ++wordNum;
231 }
232 if (tb->str[j + 1] != ' ') {
233 assert(totalWidth < w);
234 words[wordNum].start = j + 1;
235 words[wordNum].xoff = totalWidth;
236 words[wordNum].yoff = txtH * (numLines - 1);
237 words[wordNum].w = 0;
238 }
239 } else {
240 if (tb->str[j] != ' ')
241 nonSpaceSeen = TRUE;
242 totalWidth += widths[j];
243 words[wordNum].w += widths[j];
244 if ((uchar)tb->str[j] < BUTWRITE_MINPRINT)
245 ++j;
246 }
247 ++j;
248 }
249 words[wordNum].len = i - tb->words[wordNum].start;
250 while ((tb->str[j] != ' ') && (tb->str[j] != '\0'))
251 words[wordNum].w += widths[j++];
252 break;
253 case butText_left:
254 words[wordNum].w = widthAtLastSpace;
255 break;
256 }
257 ++wordNum;
258 while (tb->str[i] == ' ') {
259 ++i;
260 widths[i] = butEnv_charWidth(env, tb->str+i, tb->font);
261 }
262 lastSpaceSeen = i;
263 widthNonSpace = totalWidth = widths[i];
264 spacesThisLine = 0;
265 nonSpaceSeen = TRUE;
266 prevCharWasSpace = FALSE;
267 words[wordNum].start = i;
268 words[wordNum].xoff = 0;
269 words[wordNum].yoff = txtH * numLines;
270 words[wordNum].w = 0;
271 ++numLines;
272
273 /*
274 * If this was a special character then step over it's second byte.
275 */
276 if ((uchar)tb->str[i] < BUTWRITE_MINPRINT) {
277 ++i;
278 widths[i] = 0;
279 }
280 }
281 }
282 words[wordNum].len = i - words[wordNum].start;
283 if (tb->align == butText_center)
284 words[wordNum].xoff += (w - totalWidth) / 2;
285 else if (tb->align == butText_right)
286 words[wordNum].xoff += (w - totalWidth);
287 words[wordNum].w = totalWidth;
288 tb->nwords = wordNum + 1;
289
290 /* We probably allocated way too many words, so allocate a new block
291 * of exactly the right size and move to it.
292 */
293 words = wms_malloc(tb->nwords * sizeof(Word));
294 memcpy(words, tb->words, tb->nwords * sizeof(Word));
295 for (i = 0; i < tb->nwords; ++i) {
296 words[i].w = 0;
297 for (j = words[i].start; j < words[i].start+words[i].len; ++j)
298 words[i].w += widths[j];
299 }
300 wms_free(widths);
301 wms_free(tb->words);
302 tb->words = words;
303 }
304
305
306 /* Returns the height of the text block after being resized. */
butTblock_resize(But * but,int x,int y,int w)307 int butTblock_resize(But *but, int x, int y, int w) {
308 int old_w = but->w, h;
309
310 assert(MAGIC(but));
311 assert(but->action == &action);
312 but->w = w;
313 butTblock_setText(but, NULL);
314 but->w = old_w;
315 but_resize(but, x, y, w, h = butTblock_getH(but));
316 return(h);
317 }
318
319
destroy(But * but)320 static ButOut destroy(But *but) {
321 Tb *tb = but->iPacket;
322
323 if (tb->str != NULL)
324 wms_free(tb->str);
325 if (tb->words != NULL)
326 wms_free(tb->words);
327 wms_free(tb);
328 return(0);
329 }
330
331
draw(But * but,int dx,int dy,int dw,int dh)332 static void draw(But *but, int dx, int dy, int dw, int dh) {
333 Tb *tb = but->iPacket;
334 ButEnv *env = but->win->env;
335 int i, x, y;
336 char temp;
337 int fonth;
338
339 if (but->w != tb->setup_w)
340 butTblock_setText(but, NULL);
341 fonth = env->fonts[tb->font]->ascent + env->fonts[tb->font]->descent;
342 butEnv_setXFg(env, BUT_FG);
343 x = but->x;
344 y = but->y;
345 for (i = 0; i < tb->nwords; ++i) {
346 assert((tb->words[i].xoff >= 0) &&
347 (tb->words[i].xoff < but->w) &&
348 (tb->words[i].yoff >= 0) &&
349 (tb->words[i].yoff < but->h));
350 assert((tb->words[i].w > 0) && (tb->words[i].w <= but->w));
351 if ((x+tb->words[i].xoff < dx+dw) &&
352 (y+tb->words[i].yoff < dy+dh) &&
353 (x+tb->words[i].xoff + tb->words[i].w >= dx) &&
354 (y+tb->words[i].yoff + fonth >= dy)) {
355 temp = tb->str[tb->words[i].start + tb->words[i].len];
356 tb->str[tb->words[i].start + tb->words[i].len] = '\0';
357 butWin_write(but->win, x+tb->words[i].xoff, y+tb->words[i].yoff,
358 tb->str+tb->words[i].start, tb->font);
359 tb->str[tb->words[i].start + tb->words[i].len] = temp;
360 }
361 }
362 }
363
364
butTblock_guessH(ButEnv * env,const char * text,int w,int fontNum)365 int butTblock_guessH(ButEnv *env, const char *text, int w, int fontNum) {
366 int curW = 0, curH;
367 int wLastSpace = 0;
368 int fontH;
369 int i;
370
371 fontH = butEnv_fontH(env, fontNum);
372 curH = fontH;
373 for (i = 0; text[i]; ++i) {
374 if (text[i] == ' ') {
375 curW += butEnv_charWidth(env, text+i, fontNum);
376 wLastSpace = curW;
377 if (curW >= w) {
378 while (i+1 == ' ')
379 ++i;
380 curH += fontH;
381 wLastSpace = curW = 0;
382 }
383 } else {
384 curW += butEnv_charWidth(env, text+i, fontNum);
385 if (text[i] <= '\2')
386 ++i;
387 if (curW > w) {
388 curH += fontH;
389 if (wLastSpace) {
390 curW -= wLastSpace;
391 wLastSpace = 0;
392 } else {
393 /* No spaces, break the word in half. */
394 curW = butEnv_charWidth(env, text+i, fontNum);
395 }
396 }
397 }
398 }
399 return(curH);
400 }
401
402
403 #endif
404