Subversion Repositories psp

[/] [trunk/] [neopop/] [System_PSP/] [psplib/] [ui.c] - Blame information for rev 145

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 9 jack
/** PSP helper library ***************************************/
2
/**                                                         **/
3
/**                           ui.c                          **/
4
/**                                                         **/
5
/** This file contains a simple GUI rendering library       **/
6
/**                                                         **/
7
/** Copyright (C) Akop Karapetyan 2007                      **/
8
/**     You are not allowed to distribute this software     **/
9
/**     commercially. Please, notify me, if you make any    **/
10
/**     changes to this file.                               **/
11
/*************************************************************/
12
#include <malloc.h>
13
#include <math.h>
14
#include <stdio.h>
15
#include <string.h>
16
#include <pspkernel.h>
17
#include <psprtc.h>
18 64 jack
#include <psppower.h>
19 128 jack
#include <pspgu.h>
20 9 jack
 
21
#include "psp.h"
22
#include "fileio.h"
23
#include "ctrl.h"
24
#include "ui.h"
25 109 jack
#include "font.h"
26 9 jack
 
27 109 jack
#define MAX_DIR_LEN 1024
28 9 jack
 
29 128 jack
#define UI_ANIM_FRAMES   8
30
#define UI_ANIM_FOG_STEP 0x0f
31
 
32
#define CONTROL_BUTTON_MASK \
33 9 jack
  (PSP_CTRL_CIRCLE | PSP_CTRL_TRIANGLE | PSP_CTRL_CROSS | PSP_CTRL_SQUARE | \
34
   PSP_CTRL_LTRIGGER | PSP_CTRL_RTRIGGER | PSP_CTRL_SELECT | PSP_CTRL_START)
35
 
36
static const char
37
  *AlertDialogButtonTemplate   = "\026\001\020/\026\002\020 Close",
38
  *ConfirmDialogButtonTemplate = "\026\001\020 Confirm\t\026\002\020 Cancel",
39 109 jack
  *YesNoCancelDialogButtonTemplate =
40
    "\026\001\020 Yes\t\026"PSP_CHAR_SQUARE"\020 No\t\026\002\020 Cancel",
41 9 jack
 
42 106 jack
  *SelectorTemplate = "\026\001\020 Confirm\t\026\002\020 Cancel",
43
 
44 145 jack
  *BrowserTemplates[] = {
45
    "\026\002\020 Cancel\t\026\001\020 Open",
46
    "\026\002\020 Cancel\t\026\001\020 Enter directory",
47
    "\026\002\020 Cancel\t\026\001\020 Open\t\026"PSP_CHAR_TRIANGLE"\020 Parent directory",
48
    "\026\002\020 Cancel\t\026\001\020 Enter directory\t\026"PSP_CHAR_TRIANGLE"\020 Parent directory"
49
   },
50 9 jack
 
51 34 jack
  *SplashStatusBarTemplate  = "\026\255\020/\026\256\020 Switch tabs",
52
 
53 9 jack
  *OptionModeTemplate =
54
    "\026\245\020/\026\246\020 Select\t\026\247\020/\026\002\020 Cancel\t\026\250\020/\026\001\020 Confirm";
55
 
56 145 jack
enum
57
{
58
  BrowserTemplateOpenTop  = 0,
59
  BrowserTemplateEnterTop = 1,
60
  BrowserTemplateOpen     = 2,
61
  BrowserTemplateEnter    = 3,
62
};
63
 
64
#define BROWSER_TEMPLATE_COUNT 4
65
 
66 9 jack
struct UiPos
67
{
68
  int Index;
69
  int Offset;
70
  const PspMenuItem *Top;
71
};
72
 
73 145 jack
/* TODO: dynamically allocate ?? */
74
static unsigned int __attribute__((aligned(16))) call_list[524288];//262144];
75 9 jack
 
76 128 jack
/* Gets status string - containing current time and battery information */
77
static void GetStatusString(char *status, int length)
78 9 jack
{
79 128 jack
  static char main_str[128], batt_str[32];
80
  pspTime time;
81
 
82
  /* Get current time */
83
  sceRtcGetCurrentClockLocalTime(&time);
84
 
85
  /* Get the battery/power-related information */
86
  if (!scePowerIsBatteryExist()) sprintf(batt_str, PSP_CHAR_POWER);
87
  else
88
  {
89
    /* If the battery's online, display charging stats */
90
    int batt_time = scePowerGetBatteryLifeTime();
91
    int batt_percent = scePowerGetBatteryLifePercent();
92
    int i, charging = scePowerIsBatteryCharging();
93
 
94
    static int percentiles[] = { 60, 30, 12, 0 };
95
    for (i = 0; i < 4; i++)
96
      if (batt_percent >= percentiles[i])
97
        break;
98
 
99
    /* Fix for when battery switches state from AC to batt */
100
    batt_time = (batt_time >= 0) ? batt_time : 0;
101
 
102
    sprintf(batt_str, "%c%3i%% (%02i:%02i)",
103
      (charging) ? *PSP_CHAR_POWER : *PSP_CHAR_FULL_BATT + i,
104
      batt_percent, batt_time / 60, batt_time % 60);
105
  }
106
 
107
  /* Write the rest of the string */
108
  sprintf(main_str, "\270%2i/%2i %02i%c%02i %s ",
109
    time.month, time.day, time.hour, (time.microseconds > 500000) ? ':' : ' ',
110
    time.minutes, batt_str);
111
 
112
  strncpy(status, main_str, length);
113
  status[length - 1] = '\0';
114
}
115
 
116
static inline void RenderStatus()
117
{
118 145 jack
  static char status[128];
119
  GetStatusString(status, sizeof(status));
120
 
121
  int width = pspFontGetTextWidth(UiMetric.Font, status);
122 128 jack
  pspVideoPrint(UiMetric.Font, SCR_WIDTH - width, 0, status, PSP_COLOR_WHITE);
123 145 jack
}
124 128 jack
 
125
static void ReplaceIcons(char *string)
126
{
127 9 jack
  char *ch;
128
 
129
  for (ch = string; *ch; ch++)
130
  {
131
    switch(*ch)
132
    {
133
    case '\001': *ch = pspUiGetButtonIcon(UiMetric.OkButton); break;
134
    case '\002': *ch = pspUiGetButtonIcon(UiMetric.CancelButton); break;
135
    }
136
  }
137
}
138
 
139
char pspUiGetButtonIcon(u32 button_mask)
140
{
141
  switch (button_mask)
142
  {
143 67 jack
  case PSP_CTRL_CROSS:    return *PSP_CHAR_CROSS;
144
  case PSP_CTRL_CIRCLE:   return *PSP_CHAR_CIRCLE;
145
  case PSP_CTRL_TRIANGLE: return *PSP_CHAR_TRIANGLE;
146
  case PSP_CTRL_SQUARE:   return *PSP_CHAR_SQUARE;
147 9 jack
  default:                return '?';
148
  }
149
}
150
 
151
void pspUiAlert(const char *message)
152
{
153 128 jack
  PspImage *screen = NULL;
154 9 jack
  int sx, sy, dx, dy, th, fh, mw, cw, w, h;
155 128 jack
  int i, n = UI_ANIM_FRAMES;
156 9 jack
  char *instr = strdup(AlertDialogButtonTemplate);
157 128 jack
  ReplaceIcons(instr);
158 9 jack
 
159
  mw = pspFontGetTextWidth(UiMetric.Font, message);
160
  cw = pspFontGetTextWidth(UiMetric.Font, instr);
161
  fh = pspFontGetLineHeight(UiMetric.Font);
162
  th = pspFontGetTextHeight(UiMetric.Font, message);
163
 
164
  w = ((mw > cw) ? mw : cw) + 50;
165
  h = th + fh * 3;
166
  sx = SCR_WIDTH / 2 - w / 2;
167
  sy = SCR_HEIGHT / 2 - h / 2;
168
  dx = sx + w;
169
  dy = sy + h;
170
 
171 128 jack
  /* Intro animation */
172
  if (UiMetric.Animate)
173
  {
174
    /* Get copy of screen */
175
    screen = pspVideoGetVramBufferCopy();
176
 
177
    for (i = 0; i < n; i++)
178
    {
179
          pspVideoBegin();
180
 
181
          /* Clear screen */
182 136 jack
          pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
183 128 jack
 
184
          /* Apply fog and draw frame */
185
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
186
            COLOR(0,0,0,UI_ANIM_FOG_STEP*i));
187
          pspVideoFillRect(SCR_WIDTH/2-(((dx-sx)/n)*i)/2,
188
            SCR_HEIGHT/2-(((dy-sy)/n)*i)/2,
189
            SCR_WIDTH/2+(((dx-sx)/n)*i)/2, SCR_HEIGHT/2+(((dy-sy)/n)*i)/2,
190
            COLOR(RED_32(UiMetric.MenuOptionBoxBg),
191
              GREEN_32(UiMetric.MenuOptionBoxBg),
192
              BLUE_32(UiMetric.MenuOptionBoxBg),(0xff/n)*i));
193
 
194
          pspVideoEnd();
195
 
196
      /* Swap buffers */
197
      pspVideoWaitVSync();
198
      pspVideoSwapBuffers();
199
        }
200
 }
201
 
202 9 jack
  pspVideoBegin();
203
 
204 128 jack
  if (UiMetric.Animate)
205 136 jack
    pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
206 9 jack
 
207 128 jack
  pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
208
    COLOR(0,0,0,UI_ANIM_FOG_STEP*n));
209
  pspVideoFillRect(sx, sy, dx, dy, UiMetric.MenuOptionBoxBg);
210 9 jack
  pspVideoPrint(UiMetric.Font, SCR_WIDTH / 2 - mw / 2, sy + fh * 0.5, message,
211
    UiMetric.TextColor);
212
  pspVideoPrint(UiMetric.Font, SCR_WIDTH / 2 - cw / 2, dy - fh * 1.5, instr,
213
    UiMetric.TextColor);
214 128 jack
  pspVideoGlowRect(sx, sy, dx, dy,
215
    COLOR(0xff,0xff,0xff,UI_ANIM_FOG_STEP*n), 2);
216 9 jack
 
217
  pspVideoEnd();
218
 
219
  /* Swap buffers */
220
  pspVideoWaitVSync();
221
  pspVideoSwapBuffers();
222
 
223
  SceCtrlData pad;
224
 
225
  /* Loop until X or O is pressed */
226
  while (!ExitPSP)
227
  {
228
    if (!pspCtrlPollControls(&pad))
229
      continue;
230
 
231
    if (pad.Buttons & UiMetric.OkButton || pad.Buttons & UiMetric.CancelButton)
232
      break;
233
  }
234 128 jack
 
235
  if (!ExitPSP && UiMetric.Animate)
236
  {
237
          /* Exit animation */
238
          for (i = n - 1; i >= 0; i--)
239
          {
240
                  pspVideoBegin();
241
 
242
                  /* Clear screen */
243 136 jack
                  pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
244 128 jack
 
245
                  /* Apply fog and draw frame */
246
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
247
            COLOR(0,0,0,UI_ANIM_FOG_STEP*i));
248
          pspVideoFillRect(SCR_WIDTH/2-(((dx-sx)/n)*i)/2,
249
            SCR_HEIGHT/2-(((dy-sy)/n)*i)/2,
250
            SCR_WIDTH/2+(((dx-sx)/n)*i)/2, SCR_HEIGHT/2+(((dy-sy)/n)*i)/2,
251
            COLOR(RED_32(UiMetric.MenuOptionBoxBg),
252
              GREEN_32(UiMetric.MenuOptionBoxBg),
253
              BLUE_32(UiMetric.MenuOptionBoxBg),(0xff/n)*i));
254
 
255
                  pspVideoEnd();
256
 
257
            /* Swap buffers */
258
            pspVideoWaitVSync();
259
            pspVideoSwapBuffers();
260
                }
