3 * UCC (University [of WA] Computer Club) Electronic Accounting System
6 * menu.c - ncurses dispense menu
8 * This file is licenced under the 3-clause BSD Licence. See the file
9 * COPYING for full details.
13 #include <pwd.h> // getpwuids
14 #include <unistd.h> // getuid
18 int ShowItemAt(int Row, int Col, int Width, int Index, int bHilighted);
19 void PrintAlign(int Row, int Col, int Width, const char *Left, char Pad1, const char *Mid, char Pad2, const char *Right, ...);
21 // -------------------
22 // --- NCurses GUI ---
23 // -------------------
25 * \brief Render the NCurses UI
27 int ShowNCursesUI(void)
32 const int displayMinWidth = 50;
33 const char *titleString = "Dispense";
38 int ret = -2; // -2: Used for marking "no return yet"
40 char balance_str[5+1+2+1]; // If $9999.99 is too little, something's wrong
50 while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 )
53 if( currentItem >= maxItemIndex ) {
56 while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 )
65 while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 )
68 if( currentItem < 0 ) {
69 currentItem = maxItemIndex - 1;
71 while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 )
78 username = gsEffectiveUser;
80 pwd = getpwuid( getuid() );
81 username = pwd->pw_name;
84 snprintf(balance_str, sizeof(balance_str), "$%i.%02i", giUserBalance/100, abs(giUserBalance)%100);
91 maxItemIndex = ShowItemAt(0, 0, 0, -1, 0);
92 // Get item count per screen
93 // - 6: randomly chosen (Need at least 3)
94 items_in_view = LINES - 6;
95 if( items_in_view > maxItemIndex )
96 items_in_view = maxItemIndex;
99 while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 )
104 height = items_in_view + 3;
105 width = displayMinWidth;
108 xBase = COLS/2 - width/2;
109 yBase = LINES/2 - height/2;
114 PrintAlign(yBase, xBase, width, "/", '-', titleString, '-', "\\");
117 for( i = 0; i < items_in_view; i ++ )
121 move( yBase + 1 + i, xBase );
126 // Check for the '...' row
127 // - Oh god, magic numbers!
128 if( (i == 0 && itemBase > 0)
129 || (i == items_in_view - 1 && itemBase < maxItemIndex - items_in_view) )
131 printw(" ..."); pos += 8;
132 times = (width - pos) - 1;
133 while(times--) addch(' ');
138 yBase + 1 + i, xBase + pos, // Position
139 (width - pos) - 3, // Width
140 itemBase + i, // Index
141 !!(currentItem == itemBase + i) // Hilighted
146 // Scrollbar (if needed)
147 if( maxItemIndex > items_in_view ) {
151 else if( i == items_in_view - 1 ) {
155 int percentage = itemBase * 100 / (maxItemIndex-items_in_view);
156 if( i-1 == percentage*(items_in_view-3)/100 ) {
170 PrintAlign(yBase+height-2, xBase, width, "\\", '-', "", '-', "/");
173 // - Username, balance, flags
174 PrintAlign(yBase+height-1, xBase+1, width-2,
175 username, ' ', balance_str, ' ', gsUserFlags);
176 PrintAlign(yBase+height, xBase+1, width-2,
177 "q: Quit", ' ', "Arrow: Select", ' ', "Enter: Drop");
190 case 'B': _ItemDown(); break;
191 case 'A': _ItemUp(); break;
194 else if( ch == ERR || ch == '\x1b' ) {
199 fprintf(stderr, "Unknown character 0x%x\n", ch);
206 ret = ShowItemAt(0, 0, 0, currentItem, 0);
209 case 'j': _ItemDown(); break;
210 case 'k': _ItemUp(); break;
214 ret = -1; // -1: Return with no dispense
218 // Check if the return value was changed
219 if( ret != -2 ) break;
222 // Scroll only if needed
223 if( items_in_view < maxItemIndex )
225 // - If the current item is above the second item shown, and we're not at the top
226 if( currentItem < itemBase + 2 && itemBase > 0 ) {
227 itemBase = currentItem - 2;
228 if(itemBase < 0) itemBase = 0;
230 // - If the current item is below the second item show, and we're not at the bottom
231 if( currentItem > itemBase + items_in_view - 2 && itemBase + items_in_view < maxItemIndex ) {
232 itemBase = currentItem - items_in_view + 2;
233 if( itemBase > maxItemIndex - items_in_view )
234 itemBase = maxItemIndex - items_in_view;
246 * \brief Show item \a Index at (\a Col, \a Row)
247 * \return Dispense index of item
248 * \note Part of the NCurses UI
250 int ShowItemAt(int Row, int Col, int Width, int Index, int bHilighted)
259 // - This assumes that
260 case UI_MODE_STANDARD:
262 // Index = -1, request limit
263 if( Index < 0 || Index >= giNumItems+2 )
269 name = "Coke Machine";
270 Index = -1; // -1 indicates a label
277 name = gaItems[Index].Desc;
278 price = gaItems[Index].Price;
279 status = gaItems[Index].Status;
287 name = "Electronic Payment System";
288 Index = -1; // -1 indicates a label
293 name = gaItems[Index].Desc;
294 price = gaItems[Index].Price;
295 status = gaItems[Index].Status;
301 // Width = 0, don't print
304 // 4 preceding, 5 price
305 int nameWidth = Width - 4 - snprintf(NULL, 0, " %4i", price);
310 // Show hilight and status
329 printw("%-*.*s", nameWidth, nameWidth, name);
331 printw(" %4i", price);
335 printw("-- %-*.*s ", Width-4, Width-4, name);
339 // If the item isn't availiable for sale, return -1 (so it's skipped)
340 if( status || (price > giUserBalance && gbDisallowSelectWithoutBalance) )
347 * \brief Print a three-part string at the specified position (formatted)
348 * \note NCurses UI Helper
350 * Prints \a Left on the left of the area, \a Right on the righthand side
351 * and \a Mid in the middle of the area. These are padded with \a Pad1
352 * between \a Left and \a Mid, and \a Pad2 between \a Mid and \a Right.
354 * ::printf style format codes are allowed in \a Left, \a Mid and \a Right,
355 * and the arguments to these are read in that order.
357 void PrintAlign(int Row, int Col, int Width, const char *Left, char Pad1,
358 const char *Mid, char Pad2, const char *Right, ...)
360 int lLen, mLen, rLen;
365 // Get the length of the strings
366 va_start(args, Right);
367 lLen = vsnprintf(NULL, 0, Left, args);
368 mLen = vsnprintf(NULL, 0, Mid, args);
369 rLen = vsnprintf(NULL, 0, Right, args);
373 if( lLen + mLen/2 > Width/2 || mLen/2 + rLen > Width/2 ) {
374 return ; // TODO: What to do?
380 va_start(args, Right);
384 vsnprintf(tmp, lLen+1, Left, args);
388 times = (Width - mLen)/2 - lLen;
389 while(times--) addch(Pad1);
393 vsnprintf(tmp, mLen+1, Mid, args);
397 times = (Width - mLen)/2 - rLen;
398 if( (Width - mLen) % 2 ) times ++;
399 while(times--) addch(Pad2);
403 vsnprintf(tmp, rLen+1, Right, args);