mirror of
https://github.com/nzbget/nzbget.git
synced 2026-02-23 09:44:12 -05:00
1509 lines
38 KiB
C++
1509 lines
38 KiB
C++
/*
|
|
* This file is part of nzbget
|
|
*
|
|
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
|
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $Revision$
|
|
* $Date$
|
|
*
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include "win32.h"
|
|
#endif
|
|
|
|
#ifndef DISABLE_CURSES
|
|
|
|
#ifdef HAVE_NCURSES_H
|
|
#include <ncurses.h>
|
|
#endif
|
|
#ifdef HAVE_NCURSES_NCURSES_H
|
|
#include <ncurses/ncurses.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "nzbget.h"
|
|
#include "NCursesFrontend.h"
|
|
#include "Options.h"
|
|
#include "Util.h"
|
|
|
|
#ifdef HAVE_CURSES_H
|
|
// curses.h header must be included last to avoid problems on Solaris
|
|
// (and possibly other systems, that uses curses.h (not ncurses.h)
|
|
#include <curses.h>
|
|
// "#undef erase" is neccessary on Solaris
|
|
#undef erase
|
|
#endif
|
|
|
|
#ifndef WIN32
|
|
// curses.h on Solaris declares "clear()" via DEFINE. That causes problems, because
|
|
// it also affects calls to deque's method "clear()", producing compiler errors.
|
|
// We use function "curses_clear()" to call macro "clear" of curses, then
|
|
// undefine macro "clear".
|
|
void curses_clear()
|
|
{
|
|
clear();
|
|
}
|
|
#undef clear
|
|
#endif
|
|
|
|
extern Options* g_pOptions;
|
|
extern void ExitProc();
|
|
|
|
static const int NCURSES_COLORPAIR_TEXT = 1;
|
|
static const int NCURSES_COLORPAIR_INFO = 2;
|
|
static const int NCURSES_COLORPAIR_WARNING = 3;
|
|
static const int NCURSES_COLORPAIR_ERROR = 4;
|
|
static const int NCURSES_COLORPAIR_DEBUG = 5;
|
|
static const int NCURSES_COLORPAIR_DETAIL = 6;
|
|
static const int NCURSES_COLORPAIR_STATUS = 7;
|
|
static const int NCURSES_COLORPAIR_KEYBAR = 8;
|
|
static const int NCURSES_COLORPAIR_INFOLINE = 9;
|
|
static const int NCURSES_COLORPAIR_TEXTHIGHL = 10;
|
|
static const int NCURSES_COLORPAIR_CURSOR = 11;
|
|
static const int NCURSES_COLORPAIR_HINT = 12;
|
|
|
|
static const int MAX_SCREEN_WIDTH = 512;
|
|
|
|
#ifdef WIN32
|
|
static const int COLOR_BLACK = 0;
|
|
static const int COLOR_BLUE = FOREGROUND_BLUE;
|
|
static const int COLOR_RED = FOREGROUND_RED;
|
|
static const int COLOR_GREEN = FOREGROUND_GREEN;
|
|
static const int COLOR_WHITE = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
|
|
static const int COLOR_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE;
|
|
static const int COLOR_CYAN = FOREGROUND_BLUE | FOREGROUND_GREEN;
|
|
static const int COLOR_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
|
|
|
|
static const int READKEY_EMPTY = 0;
|
|
|
|
#define KEY_DOWN VK_DOWN
|
|
#define KEY_UP VK_UP
|
|
#define KEY_PPAGE VK_PRIOR
|
|
#define KEY_NPAGE VK_NEXT
|
|
#define KEY_END VK_END
|
|
#define KEY_HOME VK_HOME
|
|
#define KEY_BACKSPACE VK_BACK
|
|
|
|
#else
|
|
|
|
static const int READKEY_EMPTY = ERR;
|
|
|
|
#endif
|
|
|
|
NCursesFrontend::NCursesFrontend()
|
|
{
|
|
m_iScreenHeight = 0;
|
|
m_iScreenWidth = 0;
|
|
m_iInputNumberIndex = 0;
|
|
m_eInputMode = eNormal;
|
|
m_bSummary = true;
|
|
m_bFileList = true;
|
|
m_iNeededLogEntries = 0;
|
|
m_iQueueWinTop = 0;
|
|
m_iQueueWinHeight = 0;
|
|
m_iQueueWinClientHeight = 0;
|
|
m_iMessagesWinTop = 0;
|
|
m_iMessagesWinHeight = 0;
|
|
m_iMessagesWinClientHeight = 0;
|
|
m_iSelectedQueueEntry = 0;
|
|
m_iQueueScrollOffset = 0;
|
|
m_bShowNZBname = g_pOptions->GetCursesNZBName();
|
|
m_bShowTimestamp = g_pOptions->GetCursesTime();
|
|
m_bGroupFiles = g_pOptions->GetCursesGroup();
|
|
m_QueueWindowPercentage = 0.5f;
|
|
m_iDataUpdatePos = 0;
|
|
m_bUpdateNextTime = false;
|
|
m_iLastEditEntry = -1;
|
|
m_bLastPausePars = false;
|
|
m_szHint = NULL;
|
|
|
|
// Setup curses
|
|
#ifdef WIN32
|
|
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
m_pScreenBuffer = NULL;
|
|
m_pOldScreenBuffer = NULL;
|
|
m_ColorAttr.clear();
|
|
|
|
CONSOLE_CURSOR_INFO ConsoleCursorInfo;
|
|
GetConsoleCursorInfo(hConsole, &ConsoleCursorInfo);
|
|
ConsoleCursorInfo.bVisible = false;
|
|
SetConsoleCursorInfo(hConsole, &ConsoleCursorInfo);
|
|
if (IsRemoteMode())
|
|
{
|
|
SetConsoleTitle("NZBGet - remote mode");
|
|
}
|
|
else
|
|
{
|
|
SetConsoleTitle("NZBGet");
|
|
}
|
|
|
|
m_bUseColor = true;
|
|
#else
|
|
m_pWindow = initscr();
|
|
if (m_pWindow == NULL)
|
|
{
|
|
printf("ERROR: m_pWindow == NULL\n");
|
|
exit(-1);
|
|
}
|
|
keypad(stdscr, true);
|
|
nodelay((WINDOW*)m_pWindow, true);
|
|
noecho();
|
|
curs_set(0);
|
|
m_bUseColor = has_colors();
|
|
#endif
|
|
|
|
if (m_bUseColor)
|
|
{
|
|
#ifndef WIN32
|
|
start_color();
|
|
#endif
|
|
init_pair(0, COLOR_WHITE, COLOR_BLUE);
|
|
init_pair(NCURSES_COLORPAIR_TEXT, COLOR_WHITE, COLOR_BLACK);
|
|
init_pair(NCURSES_COLORPAIR_INFO, COLOR_GREEN, COLOR_BLACK);
|
|
init_pair(NCURSES_COLORPAIR_WARNING, COLOR_MAGENTA, COLOR_BLACK);
|
|
init_pair(NCURSES_COLORPAIR_ERROR, COLOR_RED, COLOR_BLACK);
|
|
init_pair(NCURSES_COLORPAIR_DEBUG, COLOR_WHITE, COLOR_BLACK);
|
|
init_pair(NCURSES_COLORPAIR_DETAIL, COLOR_GREEN, COLOR_BLACK);
|
|
init_pair(NCURSES_COLORPAIR_STATUS, COLOR_BLUE, COLOR_WHITE);
|
|
init_pair(NCURSES_COLORPAIR_KEYBAR, COLOR_WHITE, COLOR_BLUE);
|
|
init_pair(NCURSES_COLORPAIR_INFOLINE, COLOR_WHITE, COLOR_BLUE);
|
|
init_pair(NCURSES_COLORPAIR_TEXTHIGHL, COLOR_BLACK, COLOR_CYAN);
|
|
init_pair(NCURSES_COLORPAIR_CURSOR, COLOR_BLACK, COLOR_YELLOW);
|
|
init_pair(NCURSES_COLORPAIR_HINT, COLOR_WHITE, COLOR_RED);
|
|
}
|
|
}
|
|
|
|
NCursesFrontend::~NCursesFrontend()
|
|
{
|
|
#ifdef WIN32
|
|
if (m_pScreenBuffer)
|
|
{
|
|
free(m_pScreenBuffer);
|
|
}
|
|
if (m_pOldScreenBuffer)
|
|
{
|
|
free(m_pOldScreenBuffer);
|
|
}
|
|
m_ColorAttr.clear();
|
|
|
|
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
CONSOLE_CURSOR_INFO ConsoleCursorInfo;
|
|
GetConsoleCursorInfo(hConsole, &ConsoleCursorInfo);
|
|
ConsoleCursorInfo.bVisible = true;
|
|
SetConsoleCursorInfo(hConsole, &ConsoleCursorInfo);
|
|
#else
|
|
keypad(stdscr, false);
|
|
echo();
|
|
curs_set(1);
|
|
endwin();
|
|
#endif
|
|
printf("\n");
|
|
SetHint(NULL);
|
|
}
|
|
|
|
void NCursesFrontend::Run()
|
|
{
|
|
debug("Entering NCursesFrontend-loop");
|
|
|
|
m_iDataUpdatePos = 0;
|
|
|
|
while (!IsStopped())
|
|
{
|
|
// The data (queue and log) is updated each m_iUpdateInterval msec,
|
|
// but the window is updated more often for better reaction on user's input
|
|
|
|
bool updateNow = false;
|
|
int iKey = ReadConsoleKey();
|
|
|
|
if (iKey != READKEY_EMPTY)
|
|
{
|
|
// Update now and next if a key is pressed.
|
|
updateNow = true;
|
|
m_bUpdateNextTime = true;
|
|
}
|
|
else if (m_bUpdateNextTime)
|
|
{
|
|
// Update due to key being pressed during previous call.
|
|
updateNow = true;
|
|
m_bUpdateNextTime = false;
|
|
}
|
|
else if (m_iDataUpdatePos <= 0)
|
|
{
|
|
updateNow = true;
|
|
m_bUpdateNextTime = false;
|
|
}
|
|
|
|
if (updateNow)
|
|
{
|
|
Update(iKey);
|
|
}
|
|
|
|
if (m_iDataUpdatePos <= 0)
|
|
{
|
|
m_iDataUpdatePos = m_iUpdateInterval;
|
|
}
|
|
|
|
usleep(10 * 1000);
|
|
m_iDataUpdatePos -= 10;
|
|
}
|
|
|
|
FreeData();
|
|
ClearGroupQueue();
|
|
|
|
debug("Exiting NCursesFrontend-loop");
|
|
}
|
|
|
|
void NCursesFrontend::NeedUpdateData()
|
|
{
|
|
m_iDataUpdatePos = 10;
|
|
m_bUpdateNextTime = true;
|
|
}
|
|
|
|
void NCursesFrontend::Update(int iKey)
|
|
{
|
|
// Figure out how big the screen is
|
|
CalcWindowSizes();
|
|
|
|
if (m_iDataUpdatePos <= 0)
|
|
{
|
|
FreeData();
|
|
ClearGroupQueue();
|
|
m_iNeededLogEntries = m_iMessagesWinClientHeight;
|
|
if (!PrepareData())
|
|
{
|
|
return;
|
|
}
|
|
PrepareGroupQueue();
|
|
|
|
// recalculate frame sizes
|
|
CalcWindowSizes();
|
|
}
|
|
|
|
if (m_eInputMode == eEditQueue)
|
|
{
|
|
int iQueueSize = CalcQueueSize();
|
|
if (iQueueSize == 0)
|
|
{
|
|
m_iSelectedQueueEntry = 0;
|
|
m_eInputMode = eNormal;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------
|
|
// Print Current NZBInfoList
|
|
//------------------------------------------
|
|
if (m_iQueueWinHeight > 0)
|
|
{
|
|
PrintQueue();
|
|
}
|
|
|
|
//------------------------------------------
|
|
// Print Messages
|
|
//------------------------------------------
|
|
if (m_iMessagesWinHeight > 0)
|
|
{
|
|
PrintMessages();
|
|
}
|
|
|
|
PrintStatus();
|
|
|
|
PrintKeyInputBar();
|
|
|
|
UpdateInput(iKey);
|
|
|
|
RefreshScreen();
|
|
}
|
|
|
|
void NCursesFrontend::CalcWindowSizes()
|
|
{
|
|
int iNrRows, iNrColumns;
|
|
#ifdef WIN32
|
|
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
|
|
GetConsoleScreenBufferInfo(hConsole, &BufInfo);
|
|
iNrRows = BufInfo.srWindow.Bottom - BufInfo.srWindow.Top + 1;
|
|
iNrColumns = BufInfo.srWindow.Right - BufInfo.srWindow.Left + 1;
|
|
#else
|
|
getmaxyx(stdscr, iNrRows, iNrColumns);
|
|
#endif
|
|
if (iNrRows != m_iScreenHeight || iNrColumns != m_iScreenWidth)
|
|
{
|
|
#ifdef WIN32
|
|
m_iScreenBufferSize = iNrRows * iNrColumns * sizeof(CHAR_INFO);
|
|
m_pScreenBuffer = (CHAR_INFO*)realloc(m_pScreenBuffer, m_iScreenBufferSize);
|
|
memset(m_pScreenBuffer, 0, m_iScreenBufferSize);
|
|
m_pOldScreenBuffer = (CHAR_INFO*)realloc(m_pOldScreenBuffer, m_iScreenBufferSize);
|
|
memset(m_pOldScreenBuffer, 0, m_iScreenBufferSize);
|
|
#else
|
|
curses_clear();
|
|
#endif
|
|
m_iScreenHeight = iNrRows;
|
|
m_iScreenWidth = iNrColumns;
|
|
}
|
|
|
|
int iQueueSize = CalcQueueSize();
|
|
|
|
m_iQueueWinTop = 0;
|
|
m_iQueueWinHeight = (int)((float) (m_iScreenHeight - 2) * m_QueueWindowPercentage);
|
|
if (m_iQueueWinHeight - 1 > iQueueSize)
|
|
{
|
|
m_iQueueWinHeight = iQueueSize > 0 ? iQueueSize + 1 : 1 + 1;
|
|
}
|
|
m_iQueueWinClientHeight = m_iQueueWinHeight - 1;
|
|
if (m_iQueueWinClientHeight < 0)
|
|
{
|
|
m_iQueueWinClientHeight = 0;
|
|
}
|
|
|
|
m_iMessagesWinTop = m_iQueueWinTop + m_iQueueWinHeight;
|
|
m_iMessagesWinHeight = m_iScreenHeight - m_iQueueWinHeight - 2;
|
|
m_iMessagesWinClientHeight = m_iMessagesWinHeight - 1;
|
|
if (m_iMessagesWinClientHeight < 0)
|
|
{
|
|
m_iMessagesWinClientHeight = 0;
|
|
}
|
|
}
|
|
|
|
int NCursesFrontend::CalcQueueSize()
|
|
{
|
|
if (m_bGroupFiles)
|
|
{
|
|
return m_groupQueue.size();
|
|
}
|
|
else
|
|
{
|
|
DownloadQueue* pDownloadQueue = LockQueue();
|
|
int iQueueSize = pDownloadQueue->GetFileQueue()->size();
|
|
UnlockQueue();
|
|
return iQueueSize;
|
|
}
|
|
}
|
|
|
|
void NCursesFrontend::PlotLine(const char * szString, int iRow, int iPos, int iColorPair)
|
|
{
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
snprintf(szBuffer, sizeof(szBuffer), "%-*s", m_iScreenWidth, szString);
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
int iLen = strlen(szBuffer);
|
|
if (iLen > m_iScreenWidth - iPos && m_iScreenWidth - iPos < MAX_SCREEN_WIDTH)
|
|
{
|
|
szBuffer[m_iScreenWidth - iPos] = '\0';
|
|
}
|
|
|
|
PlotText(szBuffer, iRow, iPos, iColorPair, false);
|
|
}
|
|
|
|
void NCursesFrontend::PlotText(const char * szString, int iRow, int iPos, int iColorPair, bool bBlink)
|
|
{
|
|
#ifdef WIN32
|
|
int iBufPos = iRow * m_iScreenWidth + iPos;
|
|
int len = strlen(szString);
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
char c = szString[i];
|
|
CharToOemBuff(&c, &c, 1);
|
|
m_pScreenBuffer[iBufPos + i].Char.AsciiChar = c;
|
|
m_pScreenBuffer[iBufPos + i].Attributes = m_ColorAttr[iColorPair];
|
|
}
|
|
#else
|
|
if( m_bUseColor )
|
|
{
|
|
attron(COLOR_PAIR(iColorPair));
|
|
if (bBlink)
|
|
{
|
|
attron(A_BLINK);
|
|
}
|
|
}
|
|
mvaddstr(iRow, iPos, (char*)szString);
|
|
if( m_bUseColor )
|
|
{
|
|
attroff(COLOR_PAIR(iColorPair));
|
|
if (bBlink)
|
|
{
|
|
attroff(A_BLINK);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void NCursesFrontend::RefreshScreen()
|
|
{
|
|
#ifdef WIN32
|
|
bool bBufChanged = memcmp(m_pScreenBuffer, m_pOldScreenBuffer, m_iScreenBufferSize);
|
|
if (bBufChanged)
|
|
{
|
|
COORD BufSize;
|
|
BufSize.X = m_iScreenWidth;
|
|
BufSize.Y = m_iScreenHeight;
|
|
|
|
COORD BufCoord;
|
|
BufCoord.X = 0;
|
|
BufCoord.Y = 0;
|
|
|
|
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
|
|
GetConsoleScreenBufferInfo(hConsole, &BufInfo);
|
|
WriteConsoleOutput(hConsole, m_pScreenBuffer, BufSize, BufCoord, &BufInfo.srWindow);
|
|
|
|
BufInfo.dwCursorPosition.X = BufInfo.srWindow.Right;
|
|
BufInfo.dwCursorPosition.Y = BufInfo.srWindow.Bottom;
|
|
SetConsoleCursorPosition(hConsole, BufInfo.dwCursorPosition);
|
|
|
|
memcpy(m_pOldScreenBuffer, m_pScreenBuffer, m_iScreenBufferSize);
|
|
}
|
|
#else
|
|
// Cursor placement
|
|
wmove((WINDOW*)m_pWindow, m_iScreenHeight, m_iScreenWidth);
|
|
|
|
// NCurses refresh
|
|
refresh();
|
|
#endif
|
|
}
|
|
|
|
#ifdef WIN32
|
|
void NCursesFrontend::init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor)
|
|
{
|
|
m_ColorAttr.resize(iColorNumber + 1);
|
|
m_ColorAttr[iColorNumber] = wForeColor | (wBackColor << 4);
|
|
}
|
|
#endif
|
|
|
|
void NCursesFrontend::PrintMessages()
|
|
{
|
|
int iLineNr = m_iMessagesWinTop;
|
|
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
snprintf(szBuffer, sizeof(szBuffer), "%s Messages", m_bUseColor ? "" : "*** ");
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
PlotLine(szBuffer, iLineNr++, 0, NCURSES_COLORPAIR_INFOLINE);
|
|
|
|
int iLine = iLineNr + m_iMessagesWinClientHeight - 1;
|
|
int iLinesToPrint = m_iMessagesWinClientHeight;
|
|
|
|
Log::Messages* pMessages = LockMessages();
|
|
|
|
// print messages from bottom
|
|
for (int i = (int)pMessages->size() - 1; i >= 0 && iLinesToPrint > 0; i--)
|
|
{
|
|
int iPrintedLines = PrintMessage((*pMessages)[i], iLine, iLinesToPrint);
|
|
iLine -= iPrintedLines;
|
|
iLinesToPrint -= iPrintedLines;
|
|
}
|
|
|
|
if (iLinesToPrint > 0)
|
|
{
|
|
// too few messages, print them again from top
|
|
iLine = iLineNr + m_iMessagesWinClientHeight - 1;
|
|
while (iLinesToPrint-- > 0)
|
|
{
|
|
PlotLine("", iLine--, 0, NCURSES_COLORPAIR_TEXT);
|
|
}
|
|
int iLinesToPrint2 = m_iMessagesWinClientHeight;
|
|
for (int i = (int)pMessages->size() - 1; i >= 0 && iLinesToPrint2 > 0; i--)
|
|
{
|
|
int iPrintedLines = PrintMessage((*pMessages)[i], iLine, iLinesToPrint2);
|
|
iLine -= iPrintedLines;
|
|
iLinesToPrint2 -= iPrintedLines;
|
|
}
|
|
}
|
|
|
|
UnlockMessages();
|
|
}
|
|
|
|
int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
|
|
{
|
|
const char* szMessageType[] = { "INFO ", "WARNING ", "ERROR ", "DEBUG ", "DETAIL "};
|
|
const int iMessageTypeColor[] = { NCURSES_COLORPAIR_INFO, NCURSES_COLORPAIR_WARNING,
|
|
NCURSES_COLORPAIR_ERROR, NCURSES_COLORPAIR_DEBUG, NCURSES_COLORPAIR_DETAIL };
|
|
|
|
char* szText = (char*)Msg->GetText();
|
|
|
|
if (m_bShowTimestamp)
|
|
{
|
|
int iLen = strlen(szText) + 50;
|
|
szText = (char*)malloc(iLen);
|
|
|
|
time_t rawtime = Msg->GetTime();
|
|
char szTime[50];
|
|
#ifdef HAVE_CTIME_R_3
|
|
ctime_r(&rawtime, szTime, 50);
|
|
#else
|
|
ctime_r(&rawtime, szTime);
|
|
#endif
|
|
szTime[50-1] = '\0';
|
|
szTime[strlen(szTime) - 1] = '\0'; // trim LF
|
|
|
|
snprintf(szText, iLen, "%s - %s", szTime, Msg->GetText());
|
|
szText[iLen - 1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
szText = strdup(szText);
|
|
}
|
|
|
|
// replace some special characters with spaces
|
|
for (char* p = szText; *p; p++)
|
|
{
|
|
if (*p == '\n' || *p == '\r' || *p == '\b')
|
|
{
|
|
*p = ' ';
|
|
}
|
|
}
|
|
|
|
int iLen = strlen(szText);
|
|
int iWinWidth = m_iScreenWidth - 8;
|
|
int iMsgLines = iLen / iWinWidth;
|
|
if (iLen % iWinWidth > 0)
|
|
{
|
|
iMsgLines++;
|
|
}
|
|
|
|
int iLines = 0;
|
|
for (int i = iMsgLines - 1; i >= 0 && iLines < iMaxLines; i--)
|
|
{
|
|
int iR = iRow - iMsgLines + i + 1;
|
|
PlotLine(szText + iWinWidth * i, iR, 8, NCURSES_COLORPAIR_TEXT);
|
|
if (i == 0)
|
|
{
|
|
PlotText(szMessageType[Msg->GetKind()], iR, 0, iMessageTypeColor[Msg->GetKind()], false);
|
|
}
|
|
else
|
|
{
|
|
PlotText(" ", iR, 0, iMessageTypeColor[Msg->GetKind()], false);
|
|
}
|
|
iLines++;
|
|
}
|
|
|
|
free(szText);
|
|
|
|
return iLines;
|
|
}
|
|
|
|
void NCursesFrontend::PrintStatus()
|
|
{
|
|
char tmp[MAX_SCREEN_WIDTH];
|
|
int iStatusRow = m_iScreenHeight - 2;
|
|
|
|
char timeString[100];
|
|
timeString[0] = '\0';
|
|
|
|
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
|
|
if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
|
|
{
|
|
long long remain_sec = (long long)(m_lRemainingSize / (fCurrentDownloadSpeed * 1024));
|
|
int h = (int)(remain_sec / 3600);
|
|
int m = (int)((remain_sec % 3600) / 60);
|
|
int s = (int)(remain_sec % 60);
|
|
sprintf(timeString, " (~ %.2d:%.2d:%.2d)", h, m, s);
|
|
}
|
|
|
|
char szDownloadLimit[128];
|
|
if (m_fDownloadLimit > 0.0f)
|
|
{
|
|
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
|
|
}
|
|
else
|
|
{
|
|
szDownloadLimit[0] = 0;
|
|
}
|
|
|
|
char szPostStatus[128];
|
|
if (m_iPostJobCount > 0)
|
|
{
|
|
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
|
|
}
|
|
else
|
|
{
|
|
szPostStatus[0] = 0;
|
|
}
|
|
|
|
float fAverageSpeed = (float)(Util::Int64ToFloat(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec : 0) / 1024.0);
|
|
|
|
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s, Avg. %.*f KB/s",
|
|
m_iThreadCount, (fCurrentDownloadSpeed >= 10 ? 0 : 1), fCurrentDownloadSpeed,
|
|
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
|
|
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
|
|
m_bPauseDownload || m_bPauseDownload2 ?
|
|
(m_bPauseDownload && m_bPauseDownload2 ? " (+2)" : m_bPauseDownload2 ? " (2)" : "") : "",
|
|
szDownloadLimit, (fAverageSpeed >= 10 ? 0 : 1), fAverageSpeed);
|
|
tmp[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
PlotLine(tmp, iStatusRow, 0, NCURSES_COLORPAIR_STATUS);
|
|
}
|
|
|
|
void NCursesFrontend::PrintKeyInputBar()
|
|
{
|
|
int iQueueSize = CalcQueueSize();
|
|
int iInputBarRow = m_iScreenHeight - 1;
|
|
|
|
if (m_szHint)
|
|
{
|
|
time_t tTime = time(NULL);
|
|
if (tTime - m_tStartHint < 5)
|
|
{
|
|
PlotLine(m_szHint, iInputBarRow, 0, NCURSES_COLORPAIR_HINT);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
SetHint(NULL);
|
|
}
|
|
}
|
|
|
|
switch (m_eInputMode)
|
|
{
|
|
case eNormal:
|
|
if (m_bGroupFiles)
|
|
{
|
|
PlotLine("(Q)uit | (E)dit | (P)ause | (R)ate | (W)indow | (G)roup | (T)ime", iInputBarRow, 0, NCURSES_COLORPAIR_KEYBAR);
|
|
}
|
|
else
|
|
{
|
|
PlotLine("(Q)uit | (E)dit | (P)ause | (R)ate | (W)indow | (G)roup | (T)ime | n(Z)b", iInputBarRow, 0, NCURSES_COLORPAIR_KEYBAR);
|
|
}
|
|
break;
|
|
case eEditQueue:
|
|
{
|
|
const char* szStatus = NULL;
|
|
if (m_iSelectedQueueEntry > 0 && iQueueSize > 1 && m_iSelectedQueueEntry == iQueueSize - 1)
|
|
{
|
|
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete | (U)p/(T)op";
|
|
}
|
|
else if (iQueueSize > 1 && m_iSelectedQueueEntry == 0)
|
|
{
|
|
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete | dow(N)/(B)ottom";
|
|
}
|
|
else if (iQueueSize > 1)
|
|
{
|
|
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete | (U)p/dow(N)/(T)op/(B)ottom";
|
|
}
|
|
else
|
|
{
|
|
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete";
|
|
}
|
|
|
|
PlotLine(szStatus, iInputBarRow, 0, NCURSES_COLORPAIR_KEYBAR);
|
|
break;
|
|
}
|
|
case eDownloadRate:
|
|
char szString[128];
|
|
snprintf(szString, 128, "Download rate: %i", m_iInputValue);
|
|
szString[128-1] = '\0';
|
|
PlotLine(szString, iInputBarRow, 0, NCURSES_COLORPAIR_KEYBAR);
|
|
// Print the cursor
|
|
PlotText(" ", iInputBarRow, 15 + m_iInputNumberIndex, NCURSES_COLORPAIR_CURSOR, true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NCursesFrontend::SetHint(const char* szHint)
|
|
{
|
|
if (m_szHint)
|
|
{
|
|
free(m_szHint);
|
|
m_szHint = NULL;
|
|
}
|
|
if (szHint)
|
|
{
|
|
m_szHint = strdup(szHint);
|
|
m_tStartHint = time(NULL);
|
|
}
|
|
}
|
|
|
|
void NCursesFrontend::PrintQueue()
|
|
{
|
|
if (m_bGroupFiles)
|
|
{
|
|
PrintGroupQueue();
|
|
}
|
|
else
|
|
{
|
|
PrintFileQueue();
|
|
}
|
|
}
|
|
|
|
void NCursesFrontend::PrintFileQueue()
|
|
{
|
|
int iLineNr = m_iQueueWinTop;
|
|
|
|
DownloadQueue* pDownloadQueue = LockQueue();
|
|
if (pDownloadQueue->GetFileQueue()->empty())
|
|
{
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
snprintf(szBuffer, sizeof(szBuffer), "%s Files for downloading", m_bUseColor ? "" : "*** ");
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
PrintTopHeader(szBuffer, iLineNr++, true);
|
|
PlotLine("Ready to receive nzb-job", iLineNr++, 0, NCURSES_COLORPAIR_TEXT);
|
|
}
|
|
else
|
|
{
|
|
iLineNr++;
|
|
long long lRemaining = 0;
|
|
long long lPaused = 0;
|
|
int iPausedFiles = 0;
|
|
int i = 0;
|
|
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++, i++)
|
|
{
|
|
FileInfo* pFileInfo = *it;
|
|
|
|
if (i >= m_iQueueScrollOffset && i < m_iQueueScrollOffset + m_iQueueWinHeight -1)
|
|
{
|
|
PrintFilename(pFileInfo, iLineNr++, i == m_iSelectedQueueEntry);
|
|
}
|
|
|
|
if (pFileInfo->GetPaused())
|
|
{
|
|
iPausedFiles++;
|
|
lPaused += pFileInfo->GetRemainingSize();
|
|
}
|
|
lRemaining += pFileInfo->GetRemainingSize();
|
|
}
|
|
|
|
char szRemaining[20];
|
|
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
|
|
|
|
char szUnpaused[20];
|
|
Util::FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
|
|
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
snprintf(szBuffer, sizeof(szBuffer), " %sFiles for downloading - %i / %i files in queue - %s / %s",
|
|
m_bUseColor ? "" : "*** ", pDownloadQueue->GetFileQueue()->size(),
|
|
pDownloadQueue->GetFileQueue()->size() - iPausedFiles, szRemaining, szUnpaused);
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
PrintTopHeader(szBuffer, m_iQueueWinTop, true);
|
|
}
|
|
UnlockQueue();
|
|
}
|
|
|
|
void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelected)
|
|
{
|
|
int color = 0;
|
|
const char* Brace1 = "[";
|
|
const char* Brace2 = "]";
|
|
if (m_eInputMode == eEditQueue && bSelected)
|
|
{
|
|
color = NCURSES_COLORPAIR_TEXTHIGHL;
|
|
if (!m_bUseColor)
|
|
{
|
|
Brace1 = "<";
|
|
Brace2 = ">";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
color = NCURSES_COLORPAIR_TEXT;
|
|
}
|
|
|
|
const char* szDownloading = "";
|
|
if (pFileInfo->GetActiveDownloads() > 0)
|
|
{
|
|
szDownloading = " *";
|
|
}
|
|
|
|
char szPriority[100];
|
|
szPriority[0] = '\0';
|
|
if (pFileInfo->GetPriority() != 0)
|
|
{
|
|
sprintf(szPriority, " [%+i]", pFileInfo->GetPriority());
|
|
}
|
|
|
|
char szCompleted[20];
|
|
szCompleted[0] = '\0';
|
|
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
|
|
{
|
|
sprintf(szCompleted, ", %i%%", (int)(100 - Util::Int64ToFloat(pFileInfo->GetRemainingSize()) * 100.0 / Util::Int64ToFloat(pFileInfo->GetSize())));
|
|
}
|
|
|
|
char szNZBNiceName[1024];
|
|
if (m_bShowNZBname)
|
|
{
|
|
strncpy(szNZBNiceName, pFileInfo->GetNZBInfo()->GetName(), 1023);
|
|
int len = strlen(szNZBNiceName);
|
|
szNZBNiceName[len] = PATH_SEPARATOR;
|
|
szNZBNiceName[len + 1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
szNZBNiceName[0] = '\0';
|
|
}
|
|
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s%s%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(),
|
|
Brace2, szPriority, szDownloading, szNZBNiceName, pFileInfo->GetFilename(),
|
|
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
|
|
szCompleted, pFileInfo->GetPaused() ? " (paused)" : "");
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
|
|
PlotLine(szBuffer, iRow, 0, color);
|
|
}
|
|
|
|
void NCursesFrontend::PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime)
|
|
{
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
snprintf(szBuffer, sizeof(szBuffer), "%-*s", m_iScreenWidth, szHeader);
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
int iHeaderLen = strlen(szHeader);
|
|
int iCharsLeft = m_iScreenWidth - iHeaderLen - 2;
|
|
|
|
int iTime = bUpTime ? m_iUpTimeSec : m_iDnTimeSec;
|
|
int d = iTime / 3600 / 24;
|
|
int h = (iTime % (3600 * 24)) / 3600;
|
|
int m = (iTime % 3600) / 60;
|
|
int s = iTime % 60;
|
|
char szTime[30];
|
|
|
|
if (d == 0)
|
|
{
|
|
snprintf(szTime, 30, "%.2d:%.2d:%.2d", h, m, s);
|
|
if ((int)strlen(szTime) > iCharsLeft)
|
|
{
|
|
snprintf(szTime, 30, "%.2d:%.2d", h, m);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snprintf(szTime, 30, "%i %s %.2d:%.2d:%.2d", d, (d == 1 ? "day" : "days"), h, m, s);
|
|
if ((int)strlen(szTime) > iCharsLeft)
|
|
{
|
|
snprintf(szTime, 30, "%id %.2d:%.2d:%.2d", d, h, m, s);
|
|
}
|
|
if ((int)strlen(szTime) > iCharsLeft)
|
|
{
|
|
snprintf(szTime, 30, "%id %.2d:%.2d", d, h, m);
|
|
}
|
|
}
|
|
|
|
szTime[29] = '\0';
|
|
const char* szShortCap = bUpTime ? " Up " : "Dn ";
|
|
const char* szLongCap = bUpTime ? " Uptime " : " Download-time ";
|
|
|
|
int iTimeLen = strlen(szTime);
|
|
int iShortCapLen = strlen(szShortCap);
|
|
int iLongCapLen = strlen(szLongCap);
|
|
|
|
if (iCharsLeft - iTimeLen - iLongCapLen >= 0)
|
|
{
|
|
snprintf(szBuffer + m_iScreenWidth - iTimeLen - iLongCapLen, MAX_SCREEN_WIDTH - (m_iScreenWidth - iTimeLen - iLongCapLen), "%s%s", szLongCap, szTime);
|
|
}
|
|
else if (iCharsLeft - iTimeLen - iShortCapLen >= 0)
|
|
{
|
|
snprintf(szBuffer + m_iScreenWidth - iTimeLen - iShortCapLen, MAX_SCREEN_WIDTH - (m_iScreenWidth - iTimeLen - iShortCapLen), "%s%s", szShortCap, szTime);
|
|
}
|
|
else if (iCharsLeft - iTimeLen >= 0)
|
|
{
|
|
snprintf(szBuffer + m_iScreenWidth - iTimeLen, MAX_SCREEN_WIDTH - (m_iScreenWidth - iTimeLen), "%s", szTime);
|
|
}
|
|
|
|
PlotLine(szBuffer, iLineNr, 0, NCURSES_COLORPAIR_INFOLINE);
|
|
}
|
|
|
|
void NCursesFrontend::PrintGroupQueue()
|
|
{
|
|
int iLineNr = m_iQueueWinTop;
|
|
|
|
LockQueue();
|
|
GroupQueue* pGroupQueue = &m_groupQueue;
|
|
if (pGroupQueue->empty())
|
|
{
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
snprintf(szBuffer, sizeof(szBuffer), "%s NZBs for downloading", m_bUseColor ? "" : "*** ");
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
PrintTopHeader(szBuffer, iLineNr++, false);
|
|
PlotLine("Ready to receive nzb-job", iLineNr++, 0, NCURSES_COLORPAIR_TEXT);
|
|
}
|
|
else
|
|
{
|
|
iLineNr++;
|
|
|
|
ResetColWidths();
|
|
int iCalcLineNr = iLineNr;
|
|
int i = 0;
|
|
for (GroupQueue::iterator it = pGroupQueue->begin(); it != pGroupQueue->end(); it++, i++)
|
|
{
|
|
GroupInfo* pGroupInfo = *it;
|
|
|
|
if (i >= m_iQueueScrollOffset && i < m_iQueueScrollOffset + m_iQueueWinHeight -1)
|
|
{
|
|
PrintGroupname(pGroupInfo, iCalcLineNr++, false, true);
|
|
}
|
|
}
|
|
|
|
long long lRemaining = 0;
|
|
long long lPaused = 0;
|
|
i = 0;
|
|
for (GroupQueue::iterator it = pGroupQueue->begin(); it != pGroupQueue->end(); it++, i++)
|
|
{
|
|
GroupInfo* pGroupInfo = *it;
|
|
|
|
if (i >= m_iQueueScrollOffset && i < m_iQueueScrollOffset + m_iQueueWinHeight -1)
|
|
{
|
|
PrintGroupname(pGroupInfo, iLineNr++, i == m_iSelectedQueueEntry, false);
|
|
}
|
|
|
|
lRemaining += pGroupInfo->GetRemainingSize();
|
|
lPaused += pGroupInfo->GetPausedSize();
|
|
}
|
|
|
|
char szRemaining[20];
|
|
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
|
|
|
|
char szUnpaused[20];
|
|
Util::FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
|
|
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
snprintf(szBuffer, sizeof(szBuffer), " %sNZBs for downloading - %i NZBs in queue - %s / %s",
|
|
m_bUseColor ? "" : "*** ", pGroupQueue->size(), szRemaining, szUnpaused);
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
PrintTopHeader(szBuffer, m_iQueueWinTop, false);
|
|
}
|
|
UnlockQueue();
|
|
}
|
|
|
|
void NCursesFrontend::ResetColWidths()
|
|
{
|
|
m_iColWidthFiles = 0;
|
|
m_iColWidthTotal = 0;
|
|
m_iColWidthLeft = 0;
|
|
}
|
|
|
|
void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected, bool bCalcColWidth)
|
|
{
|
|
int color = NCURSES_COLORPAIR_TEXT;
|
|
char chBrace1 = '[';
|
|
char chBrace2 = ']';
|
|
if (m_eInputMode == eEditQueue && bSelected)
|
|
{
|
|
color = NCURSES_COLORPAIR_TEXTHIGHL;
|
|
if (!m_bUseColor)
|
|
{
|
|
chBrace1 = '<';
|
|
chBrace2 = '>';
|
|
}
|
|
}
|
|
|
|
const char* szDownloading = "";
|
|
if (pGroupInfo->GetActiveDownloads() > 0)
|
|
{
|
|
szDownloading = " *";
|
|
}
|
|
|
|
long long lUnpausedRemainingSize = pGroupInfo->GetRemainingSize() - pGroupInfo->GetPausedSize();
|
|
|
|
char szRemaining[20];
|
|
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
|
|
|
|
char szPriority[100];
|
|
szPriority[0] = '\0';
|
|
if (pGroupInfo->GetMinPriority() != 0 || pGroupInfo->GetMaxPriority() != 0)
|
|
{
|
|
if (pGroupInfo->GetMinPriority() == pGroupInfo->GetMaxPriority())
|
|
{
|
|
sprintf(szPriority, " [%+i]", pGroupInfo->GetMinPriority());
|
|
}
|
|
else
|
|
{
|
|
sprintf(szPriority, " [%+i..%+i]", pGroupInfo->GetMinPriority(), pGroupInfo->GetMaxPriority());
|
|
}
|
|
}
|
|
|
|
char szBuffer[MAX_SCREEN_WIDTH];
|
|
|
|
// Format:
|
|
// [id - id] Name Left-Files/Paused Total Left Time
|
|
// [1-2] Nzb-name 999/999 999.99 MB 999.99 MB 00:00:00
|
|
|
|
int iNameLen = 0;
|
|
if (bCalcColWidth)
|
|
{
|
|
iNameLen = m_iScreenWidth - 1 - 9 - 11 - 11 - 9;
|
|
}
|
|
else
|
|
{
|
|
iNameLen = m_iScreenWidth - 1 - m_iColWidthFiles - 2 - m_iColWidthTotal - 2 - m_iColWidthLeft - 2 - 9;
|
|
}
|
|
|
|
bool bPrintFormatted = iNameLen > 20;
|
|
|
|
if (bPrintFormatted)
|
|
{
|
|
char szFiles[20];
|
|
snprintf(szFiles, 20, "%i/%i", pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetPausedFileCount());
|
|
szFiles[20-1] = '\0';
|
|
|
|
char szTotal[20];
|
|
Util::FormatFileSize(szTotal, sizeof(szTotal), pGroupInfo->GetNZBInfo()->GetSize());
|
|
|
|
char szNameWithIds[1024];
|
|
snprintf(szNameWithIds, 1024, "%c%i-%i%c%s%s %s", chBrace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), chBrace2,
|
|
szPriority, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
|
|
szNameWithIds[iNameLen] = '\0';
|
|
|
|
char szTime[100];
|
|
szTime[0] = '\0';
|
|
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
|
|
if (pGroupInfo->GetPausedSize() > 0 && lUnpausedRemainingSize == 0)
|
|
{
|
|
snprintf(szTime, 100, "[paused]");
|
|
Util::FormatFileSize(szRemaining, sizeof(szRemaining), pGroupInfo->GetRemainingSize());
|
|
}
|
|
else if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
|
|
{
|
|
long long remain_sec = (long long)(lUnpausedRemainingSize / (fCurrentDownloadSpeed * 1024));
|
|
int h = (int)(remain_sec / 3600);
|
|
int m = (int)((remain_sec % 3600) / 60);
|
|
int s = (int)(remain_sec % 60);
|
|
if (h < 100)
|
|
{
|
|
snprintf(szTime, 100, "%.2d:%.2d:%.2d", h, m, s);
|
|
}
|
|
else
|
|
{
|
|
snprintf(szTime, 100, "99:99:99");
|
|
}
|
|
}
|
|
|
|
if (bCalcColWidth)
|
|
{
|
|
int iColWidthFiles = strlen(szFiles);
|
|
m_iColWidthFiles = iColWidthFiles > m_iColWidthFiles ? iColWidthFiles : m_iColWidthFiles;
|
|
|
|
int iColWidthTotal = strlen(szTotal);
|
|
m_iColWidthTotal = iColWidthTotal > m_iColWidthTotal ? iColWidthTotal : m_iColWidthTotal;
|
|
|
|
int iColWidthLeft = strlen(szRemaining);
|
|
m_iColWidthLeft = iColWidthLeft > m_iColWidthLeft ? iColWidthLeft : m_iColWidthLeft;
|
|
}
|
|
else
|
|
{
|
|
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%-*s %*s %*s %*s %8s", iNameLen, szNameWithIds, m_iColWidthFiles, szFiles, m_iColWidthTotal, szTotal, m_iColWidthLeft, szRemaining, szTime);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%c%i-%i%c%s %s", chBrace1, pGroupInfo->GetFirstID(),
|
|
pGroupInfo->GetLastID(), chBrace2, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
|
|
}
|
|
|
|
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
|
|
|
if (!bCalcColWidth)
|
|
{
|
|
PlotLine(szBuffer, iRow, 0, color);
|
|
}
|
|
}
|
|
|
|
void NCursesFrontend::PrepareGroupQueue()
|
|
{
|
|
m_groupQueue.clear();
|
|
|
|
DownloadQueue* pDownloadQueue = LockQueue();
|
|
pDownloadQueue->BuildGroups(&m_groupQueue);
|
|
UnlockQueue();
|
|
}
|
|
|
|
void NCursesFrontend::ClearGroupQueue()
|
|
{
|
|
for (GroupQueue::iterator it = m_groupQueue.begin(); it != m_groupQueue.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
m_groupQueue.clear();
|
|
}
|
|
|
|
bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
|
|
{
|
|
int ID = 0;
|
|
|
|
if (m_bGroupFiles)
|
|
{
|
|
if (m_iSelectedQueueEntry >= 0 && m_iSelectedQueueEntry < (int)m_groupQueue.size())
|
|
{
|
|
GroupInfo* pGroupInfo = m_groupQueue[m_iSelectedQueueEntry];
|
|
ID = pGroupInfo->GetLastID();
|
|
if (eAction == QueueEditor::eaFilePause)
|
|
{
|
|
if (pGroupInfo->GetRemainingSize() == pGroupInfo->GetPausedSize())
|
|
{
|
|
eAction = QueueEditor::eaFileResume;
|
|
}
|
|
else if (pGroupInfo->GetPausedSize() == 0 && (pGroupInfo->GetRemainingParCount() > 0) &&
|
|
!(m_bLastPausePars && m_iLastEditEntry == m_iSelectedQueueEntry))
|
|
{
|
|
eAction = QueueEditor::eaFilePauseExtraPars;
|
|
m_bLastPausePars = true;
|
|
}
|
|
else
|
|
{
|
|
eAction = QueueEditor::eaFilePause;
|
|
m_bLastPausePars = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// map file-edit-actions to group-edit-actions
|
|
QueueEditor::EEditAction FileToGroupMap[] = {
|
|
(QueueEditor::EEditAction)0,
|
|
QueueEditor::eaGroupMoveOffset,
|
|
QueueEditor::eaGroupMoveTop,
|
|
QueueEditor::eaGroupMoveBottom,
|
|
QueueEditor::eaGroupPause,
|
|
QueueEditor::eaGroupResume,
|
|
QueueEditor::eaGroupDelete,
|
|
QueueEditor::eaGroupPauseAllPars,
|
|
QueueEditor::eaGroupPauseExtraPars };
|
|
eAction = FileToGroupMap[eAction];
|
|
}
|
|
else
|
|
{
|
|
DownloadQueue* pDownloadQueue = LockQueue();
|
|
if (m_iSelectedQueueEntry >= 0 && m_iSelectedQueueEntry < (int)pDownloadQueue->GetFileQueue()->size())
|
|
{
|
|
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(m_iSelectedQueueEntry);
|
|
ID = pFileInfo->GetID();
|
|
if (eAction == QueueEditor::eaFilePause)
|
|
{
|
|
eAction = !pFileInfo->GetPaused() ? QueueEditor::eaFilePause : QueueEditor::eaFileResume;
|
|
}
|
|
}
|
|
UnlockQueue();
|
|
}
|
|
|
|
m_iLastEditEntry = m_iSelectedQueueEntry;
|
|
|
|
NeedUpdateData();
|
|
|
|
if (ID != 0)
|
|
{
|
|
return ServerEditQueue(eAction, iOffset, ID);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void NCursesFrontend::SetCurrentQueueEntry(int iEntry)
|
|
{
|
|
int iQueueSize = CalcQueueSize();
|
|
|
|
if (iEntry < 0)
|
|
{
|
|
iEntry = 0;
|
|
}
|
|
else if (iEntry > iQueueSize - 1)
|
|
{
|
|
iEntry = iQueueSize - 1;
|
|
}
|
|
|
|
if (iEntry > m_iQueueScrollOffset + m_iQueueWinClientHeight ||
|
|
iEntry < m_iQueueScrollOffset - m_iQueueWinClientHeight)
|
|
{
|
|
m_iQueueScrollOffset = iEntry - m_iQueueWinClientHeight / 2;
|
|
}
|
|
else if (iEntry < m_iQueueScrollOffset)
|
|
{
|
|
m_iQueueScrollOffset -= m_iQueueWinClientHeight;
|
|
}
|
|
else if (iEntry >= m_iQueueScrollOffset + m_iQueueWinClientHeight)
|
|
{
|
|
m_iQueueScrollOffset += m_iQueueWinClientHeight;
|
|
}
|
|
|
|
if (m_iQueueScrollOffset > iQueueSize - m_iQueueWinClientHeight)
|
|
{
|
|
m_iQueueScrollOffset = iQueueSize - m_iQueueWinClientHeight;
|
|
}
|
|
if (m_iQueueScrollOffset < 0)
|
|
{
|
|
m_iQueueScrollOffset = 0;
|
|
}
|
|
|
|
m_iSelectedQueueEntry = iEntry;
|
|
}
|
|
|
|
|
|
/*
|
|
* Process keystrokes starting with the initialKey, which must not be
|
|
* READKEY_EMPTY but has alread been set via ReadConsoleKey.
|
|
*/
|
|
void NCursesFrontend::UpdateInput(int initialKey)
|
|
{
|
|
int iKey = initialKey;
|
|
while (iKey != READKEY_EMPTY)
|
|
{
|
|
int iQueueSize = CalcQueueSize();
|
|
|
|
// Normal or edit queue mode
|
|
if (m_eInputMode == eNormal || m_eInputMode == eEditQueue)
|
|
{
|
|
switch (iKey)
|
|
{
|
|
case 'q':
|
|
// Key 'q' for quit
|
|
ExitProc();
|
|
break;
|
|
case 'z':
|
|
// show/hide NZBFilename
|
|
m_bShowNZBname = !m_bShowNZBname;
|
|
break;
|
|
case 'w':
|
|
// swicth window sizes
|
|
if (m_QueueWindowPercentage == 0.5)
|
|
{
|
|
m_QueueWindowPercentage = 1;
|
|
}
|
|
else if (m_QueueWindowPercentage == 1 && m_eInputMode != eEditQueue)
|
|
{
|
|
m_QueueWindowPercentage = 0;
|
|
}
|
|
else
|
|
{
|
|
m_QueueWindowPercentage = 0.5;
|
|
}
|
|
CalcWindowSizes();
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry);
|
|
break;
|
|
case 'g':
|
|
// group/ungroup files
|
|
m_bGroupFiles = !m_bGroupFiles;
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry);
|
|
NeedUpdateData();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Normal mode
|
|
if (m_eInputMode == eNormal)
|
|
{
|
|
switch (iKey)
|
|
{
|
|
case 'p':
|
|
// Key 'p' for pause
|
|
if (!IsRemoteMode())
|
|
{
|
|
info(m_bPauseDownload || m_bPauseDownload2 ? "Unpausing download" : "Pausing download");
|
|
}
|
|
ServerPauseUnpause(!(m_bPauseDownload || m_bPauseDownload2), m_bPauseDownload2 && !m_bPauseDownload);
|
|
break;
|
|
case '\'':
|
|
ServerDumpDebug();
|
|
break;
|
|
case 'e':
|
|
case 10: // return
|
|
case 13: // enter
|
|
if (iQueueSize > 0)
|
|
{
|
|
m_eInputMode = eEditQueue;
|
|
if (m_QueueWindowPercentage == 0)
|
|
{
|
|
m_QueueWindowPercentage = 0.5;
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
case 'r':
|
|
// Download rate
|
|
m_eInputMode = eDownloadRate;
|
|
m_iInputNumberIndex = 0;
|
|
m_iInputValue = 0;
|
|
return;
|
|
case 't':
|
|
// show/hide Timestamps
|
|
m_bShowTimestamp = !m_bShowTimestamp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Edit Queue mode
|
|
if (m_eInputMode == eEditQueue)
|
|
{
|
|
switch (iKey)
|
|
{
|
|
case 'e':
|
|
case 10: // return
|
|
case 13: // enter
|
|
m_eInputMode = eNormal;
|
|
return;
|
|
case KEY_DOWN:
|
|
if (m_iSelectedQueueEntry < iQueueSize - 1)
|
|
{
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry + 1);
|
|
}
|
|
break;
|
|
case KEY_UP:
|
|
if (m_iSelectedQueueEntry > 0)
|
|
{
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry - 1);
|
|
}
|
|
break;
|
|
case KEY_PPAGE:
|
|
if (m_iSelectedQueueEntry > 0)
|
|
{
|
|
if (m_iSelectedQueueEntry == m_iQueueScrollOffset)
|
|
{
|
|
m_iQueueScrollOffset -= m_iQueueWinClientHeight;
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry - m_iQueueWinClientHeight);
|
|
}
|
|
else
|
|
{
|
|
SetCurrentQueueEntry(m_iQueueScrollOffset);
|
|
}
|
|
}
|
|
break;
|
|
case KEY_NPAGE:
|
|
if (m_iSelectedQueueEntry < iQueueSize - 1)
|
|
{
|
|
if (m_iSelectedQueueEntry == m_iQueueScrollOffset + m_iQueueWinClientHeight - 1)
|
|
{
|
|
m_iQueueScrollOffset += m_iQueueWinClientHeight;
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry + m_iQueueWinClientHeight);
|
|
}
|
|
else
|
|
{
|
|
SetCurrentQueueEntry(m_iQueueScrollOffset + m_iQueueWinClientHeight - 1);
|
|
}
|
|
}
|
|
break;
|
|
case KEY_HOME:
|
|
SetCurrentQueueEntry(0);
|
|
break;
|
|
case KEY_END:
|
|
SetCurrentQueueEntry(iQueueSize > 0 ? iQueueSize - 1 : 0);
|
|
break;
|
|
case 'p':
|
|
// Key 'p' for pause
|
|
EditQueue(QueueEditor::eaFilePause, 0);
|
|
break;
|
|
case 'd':
|
|
SetHint(" Use Uppercase \"D\" for delete");
|
|
break;
|
|
case 'D':
|
|
// Delete entry
|
|
if (EditQueue(QueueEditor::eaFileDelete, 0))
|
|
{
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry);
|
|
}
|
|
break;
|
|
case 'u':
|
|
if (EditQueue(QueueEditor::eaFileMoveOffset, -1))
|
|
{
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry - 1);
|
|
}
|
|
break;
|
|
case 'n':
|
|
if (EditQueue(QueueEditor::eaFileMoveOffset, +1))
|
|
{
|
|
SetCurrentQueueEntry(m_iSelectedQueueEntry + 1);
|
|
}
|
|
break;
|
|
case 't':
|
|
if (EditQueue(QueueEditor::eaFileMoveTop, 0))
|
|
{
|
|
SetCurrentQueueEntry(0);
|
|
}
|
|
break;
|
|
case 'b':
|
|
if (EditQueue(QueueEditor::eaFileMoveBottom, 0))
|
|
{
|
|
SetCurrentQueueEntry(iQueueSize > 0 ? iQueueSize - 1 : 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Edit download rate input mode
|
|
if (m_eInputMode == eDownloadRate)
|
|
{
|
|
// Numbers
|
|
if (m_iInputNumberIndex < 5 && iKey >= '0' && iKey <= '9')
|
|
{
|
|
m_iInputValue = (m_iInputValue * 10) + (iKey - '0');
|
|
m_iInputNumberIndex++;
|
|
}
|
|
// Enter
|
|
else if (iKey == 10 || iKey == 13)
|
|
{
|
|
ServerSetDownloadRate((float)m_iInputValue);
|
|
m_eInputMode = eNormal;
|
|
return;
|
|
}
|
|
// Escape
|
|
else if (iKey == 27)
|
|
{
|
|
m_eInputMode = eNormal;
|
|
return;
|
|
}
|
|
// Backspace
|
|
else if (m_iInputNumberIndex > 0 && iKey == KEY_BACKSPACE)
|
|
{
|
|
int iRemain = m_iInputValue % 10;
|
|
|
|
m_iInputValue = (m_iInputValue - iRemain) / 10;
|
|
m_iInputNumberIndex--;
|
|
}
|
|
}
|
|
|
|
iKey = ReadConsoleKey();
|
|
}
|
|
}
|
|
|
|
int NCursesFrontend::ReadConsoleKey()
|
|
{
|
|
#ifdef WIN32
|
|
HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
|
DWORD NumberOfEvents;
|
|
BOOL bOK = GetNumberOfConsoleInputEvents(hConsole, &NumberOfEvents);
|
|
if (bOK && NumberOfEvents > 0)
|
|
{
|
|
while (NumberOfEvents--)
|
|
{
|
|
INPUT_RECORD InputRecord;
|
|
DWORD NumberOfEventsRead;
|
|
if (ReadConsoleInput(hConsole, &InputRecord, 1, &NumberOfEventsRead) &&
|
|
NumberOfEventsRead > 0 &&
|
|
InputRecord.EventType == KEY_EVENT &&
|
|
InputRecord.Event.KeyEvent.bKeyDown)
|
|
{
|
|
char c = tolower(InputRecord.Event.KeyEvent.wVirtualKeyCode);
|
|
if (bool(InputRecord.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON) ^
|
|
bool(InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED))
|
|
{
|
|
c = toupper(c);
|
|
}
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
return READKEY_EMPTY;
|
|
#else
|
|
return getch();
|
|
#endif
|
|
}
|
|
|
|
#endif
|