ui.c (5404B)


      1 #include <stdio.h>
      2 
      3 #include <sqlite3.h>
      4 
      5 #include "db.h"
      6 #include "loc.h"
      7 
      8 #define TB_IMPL
      9 #include "ui.h"
     10 
     11 int
     12 display_books(sqlite3* db, int limit, int offset, int width)
     13 {
     14   sqlite3_stmt* stmt;
     15   if (sqlite3_prepare_v2(db,
     16                          "SELECT"
     17                          " lcc, year, title, author"
     18                          " FROM books"
     19                          " ORDER BY lcc ASC"
     20                          " LIMIT ? OFFSET ?;",
     21                          -1,
     22                          &stmt,
     23                          0) != SQLITE_OK)
     24     return 1;
     25 
     26   sqlite3_bind_int(stmt, 1, limit);
     27   sqlite3_bind_int(stmt, 2, offset);
     28 
     29   int c1 = 1;
     30   int b1 = c1 + 23;
     31   int c2 = b1 + 2;
     32   int cw = (width - c2) / 2 - 3;
     33   int b2 = c2 + cw + 2;
     34   int c3 = b2 + 2;
     35 
     36   int books = 0;
     37  
     38   int res;
     39   for (int y = 2; y < limit + 2; y++) {
     40     res = sqlite3_step(stmt);
     41 
     42     if (res != SQLITE_ROW)
     43       break;
     44 
     45     books++;
     46 
     47     tb_print(
     48       c1, y, TB_DEFAULT, TB_DEFAULT, (const char*)sqlite3_column_text(stmt, 0));
     49     tb_set_cell(b1, y, 0x2502, TB_DEFAULT, TB_DEFAULT);
     50 
     51     const char* title = (const char*)sqlite3_column_text(stmt, 2);
     52     tb_printf(
     53       c2, y, TB_DEFAULT, TB_DEFAULT, "%.*s%s",
     54       cw,
     55       title,
     56       strlen(title) > cw ? "\u2026" : "");
     57     tb_set_cell(b2, y, 0x2502, TB_DEFAULT, TB_DEFAULT);
     58 
     59     const char* author = (const char*)sqlite3_column_text(stmt, 3);
     60     tb_printf(
     61       c3, y, TB_DEFAULT, TB_DEFAULT, "%.*s%s",
     62       cw,
     63       author,
     64     strlen(author) > cw ? "\u2026" : "");
     65   }
     66 
     67   if (res != SQLITE_DONE) {
     68     fprintf(stderr, "Failed to fetch books!\n");
     69     return -1;
     70   }
     71 
     72   if (books == 0) {
     73     tb_print(c1, 2, TB_DEFAULT, TB_DEFAULT, "No books yet!");
     74     tb_print(c1, 3, TB_DEFAULT, TB_DEFAULT, "Press A to search for a book by ISBN.");
     75   }
     76 
     77   return 0;
     78 }
     79 
     80 static void
     81 ui_repaint(struct ui_state* state)
     82 {
     83   tb_clear();
     84 
     85   size_t out;
     86   tb_print_ex(0, 0, TB_WHITE | TB_BOLD, TB_YELLOW, &out, " Library ");
     87   for (int x = out; x < state->w; x++)
     88     tb_set_cell(x, 0, ' ', TB_DEFAULT, TB_YELLOW);
     89 
     90   if (state->state == STATE_VIEW) {
     91     display_books(state->db, state->h - 2, 0, state->w);
     92   } else if (state->state == STATE_ADD) {
     93     tb_print(2, 2, TB_DEFAULT, TB_DEFAULT, "Enter ISBN (ESC to cancel)");
     94 
     95     tb_print(2, 3, TB_DEFAULT, TB_DEFAULT, state->input_buffer);
     96     tb_set_cell(2 + state->input_pos, 3, '_', TB_DEFAULT, TB_DEFAULT);
     97   }
     98 
     99   tb_present();
    100 }
    101 
    102 int
    103 ui_init(struct ui_state* state, sqlite3* db)
    104 {
    105   state->state = STATE_VIEW;
    106 
    107   state->db = db;
    108   state->dirty = 1;
    109   state->exit = 0;
    110 
    111   tb_init();
    112 
    113   state->w = tb_width();
    114   state->h = tb_height();
    115 
    116   ui_repaint(state);
    117 
    118   return 0;
    119 }
    120 
    121 int
    122 ui_tick(struct ui_state* state)
    123 {
    124   tb_poll_event(&state->ev);
    125 
    126   if (state->ev.type == TB_EVENT_RESIZE) {
    127     state->w = state->ev.w;
    128     state->h = state->ev.h;
    129     state->dirty = 1;
    130   } else if (state->state == STATE_VIEW) {
    131     if (state->ev.ch == 'q' || state->ev.key == TB_KEY_ESC) {
    132       state->exit = 1;
    133     } else if (state->ev.ch == 'a') {
    134       state->state = STATE_ADD;
    135       state->dirty = 1;
    136     }
    137   } else if (state->state == STATE_ADD) {
    138     if (state->ev.key == TB_KEY_ESC) {
    139       state->state = STATE_VIEW;
    140       state->dirty = 1;
    141     }
    142     /* Handle submission */
    143     else if (state->ev.key == TB_KEY_ENTER) {
    144       if (state->input_pos == 10 || state->input_pos == 13) {
    145         /* TODO: Lookup */
    146         struct lb_book book = { NULL };
    147         int result = lb_loc_lookup(&book, (char*)&state->input_buffer);
    148 
    149         if (result > 0) {
    150           tb_print(2, 5, TB_GREEN, TB_DEFAULT, "Book found!                                   ");
    151           tb_print(2, 6, TB_DEFAULT, TB_DEFAULT, book.classification);
    152           tb_print(2, 7, TB_DEFAULT, TB_DEFAULT, book.title);
    153           tb_print(2, 8, TB_DEFAULT, TB_DEFAULT, book.author);
    154 
    155           int result = db_add(state->db, book.classification, book.title, book.author);
    156 
    157           if (result) {
    158             tb_printf(2, 10, TB_RED, TB_DEFAULT, "Error %d", result);
    159           } else {
    160             tb_print(2, 10, TB_GREEN, TB_DEFAULT, "Added to library!");
    161           }
    162 
    163           lb_book_destroy(&book);
    164 
    165           /*state->state = STATE_VIEW;
    166           state->dirty = 1;*/
    167         }
    168 
    169         tb_present();
    170 
    171       } else {
    172         tb_print(2,
    173                  5,
    174                  TB_RED,
    175                  TB_DEFAULT,
    176                  "The ISBN must be exactly 10 or 13 digits long.");
    177         tb_present();
    178       }
    179     }
    180     /* Handle typing*/
    181     else {
    182       /* Valid input */
    183       if (state->ev.ch >= '0' && state->ev.ch <= '9' && state->input_pos < 13) {
    184         state->input_buffer[state->input_pos] = state->ev.ch;
    185         tb_set_cell(
    186           2 + state->input_pos, 3, state->ev.ch, TB_DEFAULT, TB_DEFAULT);
    187         state->input_pos++;
    188       }
    189       /* Backspace */
    190       else if ((state->ev.key == TB_KEY_BACKSPACE ||
    191                 state->ev.key == TB_KEY_BACKSPACE2) &&
    192                state->input_pos > 0) {
    193         state->input_buffer[state->input_pos] = 0;
    194         tb_set_cell(2 + state->input_pos, 3, ' ', TB_DEFAULT, TB_DEFAULT);
    195         state->input_pos--;
    196       }
    197 
    198       tb_set_cell(2 + state->input_pos, 3, '_', TB_DEFAULT, TB_DEFAULT);
    199       tb_present();
    200     }
    201   }
    202 
    203   /* Repaint */
    204   if (state->dirty) {
    205     ui_repaint(state);
    206     state->dirty = 0;
    207   }
    208 
    209   return 0;
    210 }
    211 
    212 int
    213 ui_destroy(struct ui_state* state)
    214 {
    215   tb_shutdown();
    216   return 0;
    217 }