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