qmenu.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #include <string.h>
  16. #include <ctype.h>
  17. #include "client.h"
  18. #include "qmenu.h"
  19. static void Action_DoEnter( menuaction_s *a );
  20. static void Action_Draw( menuaction_s *a );
  21. static void Menu_DrawStatusBar( const char *string );
  22. static void Menulist_DoEnter( menulist_s *l );
  23. static void MenuList_Draw( menulist_s *l );
  24. static void Separator_Draw( menuseparator_s *s );
  25. static void Slider_DoSlide( menuslider_s *s, int dir );
  26. static void Slider_Draw( menuslider_s *s );
  27. static void SpinControl_DoEnter( menulist_s *s );
  28. static void SpinControl_Draw( menulist_s *s );
  29. static void SpinControl_DoSlide( menulist_s *s, int dir );
  30. #define RCOLUMN_OFFSET 16
  31. #define LCOLUMN_OFFSET -16
  32. extern refexport_t re;
  33. extern viddef_t viddef;
  34. #define VID_WIDTH viddef.width
  35. #define VID_HEIGHT viddef.height
  36. #define Draw_Char re.DrawChar
  37. #define Draw_Fill re.DrawFill
  38. void Action_DoEnter( menuaction_s *a )
  39. {
  40. if ( a->generic.callback )
  41. a->generic.callback( a );
  42. }
  43. void Action_Draw( menuaction_s *a )
  44. {
  45. if ( a->generic.flags & QMF_LEFT_JUSTIFY )
  46. {
  47. if ( a->generic.flags & QMF_GRAYED )
  48. Menu_DrawStringDark( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
  49. else
  50. Menu_DrawString( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
  51. }
  52. else
  53. {
  54. if ( a->generic.flags & QMF_GRAYED )
  55. Menu_DrawStringR2LDark( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
  56. else
  57. Menu_DrawStringR2L( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
  58. }
  59. if ( a->generic.ownerdraw )
  60. a->generic.ownerdraw( a );
  61. }
  62. qboolean Field_DoEnter( menufield_s *f )
  63. {
  64. if ( f->generic.callback )
  65. {
  66. f->generic.callback( f );
  67. return true;
  68. }
  69. return false;
  70. }
  71. void Field_Draw( menufield_s *f )
  72. {
  73. int i;
  74. char tempbuffer[128]="";
  75. if ( f->generic.name )
  76. Menu_DrawStringR2LDark( f->generic.x + f->generic.parent->x + LCOLUMN_OFFSET, f->generic.y + f->generic.parent->y, f->generic.name );
  77. strncpy( tempbuffer, f->buffer + f->visible_offset, f->visible_length );
  78. Draw_Char( f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y - 4, 18 );
  79. Draw_Char( f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y + 4, 24 );
  80. Draw_Char( f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y - 4, 20 );
  81. Draw_Char( f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y + 4, 26 );
  82. for ( i = 0; i < f->visible_length; i++ )
  83. {
  84. Draw_Char( f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y - 4, 19 );
  85. Draw_Char( f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y + 4, 25 );
  86. }
  87. Menu_DrawString( f->generic.x + f->generic.parent->x + 24, f->generic.y + f->generic.parent->y, tempbuffer );
  88. if ( Menu_ItemAtCursor( f->generic.parent ) == f )
  89. {
  90. int offset;
  91. if ( f->visible_offset )
  92. offset = f->visible_length;
  93. else
  94. offset = f->cursor;
  95. if ( ( ( int ) ( Sys_Milliseconds() / 250 ) ) & 1 )
  96. {
  97. Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8,
  98. f->generic.y + f->generic.parent->y,
  99. 11 );
  100. }
  101. else
  102. {
  103. Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8,
  104. f->generic.y + f->generic.parent->y,
  105. ' ' );
  106. }
  107. }
  108. }
  109. qboolean Field_Key( menufield_s *f, int key )
  110. {
  111. extern int keydown[];
  112. switch ( key )
  113. {
  114. case K_KP_SLASH:
  115. key = '/';
  116. break;
  117. case K_KP_MINUS:
  118. key = '-';
  119. break;
  120. case K_KP_PLUS:
  121. key = '+';
  122. break;
  123. case K_KP_HOME:
  124. key = '7';
  125. break;
  126. case K_KP_UPARROW:
  127. key = '8';
  128. break;
  129. case K_KP_PGUP:
  130. key = '9';
  131. break;
  132. case K_KP_LEFTARROW:
  133. key = '4';
  134. break;
  135. case K_KP_5:
  136. key = '5';
  137. break;
  138. case K_KP_RIGHTARROW:
  139. key = '6';
  140. break;
  141. case K_KP_END:
  142. key = '1';
  143. break;
  144. case K_KP_DOWNARROW:
  145. key = '2';
  146. break;
  147. case K_KP_PGDN:
  148. key = '3';
  149. break;
  150. case K_KP_INS:
  151. key = '0';
  152. break;
  153. case K_KP_DEL:
  154. key = '.';
  155. break;
  156. }
  157. if ( key > 127 )
  158. {
  159. switch ( key )
  160. {
  161. case K_DEL:
  162. default:
  163. return false;
  164. }
  165. }
  166. /*
  167. ** support pasting from the clipboard
  168. */
  169. if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) ||
  170. ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) )
  171. {
  172. char *cbd;
  173. if ( ( cbd = Sys_GetClipboardData() ) != 0 )
  174. {
  175. strtok( cbd, "\n\r\b" );
  176. strncpy( f->buffer, cbd, f->length - 1 );
  177. f->cursor = strlen( f->buffer );
  178. f->visible_offset = f->cursor - f->visible_length;
  179. if ( f->visible_offset < 0 )
  180. f->visible_offset = 0;
  181. free( cbd );
  182. }
  183. return true;
  184. }
  185. switch ( key )
  186. {
  187. case K_KP_LEFTARROW:
  188. case K_LEFTARROW:
  189. case K_BACKSPACE:
  190. if ( f->cursor > 0 )
  191. {
  192. memmove( &f->buffer[f->cursor-1], &f->buffer[f->cursor], strlen( &f->buffer[f->cursor] ) + 1 );
  193. f->cursor--;
  194. if ( f->visible_offset )
  195. {
  196. f->visible_offset--;
  197. }
  198. }
  199. break;
  200. case K_KP_DEL:
  201. case K_DEL:
  202. memmove( &f->buffer[f->cursor], &f->buffer[f->cursor+1], strlen( &f->buffer[f->cursor+1] ) + 1 );
  203. break;
  204. case K_KP_ENTER:
  205. case K_ENTER:
  206. case K_ESCAPE:
  207. case K_TAB:
  208. return false;
  209. case K_SPACE:
  210. default:
  211. if ( !isdigit( key ) && ( f->generic.flags & QMF_NUMBERSONLY ) )
  212. return false;
  213. if ( f->cursor < f->length )
  214. {
  215. f->buffer[f->cursor++] = key;
  216. f->buffer[f->cursor] = 0;
  217. if ( f->cursor > f->visible_length )
  218. {
  219. f->visible_offset++;
  220. }
  221. }
  222. }
  223. return true;
  224. }
  225. void Menu_AddItem( menuframework_s *menu, void *item )
  226. {
  227. if ( menu->nitems == 0 )
  228. menu->nslots = 0;
  229. if ( menu->nitems < MAXMENUITEMS )
  230. {
  231. menu->items[menu->nitems] = item;
  232. ( ( menucommon_s * ) menu->items[menu->nitems] )->parent = menu;
  233. menu->nitems++;
  234. }
  235. menu->nslots = Menu_TallySlots( menu );
  236. }
  237. /*
  238. ** Menu_AdjustCursor
  239. **
  240. ** This function takes the given menu, the direction, and attempts
  241. ** to adjust the menu's cursor so that it's at the next available
  242. ** slot.
  243. */
  244. void Menu_AdjustCursor( menuframework_s *m, int dir )
  245. {
  246. menucommon_s *citem;
  247. /*
  248. ** see if it's in a valid spot
  249. */
  250. if ( m->cursor >= 0 && m->cursor < m->nitems )
  251. {
  252. if ( ( citem = Menu_ItemAtCursor( m ) ) != 0 )
  253. {
  254. if ( citem->type != MTYPE_SEPARATOR )
  255. return;
  256. }
  257. }
  258. /*
  259. ** it's not in a valid spot, so crawl in the direction indicated until we
  260. ** find a valid spot
  261. */
  262. if ( dir == 1 )
  263. {
  264. while ( 1 )
  265. {
  266. citem = Menu_ItemAtCursor( m );
  267. if ( citem )
  268. if ( citem->type != MTYPE_SEPARATOR )
  269. break;
  270. m->cursor += dir;
  271. if ( m->cursor >= m->nitems )
  272. m->cursor = 0;
  273. }
  274. }
  275. else
  276. {
  277. while ( 1 )
  278. {
  279. citem = Menu_ItemAtCursor( m );
  280. if ( citem )
  281. if ( citem->type != MTYPE_SEPARATOR )
  282. break;
  283. m->cursor += dir;
  284. if ( m->cursor < 0 )
  285. m->cursor = m->nitems - 1;
  286. }
  287. }
  288. }
  289. void Menu_Center( menuframework_s *menu )
  290. {
  291. int height;
  292. height = ( ( menucommon_s * ) menu->items[menu->nitems-1])->y;
  293. height += 10;
  294. menu->y = ( VID_HEIGHT - height ) / 2;
  295. }
  296. void Menu_Draw( menuframework_s *menu )
  297. {
  298. int i;
  299. menucommon_s *item;
  300. /*
  301. ** draw contents
  302. */
  303. for ( i = 0; i < menu->nitems; i++ )
  304. {
  305. switch ( ( ( menucommon_s * ) menu->items[i] )->type )
  306. {
  307. case MTYPE_FIELD:
  308. Field_Draw( ( menufield_s * ) menu->items[i] );
  309. break;
  310. case MTYPE_SLIDER:
  311. Slider_Draw( ( menuslider_s * ) menu->items[i] );
  312. break;
  313. case MTYPE_LIST:
  314. MenuList_Draw( ( menulist_s * ) menu->items[i] );
  315. break;
  316. case MTYPE_SPINCONTROL:
  317. SpinControl_Draw( ( menulist_s * ) menu->items[i] );
  318. break;
  319. case MTYPE_ACTION:
  320. Action_Draw( ( menuaction_s * ) menu->items[i] );
  321. break;
  322. case MTYPE_SEPARATOR:
  323. Separator_Draw( ( menuseparator_s * ) menu->items[i] );
  324. break;
  325. }
  326. }
  327. item = Menu_ItemAtCursor( menu );
  328. if ( item && item->cursordraw )
  329. {
  330. item->cursordraw( item );
  331. }
  332. else if ( menu->cursordraw )
  333. {
  334. menu->cursordraw( menu );
  335. }
  336. else if ( item && item->type != MTYPE_FIELD )
  337. {
  338. if ( item->flags & QMF_LEFT_JUSTIFY )
  339. {
  340. Draw_Char( menu->x + item->x - 24 + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) );
  341. }
  342. else
  343. {
  344. Draw_Char( menu->x + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) );
  345. }
  346. }
  347. if ( item )
  348. {
  349. if ( item->statusbarfunc )
  350. item->statusbarfunc( ( void * ) item );
  351. else if ( item->statusbar )
  352. Menu_DrawStatusBar( item->statusbar );
  353. else
  354. Menu_DrawStatusBar( menu->statusbar );
  355. }
  356. else
  357. {
  358. Menu_DrawStatusBar( menu->statusbar );
  359. }
  360. }
  361. void Menu_DrawStatusBar( const char *string )
  362. {
  363. if ( string )
  364. {
  365. int l = strlen( string );
  366. int maxrow = VID_HEIGHT / 8;
  367. int maxcol = VID_WIDTH / 8;
  368. int col = maxcol / 2 - l / 2;
  369. Draw_Fill( 0, VID_HEIGHT-8, VID_WIDTH, 8, 4 );
  370. Menu_DrawString( col*8, VID_HEIGHT - 8, string );
  371. }
  372. else
  373. {
  374. Draw_Fill( 0, VID_HEIGHT-8, VID_WIDTH, 8, 0 );
  375. }
  376. }
  377. void Menu_DrawString( int x, int y, const char *string )
  378. {
  379. unsigned i;
  380. for ( i = 0; i < strlen( string ); i++ )
  381. {
  382. Draw_Char( ( x + i*8 ), y, string[i] );
  383. }
  384. }
  385. void Menu_DrawStringDark( int x, int y, const char *string )
  386. {
  387. unsigned i;
  388. for ( i = 0; i < strlen( string ); i++ )
  389. {
  390. Draw_Char( ( x + i*8 ), y, string[i] + 128 );
  391. }
  392. }
  393. void Menu_DrawStringR2L( int x, int y, const char *string )
  394. {
  395. unsigned i;
  396. for ( i = 0; i < strlen( string ); i++ )
  397. {
  398. Draw_Char( ( x - i*8 ), y, string[strlen(string)-i-1] );
  399. }
  400. }
  401. void Menu_DrawStringR2LDark( int x, int y, const char *string )
  402. {
  403. unsigned i;
  404. for ( i = 0; i < strlen( string ); i++ )
  405. {
  406. Draw_Char( ( x - i*8 ), y, string[strlen(string)-i-1]+128 );
  407. }
  408. }
  409. void *Menu_ItemAtCursor( menuframework_s *m )
  410. {
  411. if ( m->cursor < 0 || m->cursor >= m->nitems )
  412. return 0;
  413. return m->items[m->cursor];
  414. }
  415. qboolean Menu_SelectItem( menuframework_s *s )
  416. {
  417. menucommon_s *item = ( menucommon_s * ) Menu_ItemAtCursor( s );
  418. if ( item )
  419. {
  420. switch ( item->type )
  421. {
  422. case MTYPE_FIELD:
  423. return Field_DoEnter( ( menufield_s * ) item ) ;
  424. case MTYPE_ACTION:
  425. Action_DoEnter( ( menuaction_s * ) item );
  426. return true;
  427. case MTYPE_LIST:
  428. // Menulist_DoEnter( ( menulist_s * ) item );
  429. return false;
  430. case MTYPE_SPINCONTROL:
  431. // SpinControl_DoEnter( ( menulist_s * ) item );
  432. return false;
  433. }
  434. }
  435. return false;
  436. }
  437. void Menu_SetStatusBar( menuframework_s *m, const char *string )
  438. {
  439. m->statusbar = string;
  440. }
  441. void Menu_SlideItem( menuframework_s *s, int dir )
  442. {
  443. menucommon_s *item = ( menucommon_s * ) Menu_ItemAtCursor( s );
  444. if ( item )
  445. {
  446. switch ( item->type )
  447. {
  448. case MTYPE_SLIDER:
  449. Slider_DoSlide( ( menuslider_s * ) item, dir );
  450. break;
  451. case MTYPE_SPINCONTROL:
  452. SpinControl_DoSlide( ( menulist_s * ) item, dir );
  453. break;
  454. }
  455. }
  456. }
  457. int Menu_TallySlots( menuframework_s *menu )
  458. {
  459. int i;
  460. int total = 0;
  461. for ( i = 0; i < menu->nitems; i++ )
  462. {
  463. if ( ( ( menucommon_s * ) menu->items[i] )->type == MTYPE_LIST )
  464. {
  465. int nitems = 0;
  466. const char **n = ( ( menulist_s * ) menu->items[i] )->itemnames;
  467. while (*n)
  468. nitems++, n++;
  469. total += nitems;
  470. }
  471. else
  472. {
  473. total++;
  474. }
  475. }
  476. return total;
  477. }
  478. void Menulist_DoEnter( menulist_s *l )
  479. {
  480. int start;
  481. start = l->generic.y / 10 + 1;
  482. l->curvalue = l->generic.parent->cursor - start;
  483. if ( l->generic.callback )
  484. l->generic.callback( l );
  485. }
  486. void MenuList_Draw( menulist_s *l )
  487. {
  488. const char **n;
  489. int y = 0;
  490. Menu_DrawStringR2LDark( l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y, l->generic.name );
  491. n = l->itemnames;
  492. Draw_Fill( l->generic.x - 112 + l->generic.parent->x, l->generic.parent->y + l->generic.y + l->curvalue*10 + 10, 128, 10, 16 );
  493. while ( *n )
  494. {
  495. Menu_DrawStringR2LDark( l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y + y + 10, *n );
  496. n++;
  497. y += 10;
  498. }
  499. }
  500. void Separator_Draw( menuseparator_s *s )
  501. {
  502. if ( s->generic.name )
  503. Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->generic.name );
  504. }
  505. void Slider_DoSlide( menuslider_s *s, int dir )
  506. {
  507. s->curvalue += dir;
  508. if ( s->curvalue > s->maxvalue )
  509. s->curvalue = s->maxvalue;
  510. else if ( s->curvalue < s->minvalue )
  511. s->curvalue = s->minvalue;
  512. if ( s->generic.callback )
  513. s->generic.callback( s );
  514. }
  515. #define SLIDER_RANGE 10
  516. void Slider_Draw( menuslider_s *s )
  517. {
  518. int i;
  519. Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET,
  520. s->generic.y + s->generic.parent->y,
  521. s->generic.name );
  522. s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
  523. if ( s->range < 0)
  524. s->range = 0;
  525. if ( s->range > 1)
  526. s->range = 1;
  527. Draw_Char( s->generic.x + s->generic.parent->x + RCOLUMN_OFFSET, s->generic.y + s->generic.parent->y, 128);
  528. for ( i = 0; i < SLIDER_RANGE; i++ )
  529. Draw_Char( RCOLUMN_OFFSET + s->generic.x + i*8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 129);
  530. Draw_Char( RCOLUMN_OFFSET + s->generic.x + i*8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 130);
  531. Draw_Char( ( int ) ( 8 + RCOLUMN_OFFSET + s->generic.parent->x + s->generic.x + (SLIDER_RANGE-1)*8 * s->range ), s->generic.y + s->generic.parent->y, 131);
  532. }
  533. void SpinControl_DoEnter( menulist_s *s )
  534. {
  535. s->curvalue++;
  536. if ( s->itemnames[s->curvalue] == 0 )
  537. s->curvalue = 0;
  538. if ( s->generic.callback )
  539. s->generic.callback( s );
  540. }
  541. void SpinControl_DoSlide( menulist_s *s, int dir )
  542. {
  543. s->curvalue += dir;
  544. if ( s->curvalue < 0 )
  545. s->curvalue = 0;
  546. else if ( s->itemnames[s->curvalue] == 0 )
  547. s->curvalue--;
  548. if ( s->generic.callback )
  549. s->generic.callback( s );
  550. }
  551. void SpinControl_Draw( menulist_s *s )
  552. {
  553. char buffer[100];
  554. if ( s->generic.name )
  555. {
  556. Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET,
  557. s->generic.y + s->generic.parent->y,
  558. s->generic.name );
  559. }
  560. if ( !strchr( s->itemnames[s->curvalue], '\n' ) )
  561. {
  562. Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->itemnames[s->curvalue] );
  563. }
  564. else
  565. {
  566. strcpy( buffer, s->itemnames[s->curvalue] );
  567. *strchr( buffer, '\n' ) = 0;
  568. Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, buffer );
  569. strcpy( buffer, strchr( s->itemnames[s->curvalue], '\n' ) + 1 );
  570. Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y + 10, buffer );
  571. }
  572. }