2020-01-24 18:05:16 +01:00
|
|
|
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
|
|
|
/*
|
|
|
|
* Copyright 2020 by Eric House (xwords@eehouse.org). All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <ncurses.h>
|
|
|
|
#include <glib.h>
|
2020-01-31 06:21:45 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
2020-01-24 18:05:16 +01:00
|
|
|
|
|
|
|
#include "curgamlistwin.h"
|
2020-01-31 22:29:36 +01:00
|
|
|
#include "linuxmain.h"
|
2020-05-20 22:58:53 +02:00
|
|
|
#include "device.h"
|
|
|
|
#include "strutils.h"
|
2020-09-01 02:16:14 +02:00
|
|
|
#include "linuxutl.h"
|
2020-01-24 18:05:16 +01:00
|
|
|
|
|
|
|
struct CursGameList {
|
|
|
|
WINDOW* window;
|
2020-01-31 22:29:36 +01:00
|
|
|
LaunchParams* params;
|
2020-01-24 18:05:16 +01:00
|
|
|
int width, height;
|
|
|
|
int curSel;
|
|
|
|
int yOffset;
|
|
|
|
GSList* games;
|
2020-01-31 06:21:45 +01:00
|
|
|
int pid;
|
2020-01-24 18:05:16 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static void adjustCurSel( CursGameList* cgl );
|
|
|
|
|
|
|
|
CursGameList*
|
2020-01-31 22:29:36 +01:00
|
|
|
cgl_init( LaunchParams* params, int width, int height )
|
2020-01-24 18:05:16 +01:00
|
|
|
{
|
|
|
|
CursGameList* cgl = g_malloc0( sizeof( *cgl ) );
|
2020-01-31 22:29:36 +01:00
|
|
|
cgl->params = params;
|
2020-01-24 18:05:16 +01:00
|
|
|
cgl->window = newwin( height, width, 0, 0 );
|
|
|
|
XP_LOGF( "%s(): made window with height=%d, width=%d", __func__, height, width );
|
|
|
|
cgl->width = width;
|
|
|
|
cgl->height = height;
|
2020-01-31 06:21:45 +01:00
|
|
|
cgl->pid = getpid();
|
2020-01-24 18:05:16 +01:00
|
|
|
return cgl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cgl_destroy( CursGameList* cgl )
|
|
|
|
{
|
2023-03-07 06:10:09 +01:00
|
|
|
if ( !!cgl->window ) {
|
|
|
|
g_slist_free_full( cgl->games, g_free );
|
|
|
|
delwin( cgl->window );
|
|
|
|
} else {
|
|
|
|
XP_LOGFF( "no window??" );
|
|
|
|
}
|
2020-01-24 18:05:16 +01:00
|
|
|
g_free( cgl );
|
|
|
|
}
|
|
|
|
|
2020-01-31 21:29:45 +01:00
|
|
|
void
|
|
|
|
cgl_resized( CursGameList* cgl, int width, int height )
|
|
|
|
{
|
|
|
|
wresize( cgl->window, height, width );
|
|
|
|
cgl->width = width;
|
|
|
|
cgl->height = height;
|
|
|
|
cgl_draw( cgl );
|
|
|
|
}
|
|
|
|
|
2020-01-24 18:05:16 +01:00
|
|
|
static void
|
|
|
|
addOne( CursGameList* cgl, sqlite3_int64 rowid )
|
|
|
|
{
|
|
|
|
GameInfo gib;
|
2023-11-18 04:39:45 +01:00
|
|
|
if ( gdb_getGameInfoForRow( cgl->params->pDb, rowid, &gib ) ) {
|
2020-01-24 18:05:16 +01:00
|
|
|
GameInfo* gibp = g_malloc( sizeof(*gibp) );
|
|
|
|
*gibp = gib;
|
|
|
|
cgl->games = g_slist_append( cgl->games, gibp );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Load from the DB */
|
|
|
|
void
|
|
|
|
cgl_refresh( CursGameList* cgl )
|
|
|
|
{
|
|
|
|
g_slist_free_full( cgl->games, g_free );
|
|
|
|
cgl->games = NULL;
|
|
|
|
|
2020-01-31 22:29:36 +01:00
|
|
|
sqlite3* pDb = cgl->params->pDb;
|
2020-09-25 05:56:46 +02:00
|
|
|
GSList* games = gdb_listGames( pDb );
|
2020-01-24 18:05:16 +01:00
|
|
|
for ( GSList* iter = games; !!iter; iter = iter->next ) {
|
|
|
|
sqlite3_int64* rowid = (sqlite3_int64*)iter->data;
|
|
|
|
addOne( cgl, *rowid );
|
|
|
|
}
|
|
|
|
cgl_draw( cgl );
|
|
|
|
}
|
|
|
|
|
|
|
|
static GSList*
|
|
|
|
findFor( CursGameList* cgl, sqlite3_int64 rowid )
|
|
|
|
{
|
|
|
|
GSList* result = NULL;
|
|
|
|
for ( GSList* iter = cgl->games; !!iter && !result; iter = iter->next ) {
|
|
|
|
GameInfo* gib = (GameInfo*)iter->data;
|
|
|
|
if ( gib->rowid == rowid ) {
|
|
|
|
result = iter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cgl_refreshOne( CursGameList* cgl, sqlite3_int64 rowid, bool select )
|
|
|
|
{
|
|
|
|
// Update the info. In place if it exists, otherwise creating a new list
|
|
|
|
// elem
|
|
|
|
|
|
|
|
GameInfo gib;
|
2023-11-18 04:39:45 +01:00
|
|
|
if ( gdb_getGameInfoForRow( cgl->params->pDb, rowid, &gib ) ) {
|
2020-01-24 18:05:16 +01:00
|
|
|
GameInfo* found;
|
|
|
|
GSList* elem = findFor( cgl, rowid );
|
|
|
|
if ( !!elem ) {
|
|
|
|
found = (GameInfo*)elem->data;
|
|
|
|
*found = gib;
|
|
|
|
} else {
|
|
|
|
found = g_malloc( sizeof(*found) );
|
|
|
|
*found = gib;
|
|
|
|
cgl->games = g_slist_append( cgl->games, found );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( select ) {
|
|
|
|
cgl->curSel = g_slist_index( cgl->games, found );
|
|
|
|
}
|
|
|
|
adjustCurSel( cgl );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cgl_remove( CursGameList* cgl, sqlite3_int64 rowid )
|
|
|
|
{
|
|
|
|
GSList* elem = findFor( cgl, rowid );
|
|
|
|
if ( !!elem ) {
|
|
|
|
g_free( elem->data );
|
|
|
|
cgl->games = g_slist_delete_link( cgl->games, elem );
|
|
|
|
}
|
|
|
|
adjustCurSel( cgl );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cgl_moveSel( CursGameList* cgl, bool down )
|
|
|
|
{
|
|
|
|
int nGames = g_slist_length( cgl->games );
|
|
|
|
cgl->curSel += nGames + (down ? 1 : -1);
|
|
|
|
cgl->curSel %= nGames;
|
|
|
|
adjustCurSel( cgl );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
adjustCurSel( CursGameList* cgl )
|
|
|
|
{
|
|
|
|
int nGames = g_slist_length( cgl->games );
|
2020-01-29 19:59:57 +01:00
|
|
|
XP_LOGF( "%s() start: curSel: %d; yOffset: %d; nGames: %d", __func__,
|
|
|
|
cgl->curSel, cgl->yOffset, nGames );
|
2020-01-24 18:05:16 +01:00
|
|
|
if ( cgl->curSel >= nGames ) {
|
|
|
|
cgl->curSel = nGames - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now adjust yOffset */
|
|
|
|
int nVisRows = cgl->height - 2; /* 1 for the title and header rows */
|
2020-01-29 19:59:57 +01:00
|
|
|
if ( nGames < nVisRows ) {
|
|
|
|
cgl->yOffset = 0;
|
|
|
|
} else if ( cgl->curSel - cgl->yOffset >= nVisRows ) {
|
2020-01-24 18:05:16 +01:00
|
|
|
cgl->yOffset = cgl->curSel - nVisRows + 1;
|
|
|
|
} else {
|
|
|
|
while ( cgl->curSel < cgl->yOffset ) {
|
|
|
|
--cgl->yOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
XP_LOGF( "%s() end: curSel: %d; yOffset: %d", __func__, cgl->curSel, cgl->yOffset );
|
|
|
|
cgl_draw( cgl );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cgl_draw( CursGameList* cgl )
|
|
|
|
{
|
|
|
|
WINDOW* win = cgl->window;
|
|
|
|
werase( win );
|
|
|
|
|
|
|
|
const int nGames = g_slist_length( cgl->games );
|
|
|
|
|
|
|
|
/* Draw '+' at far right if scrollable */
|
|
|
|
int nBelow = nGames - (cgl->height-2) - cgl->yOffset;
|
|
|
|
XP_LOGF( "%s(): yOffset: %d; nBelow: %d", __func__, cgl->yOffset, nBelow );
|
|
|
|
if ( 0 < nBelow ) {
|
|
|
|
mvwaddstr( win, cgl->height-2, cgl->width - 1, "+" );
|
|
|
|
}
|
|
|
|
if ( 0 < cgl->yOffset ) {
|
|
|
|
mvwaddstr( win, 0, cgl->width-1, "+" );
|
|
|
|
}
|
|
|
|
|
2022-01-18 05:22:27 +01:00
|
|
|
const char* cols[] = {"#", "RowID", "Lang", "Scores", "GameID", "Role",
|
2020-02-03 04:12:35 +01:00
|
|
|
"nTot", "nMiss", "Seed", "#Mv", "Turn", "nPend", "DupTimer" };
|
2020-01-24 18:05:16 +01:00
|
|
|
|
|
|
|
int nShown = nGames <= cgl->height - 2 ? nGames : cgl->height - 2;
|
|
|
|
char* data[nShown + 1][VSIZE(cols)];
|
|
|
|
for ( int ii = 0; ii < VSIZE(cols); ++ii ) {
|
|
|
|
data[0][ii] = g_strdup(cols[ii]);
|
|
|
|
}
|
|
|
|
int line = 1;
|
|
|
|
for ( int ii = 0; ii < nShown; ++ii ) {
|
|
|
|
const GameInfo* gi = g_slist_nth_data( cgl->games, ii + cgl->yOffset );
|
|
|
|
int col = 0;
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", ii + cgl->yOffset + 1 ); /* 1-based */
|
|
|
|
data[line][col++] = g_strdup_printf( "%05lld", gi->rowid );
|
2022-06-04 23:03:03 +02:00
|
|
|
data[line][col++] = g_strdup( gi->isoCode );
|
2020-01-24 18:05:16 +01:00
|
|
|
data[line][col++] = g_strdup( gi->scores );
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", gi->gameID );
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", gi->role );
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", gi->nTotal );
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", countBits(gi->nMissing) );
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", gi->seed );
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", gi->nMoves );
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", gi->turn );
|
|
|
|
data[line][col++] = g_strdup_printf( "%d", gi->nPending );
|
2020-09-01 02:16:14 +02:00
|
|
|
gchar buf[64];
|
|
|
|
formatSeconds( gi->dupTimerExpires, buf, VSIZE(buf) );
|
|
|
|
data[line][col++] = g_strdup( buf );
|
2020-02-03 04:12:35 +01:00
|
|
|
|
2020-01-24 18:05:16 +01:00
|
|
|
XP_ASSERT( col == VSIZE(data[line]) );
|
|
|
|
++line;
|
|
|
|
}
|
|
|
|
|
|
|
|
int maxlen = 0;
|
|
|
|
int offset = 0;
|
|
|
|
for ( int col = 0; col < VSIZE(data[0]); ++col ) {
|
|
|
|
for ( int line = 0; line < VSIZE(data); ++line ) {
|
|
|
|
char* str = data[line][col];
|
|
|
|
int len = strlen(str);
|
|
|
|
if ( maxlen < len ) {
|
|
|
|
maxlen = len;
|
|
|
|
}
|
|
|
|
bool highlight = cgl->yOffset + line - 1 == cgl->curSel;
|
|
|
|
if ( highlight ) {
|
|
|
|
wstandout( win );
|
|
|
|
}
|
|
|
|
mvwaddstr( win, line + 1, offset, str );
|
|
|
|
if ( highlight ) {
|
|
|
|
wstandend( win );
|
|
|
|
}
|
|
|
|
g_free( str );
|
|
|
|
}
|
|
|
|
offset += maxlen + 2;
|
|
|
|
maxlen = 0;
|
|
|
|
}
|
|
|
|
|
2020-01-31 22:29:36 +01:00
|
|
|
XP_U32 relayID = linux_getDevIDRelay( cgl->params );
|
2020-01-24 18:05:16 +01:00
|
|
|
char buf[cgl->width + 1];
|
2020-05-20 22:58:53 +02:00
|
|
|
|
|
|
|
MQTTDevID devID;
|
|
|
|
dvc_getMQTTDevID( cgl->params->dutil, NULL_XWE, &devID );
|
|
|
|
XP_UCHAR didBuf[32];
|
|
|
|
snprintf( buf, VSIZE(buf), "pid: %d; nGames: %d, relayid: %d, mqttid: %s",
|
|
|
|
cgl->pid, nGames, relayID, formatMQTTDevID( &devID, didBuf, VSIZE(didBuf) ) );
|
2020-01-24 18:05:16 +01:00
|
|
|
mvwaddstr( win, 0, 0, buf );
|
|
|
|
|
|
|
|
wrefresh( win );
|
|
|
|
}
|
|
|
|
|
|
|
|
const GameInfo*
|
|
|
|
cgl_getSel( CursGameList* cgl )
|
|
|
|
{
|
|
|
|
return g_slist_nth_data( cgl->games, cgl->curSel );
|
|
|
|
}
|
|
|
|
|
2023-11-18 04:39:45 +01:00
|
|
|
void
|
|
|
|
cgl_setSel( CursGameList* cgl, int sel )
|
|
|
|
{
|
|
|
|
if ( sel < 0 ) {
|
|
|
|
sel = XP_RANDOM() % g_slist_length( cgl->games );
|
|
|
|
}
|
|
|
|
cgl->curSel = sel;
|
|
|
|
adjustCurSel( cgl );
|
|
|
|
}
|
|
|
|
|
2020-01-24 18:05:16 +01:00
|
|
|
int
|
|
|
|
cgl_getNGames( CursGameList* cgl )
|
|
|
|
{
|
|
|
|
return g_slist_length( cgl->games );
|
|
|
|
}
|