261
        }
262
 
263
  if (screen) pspImageDestroy(screen);
264
  free(instr);
265 9 jack
}
266
 
267 109 jack
int pspUiYesNoCancel(const char *message)
268
{
269 128 jack
  PspImage *screen = NULL;
270 109 jack
  int sx, sy, dx, dy, th, fh, mw, cw, w, h;
271 128 jack
  int i, n = UI_ANIM_FRAMES;
272 109 jack
  char *instr = strdup(YesNoCancelDialogButtonTemplate);
273 128 jack
  ReplaceIcons(instr);
274 109 jack
 
275
  mw = pspFontGetTextWidth(UiMetric.Font, message);
276
  cw = pspFontGetTextWidth(UiMetric.Font, instr);
277
  fh = pspFontGetLineHeight(UiMetric.Font);
278
  th = pspFontGetTextHeight(UiMetric.Font, message);
279
 
280
  w = ((mw > cw) ? mw : cw) + 50;
281
  h = th + fh * 3;
282
  sx = SCR_WIDTH / 2 - w / 2;
283
  sy = SCR_HEIGHT / 2 - h / 2;
284
  dx = sx + w;
285
  dy = sy + h;
286
 
287 128 jack
  /* Intro animation */
288
  if (UiMetric.Animate)
289
  {
290
    /* Get copy of screen */
291
    screen = pspVideoGetVramBufferCopy();
292
 
293
    for (i = 0; i < n; i++)
294
    {
295
          pspVideoBegin();
296
 
297
          /* Clear screen */
298 136 jack
          pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
299 128 jack
 
300
          /* Apply fog and draw frame */
301
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
302
            COLOR(0,0,0,UI_ANIM_FOG_STEP*i));
303
          pspVideoFillRect(SCR_WIDTH/2-(((dx-sx)/n)*i)/2,
304
            SCR_HEIGHT/2-(((dy-sy)/n)*i)/2,
305
            SCR_WIDTH/2+(((dx-sx)/n)*i)/2, SCR_HEIGHT/2+(((dy-sy)/n)*i)/2,
306
            COLOR(RED_32(UiMetric.MenuOptionBoxBg),
307
              GREEN_32(UiMetric.MenuOptionBoxBg),
308
              BLUE_32(UiMetric.MenuOptionBoxBg),(0xff/n)*i));
309
 
310
          pspVideoEnd();
311
 
312
      /* Swap buffers */
313
      pspVideoWaitVSync();
314
      pspVideoSwapBuffers();
315
        }
316
  }
317
 
318 109 jack
  pspVideoBegin();
319
 
320 128 jack
  if (UiMetric.Animate)
321 136 jack
    pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
322 128 jack
  pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
323
    COLOR(0,0,0,UI_ANIM_FOG_STEP*n));
324
  pspVideoFillRect(sx, sy, dx, dy, UiMetric.MenuOptionBoxBg);
325 109 jack
  pspVideoPrint(UiMetric.Font, SCR_WIDTH / 2 - mw / 2, sy + fh * 0.5, message,
326
    UiMetric.TextColor);
327
  pspVideoPrint(UiMetric.Font, SCR_WIDTH / 2 - cw / 2, dy - fh * 1.5, instr,
328
    UiMetric.TextColor);
329 128 jack
  pspVideoGlowRect(sx, sy, dx, dy,
330
    COLOR(0xff,0xff,0xff,UI_ANIM_FOG_STEP*n), 2);
331 109 jack
 
332
  pspVideoEnd();
333
 
334
  /* Swap buffers */
335
  pspVideoWaitVSync();
336
  pspVideoSwapBuffers();
337
 
338
  SceCtrlData pad;
339
 
340
  /* Loop until X or O is pressed */
341
  while (!ExitPSP)
342
  {
343
    if (!pspCtrlPollControls(&pad))
344
      continue;
345
 
346
    if (pad.Buttons & UiMetric.OkButton || pad.Buttons & UiMetric.CancelButton
347
      || pad.Buttons & PSP_CTRL_SQUARE) break;
348
  }
349
 
350 128 jack
  if (!ExitPSP && UiMetric.Animate)
351
  {
352
          /* Exit animation */
353
          for (i = n - 1; i >= 0; i--)
354
          {
355
                  pspVideoBegin();
356
 
357
                  /* Clear screen */
358 136 jack
                  pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
359 128 jack
 
360
                  /* Apply fog and draw frame */
361
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
362
            COLOR(0,0,0,UI_ANIM_FOG_STEP*i));
363
          pspVideoFillRect(SCR_WIDTH/2-(((dx-sx)/n)*i)/2,
364
            SCR_HEIGHT/2-(((dy-sy)/n)*i)/2,
365
            SCR_WIDTH/2+(((dx-sx)/n)*i)/2, SCR_HEIGHT/2+(((dy-sy)/n)*i)/2,
366
            COLOR(RED_32(UiMetric.MenuOptionBoxBg),
367
              GREEN_32(UiMetric.MenuOptionBoxBg),
368
              BLUE_32(UiMetric.MenuOptionBoxBg),(0xff/n)*i));
369
 
370
                  pspVideoEnd();
371
 
372
            /* Swap buffers */
373
            pspVideoWaitVSync();
374
            pspVideoSwapBuffers();
375
                }
376
        }
377
 
378
  if (screen) pspImageDestroy(screen);
379
  free(instr);
380
 
381 109 jack
  if (pad.Buttons & UiMetric.CancelButton) return PSP_UI_CANCEL;
382
  else if (pad.Buttons & PSP_CTRL_SQUARE) return PSP_UI_NO;
383
  else return PSP_UI_YES;
384
}
385
 
386 9 jack
int pspUiConfirm(const char *message)
387
{
388 128 jack
  PspImage *screen = NULL;
389 106 jack
  int sx, sy, dx, dy, th, fh, mw, cw, w, h;
390 128 jack
  int i, n = UI_ANIM_FRAMES;
391 9 jack
  char *instr = strdup(ConfirmDialogButtonTemplate);
392 128 jack
  ReplaceIcons(instr);
393 9 jack
 
394
  mw = pspFontGetTextWidth(UiMetric.Font, message);
395
  cw = pspFontGetTextWidth(UiMetric.Font, instr);
396
  fh = pspFontGetLineHeight(UiMetric.Font);
397 106 jack
  th = pspFontGetTextHeight(UiMetric.Font, message);
398 9 jack
 
399
  w = ((mw > cw) ? mw : cw) + 50;
400 106 jack
  h = th + fh * 3;
401 9 jack
  sx = SCR_WIDTH / 2 - w / 2;
402
  sy = SCR_HEIGHT / 2 - h / 2;
403
  dx = sx + w;
404
  dy = sy + h;
405
 
406 128 jack
  if (UiMetric.Animate)
407
  {
408
    /* Get copy of screen */
409
    screen = pspVideoGetVramBufferCopy();
410
 
411
    /* Intro animation */
412
    for (i = 0; i < n; i++)
413
    {
414
          pspVideoBegin();
415
 
416
          /* Clear screen */
417 136 jack
          pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
418 128 jack
 
419
          /* Apply fog and draw frame */
420
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
421
            COLOR(0,0,0,UI_ANIM_FOG_STEP*i));
422
          pspVideoFillRect(SCR_WIDTH/2-(((dx-sx)/n)*i)/2,
423
            SCR_HEIGHT/2-(((dy-sy)/n)*i)/2,
424
            SCR_WIDTH/2+(((dx-sx)/n)*i)/2, SCR_HEIGHT/2+(((dy-sy)/n)*i)/2,
425
            COLOR(RED_32(UiMetric.MenuOptionBoxBg),
426
              GREEN_32(UiMetric.MenuOptionBoxBg),
427
              BLUE_32(UiMetric.MenuOptionBoxBg),(0xff/n)*i));
428
 
429
          pspVideoEnd();
430
 
431
      /* Swap buffers */
432
      pspVideoWaitVSync();
433
      pspVideoSwapBuffers();
434
        }
435
  }
436
 
437 9 jack
  pspVideoBegin();
438
 
439 128 jack
  if (UiMetric.Animate)
440 136 jack
    pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
441 128 jack
  pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
442
    COLOR(0,0,0,UI_ANIM_FOG_STEP*n));
443
  pspVideoFillRect(sx, sy, dx, dy, UiMetric.MenuOptionBoxBg);
444 106 jack
  pspVideoPrint(UiMetric.Font, SCR_WIDTH / 2 - mw / 2, sy + fh * 0.5, message,
445
    UiMetric.TextColor);
446
  pspVideoPrint(UiMetric.Font, SCR_WIDTH / 2 - cw / 2, dy - fh * 1.5, instr,
447
    UiMetric.TextColor);
448 128 jack
  pspVideoGlowRect(sx, sy, dx, dy,
449
    COLOR(0xff,0xff,0xff,UI_ANIM_FOG_STEP*n), 2);
450 9 jack
 
451
  pspVideoEnd();
452
 
453
  /* Swap buffers */
454
  pspVideoWaitVSync();
455
  pspVideoSwapBuffers();
456
 
457
  SceCtrlData pad;
458
 
459
  /* Loop until X or O is pressed */
460
  while (!ExitPSP)
461
  {
462
    if (!pspCtrlPollControls(&pad))
463
      continue;
464
 
465
    if (pad.Buttons & UiMetric.OkButton || pad.Buttons & UiMetric.CancelButton)
466
      break;
467
  }
468
 
469 128 jack
  if (!ExitPSP && UiMetric.Animate)
470
  {
471
          /* Exit animation */
472
          for (i = n - 1; i >= 0; i--)
473
          {
474
                  pspVideoBegin();
475
 
476
                  /* Clear screen */
477 136 jack
                  pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
478 128 jack
 
479
                  /* Apply fog and draw frame */
480
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
481
            COLOR(0,0,0,UI_ANIM_FOG_STEP*i));
482
          pspVideoFillRect(SCR_WIDTH/2-(((dx-sx)/n)*i)/2,
483
            SCR_HEIGHT/2-(((dy-sy)/n)*i)/2,
484
            SCR_WIDTH/2+(((dx-sx)/n)*i)/2, SCR_HEIGHT/2+(((dy-sy)/n)*i)/2,
485
            COLOR(RED_32(UiMetric.MenuOptionBoxBg),
486
              GREEN_32(UiMetric.MenuOptionBoxBg),
487
              BLUE_32(UiMetric.MenuOptionBoxBg),(0xff/n)*i));
488
 
489
                  pspVideoEnd();
490
 
491
            /* Swap buffers */
492
            pspVideoWaitVSync();
493
            pspVideoSwapBuffers();
494
                }
495
        }
496
 
497
  if (screen) pspImageDestroy(screen);
498
  free(instr);
499
 
500 9 jack
  return pad.Buttons & UiMetric.OkButton;
501
}
502
 
