Merge pull request #360 from pryon/UINotepad-fixes

UINotepad fixes and improvements
This commit is contained in:
smallmodel 2024-09-22 19:35:52 +02:00 committed by GitHub
commit 1fd6c2bd52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 486 additions and 335 deletions

View file

@ -83,7 +83,7 @@ void PickFile(const char *name, Listener *obj, Event& event)
if (name && *name && strchr(name, '/')) {
currentpath = name;
for (i = currentpath.length(); i > 0; i--) {
for (i = currentpath.length() - 1; i > 0; i--) {
if (currentpath[i] == '/') {
break;
}

View file

@ -35,6 +35,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifndef _WIN32
# include <sys/types.h>
# include <sys/stat.h>
# include <errno.h>
# include <dirent.h>
#endif
@ -4445,13 +4447,51 @@ void FS_CanonicalFilename(char* filename)
*dest = 0;
}
void FS_FileTime(const char* filename, char* date, char* size) {
const char* ospath;
void FS_FileTime(const char *filename, char *date, char *size)
{
const char *ospath;
struct stat fileStat;
int result = -1;
date[0] = 0;
size[0] = 0;
date[0] = 0;
size[0] = 0;
ospath = FS_BuildOSPath(fs_homepath->string, fs_gamedir, filename);
// Fixed in OPM:
// don't only check the home directory (fs_homepath) for possible files,
// but look through all searchpaths
//ospath = FS_BuildOSPath(fs_homepath->string, fs_gamedir, filename);
for (auto search = fs_searchpaths; search; search = search->next) {
if (search->dir != NULL && search->dir->path != NULL) {
ospath = FS_BuildOSPath(search->dir->path, fs_gamedir, filename);
result = stat(ospath, &fileStat);
if (result != -1) {
// found the valid file
break;
}
}
}
// FIXME: unimplemented
if (result == -1) {
int err = errno;
return;
}
time_t modTime = fileStat.st_mtime;
off_t fileSize = fileStat.st_size;
// FIXME: provide option not to use idiotic date formats
tm tm = *localtime(&modTime);
Q_snprintf(
date,
128,
"%2d/%02d/%04d %2d:%02d%c",
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_year + 1900,
(tm.tm_hour + 12) % 12,
tm.tm_min,
tm.tm_hour < 12 ? 'a' : 'p'
);
Q_snprintf(size, 128, "%ld", fileSize);
}

View file

@ -24,6 +24,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "../win_localization.h"
#include "../sys_loadlib.h"
// a pointer to the last piece of data retrieved from the clipboard is stored here,
// so that it can be cleaned up when new data is retrieved, preventing memory leaks
static void* clipboard_text = NULL;
static void* game_library = NULL;
static void* cgame_library = NULL;
qboolean GLimp_SpawnRenderThread(void (*function)(void))
@ -134,7 +138,36 @@ Sys_GetWholeClipboard
*/
const char* Sys_GetWholeClipboard(void)
{
#ifndef DEDICATED
char *data = NULL;
char *cliptext;
if ((cliptext = SDL_GetClipboardText()) != NULL) {
if (cliptext[0] != NULL) {
// It's necessary to limit buffersize to 4096 as each character
// is pasted via CharEvent, which is very-very slow and jams up the EventQueue.
// A smaller buffer doesn't jam the EventQueue up as much and avoids dropping
// characters that otherwise happens when the EventQueue is overloaded.
// FIXME: speed up paste logic so this restriction can be removed
size_t bufsize = Q_min(strlen(cliptext) + 1, 4096);
if (clipboard_text != NULL) {
// clean up previously allocated clipboard buffer
Z_Free(clipboard_text);
clipboard_text = NULL;
}
data = clipboard_text = Z_Malloc(bufsize);
// Changed in OPM:
// original game skips the Windows-specific '\r' (carriage return) char here!
Q_strncpyz(data, cliptext, bufsize);
}
SDL_free(cliptext);
}
return data;
#else
return NULL;
#endif
}
/*
@ -142,8 +175,15 @@ const char* Sys_GetWholeClipboard(void)
Sys_SetClipboard
==============
*/
void Sys_SetClipboard(const char* contents)
void Sys_SetClipboard(const char *contents)
{
#ifndef DEDICATED
if (contents == NULL || contents[0] == NULL) {
return;
}
SDL_SetClipboardText(contents);
#endif
}
/*

View file

@ -146,26 +146,8 @@ Sys_GetClipboardData
*/
char *Sys_GetClipboardData(void)
{
#ifdef DEDICATED
return NULL;
#else
char *data = NULL;
char *cliptext;
if ( ( cliptext = SDL_GetClipboardText() ) != NULL ) {
if ( cliptext[0] != '\0' ) {
size_t bufsize = strlen( cliptext ) + 1;
data = Z_Malloc( bufsize );
Q_strncpyz( data, cliptext, bufsize );
// find first listed char and set to '\0'
strtok( data, "\n\r\b" );
}
SDL_free( cliptext );
}
return data;
#endif
// unused - actual function is implemented in sys_main_new.c
return NULL;
}
#ifdef DEDICATED

View file

@ -369,60 +369,76 @@ DIRECTORY SCANNING
Sys_ListFilteredFiles
==================
*/
void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles )
void Sys_ListFilteredFiles(
const char *basedir, char *subdirs, char *filter, qboolean wantsubs, char **list, int *numfiles
)
{
char search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
char filename[MAX_OSPATH];
DIR *fdir;
struct dirent *d;
struct stat st;
char search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
char filename[MAX_OSPATH];
DIR *fdir;
struct dirent *d;
struct stat st;
if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
return;
}
if (*numfiles >= MAX_FOUND_FILES - 1) {
return;
}
if ( basedir[0] == '\0' ) {
return;
}
if (basedir[0] == '\0') {
return;
}
if (strlen(subdirs)) {
Com_sprintf( search, sizeof(search), "%s/%s", basedir, subdirs );
}
else {
Com_sprintf( search, sizeof(search), "%s", basedir );
}
if (strlen(subdirs)) {
Com_sprintf(search, sizeof(search), "%s/%s", basedir, subdirs);
} else {
Com_sprintf(search, sizeof(search), "%s", basedir);
}
if ((fdir = opendir(search)) == NULL) {
return;
}
if ((fdir = opendir(search)) == NULL) {
return;
}
while ((d = readdir(fdir)) != NULL) {
Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name);
if (stat(filename, &st) == -1)
continue;
while ((d = readdir(fdir)) != NULL) {
// Fixed in OPM:
// don't show current and parent dir entries twice
if (!(Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..")) && Q_stricmp(d->d_name, "cvs")) {
continue;
}
if (st.st_mode & S_IFDIR) {
if (Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..")) {
if (strlen(subdirs)) {
Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name);
}
else {
Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", d->d_name);
}
Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles );
}
}
if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
break;
}
Com_sprintf( filename, sizeof(filename), "%s/%s", subdirs, d->d_name );
if (!Com_FilterPath( filter, filename, qfalse ))
continue;
list[ *numfiles ] = CopyString( filename );
(*numfiles)++;
}
Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name);
if (stat(filename, &st) == -1) {
continue;
}
closedir(fdir);
if ((st.st_mode & S_IFDIR) != 0 && wantsubs) {
if (strlen(subdirs)) {
Com_sprintf(newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name);
} else {
Com_sprintf(newsubdirs, sizeof(newsubdirs), "%s", d->d_name);
}
// recursively iterate into subdirectory
Sys_ListFilteredFiles(basedir, newsubdirs, filter, wantsubs, list, numfiles);
}
if (*numfiles >= MAX_FOUND_FILES - 1) {
break;
}
if (strlen(subdirs)) {
Com_sprintf(filename, sizeof(filename), "%s/%s", subdirs, d->d_name);
} else {
Q_strncpyz(filename, d->d_name, sizeof(filename));
}
if (!Com_FilterPath(filter, filename, qfalse)) {
continue;
}
list[*numfiles] = CopyString(filename);
(*numfiles)++;
}
closedir(fdir);
}
/*
@ -430,104 +446,105 @@ void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, ch
Sys_ListFiles
==================
*/
char **Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs )
char **Sys_ListFiles(const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs)
{
struct dirent *d;
DIR *fdir;
qboolean dironly = wantsubs;
char search[MAX_OSPATH];
int nfiles;
char **listCopy;
char *list[MAX_FOUND_FILES];
int i;
struct stat st;
struct dirent *d;
DIR *fdir;
char search[MAX_OSPATH];
int nfiles;
char **listCopy;
char *list[MAX_FOUND_FILES];
int i;
struct stat st;
char buffer[64];
int extLen;
if (directory[0] == '\0') {
*numfiles = 0;
return NULL;
}
if (filter) {
if (!extension) {
extension = "";
}
nfiles = 0;
Sys_ListFilteredFiles( directory, "", filter, list, &nfiles );
// passing a slash as extension will find directories,
// anything else looks only for files with that extension
if (!filter && (extension[0] != '/' || extension[1])) {
Q_snprintf(buffer, sizeof(buffer), "*%s", extension);
filter = buffer;
}
list[ nfiles ] = NULL;
*numfiles = nfiles;
if (filter) {
nfiles = 0;
Sys_ListFilteredFiles(directory, "", filter, wantsubs, list, &nfiles);
if (!nfiles)
return NULL;
list[nfiles] = NULL;
*numfiles = nfiles;
if (!nfiles) {
return NULL;
}
listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
for ( i = 0 ; i < nfiles ; i++ ) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
listCopy = Z_Malloc((nfiles + 1) * sizeof(*listCopy));
for (i = 0; i < nfiles; i++) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
return listCopy;
}
return listCopy;
}
if ( directory[0] == '\0' ) {
*numfiles = 0;
return NULL;
}
// only enumerate directories from this point onward
if ( !extension)
extension = "";
// search
nfiles = 0;
if ( extension[0] == '/' && extension[1] == 0 ) {
extension = "";
dironly = qtrue;
}
if ((fdir = opendir(directory)) == NULL) {
*numfiles = 0;
return NULL;
}
extLen = strlen( extension );
while ((d = readdir(fdir)) != NULL) {
Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name);
if (stat(search, &st) == -1) {
continue;
}
// search
nfiles = 0;
// Fixed in OPM:
// don't show current and parent dir entries twice
if (!(Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..") && Q_stricmp(d->d_name, "cvs"))) {
continue;
}
if ((fdir = opendir(directory)) == NULL) {
*numfiles = 0;
return NULL;
}
if ((st.st_mode & S_IFDIR) == 0) {
continue;
}
while ((d = readdir(fdir)) != NULL) {
Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name);
if (stat(search, &st) == -1)
continue;
if ((dironly && !(st.st_mode & S_IFDIR)) ||
(!dironly && (st.st_mode & S_IFDIR)))
continue;
if (nfiles == MAX_FOUND_FILES - 1) {
break;
}
if (*extension) {
if ( strlen( d->d_name ) < extLen ||
Q_stricmp(
d->d_name + strlen( d->d_name ) - extLen,
extension ) ) {
continue; // didn't match
}
}
list[nfiles] = CopyString(d->d_name);
nfiles++;
}
if ( nfiles == MAX_FOUND_FILES - 1 )
break;
list[ nfiles ] = CopyString( d->d_name );
nfiles++;
}
list[nfiles] = NULL;
list[ nfiles ] = NULL;
closedir(fdir);
closedir(fdir);
// return a copy of the list
*numfiles = nfiles;
// return a copy of the list
*numfiles = nfiles;
if (!nfiles) {
return NULL;
}
if ( !nfiles ) {
return NULL;
}
listCopy = Z_Malloc((nfiles + 1) * sizeof(*listCopy));
for (i = 0; i < nfiles; i++) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
for ( i = 0 ; i < nfiles ; i++ ) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
return listCopy;
return listCopy;
}
/*

View file

@ -477,56 +477,78 @@ DIRECTORY SCANNING
Sys_ListFilteredFiles
==============
*/
void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles )
void Sys_ListFilteredFiles(
const char *basedir, const char *subdirs, char *filter, qboolean wantsubs, char **list, int *numfiles
)
{
char search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
char filename[MAX_OSPATH];
intptr_t findhandle;
struct _finddata_t findinfo;
char search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
char filename[MAX_OSPATH];
intptr_t findhandle;
struct _finddata_t findinfo;
if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
return;
}
if (*numfiles >= MAX_FOUND_FILES - 1) {
return;
}
if ( basedir[0] == '\0' ) {
return;
}
if (basedir[0] == '\0') {
return;
}
if (strlen(subdirs)) {
Com_sprintf( search, sizeof(search), "%s\\%s\\*", basedir, subdirs );
}
else {
Com_sprintf( search, sizeof(search), "%s\\*", basedir );
}
if (strlen(subdirs)) {
Com_sprintf(search, sizeof(search), "%s\\%s\\*", basedir, subdirs);
} else {
Com_sprintf(search, sizeof(search), "%s\\*", basedir);
}
findhandle = _findfirst (search, &findinfo);
if (findhandle == -1) {
return;
}
findhandle = _findfirst(search, &findinfo);
if (findhandle == -1) {
return;
}
do {
if (findinfo.attrib & _A_SUBDIR) {
if (Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..")) {
if (strlen(subdirs)) {
Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name);
}
else {
Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name);
}
Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles );
}
}
if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
break;
}
Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name );
if (!Com_FilterPath( filter, filename, qfalse ))
continue;
list[ *numfiles ] = CopyString( filename );
(*numfiles)++;
} while ( _findnext (findhandle, &findinfo) != -1 );
do {
// Fixed in OPM:
// don't show current and parent dir entries twice
if (!(Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..") && Q_stricmp(findinfo.name, "cvs"))) {
continue;
}
_findclose (findhandle);
if ((findinfo.attrib & _A_SUBDIR) != 0 && wantsubs) {
if (strlen(subdirs)) {
Com_sprintf(newsubdirs, sizeof(newsubdirs), "%s\\%s\\*", subdirs, findinfo.name);
} else {
Com_sprintf(newsubdirs, sizeof(newsubdirs), "%s", findinfo.name);
}
// recursively iterate into subdirectory
Sys_ListFilteredFiles(basedir, newsubdirs, filter, wantsubs, list, numfiles);
}
if (*numfiles >= MAX_FOUND_FILES - 1) {
break;
}
if (strlen(subdirs)) {
Com_sprintf(filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name);
} else {
Q_strncpyz(filename, findinfo.name, sizeof(filename));
}
if (!Com_FilterPath(filter, filename, qfalse)) {
continue;
}
list[*numfiles] = CopyString(filename);
// replace backslashes with forward slashes, if any
// FS_ReplaceSeparators would be nice but it does the opposite
for (char *c = strchr(list[*numfiles], '\\'); c; c = strchr(list[*numfiles], '\\')) {
*c = '/';
}
(*numfiles)++;
} while (_findnext(findhandle, &findinfo) != -1);
_findclose(findhandle);
}
/*
@ -536,24 +558,24 @@ strgtr
*/
static qboolean strgtr(const char *s0, const char *s1)
{
int l0, l1, i;
int l0, l1, i;
l0 = strlen(s0);
l1 = strlen(s1);
l0 = strlen(s0);
l1 = strlen(s1);
if (l1<l0) {
l0 = l1;
}
if (l1 < l0) {
l0 = l1;
}
for(i=0;i<l0;i++) {
if (s1[i] > s0[i]) {
return qtrue;
}
if (s1[i] < s0[i]) {
return qfalse;
}
}
return qfalse;
for (i = 0; i < l0; i++) {
if (s1[i] > s0[i]) {
return qtrue;
}
if (s1[i] < s0[i]) {
return qfalse;
}
}
return qfalse;
}
/*
@ -561,116 +583,121 @@ static qboolean strgtr(const char *s0, const char *s1)
Sys_ListFiles
==============
*/
char **Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs )
char **Sys_ListFiles(const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs)
{
char search[MAX_OSPATH];
int nfiles;
char **listCopy;
char *list[MAX_FOUND_FILES];
struct _finddata_t findinfo;
intptr_t findhandle;
int flag;
int i;
int extLen;
char search[MAX_OSPATH];
int nfiles;
char **listCopy;
char *list[MAX_FOUND_FILES] = {NULL};
struct _finddata_t findinfo;
intptr_t findhandle;
qboolean swapped;
int i;
char buffer[64];
if (filter) {
if (directory[0] == NULL) {
*numfiles = 0;
return NULL;
}
nfiles = 0;
Sys_ListFilteredFiles( directory, "", filter, list, &nfiles );
if (!extension) {
extension = "";
}
list[ nfiles ] = 0;
*numfiles = nfiles;
// passing a slash as extension will find directories,
// anything else looks only for files with that extension
if (!filter && (extension[0] != '/' || extension[1])) {
Q_snprintf(buffer, sizeof(buffer), "*%s", extension);
filter = buffer;
}
if (!nfiles)
return NULL;
if (filter) {
nfiles = 0;
Sys_ListFilteredFiles(directory, "", filter, wantsubs, list, &nfiles);
listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
for ( i = 0 ; i < nfiles ; i++ ) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
list[nfiles] = NULL;
*numfiles = nfiles;
if (!nfiles) {
return NULL;
}
return listCopy;
}
listCopy = Z_Malloc((nfiles + 1) * sizeof(*listCopy));
for (i = 0; i < nfiles; i++) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
if ( directory[0] == '\0' ) {
*numfiles = 0;
return NULL;
}
return listCopy;
}
if ( !extension) {
extension = "";
}
// only enumerate directories from this point onward
Com_sprintf(search, 256, "%s\\*", directory);
// passing a slash as extension will find directories
if ( extension[0] == '/' && extension[1] == 0 ) {
extension = "";
flag = 0;
} else {
flag = _A_SUBDIR;
}
// search
nfiles = 0;
findhandle = _findfirst(search, &findinfo);
if (findhandle == -1) {
*numfiles = 0;
return NULL;
}
extLen = strlen( extension );
do {
// Fixed in OPM:
// don't show current and parent dir entries twice
if (!(Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..") && Q_stricmp(findinfo.name, "cvs"))) {
continue;
}
Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension );
if ((findinfo.attrib & _A_SUBDIR) == 0) {
continue;
}
// search
nfiles = 0;
if (nfiles >= MAX_FOUND_FILES - 1) {
break;
}
findhandle = _findfirst (search, &findinfo);
if (findhandle == -1) {
*numfiles = 0;
return NULL;
}
list[nfiles] = CopyString(findinfo.name);
do {
if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) {
if (*extension) {
if ( strlen( findinfo.name ) < extLen ||
Q_stricmp(
findinfo.name + strlen( findinfo.name ) - extLen,
extension ) ) {
continue; // didn't match
}
}
if ( nfiles == MAX_FOUND_FILES - 1 ) {
break;
}
list[ nfiles ] = CopyString( findinfo.name );
nfiles++;
}
} while ( _findnext (findhandle, &findinfo) != -1 );
// replace backslashes with forward slashes
// FS_ReplaceSeparators would be nice but it does the opposite
for (char *c = strchr(list[nfiles], '\\'); c; c = strchr(list[nfiles], '\\')) {
*c = '/';
}
list[ nfiles ] = 0;
++nfiles;
} while (_findnext(findhandle, &findinfo) != -1);
_findclose (findhandle);
list[nfiles] = NULL;
// return a copy of the list
*numfiles = nfiles;
_findclose(findhandle);
if ( !nfiles ) {
return NULL;
}
// return a copy of the list
*numfiles = nfiles;
listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
for ( i = 0 ; i < nfiles ; i++ ) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
if (!nfiles) {
return NULL;
}
do {
flag = 0;
for(i=1; i<nfiles; i++) {
if (strgtr(listCopy[i-1], listCopy[i])) {
char *temp = listCopy[i];
listCopy[i] = listCopy[i-1];
listCopy[i-1] = temp;
flag = 1;
}
}
} while(flag);
listCopy = Z_Malloc((nfiles + 1) * sizeof(*listCopy));
for (i = 0; i < nfiles; i++) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
return listCopy;
// bubble-sort name entries alphabetically, in ascending order
do {
swapped = qfalse;
for (i = 1; i < nfiles; i++) {
if (strgtr(listCopy[i - 1], listCopy[i])) {
char *temp = listCopy[i];
listCopy[i] = listCopy[i - 1];
listCopy[i - 1] = temp;
swapped = qtrue;
}
}
} while (swapped);
return listCopy;
}
/*

View file

@ -124,6 +124,7 @@ typedef struct uiimport_s {
const char *( *Key_KeynumToString )( int keynum );
const char *( *GetConfigstring )( int index );
void ( *UI_CloseDMConsole )( void );
void ( *GetClipboardData )( char *buf, int buflen );
} uiimport_t;
#if 1

View file

@ -128,7 +128,13 @@ void UIMultiLineEdit::getData(str& data)
data = "";
for (i = 0, m_lines.IterateFromHead(); m_lines.IsCurrentValid(); i++, m_lines.IterateNext()) {
data += m_lines.getCurrent();
str text = m_lines.getCurrent();
if (i == 0 && !text.length() && m_lines.getCount() <= 1) {
// file is empty
return;
}
data += text;
if (i != m_lines.getCount() - 1) {
data += "\n";
@ -213,7 +219,11 @@ void UIMultiLineEdit::Draw(void)
// print entire line with the selection highlight box around it
DrawBox(0.0f, aty, linewidth, m_font->getHeight(m_bVirtual), selectionBG, 1.f);
m_font->setColor(selectionColor);
m_font->Print(0, aty, Sys_LV_CL_ConvertString(cur), -1, m_bVirtual);
// Fixed in OPM:
// don't spam LOCALIZATION ERROR messages to console
// for clicking lines in the opened text document
//m_font->Print(0, aty, Sys_LV_CL_ConvertString(cur), -1, m_bVirtual);
m_font->Print(0, aty, cur, -1, m_bVirtual);
} else if (i != topsel->line) {
// part of this line is selected, and selection continues/began above
if (i == botsel->line) { // sanity check, should always be true
@ -261,7 +271,11 @@ void UIMultiLineEdit::Draw(void)
// no selection or highlighted text
caret = m_font->getWidth(cur, topsel->column);
m_font->setColor(m_foreground_color);
m_font->Print(0, aty, Sys_LV_CL_ConvertString(cur), -1, m_bVirtual);
// Fixed in OPM:
// don't spam LOCALIZATION ERROR messages to console
// for clicking lines in the opened text document
//m_font->Print(0, aty, Sys_LV_CL_ConvertString(cur), -1, m_bVirtual);
m_font->Print(0, aty, cur, -1, m_bVirtual);
} else {
// selection starts and ends on this line
int toplen = m_font->getWidth(cur, topsel->column); // X coord of highlighting start
@ -313,6 +327,7 @@ void UIMultiLineEdit::Draw(void)
}
}
// num is the ZERO-BASED index of the sought line!
str& UIMultiLineEdit::LineFromLineNumber(int num, bool resetpos)
{
static str emptyLine;
@ -349,9 +364,7 @@ void UIMultiLineEdit::PointToSelectionPoint(const UIPoint2D& p, selectionpoint_t
float lastWidth = 0;
clickedLine = m_vertscroll->getTopItem() + p.y / m_font->getHeight(m_bVirtual);
if (clickedLine >= m_lines.getCount()) {
clickedLine = m_lines.getCount() - 1;
}
clickedLine = Q_min(clickedLine, m_lines.getCount() - 1);
if (clickedLine < 0) {
sel.line = 0;
@ -360,12 +373,12 @@ void UIMultiLineEdit::PointToSelectionPoint(const UIPoint2D& p, selectionpoint_t
}
const char *line = LineFromLineNumber(clickedLine, true).c_str();
for (i = 0; i < line[i] && totalWidth < p.x; i++) {
for (i = 0; line[i] && totalWidth < p.x; i++) {
lastWidth = m_font->getCharWidth(line[i]);
totalWidth += lastWidth;
}
if (i < line[i] && i) {
if (line[i] && i) {
lastWidth *= 0.5f;
if (totalWidth - lastWidth >= p.x) {
i--;
@ -448,7 +461,10 @@ void UIMultiLineEdit::EnsureSelectionPointVisible(selectionpoint_t& point)
void UIMultiLineEdit::BoundSelectionPoint(selectionpoint_t& point)
{
point.line = Q_clamp_int(point.line, 0, m_lines.getCount());
// since LineFromLineNumber expects a zero-based line index,
// clamp it to one less than the number of lines if the selection point
// is right at the end of the text document
point.line = Q_clamp_int(point.line, 0, m_lines.getCount() - 1);
str& line = LineFromLineNumber(point.line, true);
point.column = Q_clamp_int(point.column, 0, line.length());
@ -618,7 +634,7 @@ void UIMultiLineEdit::DeleteSelection(void)
EnsureSelectionPointVisible(*topsel);
return;
} else if (botsel->line - topsel->line > 1) {
for (i = 0, m_lines.IterateFromHead(); m_lines.IsCurrentValid() && i < topsel->line;
for (i = 0, m_lines.IterateFromHead(); m_lines.IsCurrentValid() && i < botsel->line;
i++, m_lines.IterateNext()) {
if (i > topsel->line) {
m_lines.RemoveCurrentSetPrev();
@ -630,12 +646,19 @@ void UIMultiLineEdit::DeleteSelection(void)
for (i = 0, m_lines.IterateFromHead(); m_lines.IsCurrentValid() && i < topsel->line; i++, m_lines.IterateNext()) {}
// delete topmost line of the selection, but only up to topsel->column
str& topline = m_lines.getCurrent();
topline.CapLength(topsel->column);
if (topline.length() > topsel->column) {
topline.CapLength(topsel->column);
}
m_lines.IterateNext();
str& line = m_lines.getCurrent();
str line = m_lines.getCurrent();
// merge remainder of topmost line with the remainder after the end of selection
if (line.length() > botsel->column) {
topline += &line[botsel->column];
}
topline += &line[botsel->column];
m_lines.RemoveCurrentSetPrev();
*botsel = *topsel;
m_vertscroll->setNumItems(m_lines.getCount());
@ -675,8 +698,11 @@ void UIMultiLineEdit::CharEvent(int ch)
m_changed = true;
str& line = LineFromLineNumber(m_selection.begin.line, false);
str otherline = &line[m_selection.begin.column];
otherline.CapLength(m_selection.begin.column);
str otherline = "";
if (line.length() > m_selection.begin.column) {
otherline = &line[m_selection.begin.column];
line.CapLength(m_selection.begin.column);
}
if (m_lines.IsCurrentValid()) {
m_lines.InsertAfterCurrent(otherline);
@ -714,21 +740,24 @@ void UIMultiLineEdit::CopySelection(void)
line = LineFromLineNumber(topsel->line, true);
clipText += &line[topsel->column];
if (line.length() > topsel->column) {
clipText += &line[topsel->column];
}
if (topsel->line == botsel->line) {
clipText.CapLength(botsel->column - topsel->column);
} else {
for (int i = topsel->line + 1; i < botsel->line; ++i) {
clipText += LineFromLineNumber(i, 1);
clipText += "\n" + LineFromLineNumber(i, 1);
}
line = LineFromLineNumber(botsel->line, true);
line.CapLength(botsel->column);
if (line.length() > botsel->column) {
line.CapLength(botsel->column);
}
clipText += "\n" + line;
}
// FIXME: clipboard not implemented yet
uii.Sys_SetClipboard(clipText);
}
@ -737,12 +766,11 @@ void UIMultiLineEdit::PasteSelection(void)
str sel;
int i;
// temporary variable added in OPM as str cannot handle NULL assignment
// will be removed when Sys_GetClipboard is properly implemented
// variable added in OPM as str cannot handle NULL assignment
// we can get NULL here if clipboard is empty/couldn't be retrieved
const char *clipboardData = uii.Sys_GetClipboard();
if (clipboardData == NULL)
{
// FIXME: clipboard not implemented yet
return;
}
@ -753,8 +781,17 @@ void UIMultiLineEdit::PasteSelection(void)
for (i = 0; i < sel.length(); i++) {
if (sel[i] == '\n') {
CharEvent('\r');
} else if (sel[i] == '\r') {
// Changed in OPM:
// NOP, drop CR characters.
// On Linux/Mac they aren't present anyway,
// on Windows we already have LF chars next to them.
// The filtering for CR on the Windows side was originally done
// in Sys_GetWholeClipboard with a "manual" selective strcpy,
// but here we iterate over all characters of the clipboard anyways,
// so this feels like a better place to do the filtering.
} else {
CharEvent(sel[i]);
CharEvent(sel[i]); // FIXME: this is VERY slow and jams up the EventQueue!
}
}
}

View file

@ -303,14 +303,7 @@ UINotepad::~UINotepad()
for (int j = inner->NumObjects(); j > 0; j--) {
uipopup_describe *uipd = inner->ObjectAt(j);
inner->RemoveObjectAt(j);
// NOTE: `delete uipd` is intentionally missing here!
// Since uipds created for this class have data fields
// that contain pointers only to static Event objects,
// the fields don't need to be cleaned up as they weren't
// dynamically allocated in the first place.
// See UINotepad::Create for reference.
// However, FIXME: the uipds themselves should still
// get cleaned up, but that doesn't happen yet.
delete uipd;
}
}
}
@ -431,13 +424,16 @@ void UINotepad::Save(Event *ev)
{
if (!m_filename.length()) {
SaveAs(NULL);
// Fixed in OPM:
// devs probably forgot to return here - saving was attempted,
// even though there was no filename to save the file with
return;
}
str filecontents;
m_edit->getData(filecontents);
m_edit->setChanged(false);
// FIXME: m_filename could be blank
FS_WriteTextFile(m_filename, filecontents, filecontents.length());
str result = "Saved " + m_filename;

View file

@ -68,12 +68,19 @@ uipopup_describe::uipopup_describe
inline
uipopup_describe::~uipopup_describe()
{
if (this->data)
{
// clean up strdup'd C-string from memory
free(this->data);
this->data = NULL;
}
if (this->data == NULL) {
// nothing to clean up
return;
}
// only clean up these types of uipds, because:
// - UIP_EVENT types point to static Event instances,
// - UIP_EVENT_STRING types point to already freed Event instances
if (this->type == UIP_CMD || this->type == UIP_CVAR) {
// clean up strdup'd C-string from memory
free(this->data);
this->data = NULL;
}
}
class UIPopupMenu : public UIWidget {

View file

@ -344,7 +344,11 @@ void UIVertScroll::MouseEnter
)
{
uWinMan.ActivateControl(this);
// Fixed in OPM
// Why should hovering over the scrollbar activate the control it's on?
// This is more disorienting than useful, also no sane window manager
// works like this. Simply clicking into it should be sufficient.
//uWinMan.ActivateControl(this);
}
void UIVertScroll::MouseLeave