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 }