503
void pspUiFlashMessage(const char *message)
504
{
505 128 jack
  PspImage *screen = NULL;
506 34 jack
  int sx, sy, dx, dy, fh, mw, mh, w, h;
507 128 jack
  int i, n = UI_ANIM_FRAMES;
508 9 jack
 
509
  mw = pspFontGetTextWidth(UiMetric.Font, message);
510
  fh = pspFontGetLineHeight(UiMetric.Font);
511 34 jack
  mh = pspFontGetTextHeight(UiMetric.Font, message);
512 9 jack
 
513
  w = mw + 50;
514 34 jack
  h = mh + fh * 2;
515 9 jack
  sx = SCR_WIDTH / 2 - w / 2;
516
  sy = SCR_HEIGHT / 2 - h / 2;
517
  dx = sx + w;
518
  dy = sy + h;
519
 
520 128 jack
  if (UiMetric.Animate)
521
  {
522
    /* Get copy of screen */
523
    screen = pspVideoGetVramBufferCopy();
524
 
525
    /* Intro animation */
526
    for (i = 0; i < n; i++)
527
    {
528
          pspVideoBegin();
529
 
530
          /* Clear screen */
531 136 jack
          pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
532 128 jack
 
533
          /* Apply fog and draw frame */
534
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
535
            COLOR(0,0,0,UI_ANIM_FOG_STEP*i));
536
          pspVideoFillRect(SCR_WIDTH/2-(((dx-sx)/n)*i)/2,
537
            SCR_HEIGHT/2-(((dy-sy)/n)*i)/2,
538
            SCR_WIDTH/2+(((dx-sx)/n)*i)/2, SCR_HEIGHT/2+(((dy-sy)/n)*i)/2,
539
            COLOR(RED_32(UiMetric.MenuOptionBoxBg),
540
              GREEN_32(UiMetric.MenuOptionBoxBg),
541
              BLUE_32(UiMetric.MenuOptionBoxBg),(0xff/n)*i));
542
 
543
          pspVideoEnd();
544
 
545
      /* Swap buffers */
546
      pspVideoWaitVSync();
547
      pspVideoSwapBuffers();
548
        }
549
  }
550
 
551 9 jack
  pspVideoBegin();
552
 
553 128 jack
  if (UiMetric.Animate)
554 136 jack
    pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
555 128 jack
  pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
556
    COLOR(0,0,0,UI_ANIM_FOG_STEP*n));
557
  pspVideoFillRect(sx, sy, dx, dy, UiMetric.MenuOptionBoxBg);
558 34 jack
  pspVideoPrintCenter(UiMetric.Font,
559
    sx, sy + fh, dx, message, UiMetric.TextColor);
560 128 jack
  pspVideoGlowRect(sx, sy, dx, dy,
561
    COLOR(0xff,0xff,0xff,UI_ANIM_FOG_STEP*n), 2);
562 9 jack
 
563
  pspVideoEnd();
564
 
565
  /* Swap buffers */
566
  pspVideoWaitVSync();
567
  pspVideoSwapBuffers();
568 128 jack
 
569
  if (screen) pspImageDestroy(screen);
570 9 jack
}
571
 
