Clean up MENUQC #includes
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / draw.qc
1 #if defined(CSQC)
2 #elif defined(MENUQC)
3 #elif defined(SVQC)
4 #endif
5
6 string draw_mousepointer;
7 vector draw_mousepointer_offset;
8 vector draw_mousepointer_size;
9
10 void draw_setMousePointer(string pic, vector theSize, vector theOffset)
11 {
12         draw_mousepointer = strzone(draw_UseSkinFor(pic));
13         draw_mousepointer_size = theSize;
14         draw_mousepointer_offset = eX * (theOffset.x * theSize.x) + eY * (theOffset.y * theSize.y);
15 }
16
17 void draw_drawMousePointer(vector where)
18 {
19         drawpic(boxToGlobal(where, draw_shift, draw_scale) - draw_mousepointer_offset, draw_mousepointer, draw_mousepointer_size, '1 1 1', draw_alpha, 0);
20 }
21
22 void draw_reset(float cw, float ch, float ox, float oy)
23 {
24         draw_shift = '1 0 0' * ox + '0 1 0' * oy;
25         draw_scale = '1 0 0' * cw + '0 1 0' * ch;
26         draw_alpha = 1;
27         draw_fontscale = '1 1 0';
28         draw_endBoldFont();
29 }
30
31 void draw_beginBoldFont()
32 {
33         drawfont = FONT_USER+3;
34 }
35
36 void draw_endBoldFont()
37 {
38         drawfont = FONT_USER+0;
39 }
40
41 vector globalToBox(vector v, vector theOrigin, vector theScale)
42 {
43         v -= theOrigin;
44         v.x /= theScale.x;
45         v.y /= theScale.y;
46         return v;
47 }
48
49 vector globalToBoxSize(vector v, vector theScale)
50 {
51         v.x /= theScale.x;
52         v.y /= theScale.y;
53         return v;
54 }
55
56 vector boxToGlobal(vector v, vector theOrigin, vector theScale)
57 {
58         v.x *= theScale.x;
59         v.y *= theScale.y;
60         v += theOrigin;
61         return v;
62 }
63
64 vector boxToGlobalSize(vector v, vector theScale)
65 {
66         v.x *= theScale.x;
67         v.y *= theScale.y;
68         return v;
69 }
70
71 string draw_PreloadPicture(string pic)
72 {
73         pic = draw_UseSkinFor(pic);
74         return precache_pic(pic);
75 }
76
77 string draw_PreloadPictureWithFlags(string pic, float f)
78 {
79         pic = draw_UseSkinFor(pic);
80         return precache_pic(pic, f);
81 }
82
83 void draw_Picture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
84 {
85         if(theSize_x == 0 || theSize.y <= 0) // no default sizing please
86                 return;
87         pic = draw_UseSkinFor(pic);
88         drawpic(boxToGlobal(theOrigin, draw_shift, draw_scale), pic, boxToGlobalSize(theSize, draw_scale), theColor, theAlpha * draw_alpha, 0);
89 }
90
91 vector draw_PictureSize(string pic)
92 {
93         pic = draw_UseSkinFor(pic);
94         return drawgetimagesize(pic);
95 }
96
97 void draw_Fill(vector theOrigin, vector theSize, vector theColor, float theAlpha)
98 {
99         drawfill(boxToGlobal(theOrigin, draw_shift, draw_scale), boxToGlobalSize(theSize, draw_scale), theColor, theAlpha * draw_alpha, 0);
100 }
101
102 // a button picture is a texture containing three parts:
103 //   1/4 width: left part
104 //   1/2 width: middle part (stretched)
105 //   1/4 width: right part
106 // it is assumed to be 4x as wide as high for aspect ratio purposes, which
107 // means, the parts are a square, two squares and a square.
108 void draw_ButtonPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
109 {
110         vector square;
111         vector width, height;
112         vector bW;
113         pic = draw_UseSkinFor(pic);
114         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
115         theSize = boxToGlobalSize(theSize, draw_scale);
116         theAlpha *= draw_alpha;
117         width = eX * theSize.x;
118         height = eY * theSize.y;
119         if(theSize.x <= theSize.y * 2)
120         {
121                 // button not wide enough
122                 // draw just left and right part then
123                 square = eX * theSize.x * 0.5;
124                 bW = eX * (0.25 * theSize.x / (theSize.y * 2));
125                 drawsubpic(theOrigin,          square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, 0);
126                 drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, 0);
127         }
128         else
129         {
130                 square = eX * theSize.y;
131                 drawsubpic(theOrigin,                  height  +     square, pic, '0    0 0', '0.25 1 0', theColor, theAlpha, 0);
132                 drawsubpic(theOrigin +         square, theSize - 2 * square, pic, '0.25 0 0', '0.5  1 0', theColor, theAlpha, 0);
133                 drawsubpic(theOrigin + width - square, height  +     square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, 0);
134         }
135 }
136
137 // a vertical button picture is a texture containing three parts:
138 //   1/4 height: left part
139 //   1/2 height: middle part (stretched)
140 //   1/4 height: right part
141 // it is assumed to be 4x as high as wide for aspect ratio purposes, which
142 // means, the parts are a square, two squares and a square.
143 void draw_VertButtonPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
144 {
145         vector square;
146         vector width, height;
147         vector bH;
148         pic = draw_UseSkinFor(pic);
149         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
150         theSize = boxToGlobalSize(theSize, draw_scale);
151         theAlpha *= draw_alpha;
152         width = eX * theSize.x;
153         height = eY * theSize.y;
154         if(theSize.y <= theSize.x * 2)
155         {
156                 // button not high enough
157                 // draw just upper and lower part then
158                 square = eY * theSize.y * 0.5;
159                 bH = eY * (0.25 * theSize.y / (theSize.x * 2));
160                 drawsubpic(theOrigin,          square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, 0);
161                 drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, 0);
162         }
163         else
164         {
165                 square = eY * theSize.x;
166                 drawsubpic(theOrigin,                   width   +     square, pic, '0 0    0', '1 0.25 0', theColor, theAlpha, 0);
167                 drawsubpic(theOrigin +          square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5  0', theColor, theAlpha, 0);
168                 drawsubpic(theOrigin + height - square, width   +     square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, 0);
169         }
170 }
171
172 // a border picture is a texture containing nine parts:
173 //   1/4 width: left part
174 //   1/2 width: middle part (stretched)
175 //   1/4 width: right part
176 // divided into
177 //   1/4 height: top part
178 //   1/2 height: middle part (stretched)
179 //   1/4 height: bottom part
180 void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha, vector theBorderSize)
181 {
182         vector dX, dY;
183         vector width, height;
184         vector bW, bH;
185         pic = draw_UseSkinFor(pic);
186         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
187         theSize = boxToGlobalSize(theSize, draw_scale);
188         theBorderSize = boxToGlobalSize(theBorderSize, draw_scale);
189         theAlpha *= draw_alpha;
190         width = eX * theSize.x;
191         height = eY * theSize.y;
192         // zero size? bail out, we cannot handle this
193         if(theSize.x <= 0 || theSize.y <= 0)
194                 return;
195         if(theBorderSize.x <= 0) // no x border
196         {
197                 if(theBorderSize.y <= 0)
198                 {
199                         drawsubpic(theOrigin,                            width          + height,          pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
200                 }
201                 else if(theSize.y <= theBorderSize.y * 2)
202                 {
203                         // not high enough... draw just top and bottom then
204                         bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2));
205                         drawsubpic(theOrigin,                             width          + height * 0.5, pic, '0.25 0 0',           '0.5  0 0' + bH, theColor, theAlpha, 0);
206                         drawsubpic(theOrigin              + height * 0.5, width          + height * 0.5, pic, '0.25 0 0' + eY - bH, '0.5  0 0' + bH, theColor, theAlpha, 0);
207                 }
208                 else
209                 {
210                         dY = theBorderSize.y * eY;
211                         drawsubpic(theOrigin,                            width                   +     dY, pic, '0.25 0    0', '0.5  0.25 0', theColor, theAlpha, 0);
212                         drawsubpic(theOrigin          + dY,              width          + height - 2 * dY, pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
213                         drawsubpic(theOrigin + height - dY,              width                   +     dY, pic, '0.25 0.75 0', '0.5  0.25 0', theColor, theAlpha, 0);
214                 }
215         }
216         else if(theSize.x <= theBorderSize.x * 2)
217         {
218                 // not wide enough... draw just left and right then
219                 bW = eX * (0.25 * theSize.x / (theBorderSize.x * 2));
220                 if(theBorderSize.y <= 0)
221                 {
222                         drawsubpic(theOrigin,                             width * 0.5 + height,          pic, '0 0.25 0',           '0 0.5  0' + bW, theColor, theAlpha, 0);
223                         drawsubpic(theOrigin + width * 0.5,               width * 0.5 + height,          pic, '0 0.25 0' + eX - bW, '0 0.5  0' + bW, theColor, theAlpha, 0);
224                 }
225                 else if(theSize.y <= theBorderSize.y * 2)
226                 {
227                         // not high enough... draw just corners
228                         bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2));
229                         drawsubpic(theOrigin,                 width * 0.5 + height * 0.5, pic, '0 0 0',           bW + bH, theColor, theAlpha, 0);
230                         drawsubpic(theOrigin + width   * 0.5, width * 0.5 + height * 0.5, pic, eX - bW,           bW + bH, theColor, theAlpha, 0);
231                         drawsubpic(theOrigin + height  * 0.5, width * 0.5 + height * 0.5, pic, eY - bH,           bW + bH, theColor, theAlpha, 0);
232                         drawsubpic(theOrigin + theSize * 0.5, width * 0.5 + height * 0.5, pic, eX + eY - bW - bH, bW + bH, theColor, theAlpha, 0);
233                 }
234                 else
235                 {
236                         dY = theBorderSize.y * eY;
237                         drawsubpic(theOrigin,                             width * 0.5          +     dY, pic, '0 0    0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
238                         drawsubpic(theOrigin + width * 0.5,               width * 0.5          +     dY, pic, '0 0    0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
239                         drawsubpic(theOrigin                        + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0',           '0 0.5  0' + bW, theColor, theAlpha, 0);
240                         drawsubpic(theOrigin + width * 0.5          + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0' + eX - bW, '0 0.5  0' + bW, theColor, theAlpha, 0);
241                         drawsubpic(theOrigin               + height - dY, width * 0.5          +     dY, pic, '0 0.75 0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
242                         drawsubpic(theOrigin + width * 0.5 + height - dY, width * 0.5          +     dY, pic, '0 0.75 0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
243                 }
244         }
245         else
246         {
247                 if(theBorderSize.y <= 0)
248                 {
249                         dX = theBorderSize.x * eX;
250                         drawsubpic(theOrigin,                                        dX + height,          pic, '0    0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
251                         drawsubpic(theOrigin                       + dX, width - 2 * dX + height,          pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
252                         drawsubpic(theOrigin               + width - dX,             dX + height,          pic, '0.75 0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
253                 }
254                 else if(theSize.y <= theBorderSize.y * 2)
255                 {
256                         // not high enough... draw just top and bottom then
257                         bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2));
258                         dX = theBorderSize.x * eX;
259                         drawsubpic(theOrigin,                                         dX + height * 0.5, pic, '0    0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
260                         drawsubpic(theOrigin + dX,                        width - 2 * dX + height * 0.5, pic, '0.25 0 0',           '0.5  0 0' + bH, theColor, theAlpha, 0);
261                         drawsubpic(theOrigin + width - dX,                            dX + height * 0.5, pic, '0.75 0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
262                         drawsubpic(theOrigin              + height * 0.5,             dX + height * 0.5, pic, '0    0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
263                         drawsubpic(theOrigin + dX         + height * 0.5, width - 2 * dX + height * 0.5, pic, '0.25 0 0' + eY - bH, '0.5  0 0' + bH, theColor, theAlpha, 0);
264                         drawsubpic(theOrigin + width - dX + height * 0.5,             dX + height * 0.5, pic, '0.75 0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
265                 }
266                 else
267                 {
268                         dX = theBorderSize.x * eX;
269                         dY = theBorderSize.y * eY;
270                         drawsubpic(theOrigin,                                        dX          +     dY, pic, '0    0    0', '0.25 0.25 0', theColor, theAlpha, 0);
271                         drawsubpic(theOrigin                  + dX,      width - 2 * dX          +     dY, pic, '0.25 0    0', '0.5  0.25 0', theColor, theAlpha, 0);
272                         drawsubpic(theOrigin          + width - dX,                  dX          +     dY, pic, '0.75 0    0', '0.25 0.25 0', theColor, theAlpha, 0);
273                         drawsubpic(theOrigin          + dY,                          dX + height - 2 * dY, pic, '0    0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
274                         drawsubpic(theOrigin          + dY         + dX, width - 2 * dX + height - 2 * dY, pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
275                         drawsubpic(theOrigin          + dY + width - dX,             dX + height - 2 * dY, pic, '0.75 0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
276                         drawsubpic(theOrigin + height - dY,                          dX          +     dY, pic, '0    0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
277                         drawsubpic(theOrigin + height - dY         + dX, width - 2 * dX          +     dY, pic, '0.25 0.75 0', '0.5  0.25 0', theColor, theAlpha, 0);
278                         drawsubpic(theOrigin + height - dY + width - dX,             dX          +     dY, pic, '0.75 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
279                 }
280         }
281 }
282 void draw_Text(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
283 {
284         if(theSize.x <= 0 || theSize.y <= 0) {
285                 dprint("Drawing zero size text?\n");
286                 return;
287         }
288
289         //float wi;
290         //wi = draw_TextWidth(theText, ICanHasKallerz, theSize);
291         //draw_Fill(theOrigin, '1 0 0' * wi + '0 1 0' * theSize_y, '1 0 0', 0.3);
292
293         if(ICanHasKallerz)
294                 drawcolorcodedstring(boxToGlobal(theOrigin, draw_shift, draw_scale), theText, globalToBoxSize(boxToGlobalSize(theSize, draw_scale), draw_fontscale), theAlpha * draw_alpha, 0);
295         else
296                 drawstring(boxToGlobal(theOrigin, draw_shift, draw_scale), theText, globalToBoxSize(boxToGlobalSize(theSize, draw_scale), draw_fontscale), theColor, theAlpha * draw_alpha, 0);
297 }
298 void draw_CenterText(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
299 {
300         //dprint(strcat("orig = ", vtos(theOrigin) ," tx = ", ftos(draw_TextWidth(theText, ICanHasKallerz, theSize)), "\n"));
301         draw_Text(theOrigin - eX * 0.5 * draw_TextWidth(theText, ICanHasKallerz, theSize), theText, theSize, theColor, theAlpha, ICanHasKallerz);
302 }
303
304 float draw_TextWidth(string theText, float ICanHasKallerz, vector SizeThxBye)
305 {
306         //return strlen(theText);
307         //dprint("draw_TextWidth \"", theText, "\"\n");
308         vector v;
309         v = '0 0 0';
310         //float r;
311         v_x = stringwidth(theText, ICanHasKallerz, globalToBoxSize(boxToGlobalSize(SizeThxBye, draw_scale), draw_fontscale));
312         v = globalToBoxSize(v, draw_scale);
313         return v.x;
314 }
315
316 float draw_CondensedFontFactor(string theText, float ICanHasKallerz, vector SizeThxBye, float maxWidth)
317 {
318         float w = draw_TextWidth(theText, ICanHasKallerz, SizeThxBye);
319         if (w > maxWidth) {
320                 //dprintf("NOTE: label text %s too wide for label, condensed by factor %f\n", theText, maxWidth / w);
321                 return maxWidth / w;
322         }
323         return 1.0;
324 }
325
326 float draw_clipSet;
327 void draw_SetClip()
328 {
329         if(draw_clipSet)
330                 error("Already clipping, no stack implemented here, sorry");
331         drawsetcliparea(draw_shift.x, draw_shift.y, draw_scale.x, draw_scale.y);
332         draw_clipSet = 1;
333 }
334
335 void draw_SetClipRect(vector theOrigin, vector theScale)
336 {
337         vector o, s;
338         if(draw_clipSet)
339                 error("Already clipping, no stack implemented here, sorry");
340         o = boxToGlobal(theOrigin, draw_shift, draw_scale);
341         s = boxToGlobalSize(theScale, draw_scale);
342         drawsetcliparea(o.x, o.y, s.x, s.y);
343         draw_clipSet = 1;
344 }
345
346 void draw_ClearClip()
347 {
348         if(!draw_clipSet)
349                 error("Not clipping, can't clear it then");
350         drawresetcliparea();
351         draw_clipSet = 0;
352 }
353
354 string draw_TextShortenToWidth(string theText, float maxWidth, float ICanHasKallerz, vector SizeThxBye)
355 {
356         /*
357         if(draw_TextWidth(theText, ICanHasKallerz, SizeThxBye) <= maxWidth)
358                 return theText;
359         else
360                 return strcat(substring(theText, 0, draw_TextLengthUpToWidth(theText, maxWidth - draw_TextWidth("...", ICanHasKallerz, SizeThxBye), ICanHasKallerz, SizeThxBye)), "...");
361         */
362         if(ICanHasKallerz)
363                 return textShortenToWidth(theText, maxWidth, SizeThxBye, draw_TextWidth_WithColors);
364         else
365                 return textShortenToWidth(theText, maxWidth, SizeThxBye, draw_TextWidth_WithoutColors);
366 }
367
368 float draw_TextWidth_WithColors(string s, vector theFontSize)
369 {
370         return draw_TextWidth(s, true, theFontSize);
371 }
372
373 float draw_TextWidth_WithoutColors(string s, vector theFontSize)
374 {
375         return draw_TextWidth(s, false, theFontSize);
376 }
377
378 float draw_TextLengthUpToWidth(string theText, float maxWidth, float allowColorCodes, vector theFontSize)
379 {
380         if(allowColorCodes)
381                 return textLengthUpToWidth(theText, maxWidth, theFontSize, draw_TextWidth_WithColors);
382         else
383                 return textLengthUpToWidth(theText, maxWidth, theFontSize, draw_TextWidth_WithoutColors);
384 }