572
void pspUiOpenBrowser(PspUiFileBrowser *browser, const char *start_path)
573
{
574
  PspMenu *menu;
575
  PspFile *file;
576
  PspFileList *list;
577 128 jack
  const PspMenuItem *sel, *last_sel;
578 106 jack
  PspMenuItem *item;
579 9 jack
  SceCtrlData pad;
580 145 jack
  char *instructions[BROWSER_TEMPLATE_COUNT];
581 9 jack
 
582 145 jack
  /* Initialize instruction strings */
583
  int i;
584
  for (i = 0; i < BROWSER_TEMPLATE_COUNT; i++)
585
  {
586
    instructions[i] = strdup(BrowserTemplates[i]);
587
    ReplaceIcons(instructions[i]);
588
  }
589
 
590 9 jack
  if (!start_path)
591
    start_path = pspGetAppDirectory();
592
 
593
  char *cur_path = pspFileIoGetParentDirectory(start_path);
594
  const char *cur_file = pspFileIoGetFilename(start_path);
595
  struct UiPos pos;
596 145 jack
  int lnmax, lnhalf;
597
  int sby, sbh, j, h, w, fh = pspFontGetLineHeight(UiMetric.Font);
598 9 jack
  int sx, sy, dx, dy;
599
  int hasparent, is_dir;
600
 
601
  sx = UiMetric.Left;
602
  sy = UiMetric.Top + fh + UiMetric.TitlePadding;
603
  dx = UiMetric.Right;
604
  dy = UiMetric.Bottom;
605
  w = dx - sx - UiMetric.ScrollbarWidth;
606
  h = dy - sy;
607
 
608
  menu = pspMenuCreate();
609
 
610 128 jack
  memset(call_list, 0, sizeof(call_list));
611
 
612
  int sel_top = 0, last_sel_top = 0, fast_scroll;
613
 
614 9 jack
  /* Begin browsing (outer) loop */
615
  while (!ExitPSP)
616
  {
617 128 jack
    sel = last_sel = NULL;
618 9 jack
    pos.Top = NULL;
619
    pspMenuClear(menu);
620
 
621
    /* Load list of files for the selected path */
622
    if ((list = pspFileIoGetFileList(cur_path, browser->Filter)))
623
    {
624
      /* Check for a parent path, prepend .. if necessary */
625 106 jack
      if ((hasparent =! pspFileIoIsRootDirectory(cur_path)))
626
      {
627
        item = pspMenuAppendItem(menu, "..", 0);
628
        item->Param = (void*)PSP_FILEIO_DIR;
629
      }
630 9 jack
 
631
      /* Add a menu item for each file */
632
      for (file = list->First; file; file = file->Next)
633
      {
634
        /* Skip files that begin with '.' */
635
        if (file->Name && file->Name[0] == '.')
636
          continue;
637
 
638 106 jack
        item = pspMenuAppendItem(menu, file->Name, 0);
639
        item->Param = (void*)file->Attrs;
640
 
641 9 jack
        if (cur_file && strcmp(file->Name, cur_file) == 0)
642
          sel = item;
643
      }
644
 
645
      cur_file = NULL;
646
 
647
      /* Destroy the file list */
648
      pspFileIoDestroyFileList(list);
649
    }
650
    else
651
    {
652
      /* Check for a parent path, prepend .. if necessary */
653 106 jack
      if ((hasparent =! pspFileIoIsRootDirectory(cur_path)))
654
      {
655
        item = pspMenuAppendItem(menu, "..", 0);
656
        item->Param = (void*)PSP_FILEIO_DIR;
657
      }
658 9 jack
    }
659
 
660
    /* Initialize variables */
661
    lnmax = (dy - sy) / fh;
662
    lnhalf = lnmax >> 1;
663
    sbh = (menu->Count > lnmax) ? (int)((float)h * ((float)lnmax / (float)menu->Count)) : 0;
664
 
665
    pos.Index = pos.Offset = 0;
666
 
667
    if (!sel)
668
    {
669
      /* Select the first file/dir in the directory */
670
      if (menu->First && menu->First->Next)
671
        sel=menu->First->Next;
672
      else if (menu->First)
673
        sel=menu->First;
674
    }
675
 
676
    /* Compute index and offset of selected file */
677
    if (sel)
678
    {
679 106 jack
      pos.Top = menu->First;
680
      for (item = menu->First; item != sel; item = item->Next)
681
      {
682
        if (pos.Index + 1 >= lnmax) { pos.Offset++; pos.Top=pos.Top->Next; }
683
        else pos.Index++;
684
      }
685 9 jack
    }
686
 
687
    pspVideoWaitVSync();
688
 
689
    /* Begin navigation (inner) loop */
690
    while (!ExitPSP)
691
    {
692
      if (!pspCtrlPollControls(&pad))
693
        continue;
694
 
695 128 jack
      fast_scroll = 0;
696
 
697 9 jack
      /* Check the directional buttons */
698
      if (sel)
699
      {
700
        if ((pad.Buttons & PSP_CTRL_DOWN || pad.Buttons & PSP_CTRL_ANALDOWN) && sel->Next)
701
        {
702
          if (pos.Index+1 >= lnmax) { pos.Offset++; pos.Top=pos.Top->Next; }
703
          else pos.Index++;
704
          sel=sel->Next;
705 128 jack
          fast_scroll = pad.Buttons & PSP_CTRL_ANALDOWN;
706 9 jack
        }
707
        else if ((pad.Buttons & PSP_CTRL_UP || pad.Buttons & PSP_CTRL_ANALUP) && sel->Prev)
708
        {
709
          if (pos.Index - 1 < 0) { pos.Offset--; pos.Top=pos.Top->Prev; }
710
          else pos.Index--;
711
          sel = sel->Prev;
712 128 jack
          fast_scroll = pad.Buttons & PSP_CTRL_ANALUP;
713 9 jack
        }
714
        else if (pad.Buttons & PSP_CTRL_LEFT)
715
        {
716
          for (i=0; sel->Prev && i < lnhalf; i++)
717
          {
718
            if (pos.Index-1 < 0) { pos.Offset--; pos.Top=pos.Top->Prev; }
719
            else pos.Index--;
720
            sel=sel->Prev;
721
          }
722
        }
723
        else if (pad.Buttons & PSP_CTRL_RIGHT)
724
        {
725
          for (i=0; sel->Next && i < lnhalf; i++)
726
          {
727
            if (pos.Index + 1 >= lnmax) { pos.Offset++; pos.Top=pos.Top->Next; }
728
            else pos.Index++;
729
            sel=sel->Next;
730
          }
731
        }
732
 
733
        /* File/dir selection */
734
        if (pad.Buttons & UiMetric.OkButton)
735
        {
736 106 jack
          if (((unsigned int)sel->Param & PSP_FILEIO_DIR))
737 9 jack
          {
738
            /* Selected a directory, descend */
739
            pspFileIoEnterDirectory(&cur_path, sel->Caption);
740
            break;
741
          }
742
          else
743
          {
744
            int exit = 1;
745
 
746
            /* Selected a file */
747
            if (browser->OnOk)
748
            {
749
              char *file = malloc((strlen(cur_path) + strlen(sel->Caption) + 1) * sizeof(char));
750
              sprintf(file, "%s%s", cur_path, sel->Caption);
751
              exit = browser->OnOk(browser, file);
752
              free(file);
753
            }
754
 
755
            if (exit) goto exit_browser;
756
            else continue;
757
          }
758
        }
759
      }
760
 
761
      if (pad.Buttons & PSP_CTRL_TRIANGLE)
762
      {
763
        if (!pspFileIoIsRootDirectory(cur_path))
764
        {
765
          pspFileIoEnterDirectory(&cur_path, "..");
766
          break;
767
        }
768
      }
769
      else if (pad.Buttons & UiMetric.CancelButton)
770
      {
771
        if (browser->OnCancel)
772
          browser->OnCancel(browser, cur_path);
773
        goto exit_browser;
774
      }
775
      else if ((pad.Buttons & CONTROL_BUTTON_MASK) && browser->OnButtonPress)
776
      {
777
        char *file = NULL;
778
        int exit;
779
 
780
        if (sel)
781
        {
782
          file = malloc((strlen(cur_path) + strlen(sel->Caption) + 1) * sizeof(char));
783
          sprintf(file, "%s%s", cur_path, sel->Caption);
784
        }
785
 
786
        exit = browser->OnButtonPress(browser,
787
          file, pad.Buttons & CONTROL_BUTTON_MASK);
788
 
789
        if (file) free(file);
790
        if (exit) goto exit_browser;
791
      }
792
 
793 106 jack
      is_dir = (unsigned int)sel->Param & PSP_FILEIO_DIR;
794 9 jack
 
795 128 jack
      sceGuStart(GU_CALL, call_list);
796 9 jack
 
797
      /* Draw current path */
798 145 jack
      pspVideoPrint(UiMetric.Font, sx, UiMetric.Top, cur_path,
799 9 jack
        UiMetric.TitleColor);
800
      pspVideoDrawLine(UiMetric.Left, UiMetric.Top + fh - 1, UiMetric.Left + w,
801
        UiMetric.Top + fh - 1, UiMetric.TitleColor);
802
 
803 145 jack
      const char *instruction;
804 9 jack
      if (hasparent)
805 145 jack
        instruction = instructions[(is_dir)
806
          ? BrowserTemplateEnter : BrowserTemplateOpen];
807
      else
808
        instruction = instructions[(is_dir)
809
          ? BrowserTemplateEnterTop : BrowserTemplateOpenTop];
810
 
811
      pspVideoPrintCenter(UiMetric.Font,
812
        sx, SCR_HEIGHT - fh, dx, instruction, UiMetric.StatusBarColor);
813 9 jack
 
814
      /* Draw scrollbar */
815
      if (sbh > 0)
816
      {
817 106 jack
        sby = sy + (int)((float)(h - sbh)
818
          * ((float)(pos.Offset + pos.Index) / (float)menu->Count));
819
        pspVideoFillRect(dx - UiMetric.ScrollbarWidth, sy, dx, dy,
820
          UiMetric.ScrollbarBgColor);
821
        pspVideoFillRect(dx - UiMetric.ScrollbarWidth, sby, dx, sby + sbh,
822
          UiMetric.ScrollbarColor);
823 9 jack
      }
824
 
825
      /* Render the files */
826 106 jack
      for (item = (PspMenuItem*)pos.Top, i = 0, j = sy;
827
        item && i < lnmax; item = item->Next, j += fh, i++)
828 9 jack
      {
829 128 jack
        if (item == sel) sel_top = j;
830 9 jack
 
831 106 jack
        pspVideoPrintClipped(UiMetric.Font, sx + 10, j, item->Caption, w - 10,
832 128 jack
          "...", (item == sel) ? UiMetric.SelectedColor
833 145 jack
            : ((unsigned int)item->Param & PSP_FILEIO_DIR)
834 128 jack
            ? UiMetric.BrowserDirectoryColor : UiMetric.BrowserFileColor);
835 145 jack
     }
836 9 jack
 
837 128 jack
      /* Render status information */
838
      RenderStatus();
839
 
840 9 jack
      /* Perform any custom drawing */
841
      if (browser->OnRender)
842
        browser->OnRender(browser, "not implemented");
843
 
844 128 jack
      sceGuFinish();
845
 
846 145 jack
      if (sel != last_sel && !fast_scroll && sel && last_sel
847 128 jack
        && UiMetric.Animate)
848
      {
849
        /* Move animation */
850
        int f, n = 4;
851
        for (f = 1; f <= n; f++)
852
        {
853
          pspVideoBegin();
854
 
855
          /* Clear screen */
856
          if (!UiMetric.Background) pspVideoClearScreen();
857
          else pspVideoPutImage(UiMetric.Background, 0, 0,
858 136 jack
            UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
859 128 jack
 
860
          /* Selection box */
861
          int box_top = last_sel_top-((last_sel_top-sel_top)/n)*f;
862
          pspVideoFillRect(sx, box_top, sx+w, box_top+fh,
863
            UiMetric.SelectedBgColor);
864
 
865
          sceGuCallList(call_list);
866
 
867
          pspVideoEnd();
868
 
869
          pspVideoWaitVSync();
870
          pspVideoSwapBuffers();
871
        }
872
      }
873
 
874
      pspVideoBegin();
875
 
876
      /* Clear screen */
877
      if (UiMetric.Background)
878 136 jack
        pspVideoPutImage(UiMetric.Background, 0, 0,
879
          UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
880 128 jack
      else pspVideoClearScreen();
881
 
882
      /* Render selection box */
883
      if (sel) pspVideoFillRect(sx, sel_top, sx+w, sel_top+fh,
884
        UiMetric.SelectedBgColor);
885
 
886
      sceGuCallList(call_list);
887
 
888 9 jack
      pspVideoEnd();
889
 
890
      /* Swap buffers */
891
      pspVideoWaitVSync();
892
      pspVideoSwapBuffers();
893 128 jack
 
894
      last_sel = sel;
895
      last_sel_top = sel_top;
896 9 jack
    }
897
  }
898
 
899
exit_browser:
900
 
901 145 jack
  /* Free instruction strings */
902
  for (i = 0; i < BROWSER_TEMPLATE_COUNT; i++)
903
    free(instructions[i]);
904
 
905 9 jack
  pspMenuDestroy(menu);
906
  free(cur_path);
907
}
908
 
909
void pspUiOpenGallery(const PspUiGallery *gallery, const char *title)
910
{
911
  PspMenu *menu = gallery->Menu;
912
  const PspMenuItem *top, *item;
913
  SceCtrlData pad;
914
  PspMenuItem *sel = menu->Selected;
915
 
916 128 jack
  int sx, sy, dx, dy,
917 9 jack
    orig_w = 272, orig_h = 228, // defaults
918
    fh, c, i, j,
919
    sbh, sby,
920
    w, h,
921
    icon_w, icon_h,
922
    grid_w, grid_h,
923
    icon_idx, icon_off,
924
    rows, vis_v, vis_s,
925
    icons;
926 128 jack
  const PspMenuItem *last_sel = NULL;
927 9 jack
 
928
  /* Find first icon and save its width/height */
929
  for (item = menu->First; item; item = item->Next)
930
  {
931
    if (item->Icon)
932
    {
933 136 jack
      orig_w = ((PspImage*)item->Icon)->Viewport.Width;
934 9 jack
      orig_h = ((PspImage*)item->Icon)->Height;
935
      break;
936
    }
937
  }
938
 
939
  fh = pspFontGetLineHeight(UiMetric.Font);
940
  sx = UiMetric.Left;
941
  sy = UiMetric.Top + ((title) ? fh + UiMetric.TitlePadding : 0);
942
  dx = UiMetric.Right;
943
  dy = UiMetric.Bottom;
944
  w = (dx - sx) - UiMetric.ScrollbarWidth; // visible width
945
  h = dy - sy; // visible height
946
  icon_w = (w - UiMetric.GalleryIconMarginWidth
947
    * (UiMetric.GalleryIconsPerRow - 1)) / UiMetric.GalleryIconsPerRow; // icon width
948
  icon_h = (int)((float)icon_w
949
    / ((float)orig_w / (float)orig_h)); // icon height
950
  grid_w = icon_w + UiMetric.GalleryIconMarginWidth; // width of the grid
951
  grid_h = icon_h + (fh * 2); // half-space for margin + 1 line of text 
952
  icons = menu->Count; // number of icons total
953
  rows = ceil((float)icons / (float)UiMetric.GalleryIconsPerRow); // number of rows total
954
  vis_v = h / grid_h; // number of rows visible at any time
955
  vis_s = UiMetric.GalleryIconsPerRow * vis_v; // max. number of icons visible on screen at any time
956 128 jack
  int max_w = ((float)icon_w * 1.5); /* Maximized width  */
957
  int max_h = ((float)icon_h * 1.5); /* Maximized height */
958 9 jack
 
959
  icon_idx = 0;
960
  icon_off = 0;
961
  top = menu->First;
962
 
963
  if (!sel)
964
  {
965
    /* Select the first icon */
966
    sel = menu->First;
967
  }
968
  else
969
  {
970
    /* Find the selected icon */
971
    for (item = menu->First; item; item = item->Next)
972
    {
973
      if (item == sel)
974
        break;
975
 
976
      if (++icon_idx >= vis_s)
977
      {
978
        icon_idx=0;
979
        icon_off += vis_s;
980
        top = item;
981
      }
982
    }
983
 
984
    if (item != sel)
985
    {
986
      /* Icon not found; reset to first icon */
987
      sel = menu->First;
988
      top = menu->First;
989
      icon_idx = 0;
990
      icon_off = 0;
991
    }
992
  }
993
 
994
  /* Compute height of scrollbar */
995
  sbh = ((float)vis_v / (float)(rows + (rows % vis_v))) * (float)h;
996
 
997
  /* Compute update frequency */
998
  u32 ticks_per_sec, ticks_per_upd;
999
  u64 current_tick, last_tick;
1000
 
1001
  ticks_per_sec = sceRtcGetTickResolution();
1002
  sceRtcGetCurrentTick(&last_tick);
1003
  ticks_per_upd = ticks_per_sec / UiMetric.MenuFps;
1004
 
1005 128 jack
  memset(call_list, 0, sizeof(call_list));
1006
  int sel_left = 0, max_left = 0;
1007
  int sel_top = 0, max_top = 0;
1008
 
1009
  pspVideoWaitVSync();
1010
 
1011 9 jack
  /* Begin navigation loop */
1012
  while (!ExitPSP)
1013
  {
1014
    if (!pspCtrlPollControls(&pad))
1015
      continue;
1016
 
1017
    /* Check the directional buttons */
1018
    if (sel)
1019
    {
1020
      if (pad.Buttons & PSP_CTRL_RIGHT && sel->Next)
1021
      {
1022
        sel = sel->Next;
1023
        if (++icon_idx >= vis_s)
1024
        {
1025
          icon_idx = 0;
1026
          icon_off += vis_s;
1027
          top = sel;
1028
        }
1029
      }
1030
      else if (pad.Buttons & PSP_CTRL_LEFT && sel->Prev)
1031
      {
1032
        sel = sel->Prev;
1033
        if (--icon_idx < 0)
1034
        {
1035
          icon_idx = vis_s-1;
1036
          icon_off -= vis_s;
1037
          for (i = 0; i < vis_s && top; i++) top = top->Prev;
1038
        }
1039
      }
1040
      else if (pad.Buttons & PSP_CTRL_DOWN)
1041
      {
1042
        for (i = 0; sel->Next && i < UiMetric.GalleryIconsPerRow; i++)
1043
        {
1044
          sel = sel->Next;
1045
          if (++icon_idx >= vis_s)
1046
          {
1047
            icon_idx = 0;
1048
            icon_off += vis_s;
1049
            top = sel;
1050
          }
1051
        }
1052
      }
1053
      else if (pad.Buttons & PSP_CTRL_UP)
1054
      {
1055
        for (i = 0; sel->Prev && i < UiMetric.GalleryIconsPerRow; i++)
1056
        {
1057
          sel = sel->Prev;
1058
          if (--icon_idx < 0)
1059
          {
1060
            icon_idx = vis_s-1;
1061
            icon_off -= vis_s;
1062
            for (j = 0; j < vis_s && top; j++) top = top->Prev;
1063
          }
1064
        }
1065
      }
1066
 
1067
      if (pad.Buttons & UiMetric.OkButton)
1068
      {
1069
        pad.Buttons &= ~UiMetric.OkButton;
1070
        if (!gallery->OnOk || gallery->OnOk(gallery, sel))
1071
          break;
1072
      }
1073
    }
1074
 
1075
    if (pad.Buttons & UiMetric.CancelButton)
1076
    {
1077
      pad.Buttons &= ~UiMetric.CancelButton;
1078
      if (gallery->OnCancel)
1079
        gallery->OnCancel(gallery, sel);
1080
      break;
1081
    }
1082
 
1083
    if ((pad.Buttons & CONTROL_BUTTON_MASK) && gallery->OnButtonPress)
1084
      if (gallery->OnButtonPress(gallery, sel, pad.Buttons & CONTROL_BUTTON_MASK))
1085
          break;
1086 128 jack
 
1087
    if (last_sel != sel && last_sel && sel && sel->Icon && UiMetric.Animate)
1088
    {
1089
      /* "Implode" animation */
1090
      int f = 1, n = 2;
1091
//      for (f = n - 1; f > 0; f--)
1092
//      {
1093
        pspVideoBegin();
1094
 
1095
        /* Clear screen */
1096
        if (!UiMetric.Background) pspVideoClearScreen();
1097
        else pspVideoPutImage(UiMetric.Background, 0, 0,
1098 136 jack
          UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
1099 128 jack
 
1100
        sceGuCallList(call_list);
1101
 
1102 136 jack
        pspVideoEnd();
1103
 
1104 128 jack
        /* Render the menu items */
1105
        for (i = sy, item = top; item && i + grid_h < dy; i += grid_h)
1106
          for (j = sx, c = 0; item && c < UiMetric.GalleryIconsPerRow; j += grid_w, c++, item = item->Next)
1107
            if (item->Icon && item != last_sel)
1108
            {
1109 136 jack
              pspVideoBegin();
1110 128 jack
              pspVideoPutImage((PspImage*)item->Icon, j, i, icon_w, icon_h);
1111 136 jack
              pspVideoEnd();
1112 128 jack
            }
1113
 
1114 136 jack
        pspVideoBegin();
1115 128 jack
 
1116 136 jack
        pspVideoPutImage((PspImage*)last_sel->Icon,
1117
          sel_left-(icon_w+((max_w-icon_w)/n)*f)/2,
1118
          sel_top-(icon_h+((max_h-icon_h)/n)*f)/2,
1119
          icon_w+((max_w-icon_w)/n)*f,
1120
          icon_h+((max_h-icon_h)/n)*f);
1121
 
1122 128 jack
        pspVideoEnd();
1123
 
1124
        /* Swap buffers */
1125
        pspVideoWaitVSync();
1126
        pspVideoSwapBuffers();
1127
//      }
1128 9 jack
    }
1129
 
1130 128 jack
    sceGuStart(GU_CALL, call_list);
1131 9 jack
 
1132
    /* Draw title */
1133
    if (title)
1134
    {
1135
      pspVideoPrint(UiMetric.Font, UiMetric.Left, UiMetric.Top,
1136
        title, UiMetric.TitleColor);
1137
      pspVideoDrawLine(UiMetric.Left, UiMetric.Top + fh - 1, UiMetric.Left + w,
1138
        UiMetric.Top + fh - 1, UiMetric.TitleColor);
1139
    }
1140
 
1141
    /* Draw scrollbar */
1142
    if (sbh < h)
1143
    {
1144
      sby = sy + (((float)icon_off / (float)UiMetric.GalleryIconsPerRow)
1145
        / (float)(rows + (rows % vis_v))) * (float)h;
1146
      pspVideoFillRect(dx - UiMetric.ScrollbarWidth,
1147
        sy, dx, dy, UiMetric.ScrollbarBgColor);
1148
      pspVideoFillRect(dx - UiMetric.ScrollbarWidth,
1149
        sby, dx, sby+sbh, UiMetric.ScrollbarColor);
1150
    }
1151
 
1152
    /* Draw instructions */
1153
    if (sel && sel->HelpText)
1154
    {
1155
      static char help_copy[MAX_DIR_LEN];
1156
      strncpy(help_copy, sel->HelpText, MAX_DIR_LEN);
1157
      help_copy[MAX_DIR_LEN - 1] = '\0';
1158 128 jack
      ReplaceIcons(help_copy);
1159 9 jack
 
1160
      pspVideoPrintCenter(UiMetric.Font,
1161
        0, SCR_HEIGHT - fh, SCR_WIDTH, help_copy, UiMetric.StatusBarColor);
1162
    }
1163
 
1164 128 jack
    /* Render non-image components of each item */
1165 9 jack
    for (i = sy, item = top; item && i + grid_h < dy; i += grid_h)
1166
    {
1167
      for (j = sx, c = 0; item && c < UiMetric.GalleryIconsPerRow; j += grid_w, c++, item = item->Next)
1168
      {
1169 128 jack
        if (item != sel)
1170 9 jack
        {
1171 128 jack
          pspVideoShadowRect(j - 1, i - 1, j + icon_w, i + icon_h, PSP_COLOR_BLACK, 3);
1172
          pspVideoDrawRect(j - 1, i - 1, j + icon_w, i + icon_h, UiMetric.TextColor);
1173 9 jack
 
1174 128 jack
          if (item->Caption)
1175
          {
1176
            int cap_pos = j + icon_w / 2
1177
              - pspFontGetTextWidth(UiMetric.Font, item->Caption) / 2;
1178
            pspVideoPrint(UiMetric.Font, cap_pos,
1179
              i + icon_h + (fh / 2), item->Caption, UiMetric.TextColor);
1180
          }
1181 9 jack
        }
1182 128 jack
        else
1183 9 jack
        {
1184 128 jack
          sel_left = j + icon_w / 2;
1185
          sel_top = i + icon_h / 2;
1186 9 jack
 
1187 128 jack
          sel_left = (sel_left-max_w/2 < sx) ? sx+max_w/2 : sel_left;
1188
          sel_top = (sel_top-max_h/2 < UiMetric.Top)
1189
            ? UiMetric.Top+max_h/2 : sel_top;
1190
          sel_left = (sel_left+max_w/2 > dx) ? dx-max_w/2 : sel_left;
1191
          sel_top = (sel_top+max_h/2 > dy) ? dy-max_h/2 : sel_top;
1192 9 jack
        }
1193
      }
1194
    }
1195
 
1196 128 jack
    /* Render status information */
1197
    RenderStatus();
1198
 
1199 9 jack
    /* Perform any custom drawing */
1200
    if (gallery->OnRender)
1201
      gallery->OnRender(gallery, sel);
1202
 
1203 128 jack
    sceGuFinish();
1204
 
1205
    if (last_sel != sel && last_sel && sel && sel->Icon && UiMetric.Animate)
1206
    {
1207
      /* Popup animation */
1208
      int f = 1, n = 2;
1209
//      for (f = 1; f < n; f++)
1210
//      {
1211
        pspVideoBegin();
1212
 
1213
        /* Clear screen */
1214
        if (!UiMetric.Background) pspVideoClearScreen();
1215
        else pspVideoPutImage(UiMetric.Background, 0, 0,
1216 136 jack
          UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
1217 128 jack
 
1218
        sceGuCallList(call_list);
1219
 
1220
        pspVideoEnd();
1221
 
1222
        /* Render the menu items */
1223
        for (i = sy, item = top; item && i + grid_h < dy; i += grid_h)
1224
          for (j = sx, c = 0; item && c < UiMetric.GalleryIconsPerRow; j += grid_w, c++, item = item->Next)
1225
            if (item->Icon && item != sel)
1226
            {
1227
              pspVideoBegin();
1228
              pspVideoPutImage((PspImage*)item->Icon, j, i, icon_w, icon_h);
1229
              pspVideoEnd();
1230
            }
1231
 
1232
        pspVideoBegin();
1233
 
1234 136 jack
        pspVideoPutImage((PspImage*)sel->Icon,
1235
          sel_left-(icon_w+((max_w-icon_w)/n)*f)/2,
1236
          sel_top-(icon_h+((max_h-icon_h)/n)*f)/2,
1237
          icon_w+((max_w-icon_w)/n)*f,
1238
          icon_h+((max_h-icon_h)/n)*f);
1239 128 jack
 
1240
        pspVideoEnd();
1241
 
1242
        /* Swap buffers */
1243
        pspVideoWaitVSync();
1244
        pspVideoSwapBuffers();
1245
//      }
1246
    }
1247
 
1248
    pspVideoBegin();
1249
 
1250
    /* Clear screen */
1251
    if (!UiMetric.Background) pspVideoClearScreen();
1252
    else pspVideoPutImage(UiMetric.Background, 0, 0,
1253 136 jack
      UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
1254 128 jack
 
1255
    sceGuCallList(call_list);
1256
 
1257 9 jack
    pspVideoEnd();
1258
 
1259 128 jack
    /* Render the menu items */
1260
    for (i = sy, item = top; item && i + grid_h < dy; i += grid_h)
1261
      for (j = sx, c = 0; item && c < UiMetric.GalleryIconsPerRow; j += grid_w, c++, item = item->Next)
1262
        if (item->Icon && item != sel)
1263
        {
1264
          pspVideoBegin();
1265
          pspVideoPutImage((PspImage*)item->Icon, j, i, icon_w, icon_h);
1266
          pspVideoEnd();
1267
        }
1268
 
1269
    pspVideoBegin();
1270
 
1271
    if (sel && sel->Icon)
1272
    {
1273
      pspVideoPutImage((PspImage*)sel->Icon, sel_left-max_w/2, sel_top-max_h/2,
1274
        max_w, max_h);
1275
      pspVideoGlowRect(sel_left-max_w/2, sel_top-max_h/2,
1276
        sel_left+max_w/2, sel_top+max_h/2,
1277
        COLOR(0xff,0xff,0xff,UI_ANIM_FOG_STEP * UI_ANIM_FRAMES), 2);
1278
    }
1279
 
1280
    if (sel && sel->Caption)
1281
    {
1282
      int cap_left = sel_left
1283
        - pspFontGetTextWidth(UiMetric.Font, sel->Caption) / 2;
1284
      pspVideoPrint(UiMetric.Font, cap_left,
1285
        sel_top + max_h/2 - (fh + (fh - UiMetric.Font->Ascent)), sel->Caption,
1286
        UiMetric.TextColor);
1287
    }
1288
 
1289
    pspVideoEnd();
1290
 
1291
    last_sel = sel;
1292
 
1293 9 jack
    /* Wait if needed */
1294
    do { sceRtcGetCurrentTick(&current_tick); }
1295
    while (current_tick - last_tick < ticks_per_upd);
1296
    last_tick = current_tick;
1297
 
1298
    /* Swap buffers */
1299
    pspVideoWaitVSync();
1300
    pspVideoSwapBuffers();
1301
  }
1302
 
1303
  menu->Selected = sel;
1304
}
1305
 
1306
void pspUiOpenMenu(const PspUiMenu *uimenu, const char *title)
1307
{
1308
  struct UiPos pos;
1309
  PspMenu *menu = uimenu->Menu;
1310
  const PspMenuItem *item;
1311
  SceCtrlData pad;
1312
  const PspMenuOption *temp_option;
1313
  int lnmax;
1314
  int sby, sbh, i, j, k, h, w, fh = pspFontGetLineHeight(UiMetric.Font);
1315 128 jack
  int sx, sy, dx, dy, sel_top = 0, last_sel_top = 0;
1316 9 jack
  int max_item_w = 0, item_w;
1317
  int option_mode, max_option_w = 0;
1318
  int arrow_w = pspFontGetTextWidth(UiMetric.Font, "\272");
1319
  int anim_frame = 0, anim_incr = 1;
1320 128 jack
  PspMenuItem *sel = menu->Selected, *last_sel = NULL;
1321 9 jack
 
1322
  sx = UiMetric.Left;
1323
  sy = UiMetric.Top + ((title) ? (fh + UiMetric.TitlePadding) : 0);
1324
  dx = UiMetric.Right;
1325
  dy = UiMetric.Bottom;
1326
  w = dx - sx - UiMetric.ScrollbarWidth;
1327
  h = dy - sy;
1328
 
1329 128 jack
  memset(call_list, 0, sizeof(call_list));
1330
 
1331 9 jack
  /* Determine width of the longest caption */
1332
  for (item = menu->First; item; item = item->Next)
1333
  {
1334
    if (item->Caption)
1335
    {
1336
      item_w = pspFontGetTextWidth(UiMetric.Font, item->Caption);
1337
      if (item_w > max_item_w)
1338
        max_item_w = item_w;
1339
    }
1340
  }
1341
 
1342
  /* Initialize variables */
1343
  lnmax = (dy - sy) / fh;
1344
  sbh = (menu->Count > lnmax) ? (int)((float)h * ((float)lnmax / (float)menu->Count)) : 0;
1345
 
1346
  pos.Index = 0;
1347
  pos.Offset = 0;
1348
  pos.Top = NULL;
1349
  option_mode = 0;
1350
  temp_option = NULL;
1351
 
1352 128 jack
  int cur_x=0, min_x=0, max_x=0;
1353
  int cur_y=0, min_y=0, max_y=0;
1354
 
1355 9 jack
  /* Find first selectable item */
1356
  if (!sel)
1357
  {
1358
    for (sel = menu->First; sel; sel = sel->Next)
1359
      if (sel->Caption && sel->Caption[0] != '\t')
1360
        break;
1361
  }
1362
 
1363
  /* Compute index and offset of selected file */
1364
  pos.Top = menu->First;
1365
  for (item = menu->First; item != sel; item = item->Next)
1366
  {
1367
    if (pos.Index + 1 >= lnmax) { pos.Offset++; pos.Top = pos.Top->Next; }
1368
    else pos.Index++;
1369
  }
1370
 
1371
  pspVideoWaitVSync();
1372
  PspMenuItem *last;
1373
  struct UiPos last_valid;
1374
 
1375
  /* Compute update frequency */
1376
  u32 ticks_per_sec, ticks_per_upd;
1377
  u64 current_tick, last_tick;
1378
 
1379
  ticks_per_sec = sceRtcGetTickResolution();
1380
  sceRtcGetCurrentTick(&last_tick);
1381
  ticks_per_upd = ticks_per_sec / UiMetric.MenuFps;
1382
 
1383 128 jack
  int fast_scroll;
1384
 
1385 9 jack
  /* Begin navigation loop */
1386
  while (!ExitPSP)
1387
  {
1388
    if (!pspCtrlPollControls(&pad))
1389
      continue;
1390
 
1391 128 jack
    fast_scroll = 0;
1392
    anim_frame += (UiMetric.Animate) ? anim_incr : 0;
1393 9 jack
    if (anim_frame > 2 || anim_frame < 0)
1394
      anim_incr *= -1;
1395
 
1396
    /* Check the directional buttons */
1397
    if (sel)
1398
    {
1399
      if (pad.Buttons & PSP_CTRL_DOWN || pad.Buttons & PSP_CTRL_ANALDOWN)
1400
      {
1401 128 jack
        fast_scroll = pad.Buttons & PSP_CTRL_ANALDOWN;
1402
 
1403 9 jack
        if (option_mode)
1404
        {
1405
          if (temp_option->Next)
1406
            temp_option = temp_option->Next;
1407
        }
1408
        else
1409
        {
1410
          if (sel->Next)
1411
          {
1412
            last = sel;
1413
            last_valid = pos;
1414
 
1415
            for (;;)
1416
            {
1417
              if (pos.Index + 1 >= lnmax)
1418
              {
1419
                pos.Offset++;
1420
                pos.Top = pos.Top->Next;
1421
              }
1422
              else pos.Index++;
1423
 
1424
              sel = sel->Next;
1425
 
1426
              if (!sel)
1427
              {
1428
                sel = last;
1429
                pos = last_valid;
1430
                break;
1431
              }
1432
 
1433
              if (sel->Caption && sel->Caption[0] != '\t')
1434
                break;
1435
            }
1436
          }
1437
        }
1438
      }
1439
      else if (pad.Buttons & PSP_CTRL_UP || pad.Buttons & PSP_CTRL_ANALUP)
1440
      {
1441 128 jack
        fast_scroll = pad.Buttons & PSP_CTRL_ANALUP;
1442
 
1443 9 jack
        if (option_mode)
1444
        {
1445
          if (temp_option->Prev)
1446
            temp_option = temp_option->Prev;
1447
        }
1448
        else
1449
        {
1450
          if (sel->Prev)
1451
          {
1452
            last = sel;
1453
            last_valid = pos;
1454
 
1455
            for (;;)
1456
            {
1457
              if (pos.Index - 1 < 0)
1458
              {
1459
                pos.Offset--;
1460
                pos.Top = pos.Top->Prev;
1461
              }
1462
              else pos.Index--;
1463
 
1464
              sel = sel->Prev;
1465
 
1466
              if (!sel)
1467
              {
1468
                sel = last;
1469
                pos = last_valid;
1470
                break;
1471
              }
1472
 
1473
              if (sel->Caption && sel->Caption[0] != '\t')
1474
                break;
1475
            }
1476
          }
1477
        }
1478
      }
1479
 
1480 128 jack
      /* Recompute box bounds if scrolling in option mode */
1481
      if (option_mode && (pad.Buttons &
1482
        (PSP_CTRL_UP|PSP_CTRL_ANALUP|PSP_CTRL_DOWN|PSP_CTRL_ANALDOWN)))
1483
      {
1484
        cur_x = sx + max_item_w + UiMetric.MenuItemMargin + 10;
1485
        min_y = sy + pos.Index * fh;
1486
        cur_y = min_y + fh / 2;
1487
        max_y = sy + (pos.Index  + 1) * fh;
1488
        min_x = cur_x - UiMetric.MenuItemMargin;
1489
        max_x = cur_x + max_option_w + UiMetric.MenuItemMargin;
1490
        cur_x += pspFontGetTextWidth(UiMetric.Font, " >");
1491
        if (sel->Selected && sel->Selected->Text)
1492
          cur_x += pspFontGetTextWidth(UiMetric.Font, sel->Selected->Text);
1493
 
1494
        const PspMenuOption *option;
1495
        for (option = temp_option; option && min_y >= sy; option = option->Prev, min_y -= fh);
1496
        for (option = temp_option->Next; option && max_y < dy; option = option->Next, max_y += fh);
1497
        max_y += fh;
1498
      }
1499
 
1500 9 jack
      if (option_mode)
1501
      {
1502
        if (pad.Buttons & PSP_CTRL_RIGHT || pad.Buttons & UiMetric.OkButton)
1503
        {
1504
          option_mode = 0;
1505
 
1506
          /* If the callback function refuses the change, restore selection */
1507 128 jack
          if (!uimenu->OnItemChanged || uimenu->OnItemChanged(uimenu, sel, temp_option))
1508 9 jack
            sel->Selected = temp_option;
1509
        }
1510
        else if (pad.Buttons & PSP_CTRL_LEFT  || pad.Buttons & UiMetric.CancelButton)
1511
        {
1512
          option_mode = 0;
1513 128 jack
 
1514 9 jack
          if (pad.Buttons & UiMetric.CancelButton)
1515
            pad.Buttons &= ~UiMetric.CancelButton;
1516
        }
1517 128 jack
 
1518
        if (!option_mode)
1519
        {
1520
          if (UiMetric.Animate)
1521
          {
1522
            /* Deflation animation */
1523
            for (i = UI_ANIM_FRAMES - 1; i >= 0; i--)
1524
            {
1525
                  pspVideoBegin();
1526
              if (!UiMetric.Background) pspVideoClearScreen();
1527
                else pspVideoPutImage(UiMetric.Background, 0, 0,
1528 136 jack
                  UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
1529 128 jack
 
1530
                  pspVideoCallList(call_list);
1531
 
1532
              /* Perform any custom drawing */
1533
              if (uimenu->OnRender)
1534
                uimenu->OnRender(uimenu, sel);
1535
 
1536
                  /* Clear screen */
1537
                  pspVideoFillRect(cur_x - ((cur_x - min_x) / UI_ANIM_FRAMES) * i,
1538
                    cur_y - ((cur_y - min_y) / UI_ANIM_FRAMES) * i,
1539
                    cur_x + ((max_x - cur_x) / UI_ANIM_FRAMES) * i,
1540
                    cur_y + ((max_y - cur_y) / UI_ANIM_FRAMES) * i,
1541
                UiMetric.MenuOptionBoxBg);
1542
 
1543
              /* Selected option for the item */
1544
              if (sel->Selected && sel->Selected->Text)
1545
              pspVideoPrint(UiMetric.Font,
1546
                sx + max_item_w + UiMetric.MenuItemMargin + 10,
1547
                sy + pos.Index * fh, sel->Selected->Text, UiMetric.SelectedColor);
1548
 
1549
                  pspVideoEnd();
1550
 
1551
              /* Swap buffers */
1552
              pspVideoWaitVSync();
1553
              pspVideoSwapBuffers();
1554
                }
1555
          }
1556
        }
1557 9 jack
      }
1558
      else
1559
      {
1560 128 jack
        if ((pad.Buttons & PSP_CTRL_RIGHT)
1561
          && sel->Options && sel->Options->Next)
1562 9 jack
        {
1563 128 jack
          option_mode = 1;
1564
          max_option_w = 0;
1565
          int width;
1566
          const PspMenuOption *option;
1567
 
1568
          /* Find the longest option caption */
1569
          for (option = sel->Options; option; option = option->Next)
1570
            if (option->Text && (width = pspFontGetTextWidth(UiMetric.Font, option->Text)) > max_option_w)
1571
              max_option_w = width;
1572
 
1573
          temp_option = (sel->Selected) ? sel->Selected : sel->Options;
1574
 
1575
          /* Determine bounds */
1576
          cur_x = sx + max_item_w + UiMetric.MenuItemMargin + 10;
1577
          min_y = sy + pos.Index * fh;
1578
          cur_y = min_y + fh / 2;
1579
          max_y = sy + (pos.Index  + 1) * fh;
1580
          min_x = cur_x - UiMetric.MenuItemMargin;
1581
          max_x = cur_x + max_option_w + UiMetric.MenuItemMargin;
1582
          cur_x += pspFontGetTextWidth(UiMetric.Font, " >");
1583
          if (sel->Selected && sel->Selected->Text)
1584
            cur_x += pspFontGetTextWidth(UiMetric.Font, sel->Selected->Text);
1585
 
1586
          for (option = temp_option; option && min_y >= sy; option = option->Prev, min_y -= fh);
1587
          for (option = temp_option->Next; option && max_y < dy; option = option->Next, max_y += fh);
1588
          max_y += fh;
1589
 
1590
          if (UiMetric.Animate)
1591 9 jack
          {
1592 128 jack
            /* Expansion animation */
1593
            for (i = 0; i <= UI_ANIM_FRAMES; i++)
1594
            {
1595
                  pspVideoBegin();
1596 9 jack
 
1597 128 jack
              if (!UiMetric.Background) pspVideoClearScreen();
1598
                else pspVideoPutImage(UiMetric.Background, 0, 0,
1599 136 jack
                  UiMetric.Background->Viewport.Width,
1600
                  UiMetric.Background->Height);
1601 128 jack
 
1602
                  pspVideoCallList(call_list);
1603 9 jack
 
1604 128 jack
              /* Perform any custom drawing */
1605
              if (uimenu->OnRender)
1606
                uimenu->OnRender(uimenu, sel);
1607
 
1608
                  pspVideoFillRect(cur_x - ((cur_x - min_x) / UI_ANIM_FRAMES) * i,
1609
                    cur_y - ((cur_y - min_y) / UI_ANIM_FRAMES) * i,
1610
                    cur_x + ((max_x - cur_x) / UI_ANIM_FRAMES) * i,
1611
                    cur_y + ((max_y - cur_y) / UI_ANIM_FRAMES) * i,
1612
                UiMetric.MenuOptionBoxBg);
1613
 
1614
                  pspVideoEnd();
1615
 
1616
              /* Swap buffers */
1617
              pspVideoWaitVSync();
1618
              pspVideoSwapBuffers();
1619
                }
1620
                }
1621 9 jack
        }
1622
        else if (pad.Buttons & UiMetric.OkButton)
1623
        {
1624
          if (!uimenu->OnOk || uimenu->OnOk(uimenu, sel))
1625
            break;
1626
        }
1627
      }
1628
    }
1629
 
1630
    if (!option_mode)
1631
    {
1632
      if (pad.Buttons & UiMetric.CancelButton)
1633
      {
1634
        if (uimenu->OnCancel)
1635
          uimenu->OnCancel(uimenu, sel);
1636
        break;
1637
      }
1638
 
1639
      if ((pad.Buttons & CONTROL_BUTTON_MASK) && uimenu->OnButtonPress)
1640
      {
1641
        if (uimenu->OnButtonPress(uimenu, sel, pad.Buttons & CONTROL_BUTTON_MASK))
1642
            break;
1643
      }
1644
    }
1645
 
1646 128 jack
    /* Render to a call list */
1647
    sceGuStart(GU_CALL, call_list);
1648 9 jack
 
1649
    /* Draw instructions */
1650
    if (sel)
1651
    {
1652
      const char *dirs = NULL;
1653
 
1654
      if (!option_mode && sel->HelpText)
1655
      {
1656
        static char help_copy[MAX_DIR_LEN];
1657
        strncpy(help_copy, sel->HelpText, MAX_DIR_LEN);
1658
        help_copy[MAX_DIR_LEN - 1] = '\0';
1659 128 jack
        ReplaceIcons(help_copy);
1660 9 jack
 
1661
        dirs = help_copy;
1662
      }
1663
      else if (option_mode)
1664
      {
1665
        static char help_copy[MAX_DIR_LEN];
1666
        strncpy(help_copy, OptionModeTemplate, MAX_DIR_LEN);
1667
        help_copy[MAX_DIR_LEN - 1] = '\0';
1668 128 jack
        ReplaceIcons(help_copy);
1669 9 jack
 
1670
        dirs = help_copy;
1671
      }
1672
 
1673
      if (dirs)
1674
        pspVideoPrintCenter(UiMetric.Font,
1675
          0, SCR_HEIGHT - fh, SCR_WIDTH, dirs, UiMetric.StatusBarColor);
1676
    }
1677
 
1678
    /* Draw title */
1679
    if (title)
1680
    {
1681
      pspVideoPrint(UiMetric.Font, UiMetric.Left, UiMetric.Top,
1682
        title, UiMetric.TitleColor);
1683
      pspVideoDrawLine(UiMetric.Left, UiMetric.Top + fh - 1, UiMetric.Left + w,
1684
        UiMetric.Top + fh - 1, UiMetric.TitleColor);
1685
    }
1686
 
1687
    /* Render the menu items */
1688
    for (item = pos.Top, i = 0, j = sy; item && i < lnmax; item = item->Next, j += fh, i++)
1689
    {
1690
      if (item->Caption)
1691
      {
1692
            /* Section header */
1693
            if (item->Caption[0] == '\t')
1694
                {
1695
                  // if (i != 0) j += fh / 2;
1696
          pspVideoPrint(UiMetric.Font, sx, j, item->Caption + 1, UiMetric.TitleColor);
1697
          pspVideoDrawLine(sx, j + fh - 1, sx + w, j + fh - 1, UiMetric.TitleColor);
1698
                  continue;
1699
                }
1700
 
1701 128 jack
        if (item == sel) sel_top = j;
1702 9 jack
 
1703
        /* Item caption */
1704 128 jack
        pspVideoPrint(UiMetric.Font, sx + 10, j, item->Caption,
1705 9 jack
          (item == sel) ? UiMetric.SelectedColor : UiMetric.TextColor);
1706
 
1707 128 jack
        if (!option_mode || item != sel)
1708 9 jack
        {
1709 128 jack
          /* Selected option for the item */
1710
          if (item->Selected)
1711
          {
1712
            k = sx + max_item_w + UiMetric.MenuItemMargin + 10;
1713
            k += pspVideoPrint(UiMetric.Font, k, j, item->Selected->Text,
1714
              (item == sel) ? UiMetric.SelectedColor : UiMetric.TextColor);
1715 9 jack
 
1716 128 jack
            if (!option_mode && item == sel)
1717
              if (sel->Options && sel->Options->Next)
1718
                pspVideoPrint(UiMetric.Font, k + anim_frame, j, " >", UiMetric.MenuDecorColor);
1719 9 jack
          }
1720
        }
1721
      }
1722
    }
1723
 
1724 128 jack
    /* Render status information */
1725
    RenderStatus();
1726
 
1727
    /* Draw scrollbar */
1728
    if (sbh > 0)
1729
    {
1730
      sby = sy + (int)((float)(h - sbh) * ((float)(pos.Offset + pos.Index) / (float)menu->Count));
1731
      pspVideoFillRect(dx - UiMetric.ScrollbarWidth, sy, dx, dy, UiMetric.ScrollbarBgColor);
1732
      pspVideoFillRect(dx - UiMetric.ScrollbarWidth, sby, dx, sby + sbh, UiMetric.ScrollbarColor);
1733
    }
1734
 
1735
    /* End writing to call list */
1736
    sceGuFinish();
1737
 
1738
    if (!option_mode && !fast_scroll && sel && last_sel
1739
      && UiMetric.Animate && last_sel != sel)
1740
    {
1741
      /* Move animation */
1742
      int f, n = 4;
1743
      for (f = 1; f <= n; f++)
1744
      {
1745
        pspVideoBegin();
1746
 
1747
        /* Clear screen */
1748
        if (!UiMetric.Background) pspVideoClearScreen();
1749
        else pspVideoPutImage(UiMetric.Background, 0, 0,
1750 136 jack
          UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
1751 128 jack
 
1752
        int box_top = last_sel_top-((last_sel_top-sel_top)/n)*f;
1753
        pspVideoFillRect(sx, box_top, sx+w, box_top+fh,
1754
          UiMetric.SelectedBgColor);
1755
 
1756
        sceGuCallList(call_list);
1757
 
1758
        /* Perform any custom drawing */
1759
        if (uimenu->OnRender)
1760
          uimenu->OnRender(uimenu, sel);
1761
 
1762
        pspVideoEnd();
1763
 
1764
        /* Swap buffers */
1765
        pspVideoWaitVSync();
1766
        pspVideoSwapBuffers();
1767
      }
1768
    }
1769
 
1770
    /* Begin direct rendering */
1771
    pspVideoBegin();
1772
 
1773
    /* Clear screen */
1774
    if (!UiMetric.Background) pspVideoClearScreen();
1775
    else pspVideoPutImage(UiMetric.Background, 0, 0,
1776 136 jack
      UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
1777 128 jack
 
1778
    /* Draw the highlight for selected item */
1779
    if (!option_mode)
1780
      pspVideoFillRect(sx, sel_top, sx+w, sel_top+fh,
1781
        UiMetric.SelectedBgColor);
1782
 
1783
    pspVideoCallList(call_list);
1784
 
1785
    /* Perform any custom drawing */
1786
    if (uimenu->OnRender)
1787
      uimenu->OnRender(uimenu, sel);
1788
 
1789 9 jack
    /* Render menu options */
1790
    if (option_mode)
1791
    {
1792
      k = sx + max_item_w + UiMetric.MenuItemMargin + 10;
1793 128 jack
      int arrow_x = min_x + (UiMetric.MenuItemMargin / 2 - arrow_w / 2);
1794 9 jack
      const PspMenuOption *option;
1795
 
1796 128 jack
      /* Background */
1797
      pspVideoFillRect(min_x, min_y, max_x, max_y, UiMetric.MenuOptionBoxBg);
1798
      pspVideoFillRect(min_x, sy + pos.Index * fh, max_x,
1799
        sy + (pos.Index + 1) * fh, UiMetric.MenuSelOptionBg);
1800
      pspVideoGlowRect(min_x, min_y, max_x, max_y,
1801
        COLOR(0xff,0xff,0xff,UI_ANIM_FOG_STEP * UI_ANIM_FRAMES), 2);
1802
 
1803
      /* Render selected item + previous items */
1804
      i = sy + pos.Index * fh;
1805 9 jack
      for (option = temp_option; option && i >= sy; option = option->Prev, i -= fh)
1806 128 jack
        pspVideoPrint(UiMetric.Font, k, i, option->Text, (option == temp_option)
1807
          ? UiMetric.SelectedColor : UiMetric.MenuOptionBoxColor);
1808 9 jack
 
1809
      /* Up arrow */
1810 128 jack
      if (option) pspVideoPrint(UiMetric.Font, arrow_x,
1811 106 jack
          i + fh + anim_frame, PSP_CHAR_UP_ARROW, UiMetric.MenuDecorColor);
1812 9 jack
 
1813
      /* Render following items */
1814
      i = sy + (pos.Index  + 1) * fh;
1815
      for (option = temp_option->Next; option && i < dy; option = option->Next, i += fh)
1816 128 jack
        pspVideoPrint(UiMetric.Font, k, i, option->Text,
1817
          UiMetric.MenuOptionBoxColor);
1818 9 jack
 
1819
      /* Down arrow */
1820 128 jack
      if (option) pspVideoPrint(UiMetric.Font, arrow_x, i - fh - anim_frame,
1821 106 jack
          PSP_CHAR_DOWN_ARROW, UiMetric.MenuDecorColor);
1822 9 jack
    }
1823
 
1824
    pspVideoEnd();
1825
 
1826
    /* Wait if needed */
1827
    do { sceRtcGetCurrentTick(&current_tick); }
1828
    while (current_tick - last_tick < ticks_per_upd);
1829
    last_tick = current_tick;
1830
 
1831
    /* Swap buffers */
1832
    pspVideoWaitVSync();
1833
    pspVideoSwapBuffers();
1834 128 jack
 
1835
    last_sel = sel;
1836
    last_sel_top = sel_top;
1837 9 jack
  }
1838
 
1839
  menu->Selected = sel;
1840
}
1841
 
1842
void pspUiSplashScreen(PspUiSplash *splash)
1843
{
1844
  SceCtrlData pad;
1845
  int fh = pspFontGetLineHeight(UiMetric.Font);
1846
 
1847
  while (!ExitPSP)
1848
  {
1849
    if (!pspCtrlPollControls(&pad))
1850
      continue;
1851
 
1852
    if (pad.Buttons & UiMetric.CancelButton)
1853
    {
1854
      if (splash->OnCancel) splash->OnCancel(splash, NULL);
1855
      break;
1856
    }
1857
 
1858
    if ((pad.Buttons & CONTROL_BUTTON_MASK) && splash->OnButtonPress)
1859
    {
1860
      if (splash->OnButtonPress(splash, pad.Buttons & CONTROL_BUTTON_MASK))
1861
          break;
1862
    }
1863
 
1864
    pspVideoBegin();
1865
 
1866
    /* Clear screen */
1867
    if (UiMetric.Background)
1868
      pspVideoPutImage(UiMetric.Background, 0, 0,
1869 136 jack
        UiMetric.Background->Viewport.Width, UiMetric.Background->Height);
1870 9 jack
    else
1871
      pspVideoClearScreen();
1872
 
1873
    /* Draw instructions */
1874 34 jack
    const char *dirs = (splash->OnGetStatusBarText)
1875
      ? splash->OnGetStatusBarText(splash)
1876
      : SplashStatusBarTemplate;
1877
    pspVideoPrintCenter(UiMetric.Font, UiMetric.Left,
1878
      SCR_HEIGHT - fh, UiMetric.Right, dirs, UiMetric.StatusBarColor);
1879 9 jack
 
1880 128 jack
    /* Render status information */
1881
    RenderStatus();
1882
 
1883 9 jack
    /* Perform any custom drawing */
1884
    if (splash->OnRender)
1885
      splash->OnRender(splash, NULL);
1886
 
1887
    pspVideoEnd();
1888
 
1889
    /* Swap buffers */
1890
    pspVideoWaitVSync();
1891
    pspVideoSwapBuffers();
1892
  }
1893
}
1894 64 jack
 
1895 106 jack
const PspMenuItem* pspUiSelect(const char *title, const PspMenu *menu)
1896
{
1897 128 jack
  const PspMenuItem *sel, *item, *last_sel = NULL;
1898 106 jack
  struct UiPos pos;
1899
  int lnmax, lnhalf;
1900
  int i, j, h, w, fh = pspFontGetLineHeight(UiMetric.Font);
1901
  int sx, sy, dx, dy;
1902
  int anim_frame = 0, anim_incr = 1;
1903
  int arrow_w = pspFontGetTextWidth(UiMetric.Font, PSP_CHAR_DOWN_ARROW);
1904
  int widest = 100;
1905 128 jack
  int sel_top = 0, last_sel_top = 0;
1906 106 jack
  SceCtrlData pad;
1907
 
1908
  char *help_text = strdup(SelectorTemplate);
1909 128 jack
  ReplaceIcons(help_text);
1910 106 jack
 
1911 128 jack
  memset(call_list, 0, sizeof(call_list));
1912
 
1913 106 jack
  /* Determine width of the longest caption */
1914
  for (item = menu->First; item; item = item->Next)
1915
  {
1916
    if (item->Caption)
1917
    {
1918
      int item_w = pspFontGetTextWidth(UiMetric.Font, item->Caption);
1919
      if (item_w > widest)
1920
        widest = item_w;
1921
    }
1922
  }
1923
 
1924
  widest += UiMetric.MenuItemMargin * 2;
1925
 
1926
  sx = SCR_WIDTH - widest;
1927
  sy = UiMetric.Top;
1928
  dx = SCR_WIDTH;
1929
  dy = UiMetric.Bottom;
1930
  w = dx - sx;
1931
  h = dy - sy;
1932
 
1933
  u32 ticks_per_sec, ticks_per_upd;
1934
  u64 current_tick, last_tick;
1935
 
1936
  /* Initialize variables */
1937
  lnmax = (dy - sy) / fh;
1938
  lnhalf = lnmax >> 1;
1939
 
1940
  sel = menu->First;
1941
  pos.Top = menu->First;
1942
  pos.Index = pos.Offset = 0;
1943
 
1944
  pspVideoWaitVSync();
1945
 
1946
  /* Compute update frequency */
1947
  ticks_per_sec = sceRtcGetTickResolution();
1948
  sceRtcGetCurrentTick(&last_tick);
1949
  ticks_per_upd = ticks_per_sec / UiMetric.MenuFps;
1950
 
1951 128 jack
  /* Get copy of screen */
1952
  PspImage *screen = pspVideoGetVramBufferCopy();
1953
 
1954
  if (UiMetric.Animate)
1955
  {
1956
    /* Intro animation */
1957
    for (i = 0; i < UI_ANIM_FRAMES; i++)
1958
    {
1959
          pspVideoBegin();
1960
 
1961
          /* Clear screen */
1962 136 jack
          pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
1963 128 jack
 
1964
          /* Apply fog and draw right frame */
1965
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
1966
            COLOR(0, 0, 0, UI_ANIM_FOG_STEP * i));
1967
          pspVideoFillRect(SCR_WIDTH - (i * (widest / UI_ANIM_FRAMES)),
1968
        0, dx, SCR_HEIGHT, UiMetric.MenuOptionBoxBg);
1969
 
1970
          pspVideoEnd();
1971
 
1972
      /* Swap buffers */
1973
      pspVideoWaitVSync();
1974
      pspVideoSwapBuffers();
1975
        }
1976
  }
1977
 
1978
  int fast_scroll;
1979
 
1980 106 jack
  /* Begin navigation loop */
1981
  while (!ExitPSP)
1982
  {
1983
    if (!pspCtrlPollControls(&pad))
1984
      continue;
1985
 
1986 128 jack
    fast_scroll = 0;
1987
 
1988 106 jack
    /* Incr/decr animation frame */
1989 128 jack
    anim_frame += (UiMetric.Animate) ? anim_incr : 0;
1990 106 jack
    if (anim_frame > 2 || anim_frame < 0)
1991
      anim_incr *= -1;
1992
 
1993
    /* Check the directional buttons */
1994
    if (sel)
1995
    {
1996
      if ((pad.Buttons & PSP_CTRL_DOWN || pad.Buttons & PSP_CTRL_ANALDOWN)
1997
        && sel->Next)
1998
      {
1999 128 jack
        fast_scroll = pad.Buttons & PSP_CTRL_ANALDOWN;
2000 106 jack
        if (pos.Index + 1 >= lnmax) { pos.Offset++; pos.Top = pos.Top->Next; }
2001
        else pos.Index++;
2002
        sel = sel->Next;
2003
      }
2004
      else if ((pad.Buttons & PSP_CTRL_UP || pad.Buttons & PSP_CTRL_ANALUP)
2005
        && sel->Prev)
2006
      {
2007 128 jack
        fast_scroll = pad.Buttons & PSP_CTRL_ANALUP;
2008 106 jack
        if (pos.Index - 1 < 0) { pos.Offset--; pos.Top = pos.Top->Prev; }
2009
        else pos.Index--;
2010
        sel = sel->Prev;
2011
      }
2012
      else if (pad.Buttons & PSP_CTRL_LEFT)
2013
      {
2014
        for (i = 0; sel->Prev && i < lnhalf; i++)
2015
        {
2016
          if (pos.Index - 1 < 0) { pos.Offset--; pos.Top = pos.Top->Prev; }
2017
          else pos.Index--;
2018
          sel = sel->Prev;
2019
        }
2020
      }
2021
      else if (pad.Buttons & PSP_CTRL_RIGHT)
2022
      {
2023
        for (i = 0; sel->Next && i < lnhalf; i++)
2024
        {
2025
          if (pos.Index + 1 >= lnmax) { pos.Offset++; pos.Top = pos.Top->Next; }
2026
          else pos.Index++;
2027
          sel=sel->Next;
2028
        }
2029
      }
2030
 
2031
      if (pad.Buttons & UiMetric.OkButton) break;
2032
    }
2033
 
2034
    if (pad.Buttons & UiMetric.CancelButton) { sel = NULL; break; }
2035
 
2036 128 jack
    /* Render to a call list */
2037
    sceGuStart(GU_CALL, call_list);
2038 106 jack
 
2039 128 jack
    /* Apply fog and draw frame */
2040
    pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
2041
      COLOR(0, 0, 0, UI_ANIM_FOG_STEP * UI_ANIM_FRAMES));
2042
    pspVideoGlowRect(sx, 0, dx, SCR_HEIGHT,
2043
      COLOR(0xff,0xff,0xff,UI_ANIM_FOG_STEP * UI_ANIM_FRAMES), 2);
2044 106 jack
 
2045
    /* Title */
2046
    if (title)
2047
      pspVideoPrintCenter(UiMetric.Font, sx, 0, dx,
2048
        title, UiMetric.TitleColor);
2049
 
2050
    /* Render the items */
2051
    for (item = (PspMenuItem*)pos.Top, i = 0, j = sy;
2052
      item && i < lnmax; item = item->Next, j += fh, i++)
2053
    {
2054 128 jack
      if (item == sel) sel_top = j;
2055 106 jack
      pspVideoPrintClipped(UiMetric.Font, sx + 10, j, item->Caption, w - 10,
2056
        "...", (item == sel) ? UiMetric.SelectedColor : UiMetric.TextColor);
2057
    }
2058
 
2059
    /* Up arrow */
2060 128 jack
    if (pos.Top->Prev) pspVideoPrint(UiMetric.Font, SCR_WIDTH - arrow_w * 2,
2061 106 jack
        sy + anim_frame, PSP_CHAR_UP_ARROW, UiMetric.MenuDecorColor);
2062
 
2063
    /* Down arrow */
2064 128 jack
    if (item) pspVideoPrint(UiMetric.Font, SCR_WIDTH - arrow_w * 2,
2065 106 jack
        dy - fh - anim_frame, PSP_CHAR_DOWN_ARROW, UiMetric.MenuDecorColor);
2066
 
2067
    /* Shortcuts */
2068
    pspVideoPrintCenter(UiMetric.Font, sx, SCR_HEIGHT - fh, dx,
2069
      help_text, UiMetric.StatusBarColor);
2070
 
2071 128 jack
    sceGuFinish();
2072
 
2073
    if (sel != last_sel && !fast_scroll && sel && last_sel
2074
      && UiMetric.Animate)
2075
    {
2076
      /* Move animation */
2077
      int f, n = 4;
2078
      for (f = 1; f <= n; f++)
2079
      {
2080
        pspVideoBegin();
2081
 
2082
        /* Clear screen */
2083 136 jack
        pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
2084 128 jack
        pspVideoFillRect(sx, 0, dx, SCR_HEIGHT, UiMetric.MenuOptionBoxBg);
2085
 
2086
        /* Selection box */
2087
        int box_top = last_sel_top-((last_sel_top-sel_top)/n)*f;
2088
        pspVideoFillRect(sx, box_top, sx + w, box_top + fh,
2089
          UiMetric.SelectedBgColor);
2090
 
2091
        sceGuCallList(call_list);
2092
 
2093
        pspVideoEnd();
2094
 
2095
        pspVideoWaitVSync();
2096
        pspVideoSwapBuffers();
2097
      }
2098
    }
2099
 
2100
    pspVideoBegin();
2101
 
2102
    /* Clear screen */
2103 136 jack
    pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
2104 128 jack
    pspVideoFillRect(sx, 0, dx, SCR_HEIGHT, UiMetric.MenuOptionBoxBg);
2105
 
2106
    if (sel) pspVideoFillRect(sx, sel_top, sx + w, sel_top + fh,
2107
      UiMetric.SelectedBgColor);
2108
 
2109
    sceGuCallList(call_list);
2110
 
2111 106 jack
    pspVideoEnd();
2112
 
2113
    /* Wait if needed */
2114
    do { sceRtcGetCurrentTick(&current_tick); }
2115
    while (current_tick - last_tick < ticks_per_upd);
2116
    last_tick = current_tick;
2117
 
2118
    /* Swap buffers */
2119
    pspVideoWaitVSync();
2120
    pspVideoSwapBuffers();
2121 128 jack
 
2122
    last_sel = sel;
2123
    last_sel_top = sel_top;
2124 106 jack
  }
2125
 
2126 128 jack
  if (UiMetric.Animate)
2127
  {
2128
    /* Exit animation */
2129
    for (i = UI_ANIM_FRAMES - 1; i >= 0; i--)
2130
    {
2131
          pspVideoBegin();
2132
 
2133
          /* Clear screen */
2134 136 jack
          pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
2135 128 jack
 
2136
          /* Apply fog and draw right frame */
2137
          pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT,
2138
            COLOR(0, 0, 0, UI_ANIM_FOG_STEP * i));
2139
          pspVideoFillRect(SCR_WIDTH - (i * (widest / UI_ANIM_FRAMES)),
2140
        0, dx, SCR_HEIGHT, UiMetric.MenuOptionBoxBg);
2141
 
2142
          pspVideoEnd();
2143
 
2144
      /* Swap buffers */
2145
      pspVideoWaitVSync();
2146
      pspVideoSwapBuffers();
2147
        }
2148
  }
2149
 
2150 106 jack
  free(help_text);
2151
  pspImageDestroy(screen);
2152
 
2153
  return sel;
2154
}
2155
 
2156 128 jack
void pspUiFadeout()
2157
{
2158
  /* Get copy of screen */
2159
  PspImage *screen = pspVideoGetVramBufferCopy();
2160
 
2161
  /* Exit animation */
2162
  int i, alpha;
2163
  for (i = 0; i < UI_ANIM_FRAMES; i++)
2164
  {
2165
          pspVideoBegin();
2166
 
2167
          /* Clear screen */
2168 136 jack
          pspVideoPutImage(screen, 0, 0, screen->Viewport.Width, screen->Height);
2169 128 jack
 
2170
          /* Apply fog */
2171
          alpha = (0x100/UI_ANIM_FRAMES)*i-1;
2172
          if (alpha > 0)
2173
            pspVideoFillRect(0, 0, SCR_WIDTH, SCR_HEIGHT, COLOR(0,0,0,alpha));
2174
 
2175
          pspVideoEnd();
2176
 
2177
    /* Swap buffers */
2178
    pspVideoWaitVSync();
2179
    pspVideoSwapBuffers();
2180
        }
2181
 
2182
  pspImageDestroy(screen);
2183
}
2184