diff --git a/uppsrc/CodeEditor/Highlight.cpp b/uppsrc/CodeEditor/Highlight.cpp index abd051520..4f68ad26a 100644 --- a/uppsrc/CodeEditor/Highlight.cpp +++ b/uppsrc/CodeEditor/Highlight.cpp @@ -453,7 +453,7 @@ void CodeEditor::HighlightLine(int line, Vector& hl, int po const wchar *p = text; const wchar *e = text.End(); int uvsn; - Color c = GetUvsHighlight(text, uvsn); + GetUvsHighlight(text, uvsn); if(uvsn) { hls.SetInk(0, text.GetLength() + 1, Yellow); hls.SetPaper(0, text.GetLength() + 1, Gray); diff --git a/uppsrc/Core/App.cpp b/uppsrc/Core/App.cpp index f7c082ef1..5a547028f 100644 --- a/uppsrc/Core/App.cpp +++ b/uppsrc/Core/App.cpp @@ -402,7 +402,9 @@ void LaunchWebBrowser(const String& url) }; for(int i = 0; i < __countof(browser); i++) if(system("which " + String(browser[i])) == 0) { - system(String(browser[i]) + " " + url + " &"); + IGNORE_RESULT( + system(String(browser[i]) + " " + url + " &") + ); break; } #endif diff --git a/uppsrc/Core/BlockStream.cpp b/uppsrc/Core/BlockStream.cpp index aef2417b9..a498abd44 100644 --- a/uppsrc/Core/BlockStream.cpp +++ b/uppsrc/Core/BlockStream.cpp @@ -7,7 +7,7 @@ NAMESPACE_UPP #define LLOGHEXDUMP(x, s) // LOGHEXDUMP(x, s) void BlockStream::SetBufferSize(dword size) { - int64 p; + int64 p = 0; if(IsOpen()) { p = GetPos(); Flush(); @@ -409,7 +409,7 @@ void FileStream::SetStreamSize(int64 pos) while(pos > len) { static char buffer[1024]; int64 diff = pos - len; - int chunk = (diff > sizeof(buffer) ? sizeof(buffer) : (int)diff); + int chunk = (diff > (int64)sizeof(buffer) ? sizeof(buffer) : (int)diff); if(write(handle, buffer, chunk) != chunk) { SetLastError(); LSEEK64_(handle, cur, SEEK_SET); diff --git a/uppsrc/Core/Defs.h b/uppsrc/Core/Defs.h index 2107aec2a..9d32fe4cd 100644 --- a/uppsrc/Core/Defs.h +++ b/uppsrc/Core/Defs.h @@ -600,3 +600,6 @@ void __LOGF__(const char *format, ...); #else inline void __LOGF__(const char *format, ...); #endif + +template +void IGNORE_RESULT(const T&) {} diff --git a/uppsrc/Core/LocalProcess.cpp b/uppsrc/Core/LocalProcess.cpp index 7e7b413c7..bf632ef0e 100644 --- a/uppsrc/Core/LocalProcess.cpp +++ b/uppsrc/Core/LocalProcess.cpp @@ -1,418 +1,420 @@ -#include "Core.h" - -NAMESPACE_UPP - -#ifdef PLATFORM_POSIX -//#BLITZ_APPROVE -#include -#include -#include -#endif - -#define LLOG(x) // LOG(x) - -void LocalProcess::Init() { -#ifdef PLATFORM_WIN32 - hProcess = hOutputRead = hInputWrite = NULL; -#endif -#ifdef PLATFORM_POSIX - pid = 0; - rpipe[0] = rpipe[1] = wpipe[0] = wpipe[1] = -1; - output_read = false; -#endif - exit_code = Null; - convertcharset = true; -} - -void LocalProcess::Free() { -#ifdef PLATFORM_WIN32 - if(hProcess) { - CloseHandle(hProcess); - hProcess = NULL; - } - if(hOutputRead) { - CloseHandle(hOutputRead); - hOutputRead = NULL; - } - if(hInputWrite) { - CloseHandle(hInputWrite); - hInputWrite = NULL; - } -#endif -#ifdef PLATFORM_POSIX - LLOG("\nLocalProcess::Free, pid = " << (int)getpid()); - LLOG("rpipe[" << rpipe[0] << ", " << rpipe[1] << "]"); - LLOG("wpipe[" << wpipe[0] << ", " << wpipe[1] << "]"); - if(rpipe[0] >= 0) { close(rpipe[0]); rpipe[0] = -1; } - if(rpipe[1] >= 0) { close(rpipe[1]); rpipe[1] = -1; } - if(wpipe[0] >= 0) { close(wpipe[0]); wpipe[0] = -1; } - if(wpipe[1] >= 0) { close(wpipe[1]); wpipe[1] = -1; } - if(pid) waitpid(pid, 0, WNOHANG | WUNTRACED); - pid = 0; - output_read = false; -#endif - exit_code = Null; -} - -bool LocalProcess::Start(const char *command, const char *envptr) -{ - LLOG("LocalProcess::Start(\"" << command << "\")"); - - Kill(); - - while(*command && (byte)*command <= ' ') - command++; - -#ifdef PLATFORM_WIN32 - HANDLE hOutputReadTmp, hInputRead; - HANDLE hInputWriteTmp, hOutputWrite; - HANDLE hErrorWrite; - SECURITY_ATTRIBUTES sa; - - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - - HANDLE hp = GetCurrentProcess(); - - CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0); - DuplicateHandle(hp, hOutputWrite, hp, &hErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS); - CreatePipe(&hInputRead, &hInputWriteTmp, &sa, 0); - DuplicateHandle(hp, hOutputReadTmp, hp, &hOutputRead, 0, FALSE, DUPLICATE_SAME_ACCESS); - DuplicateHandle(hp, hInputWriteTmp, hp, &hInputWrite, 0, FALSE, DUPLICATE_SAME_ACCESS); - CloseHandle(hOutputReadTmp); - CloseHandle(hInputWriteTmp); - - PROCESS_INFORMATION pi; - STARTUPINFO si; - ZeroMemory(&si, sizeof(STARTUPINFO)); - si.cb = sizeof(STARTUPINFO); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - si.hStdInput = hInputRead; - si.hStdOutput = hOutputWrite; - si.hStdError = hErrorWrite; - int n = (int)strlen(command) + 1; - Buffer cmd(n); - memcpy(cmd, command, n); - bool h = CreateProcess(NULL, cmd, &sa, &sa, TRUE, - NORMAL_PRIORITY_CLASS, (void *)envptr, NULL, &si, &pi); - LLOG("CreateProcess " << (h ? "succeeded" : "failed")); - CloseHandle(hErrorWrite); - CloseHandle(hInputRead); - CloseHandle(hOutputWrite); - if(h) { - hProcess = pi.hProcess; - CloseHandle(pi.hThread); - } - else { - Free(); - return false; -// throw Exc(NFormat("Error running process: %s\nCommand: %s", GetErrorMessage(GetLastError()), command)); - } - return true; -#endif -#ifdef PLATFORM_POSIX - // parse command line for execve - cmd_buf.Alloc(strlen(command) + 1); - char *cmd_out = cmd_buf; - const char *p = command; - const char *b = p; - while(*p && (byte)*p > ' ') - if(*p++ == '\"') - while(*p && *p++ != '\"') - ; - const char *app = cmd_out; - args.Add(cmd_out); - memcpy(cmd_out, b, p - b); - cmd_out += p - b; - *cmd_out++ = '\0'; - - while(*p) - if((byte)*p <= ' ') - p++; - else { - args.Add(cmd_out); - while(*p && (byte)*p > ' ') - if(*p == '\\') { - if(*++p) - *cmd_out++ = *p++; - } - else if(*p == '\"') { - p++; - while(*p && *p != '\"') - if(*p == '\\') { - if(*++p) - *cmd_out++ = *p++; - } - else - *cmd_out++ = *p++; - if(*p == '\"') - p++; - } - else - *cmd_out++ = *p++; - *cmd_out++ = '\0'; - } - - args.Add(NULL); - - String app_full = GetFileOnPath(app, getenv("PATH"), true); - if(IsNull(app_full)) - return false; -// throw Exc(Format("Cannot find executable '%s'\n", app)); - - if(pipe(rpipe) || pipe(wpipe)) - return false; -// throw Exc(NFormat(t_("pipe() error; error code = %d"), errno)); - - LLOG("\nLocalProcess::Start"); - LLOG("rpipe[" << rpipe[0] << ", " << rpipe[1] << "]"); - - LLOG("wpipe[" << wpipe[0] << ", " << wpipe[1] << "]"); -#ifdef CPU_BLACKFIN - pid = vfork(); //we *can* use vfork here, since exec is done later or the parent will exit -#else - pid = fork(); -#endif - LLOG("\tfork, pid = " << (int)pid << ", getpid = " << (int)getpid()); - if(pid < 0) - return false; -// throw Exc(NFormat(t_("fork() error; error code = %d"), errno)); - - if(pid) { - LLOG("parent process - continue"); - return true; - } - LLOG("child process - execute application"); -// rpipe[1] = wpipe[0] = -1; - dup2(rpipe[0], 0); - dup2(wpipe[1], 1); - dup2(wpipe[1], 2); - -#if DO_LLOG - LLOG(args.GetCount() << "arguments:"); - for(int a = 0; a < args.GetCount(); a++) - LLOG("[" << a << "]: <" << (args[a] ? args[a] : "NULL") << ">"); -#endif//DO_LLOG - - LLOG("running execve, app = " << app << ", #args = " << args.GetCount()); - if(envptr) { - const char *from = envptr; - Vector env; - while(*from) { - env.Add(from); - from += strlen(from) + 1; - } - env.Add(NULL); - execve(app_full, args.Begin(), (char *const *)env.Begin()); - } - else - execv(app_full, args.Begin()); - LLOG("execve failed, errno = " << errno); -// printf("Error running '%s', error code %d\n", command, errno); - exit(-errno); - return true; -#endif -} - -#ifdef PLATFORM_POSIX -bool LocalProcess::DecodeExitCode(int status) -{ - if(WIFEXITED(status)) { - exit_code = (byte)WEXITSTATUS(status); - return true; - } - else if(WIFSIGNALED(status) || WIFSTOPPED(status)) { - static const struct { - const char *name; - int code; - } - signal_map[] = { -#define SIGDEF(s) { #s, s }, -SIGDEF(SIGHUP) SIGDEF(SIGINT) SIGDEF(SIGQUIT) SIGDEF(SIGILL) SIGDEF(SIGABRT) -SIGDEF(SIGFPE) SIGDEF(SIGKILL) SIGDEF(SIGSEGV) SIGDEF(SIGPIPE) SIGDEF(SIGALRM) -SIGDEF(SIGPIPE) SIGDEF(SIGTERM) SIGDEF(SIGUSR1) SIGDEF(SIGUSR2) SIGDEF(SIGTRAP) -SIGDEF(SIGURG) SIGDEF(SIGVTALRM) SIGDEF(SIGXCPU) SIGDEF(SIGXFSZ) SIGDEF(SIGIOT) -SIGDEF(SIGIO) SIGDEF(SIGWINCH) -#ifndef PLATFORM_BSD -//SIGDEF(SIGCLD) SIGDEF(SIGPWR) -#endif -//SIGDEF(SIGSTKFLT) SIGDEF(SIGUNUSED) // not in Solaris, make conditional if needed -#undef SIGDEF - }; - - int sig = (WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status)); - exit_code = (WIFSIGNALED(status) ? 1000 : 2000) + sig; - exit_string << "\nProcess " << (WIFSIGNALED(status) ? "terminated" : "stopped") << " on signal " << sig; - for(int i = 0; i < __countof(signal_map); i++) - if(signal_map[i].code == sig) - { - exit_string << " (" << signal_map[i].name << ")"; - break; - } - exit_string << "\n"; - return true; - } - return false; -} -#endif//PLATFORM_POSIX - -void LocalProcess::Kill() { -#ifdef PLATFORM_WIN32 - if(hProcess && IsRunning()) { - TerminateProcess(hProcess, (DWORD)-1); - exit_code = 255; - } -#endif -#ifdef PLATFORM_POSIX - if(IsRunning()) { - LLOG("\nLocalProcess::Kill, pid = " << (int)pid); - exit_code = 255; - kill(pid, SIGTERM); - GetExitCode(); - int status; - if(pid && waitpid(pid, &status, 0) == pid) - DecodeExitCode(status); - exit_string = "Child process has been killed.\n"; - } -#endif - Free(); -} - -void LocalProcess::Detach() -{ - Free(); -} - -bool LocalProcess::IsRunning() { -#ifdef PLATFORM_WIN32 - dword exitcode; - if(!hProcess) - return false; - if(GetExitCodeProcess(hProcess, &exitcode) && exitcode == STILL_ACTIVE) - return true; - dword n; - if(PeekNamedPipe(hOutputRead, NULL, 0, NULL, &n, NULL) && n) - return true; - exit_code = exitcode; - return false; -#endif -#ifdef PLATFORM_POSIX - if(!pid || !IsNull(exit_code)) { - LLOG("IsRunning() -> no"); - return false; - } - int status = 0, wp; - if((wp = waitpid(pid, &status, WNOHANG | WUNTRACED)) != pid || !DecodeExitCode(status)) - return true; - LLOG("IsRunning() -> no, just exited, exit code = " << exit_code); - return false; -#endif -} - -int LocalProcess::GetExitCode() { -#ifdef PLATFORM_WIN32 - return IsRunning() ? (int)Null : exit_code; -#endif -#ifdef PLATFORM_POSIX - if(!IsRunning()) - return Nvl(exit_code, -1); - int status; - if(waitpid(pid, &status, WNOHANG | WUNTRACED) != pid || !DecodeExitCode(status)) { - LLOG("GetExitCode() -> -1 (waitpid would hang)"); - return -1; - } - exit_code = WEXITSTATUS(status); - LLOG("GetExitCode() -> " << exit_code << " (just exited)"); - return exit_code; -#endif -} - -bool LocalProcess::Read(String& res) { - LLOG("LocalProcess::Read"); - res = Null; -#ifdef PLATFORM_WIN32 - if(!hOutputRead) return false; - dword n; - if(!PeekNamedPipe(hOutputRead, NULL, 0, NULL, &n, NULL) || n == 0) - return IsRunning(); - char buffer[1024]; - if(!ReadFile(hOutputRead, buffer, sizeof(buffer), &n, NULL)) - return false; - res = String(buffer, n); - if(convertcharset) - res = FromSystemCharset(res); - return true; -#endif -#ifdef PLATFORM_POSIX -//??! - if(wpipe[0] < 0) return false; - bool was_running = IsRunning(); - LLOG("output_read = " << (output_read ? "yes" : "no")); - if(!was_running && output_read) { - if(exit_string.IsEmpty()) - return false; - res = exit_string; - exit_string = Null; - return true; - } - fd_set set[1]; - FD_ZERO(set); - FD_SET(wpipe[0], set); - timeval tval = { 0, 0 }; - int sv; - while((sv = select(wpipe[0] + 1, set, NULL, NULL, &tval)) > 0) { - LLOG("Read() -> select"); - char buffer[1024]; - int done = read(wpipe[0], buffer, sizeof(buffer)); - LLOG("Read(), read -> " << done << ": " << String(buffer, done)); - if(done > 0) - res.Cat(buffer, done); - } - if(sv < 0) { - LLOG("select -> " << sv); - } - if(!was_running) - output_read = true; - if(convertcharset) - res = FromSystemCharset(res); - return !IsNull(res) || was_running; -#endif -} - -void LocalProcess::Write(String s) -{ - if(convertcharset) - s = ToSystemCharset(s); -#ifdef PLATFORM_WIN32 - dword n; - WriteFile(hInputWrite, s, s.GetLength(), &n, NULL); -#endif -#ifdef PLATFORM_POSIX - write(rpipe[1], s, s.GetLength()); -#endif -} - -int Sys(const char *cmd, String& out, bool convertcharset) -{ - out.Clear(); - LocalProcess p; - p.ConvertCharset(convertcharset); - if(!p.Start(cmd)) - return -1; - while(p.IsRunning()) { - out.Cat(p.Get()); - Sleep(1); // p.Wait would be much better here! - } - out.Cat(p.Get()); - return p.GetExitCode(); -} - -String Sys(const char *cmd, bool convertcharset) -{ - String r; - return Sys(cmd, r, convertcharset) ? String::GetVoid() : r; -} - -END_UPP_NAMESPACE +#include "Core.h" + +NAMESPACE_UPP + +#ifdef PLATFORM_POSIX +//#BLITZ_APPROVE +#include +#include +#include +#endif + +#define LLOG(x) // LOG(x) + +void LocalProcess::Init() { +#ifdef PLATFORM_WIN32 + hProcess = hOutputRead = hInputWrite = NULL; +#endif +#ifdef PLATFORM_POSIX + pid = 0; + rpipe[0] = rpipe[1] = wpipe[0] = wpipe[1] = -1; + output_read = false; +#endif + exit_code = Null; + convertcharset = true; +} + +void LocalProcess::Free() { +#ifdef PLATFORM_WIN32 + if(hProcess) { + CloseHandle(hProcess); + hProcess = NULL; + } + if(hOutputRead) { + CloseHandle(hOutputRead); + hOutputRead = NULL; + } + if(hInputWrite) { + CloseHandle(hInputWrite); + hInputWrite = NULL; + } +#endif +#ifdef PLATFORM_POSIX + LLOG("\nLocalProcess::Free, pid = " << (int)getpid()); + LLOG("rpipe[" << rpipe[0] << ", " << rpipe[1] << "]"); + LLOG("wpipe[" << wpipe[0] << ", " << wpipe[1] << "]"); + if(rpipe[0] >= 0) { close(rpipe[0]); rpipe[0] = -1; } + if(rpipe[1] >= 0) { close(rpipe[1]); rpipe[1] = -1; } + if(wpipe[0] >= 0) { close(wpipe[0]); wpipe[0] = -1; } + if(wpipe[1] >= 0) { close(wpipe[1]); wpipe[1] = -1; } + if(pid) waitpid(pid, 0, WNOHANG | WUNTRACED); + pid = 0; + output_read = false; +#endif + exit_code = Null; +} + +bool LocalProcess::Start(const char *command, const char *envptr) +{ + LLOG("LocalProcess::Start(\"" << command << "\")"); + + Kill(); + + while(*command && (byte)*command <= ' ') + command++; + +#ifdef PLATFORM_WIN32 + HANDLE hOutputReadTmp, hInputRead; + HANDLE hInputWriteTmp, hOutputWrite; + HANDLE hErrorWrite; + SECURITY_ATTRIBUTES sa; + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + HANDLE hp = GetCurrentProcess(); + + CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0); + DuplicateHandle(hp, hOutputWrite, hp, &hErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS); + CreatePipe(&hInputRead, &hInputWriteTmp, &sa, 0); + DuplicateHandle(hp, hOutputReadTmp, hp, &hOutputRead, 0, FALSE, DUPLICATE_SAME_ACCESS); + DuplicateHandle(hp, hInputWriteTmp, hp, &hInputWrite, 0, FALSE, DUPLICATE_SAME_ACCESS); + CloseHandle(hOutputReadTmp); + CloseHandle(hInputWriteTmp); + + PROCESS_INFORMATION pi; + STARTUPINFO si; + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.hStdInput = hInputRead; + si.hStdOutput = hOutputWrite; + si.hStdError = hErrorWrite; + int n = (int)strlen(command) + 1; + Buffer cmd(n); + memcpy(cmd, command, n); + bool h = CreateProcess(NULL, cmd, &sa, &sa, TRUE, + NORMAL_PRIORITY_CLASS, (void *)envptr, NULL, &si, &pi); + LLOG("CreateProcess " << (h ? "succeeded" : "failed")); + CloseHandle(hErrorWrite); + CloseHandle(hInputRead); + CloseHandle(hOutputWrite); + if(h) { + hProcess = pi.hProcess; + CloseHandle(pi.hThread); + } + else { + Free(); + return false; +// throw Exc(NFormat("Error running process: %s\nCommand: %s", GetErrorMessage(GetLastError()), command)); + } + return true; +#endif +#ifdef PLATFORM_POSIX + // parse command line for execve + cmd_buf.Alloc(strlen(command) + 1); + char *cmd_out = cmd_buf; + const char *p = command; + const char *b = p; + while(*p && (byte)*p > ' ') + if(*p++ == '\"') + while(*p && *p++ != '\"') + ; + const char *app = cmd_out; + args.Add(cmd_out); + memcpy(cmd_out, b, p - b); + cmd_out += p - b; + *cmd_out++ = '\0'; + + while(*p) + if((byte)*p <= ' ') + p++; + else { + args.Add(cmd_out); + while(*p && (byte)*p > ' ') + if(*p == '\\') { + if(*++p) + *cmd_out++ = *p++; + } + else if(*p == '\"') { + p++; + while(*p && *p != '\"') + if(*p == '\\') { + if(*++p) + *cmd_out++ = *p++; + } + else + *cmd_out++ = *p++; + if(*p == '\"') + p++; + } + else + *cmd_out++ = *p++; + *cmd_out++ = '\0'; + } + + args.Add(NULL); + + String app_full = GetFileOnPath(app, getenv("PATH"), true); + if(IsNull(app_full)) + return false; +// throw Exc(Format("Cannot find executable '%s'\n", app)); + + if(pipe(rpipe) || pipe(wpipe)) + return false; +// throw Exc(NFormat(t_("pipe() error; error code = %d"), errno)); + + LLOG("\nLocalProcess::Start"); + LLOG("rpipe[" << rpipe[0] << ", " << rpipe[1] << "]"); + + LLOG("wpipe[" << wpipe[0] << ", " << wpipe[1] << "]"); +#ifdef CPU_BLACKFIN + pid = vfork(); //we *can* use vfork here, since exec is done later or the parent will exit +#else + pid = fork(); +#endif + LLOG("\tfork, pid = " << (int)pid << ", getpid = " << (int)getpid()); + if(pid < 0) + return false; +// throw Exc(NFormat(t_("fork() error; error code = %d"), errno)); + + if(pid) { + LLOG("parent process - continue"); + return true; + } + LLOG("child process - execute application"); +// rpipe[1] = wpipe[0] = -1; + dup2(rpipe[0], 0); + dup2(wpipe[1], 1); + dup2(wpipe[1], 2); + +#if DO_LLOG + LLOG(args.GetCount() << "arguments:"); + for(int a = 0; a < args.GetCount(); a++) + LLOG("[" << a << "]: <" << (args[a] ? args[a] : "NULL") << ">"); +#endif//DO_LLOG + + LLOG("running execve, app = " << app << ", #args = " << args.GetCount()); + if(envptr) { + const char *from = envptr; + Vector env; + while(*from) { + env.Add(from); + from += strlen(from) + 1; + } + env.Add(NULL); + execve(app_full, args.Begin(), (char *const *)env.Begin()); + } + else + execv(app_full, args.Begin()); + LLOG("execve failed, errno = " << errno); +// printf("Error running '%s', error code %d\n", command, errno); + exit(-errno); + return true; +#endif +} + +#ifdef PLATFORM_POSIX +bool LocalProcess::DecodeExitCode(int status) +{ + if(WIFEXITED(status)) { + exit_code = (byte)WEXITSTATUS(status); + return true; + } + else if(WIFSIGNALED(status) || WIFSTOPPED(status)) { + static const struct { + const char *name; + int code; + } + signal_map[] = { +#define SIGDEF(s) { #s, s }, +SIGDEF(SIGHUP) SIGDEF(SIGINT) SIGDEF(SIGQUIT) SIGDEF(SIGILL) SIGDEF(SIGABRT) +SIGDEF(SIGFPE) SIGDEF(SIGKILL) SIGDEF(SIGSEGV) SIGDEF(SIGPIPE) SIGDEF(SIGALRM) +SIGDEF(SIGPIPE) SIGDEF(SIGTERM) SIGDEF(SIGUSR1) SIGDEF(SIGUSR2) SIGDEF(SIGTRAP) +SIGDEF(SIGURG) SIGDEF(SIGVTALRM) SIGDEF(SIGXCPU) SIGDEF(SIGXFSZ) SIGDEF(SIGIOT) +SIGDEF(SIGIO) SIGDEF(SIGWINCH) +#ifndef PLATFORM_BSD +//SIGDEF(SIGCLD) SIGDEF(SIGPWR) +#endif +//SIGDEF(SIGSTKFLT) SIGDEF(SIGUNUSED) // not in Solaris, make conditional if needed +#undef SIGDEF + }; + + int sig = (WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status)); + exit_code = (WIFSIGNALED(status) ? 1000 : 2000) + sig; + exit_string << "\nProcess " << (WIFSIGNALED(status) ? "terminated" : "stopped") << " on signal " << sig; + for(int i = 0; i < __countof(signal_map); i++) + if(signal_map[i].code == sig) + { + exit_string << " (" << signal_map[i].name << ")"; + break; + } + exit_string << "\n"; + return true; + } + return false; +} +#endif//PLATFORM_POSIX + +void LocalProcess::Kill() { +#ifdef PLATFORM_WIN32 + if(hProcess && IsRunning()) { + TerminateProcess(hProcess, (DWORD)-1); + exit_code = 255; + } +#endif +#ifdef PLATFORM_POSIX + if(IsRunning()) { + LLOG("\nLocalProcess::Kill, pid = " << (int)pid); + exit_code = 255; + kill(pid, SIGTERM); + GetExitCode(); + int status; + if(pid && waitpid(pid, &status, 0) == pid) + DecodeExitCode(status); + exit_string = "Child process has been killed.\n"; + } +#endif + Free(); +} + +void LocalProcess::Detach() +{ + Free(); +} + +bool LocalProcess::IsRunning() { +#ifdef PLATFORM_WIN32 + dword exitcode; + if(!hProcess) + return false; + if(GetExitCodeProcess(hProcess, &exitcode) && exitcode == STILL_ACTIVE) + return true; + dword n; + if(PeekNamedPipe(hOutputRead, NULL, 0, NULL, &n, NULL) && n) + return true; + exit_code = exitcode; + return false; +#endif +#ifdef PLATFORM_POSIX + if(!pid || !IsNull(exit_code)) { + LLOG("IsRunning() -> no"); + return false; + } + int status = 0, wp; + if((wp = waitpid(pid, &status, WNOHANG | WUNTRACED)) != pid || !DecodeExitCode(status)) + return true; + LLOG("IsRunning() -> no, just exited, exit code = " << exit_code); + return false; +#endif +} + +int LocalProcess::GetExitCode() { +#ifdef PLATFORM_WIN32 + return IsRunning() ? (int)Null : exit_code; +#endif +#ifdef PLATFORM_POSIX + if(!IsRunning()) + return Nvl(exit_code, -1); + int status; + if(waitpid(pid, &status, WNOHANG | WUNTRACED) != pid || !DecodeExitCode(status)) { + LLOG("GetExitCode() -> -1 (waitpid would hang)"); + return -1; + } + exit_code = WEXITSTATUS(status); + LLOG("GetExitCode() -> " << exit_code << " (just exited)"); + return exit_code; +#endif +} + +bool LocalProcess::Read(String& res) { + LLOG("LocalProcess::Read"); + res = Null; +#ifdef PLATFORM_WIN32 + if(!hOutputRead) return false; + dword n; + if(!PeekNamedPipe(hOutputRead, NULL, 0, NULL, &n, NULL) || n == 0) + return IsRunning(); + char buffer[1024]; + if(!ReadFile(hOutputRead, buffer, sizeof(buffer), &n, NULL)) + return false; + res = String(buffer, n); + if(convertcharset) + res = FromSystemCharset(res); + return true; +#endif +#ifdef PLATFORM_POSIX +//??! + if(wpipe[0] < 0) return false; + bool was_running = IsRunning(); + LLOG("output_read = " << (output_read ? "yes" : "no")); + if(!was_running && output_read) { + if(exit_string.IsEmpty()) + return false; + res = exit_string; + exit_string = Null; + return true; + } + fd_set set[1]; + FD_ZERO(set); + FD_SET(wpipe[0], set); + timeval tval = { 0, 0 }; + int sv; + while((sv = select(wpipe[0] + 1, set, NULL, NULL, &tval)) > 0) { + LLOG("Read() -> select"); + char buffer[1024]; + int done = read(wpipe[0], buffer, sizeof(buffer)); + LLOG("Read(), read -> " << done << ": " << String(buffer, done)); + if(done > 0) + res.Cat(buffer, done); + } + if(sv < 0) { + LLOG("select -> " << sv); + } + if(!was_running) + output_read = true; + if(convertcharset) + res = FromSystemCharset(res); + return !IsNull(res) || was_running; +#endif +} + +void LocalProcess::Write(String s) +{ + if(convertcharset) + s = ToSystemCharset(s); +#ifdef PLATFORM_WIN32 + dword n; + WriteFile(hInputWrite, s, s.GetLength(), &n, NULL); +#endif +#ifdef PLATFORM_POSIX + IGNORE_RESULT( + write(rpipe[1], s, s.GetLength()) + ); +#endif +} + +int Sys(const char *cmd, String& out, bool convertcharset) +{ + out.Clear(); + LocalProcess p; + p.ConvertCharset(convertcharset); + if(!p.Start(cmd)) + return -1; + while(p.IsRunning()) { + out.Cat(p.Get()); + Sleep(1); // p.Wait would be much better here! + } + out.Cat(p.Get()); + return p.GetExitCode(); +} + +String Sys(const char *cmd, bool convertcharset) +{ + String r; + return Sys(cmd, r, convertcharset) ? String::GetVoid() : r; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/Log.cpp b/uppsrc/Core/Log.cpp index 5a6c5a053..b4b55f8f7 100644 --- a/uppsrc/Core/Log.cpp +++ b/uppsrc/Core/Log.cpp @@ -155,12 +155,18 @@ void LogStream::Create(const char *path, bool append) } WriteFile(hfile, "\r\n", 2, &n, NULL); #else - write(hfile, h, strlen(h)); + IGNORE_RESULT( + write(hfile, h, strlen(h)) + ); if(part) { sprintf(h, ", #%d", part); - write(hfile, h, strlen(h)); + IGNORE_RESULT( + write(hfile, h, strlen(h)) + ); } - write(hfile, "\r\n", 2); + IGNORE_RESULT( + write(hfile, "\r\n", 2) + ); #endif bol = true; } @@ -186,7 +192,9 @@ void LogStream::Flush() #else if(options & LOG_FILE) if(hfile >= 0) - write(hfile, buffer, count); + IGNORE_RESULT( + write(hfile, buffer, count) + ); if(options & LOG_DBG) Cerr().Put(buffer, count); #endif @@ -252,6 +260,7 @@ bool LogStream::IsOpen() const #endif } +/* static void sLarge(String& text, size_t *large, int count, const char *txt) { int n = min(1024, count); @@ -268,6 +277,7 @@ static void sLarge(String& text, size_t *large, int count, const char *txt) text << Format("%4d`KB, %5d %s (%6d KB)\r\n", (int)(uintptr_t)(q >> 10), nn, txt, (int)(uintptr_t)((nn * q) >> 10)); } } +*/ String AsString(const MemoryProfile& mem) { diff --git a/uppsrc/Core/Path.cpp b/uppsrc/Core/Path.cpp index b128f04d1..dff1a73d5 100644 --- a/uppsrc/Core/Path.cpp +++ b/uppsrc/Core/Path.cpp @@ -202,8 +202,7 @@ String GetCurrentDirectory() { } #elif defined(PLATFORM_POSIX) char h[1024]; - getcwd(h, 1024); - return FromSystemCharset(h); + return getcwd(h, 1024) ? FromSystemCharset(h) : String(); #else #error GetCurrentDirectory not implemented for this platform, comment this line to get Null return Null; @@ -212,9 +211,9 @@ String GetCurrentDirectory() { #endif #ifdef PLATFORM_POSIX -void SetCurrentDirectory(const char *path) +bool SetCurrentDirectory(const char *path) { - chdir(path); + return chdir(path) == 0; } #endif @@ -724,24 +723,17 @@ String NormalizePath(const char *path, const char *currdir) { out = (sDirSep(currdir[0]) && sDirSep(currdir[1]) ? "//" : "/"); p = Split(currdir, CharFilterTextTest(sDirSep)); } - bool quote = false; for(; i < si.GetCount(); i++) { String s = si[i]; if(s != "." && !s.IsEmpty()) if(s[0] == '.' && s[1] == '.') { if(!p.IsEmpty()) p.Drop(); } - else { + else p.Add(s); - if(s.Find(' ') >= 0) - quote = true; - } } out.Cat(Join(p, DIR_SEPS)); -// if(quote) -// return '\"' + out + '\"'; -// else - return out; + return out; } #endif//PLATFORM_POSIX @@ -1052,7 +1044,6 @@ String GetSymLinkPath(const char *linkpath) return path; #else char buff[_MAX_PATH + 1]; - bool ret; int len = readlink(linkpath, buff, _MAX_PATH); if(len > 0 && len < _MAX_PATH) return String(buff, len); diff --git a/uppsrc/Core/Random.cpp b/uppsrc/Core/Random.cpp index 48125a5e9..8f1d46274 100644 --- a/uppsrc/Core/Random.cpp +++ b/uppsrc/Core/Random.cpp @@ -1,246 +1,250 @@ -#include "Core.h" - -NAMESPACE_UPP - -/* - A C-program for MT19937, with initialization improved 2002/1/26. - Coded by Takuji Nishimura and Makoto Matsumoto. - - Before using, initialize the state by using init_genrand(seed) - or init_by_array(init_key, key_length). - - Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - Any feedback is very welcome. - http://www.math.keio.ac.jp/matumoto/emt.html - email: matumoto@math.keio.ac.jp -*/ - -#include - -/* Period parameters */ -#define N 624 -#define M 397 -#define MATRIX_A 0x9908b0dfUL /* constant vector a */ -#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ -#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ - -struct MTrand { - dword mt[N]; - int mti; /* mti==N+1 means mt[N] is not initialized */ - dword mag01[2]; - - void init_genrand(dword s); - void init_by_array(dword *init_key, int key_length); - dword genrand(); - - MTrand(); -}; - -/* initializes mt[N] with a seed */ -void MTrand::init_genrand(dword s) -{ - mt[0]= s & 0xffffffffUL; - for (mti=1; mti> 30)) + mti); - /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ - /* In the previous versions, MSBs of the seed affect */ - /* only MSBs of the array mt[]. */ - /* 2002/01/09 modified by Makoto Matsumoto */ - mt[mti] &= 0xffffffffUL; - /* for >32 bit machines */ - } -} - -/* initialize by an array with array-length */ -/* init_key is the array for initializing keys */ -/* key_length is its length */ -/* slight change for C++, 2004/2/26 */ -void MTrand::init_by_array(dword *init_key, int key_length) -{ - int i, j, k; - init_genrand(19650218UL); - i=1; j=0; - k = (N>key_length ? N : key_length); - for (; k; k--) { - mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) - + init_key[j] + j; /* non linear */ - mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ - i++; j++; - if (i>=N) { mt[0] = mt[N-1]; i=1; } - if (j>=key_length) j=0; - } - for (k=N-1; k; k--) { - mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) - - i; /* non linear */ - mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ - i++; - if (i>=N) { mt[0] = mt[N-1]; i=1; } - } - - mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ -} - -/* generates a random number on [0,0xffffffff]-interval */ -dword MTrand::genrand() -{ - dword y; - - if (mti >= N) { /* generate N words at one time */ - int kk; - - for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; - } - for (;kk> 1) ^ mag01[y & 0x1UL]; - } - y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); - mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; - - mti = 0; - } - - y = mt[mti++]; - - /* Tempering */ - y ^= (y >> 11); - y ^= (y << 7) & 0x9d2c5680UL; - y ^= (y << 15) & 0xefc60000UL; - y ^= (y >> 18); - - return y; -} - -#ifdef PLATFORM_WIN32 - -#define Ptr Ptr_ -#include -#undef Ptr - -#pragma comment(lib, "ole32.lib") - -#endif - -MTrand::MTrand() -{ - mti = N + 1; - mag01[0] = 0; - mag01[1] = MATRIX_A; - dword seed[1024]; -#ifdef PLATFORM_POSIX - int fd = open("/dev/urandom", O_RDONLY); - read(fd, seed, sizeof(seed)); - close(fd); -#else - for(int i = 0; i < 1024; i++) { - Uuid uuid; - CoCreateGuid((GUID *)&uuid); - seed[i] = GetHashValue(uuid); - } -#endif - init_by_array(seed, 1024); -} - -#ifdef CPU_BLACKFIN -//on blackfin toolchain we dont have TLS support, but we can live here without -MTrand *sRng; -byte sRb[sizeof(MTrand)]; -#else -thread__ MTrand *sRng; -thread__ byte sRb[sizeof(MTrand)]; -#endif - -void SeedRandom(dword *seed, int len){ - if(!sRng) { - sRng = new(sRb) MTrand; - } - sRng->init_by_array(seed, len); -} - -void SeedRandom(dword seed){ - if(!sRng) { - sRng = new(sRb) MTrand; - } - sRng->init_genrand(seed); -} - -dword Random() -{ - if(!sRng) { - sRng = new(sRb) MTrand; - } - return sRng->genrand(); -} - -dword Random(dword n) -{ - dword mask = n; - mask |= mask >> 1; - mask |= mask >> 2; - mask |= mask >> 4; - mask |= mask >> 8; - mask |= mask >> 16; - - dword r; - do - r = Random() & mask; - while(r >= n); - return r; -} - -qword Random64() -{ - return MAKEQWORD(Random(), Random()); -} - -qword Random64(qword n) -{ - qword mask = n, r; - mask |= mask >> 1; mask |= mask >> 2; - mask |= mask >> 4; mask |= mask >> 8; - mask |= mask >> 16; mask |= mask >> 32; - - do - r = Random64() & mask; - while(r >= n); - return r; -} - -double Randomf() -{ - return Random64(I64(4503599627370496)) / 4503599627370496.0; -} - -END_UPP_NAMESPACE +#include "Core.h" + +NAMESPACE_UPP + +/* + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Before using, initialize the state by using init_genrand(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.keio.ac.jp/matumoto/emt.html + email: matumoto@math.keio.ac.jp +*/ + +#include + +/* Period parameters */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ + +struct MTrand { + dword mt[N]; + int mti; /* mti==N+1 means mt[N] is not initialized */ + dword mag01[2]; + + void init_genrand(dword s); + void init_by_array(dword *init_key, int key_length); + dword genrand(); + + MTrand(); +}; + +/* initializes mt[N] with a seed */ +void MTrand::init_genrand(dword s) +{ + mt[0]= s & 0xffffffffUL; + for (mti=1; mti> 30)) + mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + mt[mti] &= 0xffffffffUL; + /* for >32 bit machines */ + } +} + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +/* slight change for C++, 2004/2/26 */ +void MTrand::init_by_array(dword *init_key, int key_length) +{ + int i, j, k; + init_genrand(19650218UL); + i=1; j=0; + k = (N>key_length ? N : key_length); + for (; k; k--) { + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + + init_key[j] + j; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; j++; + if (i>=N) { mt[0] = mt[N-1]; i=1; } + if (j>=key_length) j=0; + } + for (k=N-1; k; k--) { + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) + - i; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; + if (i>=N) { mt[0] = mt[N-1]; i=1; } + } + + mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ +} + +/* generates a random number on [0,0xffffffff]-interval */ +dword MTrand::genrand() +{ + dword y; + + if (mti >= N) { /* generate N words at one time */ + int kk; + + for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; + } + for (;kk> 1) ^ mag01[y & 0x1UL]; + } + y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + mti = 0; + } + + y = mt[mti++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + +#ifdef PLATFORM_WIN32 + +#define Ptr Ptr_ +#include +#undef Ptr + +#pragma comment(lib, "ole32.lib") + +#endif + +MTrand::MTrand() +{ + mti = N + 1; + mag01[0] = 0; + mag01[1] = MATRIX_A; + dword seed[1024]; +#ifdef PLATFORM_POSIX + int fd = open("/dev/urandom", O_RDONLY); + if(fd != -1) { + IGNORE_RESULT( + read(fd, seed, sizeof(seed)) + ); + close(fd); + } +#else + for(int i = 0; i < 1024; i++) { + Uuid uuid; + CoCreateGuid((GUID *)&uuid); + seed[i] = GetHashValue(uuid); + } +#endif + init_by_array(seed, 1024); +} + +#ifdef CPU_BLACKFIN +//on blackfin toolchain we dont have TLS support, but we can live here without +MTrand *sRng; +byte sRb[sizeof(MTrand)]; +#else +thread__ MTrand *sRng; +thread__ byte sRb[sizeof(MTrand)]; +#endif + +void SeedRandom(dword *seed, int len){ + if(!sRng) { + sRng = new(sRb) MTrand; + } + sRng->init_by_array(seed, len); +} + +void SeedRandom(dword seed){ + if(!sRng) { + sRng = new(sRb) MTrand; + } + sRng->init_genrand(seed); +} + +dword Random() +{ + if(!sRng) { + sRng = new(sRb) MTrand; + } + return sRng->genrand(); +} + +dword Random(dword n) +{ + dword mask = n; + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + mask |= mask >> 16; + + dword r; + do + r = Random() & mask; + while(r >= n); + return r; +} + +qword Random64() +{ + return MAKEQWORD(Random(), Random()); +} + +qword Random64(qword n) +{ + qword mask = n, r; + mask |= mask >> 1; mask |= mask >> 2; + mask |= mask >> 4; mask |= mask >> 8; + mask |= mask >> 16; mask |= mask >> 32; + + do + r = Random64() & mask; + while(r >= n); + return r; +} + +double Randomf() +{ + return Random64(I64(4503599627370496)) / 4503599627370496.0; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/Stream.cpp b/uppsrc/Core/Stream.cpp index 4d5840c02..cb53dc046 100644 --- a/uppsrc/Core/Stream.cpp +++ b/uppsrc/Core/Stream.cpp @@ -758,6 +758,11 @@ Stream& Stream::operator/(WString& s) { return *this; } +Stream& Stream::operator/(int& i) { dword w = 0; if(IsStoring()) w = i + 1; Pack(w); i = w - 1; return *this; } +Stream& Stream::operator/(unsigned int& i) { dword w = 0; if(IsStoring()) w = i + 1; Pack(w); i = w - 1; return *this; } +Stream& Stream::operator/(long& i) { dword w = 0; if(IsStoring()) w = i + 1; Pack(w); i = w - 1; return *this; } +Stream& Stream::operator/(unsigned long& i) { dword w = 0; if(IsStoring()) w = i + 1; Pack(w); i = w - 1; return *this; } + void Stream::Magic(dword magic) { dword a = magic; *this % a; diff --git a/uppsrc/Core/Stream.h b/uppsrc/Core/Stream.h index 2b5ea5710..57352f0bf 100644 --- a/uppsrc/Core/Stream.h +++ b/uppsrc/Core/Stream.h @@ -212,10 +212,10 @@ public: Stream& operator/(WString& s); void Pack(dword& i); - Stream& operator/(int& i) { dword w = i + 1; Pack(w); i = w - 1; return *this; } - Stream& operator/(unsigned int& i) { dword w = i + 1; Pack(w); i = w - 1; return *this; } - Stream& operator/(long& i) { dword w = i + 1; Pack(w); i = w - 1; return *this; } - Stream& operator/(unsigned long& i) { dword w = i + 1; Pack(w); i = w - 1; return *this; } + Stream& operator/(int& i); + Stream& operator/(unsigned int& i); + Stream& operator/(long& i); + Stream& operator/(unsigned long& i); void Magic(dword magic = 0x7d674d7b); diff --git a/uppsrc/Core/Util.cpp b/uppsrc/Core/Util.cpp index 7a8cdae22..6376ad0ee 100644 --- a/uppsrc/Core/Util.cpp +++ b/uppsrc/Core/Util.cpp @@ -23,8 +23,12 @@ void PanicMessageBox(const char *title, const char *text) if(sPanicMessageBox) (*sPanicMessageBox)(title, text); else { - write(2, text, (int)strlen(text)); - write(2, "\n", 1); + IGNORE_RESULT( + write(2, text, (int)strlen(text)) + ); + IGNORE_RESULT( + write(2, "\n", 1) + ); } } @@ -888,7 +892,9 @@ static void LinuxBeep(const char *fn) #else if(fork()) return; #endif - system(h); + IGNORE_RESULT( + system(h) + ); _exit(EXIT_SUCCESS); } #endif diff --git a/uppsrc/CtrlCore/CtrlClip.cpp b/uppsrc/CtrlCore/CtrlClip.cpp index e538d6d53..ca9340f58 100644 --- a/uppsrc/CtrlCore/CtrlClip.cpp +++ b/uppsrc/CtrlCore/CtrlClip.cpp @@ -136,7 +136,6 @@ Image MakeDragImage(const Image& arrow, Image sample) else { b.Create(128, 128); memset(~b, 0, sizeof(RGBA) * b.GetLength()); - Size ssz = sample.GetSize(); Over(b, Point(2, 22), sample, sample.GetSize()); Unmultiply(b); for(int y = 20; y < 96; y++) { diff --git a/uppsrc/CtrlCore/CtrlMouse.cpp b/uppsrc/CtrlCore/CtrlMouse.cpp index f5a7e32f1..3a4623fdd 100644 --- a/uppsrc/CtrlCore/CtrlMouse.cpp +++ b/uppsrc/CtrlCore/CtrlMouse.cpp @@ -233,7 +233,6 @@ Ctrl *Ctrl::ChildFromPoint(Point& pt) const GuiLock __; Ctrl *q; Point p = pt; - Rect rect = GetRect(); Rect view = GetView(); if(view.Contains(p)) { Point vp = p - view.TopLeft(); @@ -600,7 +599,6 @@ Ctrl *Ctrl::GetVisibleChild(Ctrl *ctrl, Point p, bool pointinframe) if(!pointinframe) p += ctrl->GetView().TopLeft(); Ctrl *q; - Rect rect = ctrl->GetRect(); Rect view = ctrl->GetView(); if(view.Contains(p)) { p -= view.TopLeft(); diff --git a/uppsrc/CtrlCore/DrawX11.cpp b/uppsrc/CtrlCore/DrawX11.cpp index b7f1ca177..b34d64efc 100644 --- a/uppsrc/CtrlCore/DrawX11.cpp +++ b/uppsrc/CtrlCore/DrawX11.cpp @@ -200,7 +200,7 @@ void InitX11Draw(XDisplay *display) for(int g = 0; g < 24; g++) for(int b = 0; b < 12; b++) { int mind = INT_MAX; - int mini; + int mini = 0; for(int i = 0; i < colorcount; i++) { int d = ssq(r * 255 / 11 - (cs[i].red >> 8)) + ssq(g * 255 / 23 - (cs[i].green >> 8)) + diff --git a/uppsrc/CtrlCore/ParseRTF.cpp b/uppsrc/CtrlCore/ParseRTF.cpp index 0d9b08169..652d15500 100644 --- a/uppsrc/CtrlCore/ParseRTF.cpp +++ b/uppsrc/CtrlCore/ParseRTF.cpp @@ -1,1281 +1,1280 @@ -#include "CtrlCore.h" - -NAMESPACE_UPP - -#define LLOG(x) // DLOG(x) - -static int TwipDotsLim(int twips) { return minmax(TwipDots(twips), 0, MAX_DOTS); } - -static String FromCString(const char *p, const char **endptr = NULL) -{ - if(endptr) { - const char *e = p; - if(*e == '\"') - e++; - while(*e && *e != '\"') - if(*e++ == '\\' && *e) - e++; - if(*e == '\"') - e++; - *endptr = e; - } - - try { - CParser parser(p); - return parser.ReadOneString(); - } - catch(Exc e) { - return Null; - } -} - -class RTFParser -{ -public: - RTFParser(const char *rtf); - - RichText Run(); - -private: - enum TOKEN { T_EOF, T_TEXT, T_COMMAND, T_GROUP, T_END_GROUP }; - - void Flush(bool force, int itap); - void OpenTable(int level); - void FlushTable(int level); - TOKEN Fetch(); - void Skip(); - TOKEN Token() { if(!is_full) Fetch(); return token; } - bool PassIf(bool c) { is_full &= !c; return c; } - bool PassText() { return PassIf(Token() == T_TEXT); } - bool PassGroup() { return PassIf(Token() == T_GROUP); } - bool PassEndGroup() { return PassIf(Token() == T_END_GROUP || token == T_EOF); } - bool PassEndGroup(int level); - bool PassCmd(const char *cmd) { return PassIf(Token() == T_COMMAND && command == cmd); } - bool PassQ(const char *cmd) { return PassIf(command == cmd); } - - void SkipGroup() { SkipGroup(stack.GetCount()); } - void SkipGroup(int level); - int Level() const { return stack.GetCount(); } - - void ReadItem(); - void ReadItemGroup(int level); - void ReadItemGroup() { ReadItemGroup(Level()); } - void ReadText(); - void ReadText(const WString& text); - void ReadChar(word ch) { ReadText(WString(ch, 1)); } - void ReadCommand(); - - void ReadHeader(); - void ReadFaceTable(); - void ReadColorTable(); - void ReadCharSet(); - - void ReadMisc(); - void ReadField(); - bool ReadField(const char *def); - void ReadPict(); - void ReadShape(); - - void ReadParaStyle(); - void ReadTableStyle(); - void DefaultParaStyle(); - - void ReadCharStyle(); - void ReadCellBorder(int& width); - - String ReadBinHex(char& odd) const; - -private: - const char *rtf_begin; - const char *rtf; - - TOKEN token; - bool is_full; -// bool new_dest; - bool next_command; - WString text; - String command; - int command_arg; - - struct CellInfo { - CellInfo(); - - RichCell::Format format; - Rect cellmarginunits; - int shading; - Color shading_fore; - Color shading_back; - int end_dots; - }; - - struct Cell { - Cell(); - - CellInfo info; - RichTxt text; - bool merge_first; - bool merge; - int nbegin; - Size span; - }; - - struct Face : Moveable { - int face; - byte charset; - }; - - struct TableState { - TableState() : textcol(0), stylecol(0) { cells.Add(); } - - RichTable::Format tableformat; - Vector< Array > cells; - int textcol; - int stylecol; - }; - - CellInfo& CellInfoAt(int i); - Cell& CellAt(TableState& ts, int i); - void SetCellMargin(Cell& cell, int Rect::*mbr); - - struct State { - String dest; - RichPara::Format format; - RichPara::CharFormat charformat; - WithDeepCopy< Array > cellinfo; - int trgaph; - Rect rowmargin; - Rect rowmarginunits; - Rect rowspacing; - Rect rowspacingunits; - Rect cellmarginunits; - int uc_value; - int left_margin; - int right_margin; - int first_indent; - bool in_table; - int itap; - bool nestprop; - bool new_dest; - byte charset; - }; - - Array stack; - Array table_stack; - State state; - RichPara::CharFormat plain_format; - RichPara::Format pard_format; - CellInfo std_cell_info; - byte plain_charset; - byte default_charset; - int default_font; - Alignment tab_align; - byte tab_fill; - Vector face_table; - Vector color_table; - int paper_width; - int left_margin; - int right_margin; - - RichText output; - RichPara para; -}; - -RichText ParseRTF(const char *rtf) { return RTFParser(rtf).Run(); } - -RTFParser::CellInfo::CellInfo() -: end_dots(0) -, cellmarginunits(0, 0, 0, 0) -, shading(0) -, shading_fore(Black()) -, shading_back(White()) -{ -} - -RTFParser::Cell::Cell() -: nbegin(0) -, span(0, 0) -, merge_first(false) -, merge(false) -{ -} - -RTFParser::RTFParser(const char *rtf) -: rtf_begin(rtf) -, rtf(rtf) -{ -#ifdef _DEBUG - SaveFile(ConfigFile("rtfparser.rtf"), rtf); - LOG(rtf); -#endif - is_full = false; - next_command = false; - default_font = 0; - plain_charset = default_charset = state.charset = CHARSET_WIN1250; - state.uc_value = 1; - state.new_dest = false; - plain_format.Face(Font::ARIAL).Height(100); - std_cell_info.format.align = ALIGN_TOP; - std_cell_info.format.margin = Rect(25, 25, 25, 25); - DefaultParaStyle(); - state.charformat = plain_format; - tab_align = ALIGN_LEFT; - tab_fill = 0; - paper_width = 5100; - left_margin = right_margin = 750; -} - -RichText RTFParser::Run() -{ - if(!PassGroup() || !PassCmd("rtf") || command_arg != 1 && !IsNull(command_arg)) - return output; - while(Token() != T_EOF) - ReadItem(); - Flush(false, 1); - FlushTable(0); - return output; -} - -void RTFParser::FlushTable(int level) -{ - while(table_stack.GetCount() > level) { - TableState& child = table_stack.Top(); - while(!child.cells.IsEmpty() && child.cells.Top().IsEmpty()) - child.cells.Drop(); - if(child.cells.IsEmpty()) { - table_stack.Drop(); - continue; - } - Index dot_index; -// int pos = child.tableformat.lm; - dot_index.Add(child.tableformat.lm); - for(int r = 0; r < child.cells.GetCount(); r++) { - Array& rw = child.cells[r]; - int pos = child.tableformat.lm; - for(int c = 0; c < rw.GetCount(); c++) - dot_index.FindAdd(rw[c].info.end_dots); - } - Vector dot_order = dot_index.PickKeys(); - Sort(dot_order); - RichTable table; - if(table_stack.GetCount() == 1) - child.tableformat.rm = max(paper_width - left_margin - right_margin - dot_order.Top(), 0); -// child.tableformat.before = state.format.before; -// child.tableformat.after = state.format.after; - table.SetFormat(child.tableformat); - for(int c = 1; c < dot_order.GetCount(); c++) - table.AddColumn(dot_order[c] - dot_order[c - 1]); - dot_index = dot_order; - int tbl_border = Null, tbl_grid = Null; - Color clr_border = Null, clr_grid = Null; - for(int r = 0; r < child.cells.GetCount(); r++) { - Array& rw = child.cells[r]; - int pos = child.tableformat.lm; - for(int c = 0; c < rw.GetCount(); c++) { - Cell& cell = rw[c]; - if(cell.merge) { - pos = cell.info.end_dots; - continue; - } - cell.span.cy = 0; - if(cell.merge_first) { - for(int m = r + 1; m < child.cells.GetCount(); m++) { - const Array& mrw = child.cells[m]; - int mc = mrw.GetCount(); - while(--mc >= 0 && mrw[mc].info.end_dots > cell.info.end_dots) - ; - if(mc >= 0 && mrw[mc].info.end_dots == cell.info.end_dots && mrw[mc].merge) - cell.span.cy++; - else - break; - } - } - cell.nbegin = dot_index.Find(pos); - cell.span.cx = max(0, dot_index.Find(pos = cell.info.end_dots) - cell.nbegin - 1); - if(cell.span.cx < 0) { - cell.merge = true; - continue; - } - bool outer_border[] = { - cell.nbegin == 0, - r == 0, - cell.nbegin + cell.span.cx + 2 >= dot_index.GetCount(), - r + cell.span.cy + 1 >= child.cells.GetCount(), - }; - int border_width[] = { - cell.info.format.border.left, - cell.info.format.border.top, - cell.info.format.border.right, - cell.info.format.border.bottom, - }; - for(int b = 0; b < __countof(border_width); b++) { - int& out_wd = (outer_border[b] ? tbl_border : tbl_grid); - Color& out_co = (outer_border[b] ? clr_border : clr_grid); - if(IsNull(cell.info.format.bordercolor) || border_width[b] <= 0 - || !IsNull(out_co) && out_co != cell.info.format.bordercolor) - out_wd = 0; - else if(IsNull(out_wd) || border_width[b] < out_wd) { - out_wd = border_width[b]; - out_co = cell.info.format.bordercolor; - } - } - if(cell.info.shading > 0) { - Color zero = White(); - Color one = Nvl(cell.info.shading_fore, Black()); - int r = zero.GetR() + iscale(one.GetR() - zero.GetR(), cell.info.shading, 10000); - int g = zero.GetG() + iscale(one.GetG() - zero.GetG(), cell.info.shading, 10000); - int b = zero.GetB() + iscale(one.GetB() - zero.GetB(), cell.info.shading, 10000); - cell.info.format.color = Color(r, g, b); - } - } - } - table.format.frame = Nvl(tbl_border, 0); - table.format.framecolor = (table.format.frame > 0 ? clr_border : Color(Null)); - table.format.grid = Nvl(tbl_grid, 0); - table.format.gridcolor = (table.format.grid > 0 ? clr_grid : Color(Null)); - for(int r = 0; r < child.cells.GetCount(); r++) { - Array& rw = child.cells[r]; -// int pos = child.tableformat.lm; - for(int c = 0; c < rw.GetCount(); c++) { - Cell& cell = rw[c]; - if(cell.merge) - continue; - if(cell.span.cx || cell.span.cy) - table.SetSpan(r, cell.nbegin, cell.span.cy, cell.span.cx); - bool outer_border[] = { - cell.nbegin == 0, - r == 0, - cell.nbegin + cell.span.cx + 2 >= dot_index.GetCount(), - r + cell.span.cy + 1 >= child.cells.GetCount(), - }; - int *border_width[] = { - &cell.info.format.border.left, - &cell.info.format.border.top, - &cell.info.format.border.right, - &cell.info.format.border.bottom, - }; - for(int b = 0; b < __countof(border_width); b++) { - int tbl_wd = (outer_border[b] ? tbl_border : tbl_grid); - Color tbl_co = (outer_border[b] ? clr_border : clr_grid); - if(*border_width[b] <= tbl_wd) - *border_width[b] = 0; - } - table.SetFormat(r, cell.nbegin, cell.info.format); - cell.text.Normalize(); - table.SetPick(r, cell.nbegin, cell.text); - } - } - table.Normalize(); - table_stack.Drop(); - if(table_stack.IsEmpty()) - output.CatPick(table); - else { - TableState& par = table_stack.Top(); - CellAt(par, par.textcol).text.CatPick(table); - } - } -} - -void RTFParser::Flush(bool force, int itap) -{ - if(!para.part.IsEmpty() || force) { - int fi = state.first_indent, li = state.left_margin, ri = state.right_margin; - if(state.format.bullet != RichPara::BULLET_NONE) { - Swap(li, fi); -// li += fi; -// fi = -fi; - } - state.format.indent = minmax(fi, 0, MAX_DOTS); - state.format.lm = minmax(li, 0, MAX_DOTS); - state.format.rm = minmax(ri, 0, MAX_DOTS); - para.format = state.format; - if(state.in_table) { - FlushTable(itap); - OpenTable(itap); - TableState& ts = table_stack[itap - 1]; - CellAt(ts, ts.textcol).text.Cat(para, output.GetStyles()); - } - else { - FlushTable(0); - output.Cat(para); - } - para.part.Clear(); - } - else - FlushTable(itap); -} - -RTFParser::TOKEN RTFParser::Fetch() -{ - is_full = true; - text = WString(); - if(next_command) - { - next_command = false; - return token = T_COMMAND; - } - - text = Null; - command = Null; - command_arg = Null; - - int skip = 0; - while(*rtf && *rtf != '{' && *rtf != '}') - { - int c = 0, nskip = max(skip - 1, 0); - if((byte)*rtf < ' ') - rtf++; - else if(*rtf != '\\') - c = ToUnicode(*rtf++, state.charset); - else - switch(rtf++, *rtf++) - { - case 0: { - rtf--; - break; - } - - case '{': - case '}': - case '\\': { - c = rtf[-1]; - break; - } - - case '~': { - c = 160; - break; - } - - case '|': - case '-': - case '_': - case ':': { - command = String(rtf - 1, 1); - if(text.IsEmpty()) - return token = T_COMMAND; - next_command = true; - return token = T_TEXT; - } - - case '\'': { - int c1 = ctoi(*rtf); - if(c1 < 16) { - int c2 = ctoi(*++rtf); - if(c2 < 16) { - c1 = c1 * 16 + c2; - rtf++; - } - c = ToUnicode(c1, state.charset); - } - break; - } - - default: { - if(IsAlpha(*--rtf) || *rtf == '*' && rtf[1] == '\\' && IsAlpha(rtf[2])) { - if(*rtf == '*') { - rtf += 2; - state.new_dest = true; - } - const char *b = rtf; - while(IsAlpha(*++rtf)) - ; - command = String(b, rtf); - if(IsDigit(*rtf) || *rtf == '-') - command_arg = strtol(rtf, (char **)&rtf, 10); - if(*rtf == ' ') - rtf++; - if(command == "uc") - state.uc_value = command_arg; - else if(command == "u") { - c = command_arg; - nskip = state.uc_value; - } - else { // command - quit reading text - if(text.IsEmpty()) - return token = T_COMMAND; - next_command = true; - return token = T_TEXT; - } - } - break; - } - } - if(c && !skip) - text.Cat(c); - skip = nskip; - } - - if(!text.IsEmpty()) - return token = T_TEXT; - - if(*rtf == '{') { - stack.Add(state); - rtf++; - return token = T_GROUP; - } - - if(*rtf == '}') { - if(!stack.IsEmpty()) { - state = stack.Top(); - stack.Drop(); - } - rtf++; - return token = T_END_GROUP; - } - - return token = T_EOF; -} - -bool RTFParser::PassEndGroup(int level) -{ - if(Token() == T_EOF) - return true; - if(token != T_END_GROUP) - return false; - is_full = false; - return Level() < level; -} - -void RTFParser::Skip() -{ - bool is_group = (token == T_GROUP || token == T_COMMAND && state.new_dest); - is_full = false; - if(is_group) - SkipGroup(); -} - -void RTFParser::SkipGroup(int level) -{ - while(!PassEndGroup(level)) - is_full = false; -} - -void RTFParser::ReadItem() -{ - const char *p = rtf; - if(token == T_COMMAND) - ReadCommand(); - else if(token == T_TEXT) - ReadText(); - if(rtf == p && is_full) { - is_full = false; - if(token == T_COMMAND && state.new_dest) - SkipGroup(); - } -} - -void RTFParser::ReadItemGroup(int level) -{ - while(!PassEndGroup(level)) - ReadItem(); -} - -void RTFParser::ReadText() -{ - if(!IsNull(text)) - ReadText(text); -} - -void RTFParser::ReadText(const WString& text) -{ - if(!IsNull(state.dest)) - return; - LLOG("Output text: <" << FromUnicode(text, state.charset) << ">, " << state.charformat); - para.Cat(text, state.charformat); -} - -void RTFParser::ReadCommand() -{ - if(Token() == T_COMMAND) ReadHeader(); - if(Token() == T_COMMAND) ReadMisc(); - if(Token() == T_COMMAND) ReadParaStyle(); - if(Token() == T_COMMAND) ReadTableStyle(); - if(Token() == T_COMMAND) ReadCharStyle(); -} - -void RTFParser::ReadHeader() -{ - if(PassCmd("deff")) - default_font = command_arg; - else if(PassQ("fonttbl")) { - state.dest = command; - ReadFaceTable(); - } - else if(PassQ("colortbl")) { - state.dest = command; - ReadColorTable(); - } - else if(PassQ("stylesheet") || PassQ("list") || PassQ("listoverride") || PassQ("info")) { - state.dest = command; - SkipGroup(); - } - else if(Token() == T_COMMAND) - ReadCharSet(); -} - -void RTFParser::ReadCharSet() -{ - if(PassQ("ansi")) {} - else if(PassQ("mac")) {} - else if(PassQ("pc")) {} - else if(PassQ("pca")) {} - else if(PassQ("ansicpg")) { - static const struct { - int ansicpg; - byte charset; - } - charsets[] = - { - { 1250, CHARSET_WIN1250 }, - { 1251, CHARSET_WIN1251 }, - { 1252, CHARSET_WIN1252 }, - { 1253, CHARSET_WIN1253 }, - { 1254, CHARSET_WIN1254 }, - { 1255, CHARSET_WIN1255 }, - { 1256, CHARSET_WIN1256 }, - { 1257, CHARSET_WIN1257 }, - }; - for(int c = 0; c < __countof(charsets); c++) - if(charsets[c].ansicpg == command_arg) { - default_charset = state.charset = charsets[c].charset; - break; - } - } -} - -void RTFParser::ReadFaceTable() -{ - int fx = 0; - while(!PassEndGroup()) { - if(!PassGroup()) { - Skip(); - continue; - } - Face n; - n.face = Font::ARIAL; - n.charset = default_charset; - while(!PassEndGroup()) { - if(PassCmd("f")) - fx = command_arg; - else if(PassCmd("fnil")) - ; - else if(PassCmd("froman")) - n.face = Font::ROMAN; - else if(PassCmd("fswiss")) - n.face = Font::ARIAL; - else if(PassCmd("fmodern")) - n.face = Font::ARIAL; - else if(PassCmd("ftech")) -#ifdef PLATFORM_WIN32 - n.face = Font::SYMBOL; -#else - n.face = Font::ARIAL; -#endif - else if(PassCmd("fcharset")) { - switch(command_arg) { - case 0: n.charset = CHARSET_WIN1252; break; // ANSI - case 1: n.charset = default_charset; break; // Default - case 2: n.charset = CHARSET_WIN1252; break; // Symbol - case 3: break; // Invalid - case 77: break; // Mac - case 128: break; // Shift Jis - case 129: break; // Hangul - case 130: break; // Johab - case 134: break; // GB2312 - case 136: break; // Big5 - case 161: n.charset = CHARSET_WIN1253; break; // Greek - case 162: n.charset = CHARSET_WIN1254; break; // Turkish - case 163: break; // Vietnamese - case 177: n.charset = CHARSET_WIN1255; break; // Hebrew - case 178: break; // Arabic - case 179: break; // Arabic Traditional - case 180: break; // Arabic user - case 181: break; // Hebrew user - case 186: break; // Baltic - case 204: n.charset = CHARSET_WIN1251; break; // Russian - case 222: break; // Thai - case 238: n.charset = CHARSET_WIN1250; break; // Eastern European - case 254: break; // PC 437 - case 255: n.charset = CHARSET_WIN1252; break; // OEM - } - } -/* else if(PassText()) { - String s = FromUnicode(text, charset); - if(!s.IsEmpty() && *s.Last() == ';') - s.Trim(s.GetLength() - 1); - if(!s.IsEmpty()) - f = Font::FindFaceNameIndex(s); - } - else if(PassGroup()) { - int level = Level(); - if(PassCmd("falt") && PassText() && f < 0) - f = Font::FindFaceNameIndex(FromUnicode(text, charset)); - SkipGroup(level); - }*/ //Cxl 2005-11-29 - "Arial CE" makes mess here! - else - Skip(); - } - if(fx >= 0 && fx < MAX_FONTS) { -// if(f < 0) // Cxl 2005-11-29 - if(default_font == fx) { - plain_format.Face(n.face); - plain_charset = n.charset; - } - Face dflt; - dflt.face = Font::ARIAL; - dflt.charset = default_charset; - face_table.At(fx++, dflt) = n; - } - } -} - -void RTFParser::ReadColorTable() -{ - int r = Null, g = Null, b = Null; - for(; !PassEndGroup(); Skip()) - if(PassCmd("red")) - r = command_arg; - else if(PassCmd("green")) - g = command_arg; - else if(PassCmd("blue")) - b = command_arg; - else if(PassText()) - { - Color c = Null; - if(!IsNull(r) || !IsNull(g) || !IsNull(b)) - c = Color(Nvl(r, 0), Nvl(g, 0), Nvl(b, 0)); - color_table.Add(c); - } -} - -void RTFParser::ReadMisc() -{ - if(PassQ("field")) - ReadField(); - else if(PassQ("pict")) - ReadPict(); - else if(PassQ("shpinst")) - ReadShape(); - else if(PassQ("endash")) - ReadChar(0x2013); - else if(PassQ("emdash")) - ReadChar(0x2014); - else if(PassQ("tab")) - ReadText(WString(9, 1)); - else if(PassQ("enspace")) - ReadText(WString(" ")); // todo - else if(PassQ("emspace")) - ReadText(WString(" ")); // todo - else if(PassQ("bullet")) - ReadChar(0x2022); - else if(PassQ("lquote")) - ReadChar(0x2018); - else if(PassQ("rquote")) - ReadChar(0x2019); - else if(PassQ("ldblquote")) - ReadChar(0x201C); - else if(PassQ("rdblquote")) - ReadChar(0x201D); -} - -void RTFParser::ReadField() -{ - bool ign_rslt = false; - int level = Level(); - while(!PassEndGroup(level)) - if(PassGroup() && Level() == level + 1) { - if(PassCmd("fldinst")) { - WString source; - for(; !PassEndGroup(); Skip()) - if(PassText()) - source.Cat(text); - if(ReadField(FromUnicode(source, state.charset))) - ign_rslt = true; - continue; - } - else if(PassCmd("fldrslt")) { - if(!ign_rslt) - ReadItemGroup(); - } - } - else - Skip(); -} - -bool RTFParser::ReadField(const char *p) -{ - Index symdef; - while(*p) - if((byte)*p <= ' ') - p++; - else if(*p == '\"') - symdef.Add(FromCString(p, &p)); - else { - const char *b = p; - while(*++p && *p != ' ') - ; - symdef.Add(String(b, p)); - } - if(symdef.IsEmpty()) - return false; - if(symdef[0] == "SYMBOL" && symdef.GetCount() >= 2 && IsDigit(*symdef[1])) { - int code = atoi(symdef[1]); - int face = -1; - int height = 0; - int f = symdef.Find("\\f"); - if(f >= 0 && f + 1 < symdef.GetCount()) - face = Font::FindFaceNameIndex(symdef[f + 1]); - f = symdef.Find("\\s"); - if(f >= 0 && f + 1 < symdef.GetCount()) - height = PointDots(fround(2 * atof(symdef[f + 1]))) >> 1; - if(face < 0) -#ifdef PLATFORM_WIN32 - face = Font::SYMBOL; -#else - face = Font::ARIAL; -#endif - if(height <= 0 || height >= MAX_DOT_HEIGHT) - height = state.charformat.GetHeight(); - if(code >= 0 && code < 255) { - state.charformat.Face(face).Height(height); - ReadText(WString(ToUnicode(code, state.charset), 1)); - return true; - } - } - return false; -} - -void RTFParser::DefaultParaStyle() -{ - state.format = pard_format; - state.first_indent = state.left_margin = state.right_margin = 0; -// state.cellformat = std_cell_format; - state.in_table = false; - state.itap = 1; - state.nestprop = false; - state.trgaph = 2; - state.rowmargin = Rect(25, 25, 25, 25); - state.cellmarginunits = state.rowmarginunits = Rect(0, 0, 0, 0); - state.rowspacing = Rect(0, 0, 0, 0); - state.rowspacingunits = Rect(0, 0, 0, 0); - state.charset = plain_charset; -} - -void RTFParser::ReadParaStyle() -{ - if(PassQ("par")) - Flush(true, state.itap); - else if(PassQ("cell")) { - Flush(false, 1); - if(!table_stack.IsEmpty()) - table_stack[0].textcol++; - } - else if(PassQ("nestcell")) { - Flush(false, state.itap); - if(state.itap <= table_stack.GetCount()) - table_stack[state.itap - 1].textcol++; - } - else if(PassQ("pard")) - DefaultParaStyle(); - else if(PassQ("pntext")) - SkipGroup(); - else if(PassQ("pn")) { - SkipGroup(); - state.format.bullet = RichPara::BULLET_ROUND; - } - else if(PassQ("pagebb")) - state.format.newpage = (command_arg != 0); - else if(PassQ("ql")) - state.format.align = ALIGN_LEFT; - else if(PassQ("qc")) - state.format.align = ALIGN_CENTER; - else if(PassQ("qr")) - state.format.align = ALIGN_RIGHT; - else if(PassQ("qj")) - state.format.align = ALIGN_JUSTIFY; - else if(PassQ("fi")) - state.first_indent = TwipDotsLim(command_arg); - else if(PassQ("li")) - state.left_margin = TwipDotsLim(command_arg); - else if(PassQ("ri")) - state.right_margin = TwipDotsLim(command_arg); - else if(PassQ("sb")) - state.format.before = TwipDotsLim(command_arg); - else if(PassQ("sa")) - state.format.after = TwipDotsLim(command_arg); - else if(PassQ("widctlpar")) - state.format.orphan = true; - else if(PassQ("nowidctlpar")) - state.format.orphan = false; - else if(PassQ("tql")) - tab_align = ALIGN_LEFT; - else if(PassQ("tqc")) - tab_align = ALIGN_CENTER; - else if(PassQ("tqr")) - tab_align = ALIGN_RIGHT; - else if(PassQ("tqdec")) - tab_align = ALIGN_RIGHT; // todo - else if(PassQ("tldot")) - tab_fill = 0; - else if(PassQ("tlhyph")) - tab_fill = 0; - else if(PassQ("tlul")) - tab_fill = 0; - else if(PassQ("tlth")) - tab_fill = 0; - else if(PassQ("tleq")) - tab_fill = 0; - else if(PassQ("tx") || PassQ("tb")) { // todo: bar tab ? - int pos = TwipDotSize(command_arg); - RichPara::Tab& tab = state.format.tab.Add(); - tab.align = tab_align; - tab.fillchar = tab_fill; - tab.pos = pos; - state.format.SortTabs(); - } - else if(PassQ("intbl")) - state.in_table = true; - else if(PassQ("itap")) { - state.itap = minmax(command_arg, 1, 10); - if(table_stack.GetCount() < state.itap) - OpenTable(state.itap); - } -} - -void RTFParser::ReadCharStyle() -{ - if(PassQ("plain")) { - state.charformat = plain_format; - state.charset = plain_charset; - } - else if(PassQ("b")) - state.charformat.Bold(command_arg != 0); - else if(PassQ("i")) - state.charformat.Italic(command_arg != 0); - else if(PassQ("ul") || PassQ("uld") || PassQ("uldb") - || PassQ("uldash") || PassQ("uldashd") || PassQ("uldashdd") - || PassQ("ulth") || PassQ("ulw") || PassQ("ulwave")) - state.charformat.Underline(command_arg != 0); - else if(PassQ("ulnone")) - state.charformat.Underline(false); - else if(PassQ("strike") || PassQ("strikedl")) - state.charformat.Strikeout(command_arg != 0); - else if(PassQ("caps") || PassQ("scaps")) - state.charformat.capitals = (command_arg != 0); - else if(PassQ("super") || PassQ("up")) - state.charformat.sscript = 1; - else if(PassQ("sub") || PassQ("dn")) - state.charformat.sscript = 2; - else if(PassQ("nosupersub")) - state.charformat.sscript = 0; - else if(PassQ("f") && command_arg >= 0 && command_arg < face_table.GetCount()) { - LLOG("font = " << command_arg << ", face = " << face_table[command_arg].face - << ", charset = " << face_table[command_arg].charset); - state.charformat.Face(face_table[command_arg].face); - state.charset = face_table[command_arg].charset; - } - else if(PassQ("fs")) - state.charformat.Height(PointDotHeight(command_arg)); - else if(PassQ("cf") && command_arg >= 0 && command_arg < color_table.GetCount()) - state.charformat.ink = Nvl(color_table[command_arg], Black); - else if(PassQ("cb") && command_arg >= 0 && command_arg < color_table.GetCount()) - state.charformat.paper = color_table[command_arg]; - else if(PassQ("lang")) - {} // state.language = ... -} - -void RTFParser::ReadShape() -{ - int level = Level(); - while(!PassEndGroup(level)) - if(PassCmd("shppict")) { - state.new_dest = false; - ReadItemGroup(); - } - else - is_full = false; -} - -void RTFParser::ReadPict() -{ - Size log_size(1, 1), out_size(1, 1), scaling(100, 100); - Rect crop(0, 0, 0, 0); - enum BLIPTYPE { UNK_BLIP, EMF_BLIP, PNG_BLIP, JPEG_BLIP, WMF_BLIP, DIB_BLIP }; - BLIPTYPE blip_type = UNK_BLIP; -#ifdef PLATFORM_WIN32 -#ifndef PLATFORM_WINCE - int wmf_mode = MM_ANISOTROPIC; -#endif -#endif - String blip_data; - char odd = 0; - while(!PassEndGroup()) - if(PassText()) - blip_data.Cat(ReadBinHex(odd)); - else if(Token() == T_COMMAND) { - if(PassQ("picw")) log_size.cx = minmax(command_arg, 0, 30000); - else if(PassQ("pich")) log_size.cy = minmax(command_arg, 0, 30000); - else if(PassQ("picwgoal")) out_size.cx = TwipDotSize(command_arg); - else if(PassQ("pichgoal")) out_size.cy = TwipDotSize(command_arg); - else if(PassQ("picscalex")) scaling.cx = minmax(command_arg, 1, 1000); - else if(PassQ("picscaley")) scaling.cy = minmax(command_arg, 1, 1000); - else if(PassQ("piccropl")) crop.left = TwipDotSize(command_arg); - else if(PassQ("piccropt")) crop.top = TwipDotSize(command_arg); - else if(PassQ("piccropr")) crop.right = TwipDotSize(command_arg); - else if(PassQ("piccropb")) crop.bottom = TwipDotSize(command_arg); - else if(PassQ("emfblip")) blip_type = EMF_BLIP; - else if(PassQ("pngblip")) blip_type = PNG_BLIP; - else if(PassQ("jpegblip")) blip_type = JPEG_BLIP; -#ifdef GUI_WIN -#ifndef PLATFORM_WINCE - else if(PassQ("wmetafile")) { blip_type = WMF_BLIP; wmf_mode = command_arg; } -#endif -#endif - else if(PassQ("dibitmap")) blip_type = DIB_BLIP; - else Skip(); - } - else - Skip(); - Size final_size = minmax(iscale(out_size, scaling, Size(100, 100)), Size(1, 1), Size(30000, 30000)); - Size drawing_size; - DrawingDraw dd; - RichObject object; -#ifdef GUI_WIN -#ifndef PLATFORM_WINCE - if(blip_type == EMF_BLIP || blip_type == WMF_BLIP) { - log_size = min(log_size, GetFitSize(log_size, final_size)); - dd.Create(drawing_size = log_size); - WinMetaFile wmf; - if(blip_type == EMF_BLIP) - wmf = WinMetaFile(SetEnhMetaFileBits(blip_data.GetLength(), blip_data)); - else { - METAFILEPICT mfp; - Zero(mfp); - mfp.mm = wmf_mode; - mfp.xExt = log_size.cx; - mfp.yExt = log_size.cy; - wmf = WinMetaFile(SetWinMetaFileBits(blip_data.GetLength(), blip_data, ScreenHDC(), &mfp)); - } - wmf.Paint(dd, log_size); - object = CreateDrawingObject(dd, out_size, final_size); - } - else -#endif -#endif - if(blip_type == DIB_BLIP || blip_type == PNG_BLIP || blip_type == JPEG_BLIP) { - //FIXIMAGE - Image image = StreamRaster::LoadStringAny(blip_data); - object = CreatePNGObject(image, out_size, final_size); - } - if(object) { - LLOG("object (" << object.GetTypeName() << ", " << object.Write().GetLength() << " B), pixel size " - << object.GetPixelSize() << ", final size " << object.GetSize()); - para.Cat(object, state.charformat); - } -} - -String RTFParser::ReadBinHex(char& odd) const -{ - int t = odd; - byte v = ctoi(odd); - String out; - for(const wchar *s = text.Begin(); *s; s++) { - byte w = (*s >= '0' && *s <= '9' ? *s - '0' - : *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 - : *s >= 'a' && *s <= 'f' ? *s - 'a' + 10 - : 255); - if(w < 16) - if(v >= 16) - { - t = *s; - v = w; - } - else - { - out.Cat(16 * v + w); - v = 255; - } - } - odd = (v < 16 ? t : 0); - return out; -} - -void RTFParser::OpenTable(int level) -{ - if(table_stack.GetCount() < level) { - TableState& ts = table_stack.At(level - 1); - ts.stylecol = 0; -// state.cellformat = std_cell_format; - } -} - -void RTFParser::ReadCellBorder(int& width) -{ - if(Token() == T_COMMAND && !memcmp(command, "brdr", 4)) - is_full = false; - if(PassCmd("brdrw")) - width = TwipDots(command_arg); -} - -RTFParser::Cell& RTFParser::CellAt(TableState& ts, int i) -{ - Array& top = ts.cells.Top(); - int p = top.GetCount(); - if(p <= i) - top.SetCountR(i + 1); - for(int n = p; n <= i; n++) - top[n].info = CellInfoAt(n); - return top[i]; -} - -RTFParser::CellInfo& RTFParser::CellInfoAt(int i) -{ - return state.cellinfo.At(i, std_cell_info); -} - -void RTFParser::SetCellMargin(Cell& out, int Rect::*mbr) -{ - if(out.info.cellmarginunits.*mbr == 0) { - out.info.format.margin.*mbr = state.trgaph; - if(state.rowmarginunits.*mbr == 3) - out.info.format.margin.*mbr = state.rowmargin.*mbr; - if(state.rowspacingunits.*mbr == 3) - out.info.format.margin.*mbr += state.rowspacing.*mbr; - } -} - -void RTFParser::ReadTableStyle() -{ - if(PassQ("nesttableprops")) { - state.nestprop = true; - return; - } - if(PassQ("nonesttables")) { - SkipGroup(); - return; - } - int itap = (state.nestprop ? state.itap : 1); - if(PassQ("trowd")) { - OpenTable(itap); - table_stack[itap - 1].stylecol = 0; - return; - } - if(PassQ("row") && table_stack.GetCount() >= 1) { - TableState& ts0 = table_stack[0]; - ts0.textcol = ts0.stylecol = 0; - ts0.cells.Add(); - return; - } - if(PassQ("nestrow") && table_stack.GetCount() >= state.itap) { - TableState& ts = table_stack[state.itap - 1]; - ts.textcol = ts.stylecol = 0; - ts.cells.Add(); - return; - } - if(itap > table_stack.GetCount()) - return; - TableState& ts = table_stack[itap - 1]; - if(PassQ("trgaph")) - state.trgaph = TwipDotsLim(command_arg); - else if(PassQ("trql")) {} - else if(PassQ("trqr")) {} - else if(PassQ("trqc")) {} - else if(PassQ("trleft")) { - ts.tableformat.lm = TwipDotsLim(command_arg); - } - else if(PassQ("trbrdrl")) {} - else if(PassQ("trbrdrt")) {} - else if(PassQ("trbrdrr")) {} - else if(PassQ("trbrdrb")) {} - else if(PassQ("trbrdrv")) {} - else if(PassQ("trftsWidth")) {} - else if(PassQ("trautofit")) {} - else if(PassQ("trpaddl")) - state.rowmargin.left = TwipDotsLim(command_arg); - else if(PassQ("trpaddt")) - state.rowmargin.top = TwipDotsLim(command_arg); - else if(PassQ("trpaddr")) - state.rowmargin.right = TwipDotsLim(command_arg); - else if(PassQ("trpaddb")) - state.rowmargin.bottom = TwipDotsLim(command_arg); - else if(PassQ("trpaddfl")) - state.rowmarginunits.left = command_arg; - else if(PassQ("trpaddft")) - state.rowmarginunits.top = command_arg; - else if(PassQ("trpaddfr")) - state.rowmarginunits.right = command_arg; - else if(PassQ("trpaddfb")) - state.rowmarginunits.bottom = command_arg; - else if(PassQ("trspdl")) - state.rowspacing.left = TwipDotsLim(command_arg); - else if(PassQ("trspdt")) - state.rowspacing.top = TwipDotsLim(command_arg); - else if(PassQ("trspdr")) - state.rowspacing.right = TwipDotsLim(command_arg); - else if(PassQ("trspdb")) - state.rowspacing.bottom = TwipDotsLim(command_arg); - else if(PassQ("trspdfl")) - state.rowspacingunits.left = command_arg; - else if(PassQ("trspdft")) - state.rowspacingunits.top = command_arg; - else if(PassQ("trspdfr")) - state.rowspacingunits.right = command_arg; - else if(PassQ("trspdfb")) - state.rowspacingunits.bottom = command_arg; - else if(PassQ("clpadl")) - CellInfoAt(ts.stylecol).format.margin.left = TwipDotsLim(command_arg); - else if(PassQ("clpadt")) - CellInfoAt(ts.stylecol).format.margin.top = TwipDotsLim(command_arg); - else if(PassQ("clpadr")) - CellInfoAt(ts.stylecol).format.margin.right = TwipDotsLim(command_arg); - else if(PassQ("clpadb")) - CellInfoAt(ts.stylecol).format.margin.bottom = TwipDotsLim(command_arg); - else if(PassQ("clpadfl")) - state.cellmarginunits.left = command_arg; - else if(PassQ("clpadft")) - state.cellmarginunits.top = command_arg; - else if(PassQ("clpadfr")) - state.cellmarginunits.right = command_arg; - else if(PassQ("clpadfb")) - state.cellmarginunits.bottom = command_arg; - else if(PassQ("clbrdrl")) - ReadCellBorder(CellInfoAt(ts.stylecol).format.border.left); - else if(PassQ("clbrdrt")) - ReadCellBorder(CellInfoAt(ts.stylecol).format.border.top); - else if(PassQ("clbrdrr")) - ReadCellBorder(CellInfoAt(ts.stylecol).format.border.right); - else if(PassQ("clbrdrb")) - ReadCellBorder(CellInfoAt(ts.stylecol).format.border.bottom); - else if(PassQ("cltxlrtb")) {} - else if(PassQ("clshdng")) - CellInfoAt(ts.stylecol).shading = command_arg; - else if(PassQ("clcbpat")) { - if(command_arg >= 0 && command_arg < color_table.GetCount()) - CellInfoAt(ts.stylecol).format.color = color_table[command_arg]; - } - else if(PassQ("clvmrg")) - CellAt(ts, ts.stylecol).merge = true; - else if(PassQ("clvmgf")) - CellAt(ts, ts.stylecol).merge_first = true; - else if(PassQ("clftsWidth")) {} - else if(PassQ("clwWidth")) {} - else if(PassQ("cellx")) { - int sx = ts.stylecol++; - Cell& newcell = CellAt(ts, sx); - newcell.info.end_dots = TwipDotsLim(command_arg); - SetCellMargin(newcell, &Rect::left); - SetCellMargin(newcell, &Rect::top); - SetCellMargin(newcell, &Rect::right); - SetCellMargin(newcell, &Rect::bottom); - CellInfoAt(sx) = newcell.info; - //CellFormat(sx) = std_cell_format; - } - else if(PassQ("clvertalt")) - CellInfoAt(ts.stylecol).format.align = ALIGN_TOP; - else if(PassQ("clvertalc")) - CellInfoAt(ts.stylecol).format.align = ALIGN_CENTER; - else if(PassQ("clvertalb")) - CellInfoAt(ts.stylecol).format.align = ALIGN_BOTTOM; -} - -END_UPP_NAMESPACE +#include "CtrlCore.h" + +NAMESPACE_UPP + +#define LLOG(x) // DLOG(x) + +static int TwipDotsLim(int twips) { return minmax(TwipDots(twips), 0, MAX_DOTS); } + +static String FromCString(const char *p, const char **endptr = NULL) +{ + if(endptr) { + const char *e = p; + if(*e == '\"') + e++; + while(*e && *e != '\"') + if(*e++ == '\\' && *e) + e++; + if(*e == '\"') + e++; + *endptr = e; + } + + try { + CParser parser(p); + return parser.ReadOneString(); + } + catch(Exc e) { + return Null; + } +} + +class RTFParser +{ +public: + RTFParser(const char *rtf); + + RichText Run(); + +private: + enum TOKEN { T_EOF, T_TEXT, T_COMMAND, T_GROUP, T_END_GROUP }; + + void Flush(bool force, int itap); + void OpenTable(int level); + void FlushTable(int level); + TOKEN Fetch(); + void Skip(); + TOKEN Token() { if(!is_full) Fetch(); return token; } + bool PassIf(bool c) { is_full &= !c; return c; } + bool PassText() { return PassIf(Token() == T_TEXT); } + bool PassGroup() { return PassIf(Token() == T_GROUP); } + bool PassEndGroup() { return PassIf(Token() == T_END_GROUP || token == T_EOF); } + bool PassEndGroup(int level); + bool PassCmd(const char *cmd) { return PassIf(Token() == T_COMMAND && command == cmd); } + bool PassQ(const char *cmd) { return PassIf(command == cmd); } + + void SkipGroup() { SkipGroup(stack.GetCount()); } + void SkipGroup(int level); + int Level() const { return stack.GetCount(); } + + void ReadItem(); + void ReadItemGroup(int level); + void ReadItemGroup() { ReadItemGroup(Level()); } + void ReadText(); + void ReadText(const WString& text); + void ReadChar(word ch) { ReadText(WString(ch, 1)); } + void ReadCommand(); + + void ReadHeader(); + void ReadFaceTable(); + void ReadColorTable(); + void ReadCharSet(); + + void ReadMisc(); + void ReadField(); + bool ReadField(const char *def); + void ReadPict(); + void ReadShape(); + + void ReadParaStyle(); + void ReadTableStyle(); + void DefaultParaStyle(); + + void ReadCharStyle(); + void ReadCellBorder(int& width); + + String ReadBinHex(char& odd) const; + +private: + const char *rtf_begin; + const char *rtf; + + TOKEN token; + bool is_full; +// bool new_dest; + bool next_command; + WString text; + String command; + int command_arg; + + struct CellInfo { + CellInfo(); + + RichCell::Format format; + Rect cellmarginunits; + int shading; + Color shading_fore; + Color shading_back; + int end_dots; + }; + + struct Cell { + Cell(); + + CellInfo info; + RichTxt text; + bool merge_first; + bool merge; + int nbegin; + Size span; + }; + + struct Face : Moveable { + int face; + byte charset; + }; + + struct TableState { + TableState() : textcol(0), stylecol(0) { cells.Add(); } + + RichTable::Format tableformat; + Vector< Array > cells; + int textcol; + int stylecol; + }; + + CellInfo& CellInfoAt(int i); + Cell& CellAt(TableState& ts, int i); + void SetCellMargin(Cell& cell, int Rect::*mbr); + + struct State { + String dest; + RichPara::Format format; + RichPara::CharFormat charformat; + WithDeepCopy< Array > cellinfo; + int trgaph; + Rect rowmargin; + Rect rowmarginunits; + Rect rowspacing; + Rect rowspacingunits; + Rect cellmarginunits; + int uc_value; + int left_margin; + int right_margin; + int first_indent; + bool in_table; + int itap; + bool nestprop; + bool new_dest; + byte charset; + }; + + Array stack; + Array table_stack; + State state; + RichPara::CharFormat plain_format; + RichPara::Format pard_format; + CellInfo std_cell_info; + byte plain_charset; + byte default_charset; + int default_font; + Alignment tab_align; + byte tab_fill; + Vector face_table; + Vector color_table; + int paper_width; + int left_margin; + int right_margin; + + RichText output; + RichPara para; +}; + +RichText ParseRTF(const char *rtf) { return RTFParser(rtf).Run(); } + +RTFParser::CellInfo::CellInfo() +: cellmarginunits(0, 0, 0, 0) +, shading(0) +, shading_fore(Black()) +, shading_back(White()) +, end_dots(0) +{ +} + +RTFParser::Cell::Cell() +: merge_first(false) +, merge(false) +, nbegin(0) +, span(0, 0) +{ +} + +RTFParser::RTFParser(const char *rtf) +: rtf_begin(rtf) +, rtf(rtf) +{ +#ifdef _DEBUG + SaveFile(ConfigFile("rtfparser.rtf"), rtf); + LOG(rtf); +#endif + is_full = false; + next_command = false; + default_font = 0; + plain_charset = default_charset = state.charset = CHARSET_WIN1250; + state.uc_value = 1; + state.new_dest = false; + plain_format.Face(Font::ARIAL).Height(100); + std_cell_info.format.align = ALIGN_TOP; + std_cell_info.format.margin = Rect(25, 25, 25, 25); + DefaultParaStyle(); + state.charformat = plain_format; + tab_align = ALIGN_LEFT; + tab_fill = 0; + paper_width = 5100; + left_margin = right_margin = 750; +} + +RichText RTFParser::Run() +{ + if(!PassGroup() || !PassCmd("rtf") || command_arg != 1 && !IsNull(command_arg)) + return output; + while(Token() != T_EOF) + ReadItem(); + Flush(false, 1); + FlushTable(0); + return output; +} + +void RTFParser::FlushTable(int level) +{ + while(table_stack.GetCount() > level) { + TableState& child = table_stack.Top(); + while(!child.cells.IsEmpty() && child.cells.Top().IsEmpty()) + child.cells.Drop(); + if(child.cells.IsEmpty()) { + table_stack.Drop(); + continue; + } + Index dot_index; +// int pos = child.tableformat.lm; + dot_index.Add(child.tableformat.lm); + for(int r = 0; r < child.cells.GetCount(); r++) { + Array& rw = child.cells[r]; + for(int c = 0; c < rw.GetCount(); c++) + dot_index.FindAdd(rw[c].info.end_dots); + } + Vector dot_order = dot_index.PickKeys(); + Sort(dot_order); + RichTable table; + if(table_stack.GetCount() == 1) + child.tableformat.rm = max(paper_width - left_margin - right_margin - dot_order.Top(), 0); +// child.tableformat.before = state.format.before; +// child.tableformat.after = state.format.after; + table.SetFormat(child.tableformat); + for(int c = 1; c < dot_order.GetCount(); c++) + table.AddColumn(dot_order[c] - dot_order[c - 1]); + dot_index = dot_order; + int tbl_border = Null, tbl_grid = Null; + Color clr_border = Null, clr_grid = Null; + for(int r = 0; r < child.cells.GetCount(); r++) { + Array& rw = child.cells[r]; + int pos = child.tableformat.lm; + for(int c = 0; c < rw.GetCount(); c++) { + Cell& cell = rw[c]; + if(cell.merge) { + pos = cell.info.end_dots; + continue; + } + cell.span.cy = 0; + if(cell.merge_first) { + for(int m = r + 1; m < child.cells.GetCount(); m++) { + const Array& mrw = child.cells[m]; + int mc = mrw.GetCount(); + while(--mc >= 0 && mrw[mc].info.end_dots > cell.info.end_dots) + ; + if(mc >= 0 && mrw[mc].info.end_dots == cell.info.end_dots && mrw[mc].merge) + cell.span.cy++; + else + break; + } + } + cell.nbegin = dot_index.Find(pos); + cell.span.cx = max(0, dot_index.Find(pos = cell.info.end_dots) - cell.nbegin - 1); + if(cell.span.cx < 0) { + cell.merge = true; + continue; + } + bool outer_border[] = { + cell.nbegin == 0, + r == 0, + cell.nbegin + cell.span.cx + 2 >= dot_index.GetCount(), + r + cell.span.cy + 1 >= child.cells.GetCount(), + }; + int border_width[] = { + cell.info.format.border.left, + cell.info.format.border.top, + cell.info.format.border.right, + cell.info.format.border.bottom, + }; + for(int b = 0; b < __countof(border_width); b++) { + int& out_wd = (outer_border[b] ? tbl_border : tbl_grid); + Color& out_co = (outer_border[b] ? clr_border : clr_grid); + if(IsNull(cell.info.format.bordercolor) || border_width[b] <= 0 + || !IsNull(out_co) && out_co != cell.info.format.bordercolor) + out_wd = 0; + else if(IsNull(out_wd) || border_width[b] < out_wd) { + out_wd = border_width[b]; + out_co = cell.info.format.bordercolor; + } + } + if(cell.info.shading > 0) { + Color zero = White(); + Color one = Nvl(cell.info.shading_fore, Black()); + int r = zero.GetR() + iscale(one.GetR() - zero.GetR(), cell.info.shading, 10000); + int g = zero.GetG() + iscale(one.GetG() - zero.GetG(), cell.info.shading, 10000); + int b = zero.GetB() + iscale(one.GetB() - zero.GetB(), cell.info.shading, 10000); + cell.info.format.color = Color(r, g, b); + } + } + } + table.format.frame = Nvl(tbl_border, 0); + table.format.framecolor = (table.format.frame > 0 ? clr_border : Color(Null)); + table.format.grid = Nvl(tbl_grid, 0); + table.format.gridcolor = (table.format.grid > 0 ? clr_grid : Color(Null)); + for(int r = 0; r < child.cells.GetCount(); r++) { + Array& rw = child.cells[r]; +// int pos = child.tableformat.lm; + for(int c = 0; c < rw.GetCount(); c++) { + Cell& cell = rw[c]; + if(cell.merge) + continue; + if(cell.span.cx || cell.span.cy) + table.SetSpan(r, cell.nbegin, cell.span.cy, cell.span.cx); + bool outer_border[] = { + cell.nbegin == 0, + r == 0, + cell.nbegin + cell.span.cx + 2 >= dot_index.GetCount(), + r + cell.span.cy + 1 >= child.cells.GetCount(), + }; + int *border_width[] = { + &cell.info.format.border.left, + &cell.info.format.border.top, + &cell.info.format.border.right, + &cell.info.format.border.bottom, + }; + for(int b = 0; b < __countof(border_width); b++) { + int tbl_wd = (outer_border[b] ? tbl_border : tbl_grid); +// Color tbl_co = (outer_border[b] ? clr_border : clr_grid); + if(*border_width[b] <= tbl_wd) + *border_width[b] = 0; + } + table.SetFormat(r, cell.nbegin, cell.info.format); + cell.text.Normalize(); + table.SetPick(r, cell.nbegin, cell.text); + } + } + table.Normalize(); + table_stack.Drop(); + if(table_stack.IsEmpty()) + output.CatPick(table); + else { + TableState& par = table_stack.Top(); + CellAt(par, par.textcol).text.CatPick(table); + } + } +} + +void RTFParser::Flush(bool force, int itap) +{ + if(!para.part.IsEmpty() || force) { + int fi = state.first_indent, li = state.left_margin, ri = state.right_margin; + if(state.format.bullet != RichPara::BULLET_NONE) { + Swap(li, fi); +// li += fi; +// fi = -fi; + } + state.format.indent = minmax(fi, 0, MAX_DOTS); + state.format.lm = minmax(li, 0, MAX_DOTS); + state.format.rm = minmax(ri, 0, MAX_DOTS); + para.format = state.format; + if(state.in_table) { + FlushTable(itap); + OpenTable(itap); + TableState& ts = table_stack[itap - 1]; + CellAt(ts, ts.textcol).text.Cat(para, output.GetStyles()); + } + else { + FlushTable(0); + output.Cat(para); + } + para.part.Clear(); + } + else + FlushTable(itap); +} + +RTFParser::TOKEN RTFParser::Fetch() +{ + is_full = true; + text = WString(); + if(next_command) + { + next_command = false; + return token = T_COMMAND; + } + + text = Null; + command = Null; + command_arg = Null; + + int skip = 0; + while(*rtf && *rtf != '{' && *rtf != '}') + { + int c = 0, nskip = max(skip - 1, 0); + if((byte)*rtf < ' ') + rtf++; + else if(*rtf != '\\') + c = ToUnicode(*rtf++, state.charset); + else + switch(rtf++, *rtf++) + { + case 0: { + rtf--; + break; + } + + case '{': + case '}': + case '\\': { + c = rtf[-1]; + break; + } + + case '~': { + c = 160; + break; + } + + case '|': + case '-': + case '_': + case ':': { + command = String(rtf - 1, 1); + if(text.IsEmpty()) + return token = T_COMMAND; + next_command = true; + return token = T_TEXT; + } + + case '\'': { + int c1 = ctoi(*rtf); + if(c1 < 16) { + int c2 = ctoi(*++rtf); + if(c2 < 16) { + c1 = c1 * 16 + c2; + rtf++; + } + c = ToUnicode(c1, state.charset); + } + break; + } + + default: { + if(IsAlpha(*--rtf) || *rtf == '*' && rtf[1] == '\\' && IsAlpha(rtf[2])) { + if(*rtf == '*') { + rtf += 2; + state.new_dest = true; + } + const char *b = rtf; + while(IsAlpha(*++rtf)) + ; + command = String(b, rtf); + if(IsDigit(*rtf) || *rtf == '-') + command_arg = strtol(rtf, (char **)&rtf, 10); + if(*rtf == ' ') + rtf++; + if(command == "uc") + state.uc_value = command_arg; + else if(command == "u") { + c = command_arg; + nskip = state.uc_value; + } + else { // command - quit reading text + if(text.IsEmpty()) + return token = T_COMMAND; + next_command = true; + return token = T_TEXT; + } + } + break; + } + } + if(c && !skip) + text.Cat(c); + skip = nskip; + } + + if(!text.IsEmpty()) + return token = T_TEXT; + + if(*rtf == '{') { + stack.Add(state); + rtf++; + return token = T_GROUP; + } + + if(*rtf == '}') { + if(!stack.IsEmpty()) { + state = stack.Top(); + stack.Drop(); + } + rtf++; + return token = T_END_GROUP; + } + + return token = T_EOF; +} + +bool RTFParser::PassEndGroup(int level) +{ + if(Token() == T_EOF) + return true; + if(token != T_END_GROUP) + return false; + is_full = false; + return Level() < level; +} + +void RTFParser::Skip() +{ + bool is_group = (token == T_GROUP || token == T_COMMAND && state.new_dest); + is_full = false; + if(is_group) + SkipGroup(); +} + +void RTFParser::SkipGroup(int level) +{ + while(!PassEndGroup(level)) + is_full = false; +} + +void RTFParser::ReadItem() +{ + const char *p = rtf; + if(token == T_COMMAND) + ReadCommand(); + else if(token == T_TEXT) + ReadText(); + if(rtf == p && is_full) { + is_full = false; + if(token == T_COMMAND && state.new_dest) + SkipGroup(); + } +} + +void RTFParser::ReadItemGroup(int level) +{ + while(!PassEndGroup(level)) + ReadItem(); +} + +void RTFParser::ReadText() +{ + if(!IsNull(text)) + ReadText(text); +} + +void RTFParser::ReadText(const WString& text) +{ + if(!IsNull(state.dest)) + return; + LLOG("Output text: <" << FromUnicode(text, state.charset) << ">, " << state.charformat); + para.Cat(text, state.charformat); +} + +void RTFParser::ReadCommand() +{ + if(Token() == T_COMMAND) ReadHeader(); + if(Token() == T_COMMAND) ReadMisc(); + if(Token() == T_COMMAND) ReadParaStyle(); + if(Token() == T_COMMAND) ReadTableStyle(); + if(Token() == T_COMMAND) ReadCharStyle(); +} + +void RTFParser::ReadHeader() +{ + if(PassCmd("deff")) + default_font = command_arg; + else if(PassQ("fonttbl")) { + state.dest = command; + ReadFaceTable(); + } + else if(PassQ("colortbl")) { + state.dest = command; + ReadColorTable(); + } + else if(PassQ("stylesheet") || PassQ("list") || PassQ("listoverride") || PassQ("info")) { + state.dest = command; + SkipGroup(); + } + else if(Token() == T_COMMAND) + ReadCharSet(); +} + +void RTFParser::ReadCharSet() +{ + if(PassQ("ansi")) {} + else if(PassQ("mac")) {} + else if(PassQ("pc")) {} + else if(PassQ("pca")) {} + else if(PassQ("ansicpg")) { + static const struct { + int ansicpg; + byte charset; + } + charsets[] = + { + { 1250, CHARSET_WIN1250 }, + { 1251, CHARSET_WIN1251 }, + { 1252, CHARSET_WIN1252 }, + { 1253, CHARSET_WIN1253 }, + { 1254, CHARSET_WIN1254 }, + { 1255, CHARSET_WIN1255 }, + { 1256, CHARSET_WIN1256 }, + { 1257, CHARSET_WIN1257 }, + }; + for(int c = 0; c < __countof(charsets); c++) + if(charsets[c].ansicpg == command_arg) { + default_charset = state.charset = charsets[c].charset; + break; + } + } +} + +void RTFParser::ReadFaceTable() +{ + int fx = 0; + while(!PassEndGroup()) { + if(!PassGroup()) { + Skip(); + continue; + } + Face n; + n.face = Font::ARIAL; + n.charset = default_charset; + while(!PassEndGroup()) { + if(PassCmd("f")) + fx = command_arg; + else if(PassCmd("fnil")) + ; + else if(PassCmd("froman")) + n.face = Font::ROMAN; + else if(PassCmd("fswiss")) + n.face = Font::ARIAL; + else if(PassCmd("fmodern")) + n.face = Font::ARIAL; + else if(PassCmd("ftech")) +#ifdef PLATFORM_WIN32 + n.face = Font::SYMBOL; +#else + n.face = Font::ARIAL; +#endif + else if(PassCmd("fcharset")) { + switch(command_arg) { + case 0: n.charset = CHARSET_WIN1252; break; // ANSI + case 1: n.charset = default_charset; break; // Default + case 2: n.charset = CHARSET_WIN1252; break; // Symbol + case 3: break; // Invalid + case 77: break; // Mac + case 128: break; // Shift Jis + case 129: break; // Hangul + case 130: break; // Johab + case 134: break; // GB2312 + case 136: break; // Big5 + case 161: n.charset = CHARSET_WIN1253; break; // Greek + case 162: n.charset = CHARSET_WIN1254; break; // Turkish + case 163: break; // Vietnamese + case 177: n.charset = CHARSET_WIN1255; break; // Hebrew + case 178: break; // Arabic + case 179: break; // Arabic Traditional + case 180: break; // Arabic user + case 181: break; // Hebrew user + case 186: break; // Baltic + case 204: n.charset = CHARSET_WIN1251; break; // Russian + case 222: break; // Thai + case 238: n.charset = CHARSET_WIN1250; break; // Eastern European + case 254: break; // PC 437 + case 255: n.charset = CHARSET_WIN1252; break; // OEM + } + } +/* else if(PassText()) { + String s = FromUnicode(text, charset); + if(!s.IsEmpty() && *s.Last() == ';') + s.Trim(s.GetLength() - 1); + if(!s.IsEmpty()) + f = Font::FindFaceNameIndex(s); + } + else if(PassGroup()) { + int level = Level(); + if(PassCmd("falt") && PassText() && f < 0) + f = Font::FindFaceNameIndex(FromUnicode(text, charset)); + SkipGroup(level); + }*/ //Cxl 2005-11-29 - "Arial CE" makes mess here! + else + Skip(); + } + if(fx >= 0 && fx < MAX_FONTS) { +// if(f < 0) // Cxl 2005-11-29 + if(default_font == fx) { + plain_format.Face(n.face); + plain_charset = n.charset; + } + Face dflt; + dflt.face = Font::ARIAL; + dflt.charset = default_charset; + face_table.At(fx++, dflt) = n; + } + } +} + +void RTFParser::ReadColorTable() +{ + int r = Null, g = Null, b = Null; + for(; !PassEndGroup(); Skip()) + if(PassCmd("red")) + r = command_arg; + else if(PassCmd("green")) + g = command_arg; + else if(PassCmd("blue")) + b = command_arg; + else if(PassText()) + { + Color c = Null; + if(!IsNull(r) || !IsNull(g) || !IsNull(b)) + c = Color(Nvl(r, 0), Nvl(g, 0), Nvl(b, 0)); + color_table.Add(c); + } +} + +void RTFParser::ReadMisc() +{ + if(PassQ("field")) + ReadField(); + else if(PassQ("pict")) + ReadPict(); + else if(PassQ("shpinst")) + ReadShape(); + else if(PassQ("endash")) + ReadChar(0x2013); + else if(PassQ("emdash")) + ReadChar(0x2014); + else if(PassQ("tab")) + ReadText(WString(9, 1)); + else if(PassQ("enspace")) + ReadText(WString(" ")); // todo + else if(PassQ("emspace")) + ReadText(WString(" ")); // todo + else if(PassQ("bullet")) + ReadChar(0x2022); + else if(PassQ("lquote")) + ReadChar(0x2018); + else if(PassQ("rquote")) + ReadChar(0x2019); + else if(PassQ("ldblquote")) + ReadChar(0x201C); + else if(PassQ("rdblquote")) + ReadChar(0x201D); +} + +void RTFParser::ReadField() +{ + bool ign_rslt = false; + int level = Level(); + while(!PassEndGroup(level)) + if(PassGroup() && Level() == level + 1) { + if(PassCmd("fldinst")) { + WString source; + for(; !PassEndGroup(); Skip()) + if(PassText()) + source.Cat(text); + if(ReadField(FromUnicode(source, state.charset))) + ign_rslt = true; + continue; + } + else if(PassCmd("fldrslt")) { + if(!ign_rslt) + ReadItemGroup(); + } + } + else + Skip(); +} + +bool RTFParser::ReadField(const char *p) +{ + Index symdef; + while(*p) + if((byte)*p <= ' ') + p++; + else if(*p == '\"') + symdef.Add(FromCString(p, &p)); + else { + const char *b = p; + while(*++p && *p != ' ') + ; + symdef.Add(String(b, p)); + } + if(symdef.IsEmpty()) + return false; + if(symdef[0] == "SYMBOL" && symdef.GetCount() >= 2 && IsDigit(*symdef[1])) { + int code = atoi(symdef[1]); + int face = -1; + int height = 0; + int f = symdef.Find("\\f"); + if(f >= 0 && f + 1 < symdef.GetCount()) + face = Font::FindFaceNameIndex(symdef[f + 1]); + f = symdef.Find("\\s"); + if(f >= 0 && f + 1 < symdef.GetCount()) + height = PointDots(fround(2 * atof(symdef[f + 1]))) >> 1; + if(face < 0) +#ifdef PLATFORM_WIN32 + face = Font::SYMBOL; +#else + face = Font::ARIAL; +#endif + if(height <= 0 || height >= MAX_DOT_HEIGHT) + height = state.charformat.GetHeight(); + if(code >= 0 && code < 255) { + state.charformat.Face(face).Height(height); + ReadText(WString(ToUnicode(code, state.charset), 1)); + return true; + } + } + return false; +} + +void RTFParser::DefaultParaStyle() +{ + state.format = pard_format; + state.first_indent = state.left_margin = state.right_margin = 0; +// state.cellformat = std_cell_format; + state.in_table = false; + state.itap = 1; + state.nestprop = false; + state.trgaph = 2; + state.rowmargin = Rect(25, 25, 25, 25); + state.cellmarginunits = state.rowmarginunits = Rect(0, 0, 0, 0); + state.rowspacing = Rect(0, 0, 0, 0); + state.rowspacingunits = Rect(0, 0, 0, 0); + state.charset = plain_charset; +} + +void RTFParser::ReadParaStyle() +{ + if(PassQ("par")) + Flush(true, state.itap); + else if(PassQ("cell")) { + Flush(false, 1); + if(!table_stack.IsEmpty()) + table_stack[0].textcol++; + } + else if(PassQ("nestcell")) { + Flush(false, state.itap); + if(state.itap <= table_stack.GetCount()) + table_stack[state.itap - 1].textcol++; + } + else if(PassQ("pard")) + DefaultParaStyle(); + else if(PassQ("pntext")) + SkipGroup(); + else if(PassQ("pn")) { + SkipGroup(); + state.format.bullet = RichPara::BULLET_ROUND; + } + else if(PassQ("pagebb")) + state.format.newpage = (command_arg != 0); + else if(PassQ("ql")) + state.format.align = ALIGN_LEFT; + else if(PassQ("qc")) + state.format.align = ALIGN_CENTER; + else if(PassQ("qr")) + state.format.align = ALIGN_RIGHT; + else if(PassQ("qj")) + state.format.align = ALIGN_JUSTIFY; + else if(PassQ("fi")) + state.first_indent = TwipDotsLim(command_arg); + else if(PassQ("li")) + state.left_margin = TwipDotsLim(command_arg); + else if(PassQ("ri")) + state.right_margin = TwipDotsLim(command_arg); + else if(PassQ("sb")) + state.format.before = TwipDotsLim(command_arg); + else if(PassQ("sa")) + state.format.after = TwipDotsLim(command_arg); + else if(PassQ("widctlpar")) + state.format.orphan = true; + else if(PassQ("nowidctlpar")) + state.format.orphan = false; + else if(PassQ("tql")) + tab_align = ALIGN_LEFT; + else if(PassQ("tqc")) + tab_align = ALIGN_CENTER; + else if(PassQ("tqr")) + tab_align = ALIGN_RIGHT; + else if(PassQ("tqdec")) + tab_align = ALIGN_RIGHT; // todo + else if(PassQ("tldot")) + tab_fill = 0; + else if(PassQ("tlhyph")) + tab_fill = 0; + else if(PassQ("tlul")) + tab_fill = 0; + else if(PassQ("tlth")) + tab_fill = 0; + else if(PassQ("tleq")) + tab_fill = 0; + else if(PassQ("tx") || PassQ("tb")) { // todo: bar tab ? + int pos = TwipDotSize(command_arg); + RichPara::Tab& tab = state.format.tab.Add(); + tab.align = tab_align; + tab.fillchar = tab_fill; + tab.pos = pos; + state.format.SortTabs(); + } + else if(PassQ("intbl")) + state.in_table = true; + else if(PassQ("itap")) { + state.itap = minmax(command_arg, 1, 10); + if(table_stack.GetCount() < state.itap) + OpenTable(state.itap); + } +} + +void RTFParser::ReadCharStyle() +{ + if(PassQ("plain")) { + state.charformat = plain_format; + state.charset = plain_charset; + } + else if(PassQ("b")) + state.charformat.Bold(command_arg != 0); + else if(PassQ("i")) + state.charformat.Italic(command_arg != 0); + else if(PassQ("ul") || PassQ("uld") || PassQ("uldb") + || PassQ("uldash") || PassQ("uldashd") || PassQ("uldashdd") + || PassQ("ulth") || PassQ("ulw") || PassQ("ulwave")) + state.charformat.Underline(command_arg != 0); + else if(PassQ("ulnone")) + state.charformat.Underline(false); + else if(PassQ("strike") || PassQ("strikedl")) + state.charformat.Strikeout(command_arg != 0); + else if(PassQ("caps") || PassQ("scaps")) + state.charformat.capitals = (command_arg != 0); + else if(PassQ("super") || PassQ("up")) + state.charformat.sscript = 1; + else if(PassQ("sub") || PassQ("dn")) + state.charformat.sscript = 2; + else if(PassQ("nosupersub")) + state.charformat.sscript = 0; + else if(PassQ("f") && command_arg >= 0 && command_arg < face_table.GetCount()) { + LLOG("font = " << command_arg << ", face = " << face_table[command_arg].face + << ", charset = " << face_table[command_arg].charset); + state.charformat.Face(face_table[command_arg].face); + state.charset = face_table[command_arg].charset; + } + else if(PassQ("fs")) + state.charformat.Height(PointDotHeight(command_arg)); + else if(PassQ("cf") && command_arg >= 0 && command_arg < color_table.GetCount()) + state.charformat.ink = Nvl(color_table[command_arg], Black); + else if(PassQ("cb") && command_arg >= 0 && command_arg < color_table.GetCount()) + state.charformat.paper = color_table[command_arg]; + else if(PassQ("lang")) + {} // state.language = ... +} + +void RTFParser::ReadShape() +{ + int level = Level(); + while(!PassEndGroup(level)) + if(PassCmd("shppict")) { + state.new_dest = false; + ReadItemGroup(); + } + else + is_full = false; +} + +void RTFParser::ReadPict() +{ + Size log_size(1, 1), out_size(1, 1), scaling(100, 100); + Rect crop(0, 0, 0, 0); + enum BLIPTYPE { UNK_BLIP, EMF_BLIP, PNG_BLIP, JPEG_BLIP, WMF_BLIP, DIB_BLIP }; + BLIPTYPE blip_type = UNK_BLIP; +#ifdef PLATFORM_WIN32 +#ifndef PLATFORM_WINCE + int wmf_mode = MM_ANISOTROPIC; +#endif +#endif + String blip_data; + char odd = 0; + while(!PassEndGroup()) + if(PassText()) + blip_data.Cat(ReadBinHex(odd)); + else if(Token() == T_COMMAND) { + if(PassQ("picw")) log_size.cx = minmax(command_arg, 0, 30000); + else if(PassQ("pich")) log_size.cy = minmax(command_arg, 0, 30000); + else if(PassQ("picwgoal")) out_size.cx = TwipDotSize(command_arg); + else if(PassQ("pichgoal")) out_size.cy = TwipDotSize(command_arg); + else if(PassQ("picscalex")) scaling.cx = minmax(command_arg, 1, 1000); + else if(PassQ("picscaley")) scaling.cy = minmax(command_arg, 1, 1000); + else if(PassQ("piccropl")) crop.left = TwipDotSize(command_arg); + else if(PassQ("piccropt")) crop.top = TwipDotSize(command_arg); + else if(PassQ("piccropr")) crop.right = TwipDotSize(command_arg); + else if(PassQ("piccropb")) crop.bottom = TwipDotSize(command_arg); + else if(PassQ("emfblip")) blip_type = EMF_BLIP; + else if(PassQ("pngblip")) blip_type = PNG_BLIP; + else if(PassQ("jpegblip")) blip_type = JPEG_BLIP; +#ifdef GUI_WIN +#ifndef PLATFORM_WINCE + else if(PassQ("wmetafile")) { blip_type = WMF_BLIP; wmf_mode = command_arg; } +#endif +#endif + else if(PassQ("dibitmap")) blip_type = DIB_BLIP; + else Skip(); + } + else + Skip(); + Size final_size = minmax(iscale(out_size, scaling, Size(100, 100)), Size(1, 1), Size(30000, 30000)); + Size drawing_size; + DrawingDraw dd; + RichObject object; +#ifdef GUI_WIN +#ifndef PLATFORM_WINCE + if(blip_type == EMF_BLIP || blip_type == WMF_BLIP) { + log_size = min(log_size, GetFitSize(log_size, final_size)); + dd.Create(drawing_size = log_size); + WinMetaFile wmf; + if(blip_type == EMF_BLIP) + wmf = WinMetaFile(SetEnhMetaFileBits(blip_data.GetLength(), blip_data)); + else { + METAFILEPICT mfp; + Zero(mfp); + mfp.mm = wmf_mode; + mfp.xExt = log_size.cx; + mfp.yExt = log_size.cy; + wmf = WinMetaFile(SetWinMetaFileBits(blip_data.GetLength(), blip_data, ScreenHDC(), &mfp)); + } + wmf.Paint(dd, log_size); + object = CreateDrawingObject(dd, out_size, final_size); + } + else +#endif +#endif + if(blip_type == DIB_BLIP || blip_type == PNG_BLIP || blip_type == JPEG_BLIP) { + //FIXIMAGE + Image image = StreamRaster::LoadStringAny(blip_data); + object = CreatePNGObject(image, out_size, final_size); + } + if(object) { + LLOG("object (" << object.GetTypeName() << ", " << object.Write().GetLength() << " B), pixel size " + << object.GetPixelSize() << ", final size " << object.GetSize()); + para.Cat(object, state.charformat); + } +} + +String RTFParser::ReadBinHex(char& odd) const +{ + int t = odd; + byte v = ctoi(odd); + String out; + for(const wchar *s = text.Begin(); *s; s++) { + byte w = (*s >= '0' && *s <= '9' ? *s - '0' + : *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 + : *s >= 'a' && *s <= 'f' ? *s - 'a' + 10 + : 255); + if(w < 16) + if(v >= 16) + { + t = *s; + v = w; + } + else + { + out.Cat(16 * v + w); + v = 255; + } + } + odd = (v < 16 ? t : 0); + return out; +} + +void RTFParser::OpenTable(int level) +{ + if(table_stack.GetCount() < level) { + TableState& ts = table_stack.At(level - 1); + ts.stylecol = 0; +// state.cellformat = std_cell_format; + } +} + +void RTFParser::ReadCellBorder(int& width) +{ + if(Token() == T_COMMAND && !memcmp(command, "brdr", 4)) + is_full = false; + if(PassCmd("brdrw")) + width = TwipDots(command_arg); +} + +RTFParser::Cell& RTFParser::CellAt(TableState& ts, int i) +{ + Array& top = ts.cells.Top(); + int p = top.GetCount(); + if(p <= i) + top.SetCountR(i + 1); + for(int n = p; n <= i; n++) + top[n].info = CellInfoAt(n); + return top[i]; +} + +RTFParser::CellInfo& RTFParser::CellInfoAt(int i) +{ + return state.cellinfo.At(i, std_cell_info); +} + +void RTFParser::SetCellMargin(Cell& out, int Rect::*mbr) +{ + if(out.info.cellmarginunits.*mbr == 0) { + out.info.format.margin.*mbr = state.trgaph; + if(state.rowmarginunits.*mbr == 3) + out.info.format.margin.*mbr = state.rowmargin.*mbr; + if(state.rowspacingunits.*mbr == 3) + out.info.format.margin.*mbr += state.rowspacing.*mbr; + } +} + +void RTFParser::ReadTableStyle() +{ + if(PassQ("nesttableprops")) { + state.nestprop = true; + return; + } + if(PassQ("nonesttables")) { + SkipGroup(); + return; + } + int itap = (state.nestprop ? state.itap : 1); + if(PassQ("trowd")) { + OpenTable(itap); + table_stack[itap - 1].stylecol = 0; + return; + } + if(PassQ("row") && table_stack.GetCount() >= 1) { + TableState& ts0 = table_stack[0]; + ts0.textcol = ts0.stylecol = 0; + ts0.cells.Add(); + return; + } + if(PassQ("nestrow") && table_stack.GetCount() >= state.itap) { + TableState& ts = table_stack[state.itap - 1]; + ts.textcol = ts.stylecol = 0; + ts.cells.Add(); + return; + } + if(itap > table_stack.GetCount()) + return; + TableState& ts = table_stack[itap - 1]; + if(PassQ("trgaph")) + state.trgaph = TwipDotsLim(command_arg); + else if(PassQ("trql")) {} + else if(PassQ("trqr")) {} + else if(PassQ("trqc")) {} + else if(PassQ("trleft")) { + ts.tableformat.lm = TwipDotsLim(command_arg); + } + else if(PassQ("trbrdrl")) {} + else if(PassQ("trbrdrt")) {} + else if(PassQ("trbrdrr")) {} + else if(PassQ("trbrdrb")) {} + else if(PassQ("trbrdrv")) {} + else if(PassQ("trftsWidth")) {} + else if(PassQ("trautofit")) {} + else if(PassQ("trpaddl")) + state.rowmargin.left = TwipDotsLim(command_arg); + else if(PassQ("trpaddt")) + state.rowmargin.top = TwipDotsLim(command_arg); + else if(PassQ("trpaddr")) + state.rowmargin.right = TwipDotsLim(command_arg); + else if(PassQ("trpaddb")) + state.rowmargin.bottom = TwipDotsLim(command_arg); + else if(PassQ("trpaddfl")) + state.rowmarginunits.left = command_arg; + else if(PassQ("trpaddft")) + state.rowmarginunits.top = command_arg; + else if(PassQ("trpaddfr")) + state.rowmarginunits.right = command_arg; + else if(PassQ("trpaddfb")) + state.rowmarginunits.bottom = command_arg; + else if(PassQ("trspdl")) + state.rowspacing.left = TwipDotsLim(command_arg); + else if(PassQ("trspdt")) + state.rowspacing.top = TwipDotsLim(command_arg); + else if(PassQ("trspdr")) + state.rowspacing.right = TwipDotsLim(command_arg); + else if(PassQ("trspdb")) + state.rowspacing.bottom = TwipDotsLim(command_arg); + else if(PassQ("trspdfl")) + state.rowspacingunits.left = command_arg; + else if(PassQ("trspdft")) + state.rowspacingunits.top = command_arg; + else if(PassQ("trspdfr")) + state.rowspacingunits.right = command_arg; + else if(PassQ("trspdfb")) + state.rowspacingunits.bottom = command_arg; + else if(PassQ("clpadl")) + CellInfoAt(ts.stylecol).format.margin.left = TwipDotsLim(command_arg); + else if(PassQ("clpadt")) + CellInfoAt(ts.stylecol).format.margin.top = TwipDotsLim(command_arg); + else if(PassQ("clpadr")) + CellInfoAt(ts.stylecol).format.margin.right = TwipDotsLim(command_arg); + else if(PassQ("clpadb")) + CellInfoAt(ts.stylecol).format.margin.bottom = TwipDotsLim(command_arg); + else if(PassQ("clpadfl")) + state.cellmarginunits.left = command_arg; + else if(PassQ("clpadft")) + state.cellmarginunits.top = command_arg; + else if(PassQ("clpadfr")) + state.cellmarginunits.right = command_arg; + else if(PassQ("clpadfb")) + state.cellmarginunits.bottom = command_arg; + else if(PassQ("clbrdrl")) + ReadCellBorder(CellInfoAt(ts.stylecol).format.border.left); + else if(PassQ("clbrdrt")) + ReadCellBorder(CellInfoAt(ts.stylecol).format.border.top); + else if(PassQ("clbrdrr")) + ReadCellBorder(CellInfoAt(ts.stylecol).format.border.right); + else if(PassQ("clbrdrb")) + ReadCellBorder(CellInfoAt(ts.stylecol).format.border.bottom); + else if(PassQ("cltxlrtb")) {} + else if(PassQ("clshdng")) + CellInfoAt(ts.stylecol).shading = command_arg; + else if(PassQ("clcbpat")) { + if(command_arg >= 0 && command_arg < color_table.GetCount()) + CellInfoAt(ts.stylecol).format.color = color_table[command_arg]; + } + else if(PassQ("clvmrg")) + CellAt(ts, ts.stylecol).merge = true; + else if(PassQ("clvmgf")) + CellAt(ts, ts.stylecol).merge_first = true; + else if(PassQ("clftsWidth")) {} + else if(PassQ("clwWidth")) {} + else if(PassQ("cellx")) { + int sx = ts.stylecol++; + Cell& newcell = CellAt(ts, sx); + newcell.info.end_dots = TwipDotsLim(command_arg); + SetCellMargin(newcell, &Rect::left); + SetCellMargin(newcell, &Rect::top); + SetCellMargin(newcell, &Rect::right); + SetCellMargin(newcell, &Rect::bottom); + CellInfoAt(sx) = newcell.info; + //CellFormat(sx) = std_cell_format; + } + else if(PassQ("clvertalt")) + CellInfoAt(ts.stylecol).format.align = ALIGN_TOP; + else if(PassQ("clvertalc")) + CellInfoAt(ts.stylecol).format.align = ALIGN_CENTER; + else if(PassQ("clvertalb")) + CellInfoAt(ts.stylecol).format.align = ALIGN_BOTTOM; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/CtrlCore/TopWinX11.cpp b/uppsrc/CtrlCore/TopWinX11.cpp index 7cd4c9687..d2ac4ff86 100644 --- a/uppsrc/CtrlCore/TopWinX11.cpp +++ b/uppsrc/CtrlCore/TopWinX11.cpp @@ -64,13 +64,13 @@ void TopWindow::EventProc(XWindow& w, XEvent *event) if(event->type == PropertyNotify && event->xproperty.atom == XAtom("_NET_WM_STATE")) { LLOG("_NET_WM_STATE notify"); Vector p = GetPropertyInts(GetWindow(), XAtom("_NET_WM_STATE")); - if(FindIndex(p, XAtom("_NET_WM_STATE_HIDDEN")) >= 0) { + if(FindIndex(p, (int)XAtom("_NET_WM_STATE_HIDDEN")) >= 0) { state = MINIMIZED; LLOG("MINIMIZED"); } else - if(FindIndex(p, XAtom("_NET_WM_STATE_MAXIMIZED_HORZ")) >= 0 && - FindIndex(p, XAtom("_NET_WM_STATE_MAXIMIZED_VERT")) >= 0) { + if(FindIndex(p, (int)XAtom("_NET_WM_STATE_MAXIMIZED_HORZ")) >= 0 && + FindIndex(p, (int)XAtom("_NET_WM_STATE_MAXIMIZED_VERT")) >= 0) { state = MAXIMIZED; LLOG("MAXIMIZED"); } diff --git a/uppsrc/CtrlCore/X11App.cpp b/uppsrc/CtrlCore/X11App.cpp index c56690c90..c2a84a112 100644 --- a/uppsrc/CtrlCore/X11App.cpp +++ b/uppsrc/CtrlCore/X11App.cpp @@ -155,8 +155,12 @@ void Ctrl::UntrapX11Errors(bool b) void sPanicMessageBox(const char *title, const char *text) { - write(2, text, strlen(text)); - write(2, "\n", 1); + IGNORE_RESULT( + write(2, text, strlen(text)) + ); + IGNORE_RESULT( + write(2, "\n", 1) + ); if(Ctrl::grabWindow) { LLOG("RELEASE GRAB"); XUngrabPointer(Xdisplay, CurrentTime); diff --git a/uppsrc/CtrlCore/X11DHCtrl.cpp b/uppsrc/CtrlCore/X11DHCtrl.cpp index 0b12b372b..2ef3f2e4f 100644 --- a/uppsrc/CtrlCore/X11DHCtrl.cpp +++ b/uppsrc/CtrlCore/X11DHCtrl.cpp @@ -385,11 +385,12 @@ XVisualInfo DHCtrl::GetVisualInfo(void) return *UserVisualInfo; XVisualInfo visualInfo; + memset(&visualInfo, 0, sizeof(visualInfo)); // get the active visual Visual *visual = GetVisual(); - // gets a list of all available XVsualinfo + // gets a list of all available XVisualinfo XVisualInfo *v = 0; XVisualInfo vtemplate; int nVis; diff --git a/uppsrc/CtrlCore/X11Wnd.cpp b/uppsrc/CtrlCore/X11Wnd.cpp index a949638d5..aa46a5d53 100644 --- a/uppsrc/CtrlCore/X11Wnd.cpp +++ b/uppsrc/CtrlCore/X11Wnd.cpp @@ -402,7 +402,7 @@ void Ctrl::EventLoop0(Ctrl *ctrl) LoopLevel++; int64 loopno = ++EventLoopNo; LLOG("Entering event loop at level " << LoopLevel << LOG_BEGIN); - Ctrl *ploop; + Ctrl *ploop = NULL; if(ctrl) { ploop = LoopCtrl; LoopCtrl = ctrl; diff --git a/uppsrc/CtrlLib/ChGtk0.cpp b/uppsrc/CtrlLib/ChGtk0.cpp index d2237ebb3..c72e42c01 100644 --- a/uppsrc/CtrlLib/ChGtk0.cpp +++ b/uppsrc/CtrlLib/ChGtk0.cpp @@ -74,7 +74,7 @@ Image GetGTK(GtkWidget *widget, int state, int shadow, const char *detail, int t Rect rect) { MemoryIgnoreLeaksBlock __; - GdkPixbuf *icon; + GdkPixbuf *icon = NULL; if(type == GTK_ICON || type == GTK_THEMEICON) { gtk_widget_set_sensitive(widget, 1); gtk_widget_set_state(widget, GTK_STATE_NORMAL); diff --git a/uppsrc/CtrlLib/DateTimeCtrl.cpp b/uppsrc/CtrlLib/DateTimeCtrl.cpp index 5dd340d83..f6beab153 100644 --- a/uppsrc/CtrlLib/DateTimeCtrl.cpp +++ b/uppsrc/CtrlLib/DateTimeCtrl.cpp @@ -1437,7 +1437,6 @@ void FlatSpin::SetCallbacks(const Callback &cbl, const Callback& cbr) void FlatSpin::Layout() { - Size sz = GetSize(); left.LeftPos(0, left.GetImage().GetSize().cx + 8).VSizePos(); right.RightPos(0, right.GetImage().GetSize().cx + 8).VSizePos(); } diff --git a/uppsrc/CtrlLib/DlgColor.cpp b/uppsrc/CtrlLib/DlgColor.cpp index 00d7e662c..19c27f21f 100644 --- a/uppsrc/CtrlLib/DlgColor.cpp +++ b/uppsrc/CtrlLib/DlgColor.cpp @@ -343,7 +343,6 @@ int WheelRampCtrl::LevelToClient(int l) const void WheelRampCtrl::Paint(Draw& draw) { if(!cache || cache.GetSize() != wheel_rect.GetSize() || cache_level != (ramp ? h16 : v16)) { - Size size = max(GetSize(), Size(1, 1)); cache = ramp ? PaintRamp(wheel_rect.GetSize()) : PaintWheel(wheel_rect.GetSize()); cache_level = (ramp ? h16 : v16); } @@ -469,7 +468,6 @@ enum { PREC = 64 }; Image WheelRampCtrl::PaintRamp(Size size) { ImageDraw iw(size); - Size rcsize = wheel_rect.Size(); ImageBuffer ib(PREC, PREC); for(int y = 0; y < PREC; y++) { RGBA *scan = ib[y]; diff --git a/uppsrc/CtrlLib/EditField.cpp b/uppsrc/CtrlLib/EditField.cpp index 0548881be..c3333c027 100644 --- a/uppsrc/CtrlLib/EditField.cpp +++ b/uppsrc/CtrlLib/EditField.cpp @@ -381,7 +381,6 @@ Rect EditField::GetCaretRect(int pos) const void EditField::SyncCaret() { - FontInfo fi = font.Info(); Rect r = GetCaretRect(cursor); SetCaret(r.left, r.top, r.GetWidth(), r.GetHeight()); } diff --git a/uppsrc/CtrlLib/LabelBase.cpp b/uppsrc/CtrlLib/LabelBase.cpp index b497bf15b..be9ad0b43 100644 --- a/uppsrc/CtrlLib/LabelBase.cpp +++ b/uppsrc/CtrlLib/LabelBase.cpp @@ -509,7 +509,7 @@ DisplayPopup::DisplayPopup() ONCELOCK { InstallStateHook(StateHook); } - LinkBefore(all); + LinkBefore(all()); } DisplayPopup::~DisplayPopup() @@ -526,7 +526,7 @@ void DisplayPopup::Sync() if(sz.cx + 2 * margin > item.GetWidth() || sz.cy > item.GetHeight()) { slim = item + ctrl->GetScreenView().TopLeft(); if(slim.Contains(GetMousePos())) { - Rect wa = GetWorkArea(); +// Rect wa = GetWorkArea(); Rect r = item; r.right = max(r.right, r.left + sz.cx + 2 * margin); r.bottom = max(r.bottom, r.top + sz.cy); @@ -545,12 +545,16 @@ void DisplayPopup::Sync() Close(); } -Link DisplayPopup::all[1]; +Link *DisplayPopup::all() +{ + static Link all; + return &all; +} bool DisplayPopup::StateHook(Ctrl *, int reason) { if(reason == FOCUS) - for(DisplayPopup *p = all->Link::GetNext(); p != all; p = p->Link::GetNext()) + for(DisplayPopup *p = all()->Link::GetNext(); p != all(); p = p->Link::GetNext()) p->Sync(); return false; } diff --git a/uppsrc/CtrlLib/LabelBase.h b/uppsrc/CtrlLib/LabelBase.h index 284e2fa09..4def3e11c 100644 --- a/uppsrc/CtrlLib/LabelBase.h +++ b/uppsrc/CtrlLib/LabelBase.h @@ -127,7 +127,7 @@ private: Point Op(Point p); void Sync(); - static Link all[1]; + static Link *all(); static bool StateHook(Ctrl *, int reason); public: diff --git a/uppsrc/CtrlLib/RichTextView.cpp b/uppsrc/CtrlLib/RichTextView.cpp index da1e81a2c..53bbe7ad0 100644 --- a/uppsrc/CtrlLib/RichTextView.cpp +++ b/uppsrc/CtrlLib/RichTextView.cpp @@ -35,11 +35,6 @@ void RichTextView::Paint(Draw& w) pi.usecache = true; pi.sizetracking = sizetracking; pi.shrink_oversized_objects = shrink_oversized_objects; - int y = 0; - if(vcenter && sb.GetTotal() < sb.GetPage()) { - PageY py = text.GetHeight(GetPage()); - y = (sz.cy / zoom - py.y) / 2; - } Color c = SColorPaper(); if(Grayscale(c) < 100) pi.coloroverride = true; diff --git a/uppsrc/CtrlLib/TabCtrl.cpp b/uppsrc/CtrlLib/TabCtrl.cpp index 91943f725..ba5516804 100644 --- a/uppsrc/CtrlLib/TabCtrl.cpp +++ b/uppsrc/CtrlLib/TabCtrl.cpp @@ -129,7 +129,6 @@ void TabCtrl::Layout() for(int i = 0; i < tab.GetCount(); i++) if(tab[i].ctrl) Ctrl::Add(*tab[i].ctrl); - Size sz = GetSize(); int th = style->tabheight + style->sel.top; tabs.TopPos(0, th + style->sel.bottom) .HSizePos(0, style->sel.left + style->sel.right); @@ -174,7 +173,6 @@ void TabCtrl::PaintTabs(Draw& w) int th = style->tabheight + tt; Size sz = GetSize(); ChPaint(w, 0, th, sz.cx, sz.cy - th, style->body); - Size chsz = GetTextSize("M", style->font); for(int phase = 0; phase < 2; phase++) { for(int i = tab.GetCount() - 1; i >= 0; i--) if((sel == i) == phase) { diff --git a/uppsrc/CtrlLib/TreeCtrl.cpp b/uppsrc/CtrlLib/TreeCtrl.cpp index e665ef1a4..07fd9a606 100644 --- a/uppsrc/CtrlLib/TreeCtrl.cpp +++ b/uppsrc/CtrlLib/TreeCtrl.cpp @@ -1037,7 +1037,6 @@ Image TreeCtrl::GetDragSample() Size msz = m.GetSize(display); Size isz = m.image.GetSize(); Size vsz = m.GetValueSize(display); - Rect r = RectC(0, y, vsz.cx + 2 * m.margin, msz.cy); int x = 0; if(IsSel(l.itemi)) { iw.DrawImage(x, y + (msz.cy - isz.cy) / 2, m.image); diff --git a/uppsrc/Draw/Drawing.cpp b/uppsrc/Draw/Drawing.cpp index 38a97c18b..3a8158c18 100644 --- a/uppsrc/Draw/Drawing.cpp +++ b/uppsrc/Draw/Drawing.cpp @@ -1,692 +1,692 @@ -#include "Draw.h" - -NAMESPACE_UPP - -enum { - DRAWING_BEGIN = 1, - DRAWING_OFFSET = 2, - DRAWING_CLIP = 3, - DRAWING_CLIPOFF = 4, - DRAWING_EXCLUDECLIP = 5, - DRAWING_INTERSECTCLIP = 6, - DRAWING_END = 7, - DRAWING_DRAWRECT = 8, - DRAWING_DRAWIMAGE = 9, - DRAWING_DRAWMONOIMAGE = 10, - DRAWING_DRAWDRAWING = 11, - DRAWING_DRAWLINE = 12, - DRAWING_DRAWELLIPSE = 13, - DRAWING_DRAWTEXT = 14, - DRAWING_DRAWARC = 15, - DRAWING_DRAWPOLYPOLYLINE = 16, - DRAWING_DRAWPOLYPOLYPOLYGON = 17, - DRAWING_DRAWDATA = 18, - DRAWING_DRAWPAINTING = 19, -}; - - -#define LLOG(x) // DLOG(x) - -#ifdef COMPILER_MSC -#pragma warning(disable: 4700) -#endif - -static void StreamUnpackPoints(Stream& stream, Point *out, int count) -{ - if(count == 0) - return; -#ifdef CPU_LITTLE_ENDIAN - if(sizeof(Point) == 8) { - stream.Get(out, 8 * count); - return; - } -#endif - Point *end = out + count; - byte *top = reinterpret_cast(end) - count * 8; - stream.Get(top, count * 8); - for(; out < end; out++, top += 8) { - out -> x = (short)Peek32le(top + 0); - out -> y = (short)Peek32le(top + 4); - } -} - -static void StreamPackPoints(Stream& stream, const Point *in, int count) -{ -#ifdef CPU_LITTLE_ENDIAN - if(sizeof(Point) == 8) { - stream.Put(in, 8 * count); - return; - } -#endif - enum { PART = 1024 }; - byte part[PART * 8]; - while(count > 0) - { - int part_count = min(count, PART); - for(byte *pp = part, *pe = pp + 8 * part_count; pp < pe; pp += 8, in++) { - Poke32le(pp + 0, in -> x); - Poke32le(pp + 4, in -> y); - } - stream.Put(part, part_count * 4); - count -= part_count; - } -} - -static void StreamUnpackInts(Stream& stream, int *out, int count) -{ -#ifdef CPU_LITTLE_ENDIAN - if(sizeof(int) == 4) { - stream.Get(out, count * 4); - return; - } -#endif - while(count--) - *out++ = stream.Get32le(); -} - -static void StreamPackInts(Stream& stream, const int *in, int count) -{ -#ifdef CPU_LITTLE_ENDIAN - if(sizeof(int) == 4) { - stream.Put(in, count * 4); - return; - } -#endif - while(count--) - stream.Put32le(*in++); -} - -// ------------------------------ - -Stream& DrawingDraw::DrawingOp(int code) -{ - drawing / code; - return drawing; -} - -dword DrawingDraw::GetInfo() const -{ - return dots ? DOTS : 0; -} - -Size DrawingDraw::GetPageSize() const -{ - return size; -} - -Rect DrawingDraw::GetPaintRect() const -{ - return Rect(size); -} - -void DrawingDraw::BeginOp() -{ - DrawingOp(DRAWING_BEGIN); -} - -void DrawingDraw::OffsetOp(Point p) -{ - DrawingOp(DRAWING_OFFSET) % p; -} - -bool DrawingDraw::ClipOp(const Rect& r) -{ - DrawingOp(DRAWING_CLIP) % const_cast(r); - return true; -} - -bool DrawingDraw::ClipoffOp(const Rect& r) -{ - DrawingOp(DRAWING_CLIPOFF) % const_cast(r); - return true; -} - -void DrawingDraw::EndOp() -{ - DrawingOp(DRAWING_END); -} - -bool DrawingDraw::ExcludeClipOp(const Rect& r) -{ - DrawingOp(DRAWING_EXCLUDECLIP) % const_cast(r); - return true; -} - -bool DrawingDraw::IntersectClipOp(const Rect& r) -{ - DrawingOp(DRAWING_INTERSECTCLIP) % const_cast(r); - return true; -} - -bool DrawingDraw::IsPaintingOp(const Rect& r) const -{ - return true; -} - -void DrawingDraw::DrawRectOp(int x, int y, int cx, int cy, Color color) -{ - if(!IsNull(color)) - DrawingOp(DRAWING_DRAWRECT) % x % y % cx % cy % color; -} - -void DrawingDraw::DrawImageOp(int x, int y, int cx, int cy, const Image& img, const Rect& src, Color color) -{ - Rect s = src; - DrawingOp(DRAWING_DRAWIMAGE) % x % y % cx % cy % s % color; - val.Add(img); -} - -void DrawingDraw::DrawDataOp(int x, int y, int cx, int cy, const String& data, const char *id) -{ - String h = id; - DrawingOp(DRAWING_DRAWDATA) % x % y % cx % cy % h; - val.Add(data); -} - -void DrawingDraw::DrawDrawingOp(const Rect& target, const Drawing& w) -{ - DrawingOp(DRAWING_DRAWDRAWING) % const_cast(target); - val.Add(w); -} - -void DrawingDraw::DrawPaintingOp(const Rect& target, const Painting& w) -{ - DrawingOp(DRAWING_DRAWPAINTING) % const_cast(target); - val.Add(w); -} - -void DrawingDraw::DrawLineOp(int x1, int y1, int x2, int y2, int width, Color color) -{ - DrawingOp(DRAWING_DRAWLINE) % x1 % y1 % x2 % y2 % width % color; -} - -void DrawingDraw::DrawPolyPolylineOp(const Point *vertices, int vertex_count, - const int *counts, int count_count, - int width, Color color, Color doxor) -{ - ASSERT(count_count > 0 && vertex_count > 0); - if(vertex_count < 2 || IsNull(color)) - return; - DrawingOp(DRAWING_DRAWPOLYPOLYLINE); - int version = 2; - drawing / version; - drawing % width % color % doxor; - drawing % vertex_count % count_count; - StreamPackPoints(drawing, vertices, vertex_count); - StreamPackInts(drawing, counts, count_count); -} - -void DrawingDraw::DrawPolyPolyPolygonOp(const Point *vertices, int vertex_count, - const int *subpolygon_counts, int subpolygon_count_count, - const int *disjunct_polygon_counts, int disjunct_polygon_count_count, - Color color, int width, Color outline, uint64 pattern, Color doxor) -{ - if(vertex_count == 0) - return; - DrawingOp(DRAWING_DRAWPOLYPOLYPOLYGON); - int version = 2; - drawing / version; - drawing % color % width % outline % pattern % doxor; - drawing % vertex_count % subpolygon_count_count % disjunct_polygon_count_count; - StreamPackPoints(drawing, vertices, vertex_count); - StreamPackInts(drawing, subpolygon_counts, subpolygon_count_count); - StreamPackInts(drawing, disjunct_polygon_counts, disjunct_polygon_count_count); -} - -void DrawingDraw::DrawEllipseOp(const Rect& r, Color color, int pen, Color pencolor) -{ - DrawingOp(DRAWING_DRAWELLIPSE) % const_cast(r) % color / pen % pencolor; -} - -void DrawingDraw::DrawArcOp(const Rect& rc, Point start, Point end, int width, Color color) -{ - DrawingOp(DRAWING_DRAWARC) % const_cast(rc) % start % end % color % width; -} - -void DrawingDraw::DrawTextOp(int x, int y, int angle, const wchar *text, Font font, Color ink, - int n, const int *dx) { - if(IsNull(ink)) return; - if(n < 0) - n = wstrlen((const wchar *)text); - if(n == 0) - return; - Stream& s = DrawingOp(DRAWING_DRAWTEXT); - byte cs = CHARSET_UNICODE; - s % x % y % angle % font % ink / n % cs; - s.PutW((wchar *)text, n); - bool dxb = dx; - s % dxb; - if(dx) { - int *w = const_cast(dx); - while(n--) - s / *w++; - } -} - -Drawing DrawingDraw::GetResult() -{ - Drawing out; - out.size = size; - LLOG("GetResult size: " << size); - out.data = drawing.GetResult(); - out.val = val; - return out; -} - -// ------------------------------ - -struct Draw::DrawingPos : StringStream { - Size source; - Size target; - Point srcoff; - Point trgoff; - - Vector stk; - - bool Identity() const { return source == target; } - - int GetX(int x) const; - int GetY(int y) const; - int GetCx(int cx) const; - int GetCy(int cy) const; - int GetW(int w) const; - Point Get(int x, int y) const; - Point Get(Point p) const; - Rect Get(const Rect& r) const; - Rect Get(int x, int y, int cx, int cy) const; - - Point operator()(int x, int y) const { return Get(x, y); } - Point operator()(Point p) const { return Get(p); } - Rect operator()(const Rect& r) const { return Get(r); } - Rect operator()(int x, int y, int cx, int cy) const { return Get(x, y, cx, cy); } - - void TransformX(int& x) const { x = GetX(x); } - void TransformY(int& y) const { y = GetY(y); } - void TransformW(int& w) const { w = GetW(w); } - void Transform(int& x, int& y) const { TransformX(x); TransformY(y); } - void Transform(Point& p) const { p = Get(p); } - void Transform(Rect& r) const { r = Get(r); } - - Rect GetRect(); - - void Push(); - void Pop(); - - DrawingPos(const String& src) : StringStream(src) {} -}; - -void Draw::DrawingPos::Push() -{ - stk.Add(srcoff); - stk.Add(trgoff); -} - -void Draw::DrawingPos::Pop() -{ - trgoff = stk.Pop(); - srcoff = stk.Pop(); -} - -Rect Draw::DrawingPos::GetRect() -{ - Rect r; - *this % r; - return Get(r); -} - -int Draw::DrawingPos::GetX(int x) const { - return iscale(x + srcoff.x, target.cx, source.cx) - trgoff.x; -} - -int Draw::DrawingPos::GetY(int y) const { - return iscale(y + srcoff.y, target.cy, source.cy) - trgoff.y; -} - -int Draw::DrawingPos::GetCx(int cx) const { - return iscale(cx, target.cx, source.cx); -} - -int Draw::DrawingPos::GetCy(int cy) const { - return iscale(cy, target.cy, source.cy); -} - -int Draw::DrawingPos::GetW(int w) const { - return iscale(w, target.cx + target.cy, source.cx + source.cy); -} - -Point Draw::DrawingPos::Get(int x, int y) const { - return Point(GetX(x), GetY(y)); -} - -Point Draw::DrawingPos::Get(Point p) const { - return Get(p.x, p.y); -} - -Rect Draw::DrawingPos::Get(const Rect& r) const { - return Rect(GetX(r.left), GetY(r.top), GetX(r.right), GetY(r.bottom)); -} - -Rect Draw::DrawingPos::Get(int x, int y, int cx, int cy) const { - return Get(RectC(x, y, cx, cy)); -} - -void Draw::DrawDrawingOp(const Rect& target, const Drawing& w) { -#ifdef _DEBUG - int cl = GetCloffLevel(); -#endif - DrawingPos ps(w.data); - ps.srcoff = ps.trgoff = Point(0, 0); - ps.target = target.Size(); - ps.source = w.size; - LLOG("DrawDrawingOp size: " << w.size); - if(ps.target.cx == 0 || ps.target.cy == 0 || ps.source.cx == 0 || ps.source.cy == 0) - return; - Clipoff(target); - Rect r, r1; - int x, y, cx, cy, width, vertex_count, count_count; - Color color, pencolor, doxor; - Image img; - Drawing dw; - Painting sw; - Point p, p1; - int vi = 0; - while(!ps.IsEof()) { - int code; - ps / code; - switch(code) { - case DRAWING_BEGIN: - Begin(); - ps.Push(); - break; - case DRAWING_OFFSET: - ps % p; - p1 = ps(p); - Offset(p1); - ps.Push(); - ps.srcoff += p; - ps.trgoff += p1; - break; - case DRAWING_CLIP: - Clip(ps.GetRect()); - ps.Push(); - break; - case DRAWING_CLIPOFF: - ps % r; - r1 = ps(r); - Clipoff(r1); - ps.Push(); - ps.srcoff += r.TopLeft(); - ps.trgoff += r1.TopLeft(); - break; - case DRAWING_EXCLUDECLIP: - ExcludeClip(ps.GetRect()); - break; - case DRAWING_INTERSECTCLIP: - IntersectClip(ps.GetRect()); - break; - case DRAWING_END: - End(); - ps.Pop(); - break; - case DRAWING_DRAWRECT: - ps % x % y % cx % cy % color; - DrawRect(ps(x, y, cx, cy), color); - break; - case DRAWING_DRAWIMAGE: - ps % x % y % cx % cy; - if(w.val.GetCount()) - img = w.val[vi++]; - else - ps % img; - ps % r % color; - DrawImageOp(ps.GetX(x), ps.GetY(y), ps.GetCx(cx), ps.GetCy(cy), img, r, color); - break; - case DRAWING_DRAWDATA: - { - String data, id; - ps % x % y % cx % cy % id; - if(w.val.GetCount()) - data = w.val[vi++]; - else - ps % data; - DrawData(ps(x, y, cx, cy), data, id); - } - break; - case DRAWING_DRAWDRAWING: - if(w.val.GetCount()) - dw = w.val[vi++]; - else - ps % dw; - DrawDrawing(ps.GetRect(), dw); - break; - case DRAWING_DRAWPAINTING: - if(w.val.GetCount()) - sw = w.val[vi++]; - else - ps % sw; - DrawPainting(ps.GetRect(), sw); - break; - case DRAWING_DRAWLINE: - ps % x % y % cx % cy % width % color; - DrawLine(ps(x, y), ps(cx, cy), width > 0 ? ps.GetW(width) : width, color); - break; - case DRAWING_DRAWELLIPSE: - r = ps.GetRect(); - ps % color / width % pencolor; - DrawEllipse(r, color, width > 0 ? ps.GetW(width) : width, pencolor); - break; -#ifndef PLATFORM_WINCE - case DRAWING_DRAWARC: - r = ps.GetRect(); - ps % p % p1 % color % width; - DrawArc(r, ps(p), ps(p1), width > 0 ? ps.GetW(width) : width, color); - break; - case DRAWING_DRAWPOLYPOLYLINE: - { - int version; - ps / version; - ps % width % color % doxor; - ps % vertex_count % count_count; - Buffer vertices(vertex_count); - Buffer counts(count_count); - StreamUnpackPoints(ps, vertices, vertex_count); - StreamUnpackInts(ps, counts, count_count); - if(!ps.Identity()) { - for(Point *p = vertices, *e = p + vertex_count; p < e; p++) - ps.Transform(*p); - if(width > 0) - ps.TransformW(width); - } - DrawPolyPolyline(vertices, vertex_count, counts, count_count, width, color, doxor); - } - break; - case DRAWING_DRAWPOLYPOLYPOLYGON: - { - Color outline; - uint64 pattern; - int subpolygon_count_count, disjunct_polygon_count_count; - int version = 2; - ps / version; - ps % color % width % outline % pattern % doxor; - ps % vertex_count % subpolygon_count_count % disjunct_polygon_count_count; - Buffer vertices(vertex_count); - Buffer subpolygon_counts(subpolygon_count_count); - Buffer disjunct_polygon_counts(disjunct_polygon_count_count); - StreamUnpackPoints(ps, vertices, vertex_count); - StreamUnpackInts(ps, subpolygon_counts, subpolygon_count_count); - StreamUnpackInts(ps, disjunct_polygon_counts, disjunct_polygon_count_count); - if(!ps.Identity()) { - for(Point *p = vertices, *e = p + vertex_count; p < e; p++) - ps.Transform(*p); - ps.TransformW(width); - } - DrawPolyPolyPolygon(vertices, vertex_count, - subpolygon_counts, subpolygon_count_count, - disjunct_polygon_counts, disjunct_polygon_count_count, - color, width, outline, pattern, doxor); - } - break; -#endif - case DRAWING_DRAWTEXT: - { - int n, angle; - Font font; - Color ink; - byte cs; - ps % x % y % angle % font % ink / n % cs; - if(font.GetHeight() == 0) { - FontInfo fi = font.Info(); - font.Height(fi.GetHeight() - fi.GetInternal()); - } - bool unicode = cs == CHARSET_UNICODE; - WString text; - if(unicode) { - Buffer txt(n); - ps.Stream::GetW(txt, n); - text = WString(txt, n); - } - else { - Buffer txt(n); - ps.Stream::Get(txt, n); - text = ToUnicode(txt, n, cs); - } - LLOG("wsDrawText \"" << WString(text, n) - << "\" at: (" << x << ", " << y << ", " << angle << ")"); - bool dxb; - ps % dxb; - Buffer dx(n); - int *wd = dx; - int nn = n; - angle %= 3600; - if(ps.Identity()) { - if(dxb) { - while(nn--) - ps / *wd++; - DrawText(x, y, angle, text, font, ink, dx); - } - else - DrawText(x, y, angle, text, font, ink); - } - else { - const wchar *wp = ~text; - double q = Length((Sizef)ps.target) / Length((Sizef)ps.source); - double px = 0; - while(nn--) { - int cx; - if(dxb) - ps / cx; - else - cx = font[*wp++]; - double npx = q * cx + px; - *wd++ = (int)npx - (int)px; - px = npx; - } - int ht = (int)(font.GetHeight() * min(double(ps.target.cx) / ps.source.cx, double(ps.target.cy) / ps.source.cy)); - font.Width((int)q * font.GetWidth()).Height(ht ? ht : 1); - DrawText(ps.GetX(x), ps.GetY(y), angle, text, font, ink, dx); - /* - FontInfo fi = font.Info(); - const wchar *wp = ~text; - int odd = (angle / 900) & 1; - double ang = (double) (angle % 900) * M_2PI / 3600; - double sx = (double) ps.target.cx / ps.source.cx; - double sy = (double) ps.target.cy / ps.source.cy; - double ang2 = atan((odd ? sx / sy : sy / sx) * tan(ang)); - DDUMP(odd); - DDUMP(cx); - DDUMP(sy); - DDUMP(sin(ang)); - DDUMP(sin(ang2)); - double q = (odd ? sx : sy) * sin(ang) / sin(ang2); - double error = 0; - while(nn--) { - int cx; - if(dxb) { - ps / cx; - DDUMP(cx); - } - else - cx = fi[*wp++]; - double ncx = q * cx + error; - DDUMP(q * cx); - *wd++ = cx = (int) ncx; - error = ncx - cx; - } - int ht = (int)(fi.GetFontHeight() * (sx * sin(ang) * sin(ang2) + sy * cos(ang) * cos(ang2))); - font.Width(int(q * fi.GetAveWidth())).Height(ht ? ht : 1); - DrawText(ps.GetX(x), ps.GetY(y), int(ang2 * 3600 / M_2PI) + (angle / 900) * 900, - text, font, ink, dx); - */ - } - } - } - } -// LOGEND(); - End(); -#ifdef _DEBUG - ASSERT(GetCloffLevel() == cl); -#endif -} - -void Draw::DrawDrawing(int x, int y, int cx, int cy, const Drawing& w) { - DrawDrawing(RectC(x, y, cx, cy), w); -} - -void DrawingDraw::Create(int cx, int cy, bool dots_) { - Create(Size(cx, cy), dots_); -} - -void DrawingDraw::Create(Size sz, bool dots_) { - drawing.Create(); - size = sz; - dots = dots_; - val.Clear(); - LLOG("DrawingDraw::Create, sz = " << sz << ", dots = " << dots << " -> clip = " << GetClip()); -} - -DrawingDraw::DrawingDraw() -{ -} - -DrawingDraw::DrawingDraw(Size sz, bool dots_) { - Create(sz, dots_); -} - -DrawingDraw::DrawingDraw(int cx, int cy, bool dots_) { - Create(cx, cy, dots_); -} - -Size Drawing::RatioSize(int cx, int cy) const { - return GetRatioSize(GetSize(), cx, cy); -} - -void Drawing::Append(Drawing& dw) -{ - if(IsNull(size)) - size = dw.size; - data << dw.data; - for(int i = 0; i < dw.val.GetCount(); i++) - val.Add(dw.val[i]); -} - -Drawing::Drawing(const Value& src) -{ - if(IsNull(src)) - size = Null; - else - *this = RichValue::Extract(src); -} - -void Drawing::Serialize(Stream& s) -{ - if(val.GetCount()) - size.cy |= 0x80000000; - s % size; - s % data; - if(size.cy & 0x80000000) { - size.cy &= ~0x80000000; - s % val; - } -} - -END_UPP_NAMESPACE +#include "Draw.h" + +NAMESPACE_UPP + +enum { + DRAWING_BEGIN = 1, + DRAWING_OFFSET = 2, + DRAWING_CLIP = 3, + DRAWING_CLIPOFF = 4, + DRAWING_EXCLUDECLIP = 5, + DRAWING_INTERSECTCLIP = 6, + DRAWING_END = 7, + DRAWING_DRAWRECT = 8, + DRAWING_DRAWIMAGE = 9, + DRAWING_DRAWMONOIMAGE = 10, + DRAWING_DRAWDRAWING = 11, + DRAWING_DRAWLINE = 12, + DRAWING_DRAWELLIPSE = 13, + DRAWING_DRAWTEXT = 14, + DRAWING_DRAWARC = 15, + DRAWING_DRAWPOLYPOLYLINE = 16, + DRAWING_DRAWPOLYPOLYPOLYGON = 17, + DRAWING_DRAWDATA = 18, + DRAWING_DRAWPAINTING = 19, +}; + + +#define LLOG(x) // DLOG(x) + +#ifdef COMPILER_MSC +#pragma warning(disable: 4700) +#endif + +static void StreamUnpackPoints(Stream& stream, Point *out, int count) +{ + if(count == 0) + return; +#ifdef CPU_LITTLE_ENDIAN + if(sizeof(Point) == 8) { + stream.Get(out, 8 * count); + return; + } +#endif + Point *end = out + count; + byte *top = reinterpret_cast(end) - count * 8; + stream.Get(top, count * 8); + for(; out < end; out++, top += 8) { + out -> x = (short)Peek32le(top + 0); + out -> y = (short)Peek32le(top + 4); + } +} + +static void StreamPackPoints(Stream& stream, const Point *in, int count) +{ +#ifdef CPU_LITTLE_ENDIAN + if(sizeof(Point) == 8) { + stream.Put(in, 8 * count); + return; + } +#endif + enum { PART = 1024 }; + byte part[PART * 8]; + while(count > 0) + { + int part_count = min(count, PART); + for(byte *pp = part, *pe = pp + 8 * part_count; pp < pe; pp += 8, in++) { + Poke32le(pp + 0, in -> x); + Poke32le(pp + 4, in -> y); + } + stream.Put(part, part_count * 4); + count -= part_count; + } +} + +static void StreamUnpackInts(Stream& stream, int *out, int count) +{ +#ifdef CPU_LITTLE_ENDIAN + if(sizeof(int) == 4) { + stream.Get(out, count * 4); + return; + } +#endif + while(count--) + *out++ = stream.Get32le(); +} + +static void StreamPackInts(Stream& stream, const int *in, int count) +{ +#ifdef CPU_LITTLE_ENDIAN + if(sizeof(int) == 4) { + stream.Put(in, count * 4); + return; + } +#endif + while(count--) + stream.Put32le(*in++); +} + +// ------------------------------ + +Stream& DrawingDraw::DrawingOp(int code) +{ + drawing / code; + return drawing; +} + +dword DrawingDraw::GetInfo() const +{ + return dots ? DOTS : 0; +} + +Size DrawingDraw::GetPageSize() const +{ + return size; +} + +Rect DrawingDraw::GetPaintRect() const +{ + return Rect(size); +} + +void DrawingDraw::BeginOp() +{ + DrawingOp(DRAWING_BEGIN); +} + +void DrawingDraw::OffsetOp(Point p) +{ + DrawingOp(DRAWING_OFFSET) % p; +} + +bool DrawingDraw::ClipOp(const Rect& r) +{ + DrawingOp(DRAWING_CLIP) % const_cast(r); + return true; +} + +bool DrawingDraw::ClipoffOp(const Rect& r) +{ + DrawingOp(DRAWING_CLIPOFF) % const_cast(r); + return true; +} + +void DrawingDraw::EndOp() +{ + DrawingOp(DRAWING_END); +} + +bool DrawingDraw::ExcludeClipOp(const Rect& r) +{ + DrawingOp(DRAWING_EXCLUDECLIP) % const_cast(r); + return true; +} + +bool DrawingDraw::IntersectClipOp(const Rect& r) +{ + DrawingOp(DRAWING_INTERSECTCLIP) % const_cast(r); + return true; +} + +bool DrawingDraw::IsPaintingOp(const Rect& r) const +{ + return true; +} + +void DrawingDraw::DrawRectOp(int x, int y, int cx, int cy, Color color) +{ + if(!IsNull(color)) + DrawingOp(DRAWING_DRAWRECT) % x % y % cx % cy % color; +} + +void DrawingDraw::DrawImageOp(int x, int y, int cx, int cy, const Image& img, const Rect& src, Color color) +{ + Rect s = src; + DrawingOp(DRAWING_DRAWIMAGE) % x % y % cx % cy % s % color; + val.Add(img); +} + +void DrawingDraw::DrawDataOp(int x, int y, int cx, int cy, const String& data, const char *id) +{ + String h = id; + DrawingOp(DRAWING_DRAWDATA) % x % y % cx % cy % h; + val.Add(data); +} + +void DrawingDraw::DrawDrawingOp(const Rect& target, const Drawing& w) +{ + DrawingOp(DRAWING_DRAWDRAWING) % const_cast(target); + val.Add(w); +} + +void DrawingDraw::DrawPaintingOp(const Rect& target, const Painting& w) +{ + DrawingOp(DRAWING_DRAWPAINTING) % const_cast(target); + val.Add(w); +} + +void DrawingDraw::DrawLineOp(int x1, int y1, int x2, int y2, int width, Color color) +{ + DrawingOp(DRAWING_DRAWLINE) % x1 % y1 % x2 % y2 % width % color; +} + +void DrawingDraw::DrawPolyPolylineOp(const Point *vertices, int vertex_count, + const int *counts, int count_count, + int width, Color color, Color doxor) +{ + ASSERT(count_count > 0 && vertex_count > 0); + if(vertex_count < 2 || IsNull(color)) + return; + DrawingOp(DRAWING_DRAWPOLYPOLYLINE); + int version = 2; + drawing / version; + drawing % width % color % doxor; + drawing % vertex_count % count_count; + StreamPackPoints(drawing, vertices, vertex_count); + StreamPackInts(drawing, counts, count_count); +} + +void DrawingDraw::DrawPolyPolyPolygonOp(const Point *vertices, int vertex_count, + const int *subpolygon_counts, int subpolygon_count_count, + const int *disjunct_polygon_counts, int disjunct_polygon_count_count, + Color color, int width, Color outline, uint64 pattern, Color doxor) +{ + if(vertex_count == 0) + return; + DrawingOp(DRAWING_DRAWPOLYPOLYPOLYGON); + int version = 2; + drawing / version; + drawing % color % width % outline % pattern % doxor; + drawing % vertex_count % subpolygon_count_count % disjunct_polygon_count_count; + StreamPackPoints(drawing, vertices, vertex_count); + StreamPackInts(drawing, subpolygon_counts, subpolygon_count_count); + StreamPackInts(drawing, disjunct_polygon_counts, disjunct_polygon_count_count); +} + +void DrawingDraw::DrawEllipseOp(const Rect& r, Color color, int pen, Color pencolor) +{ + DrawingOp(DRAWING_DRAWELLIPSE) % const_cast(r) % color / pen % pencolor; +} + +void DrawingDraw::DrawArcOp(const Rect& rc, Point start, Point end, int width, Color color) +{ + DrawingOp(DRAWING_DRAWARC) % const_cast(rc) % start % end % color % width; +} + +void DrawingDraw::DrawTextOp(int x, int y, int angle, const wchar *text, Font font, Color ink, + int n, const int *dx) { + if(IsNull(ink)) return; + if(n < 0) + n = wstrlen((const wchar *)text); + if(n == 0) + return; + Stream& s = DrawingOp(DRAWING_DRAWTEXT); + byte cs = CHARSET_UNICODE; + s % x % y % angle % font % ink / n % cs; + s.PutW((wchar *)text, n); + bool dxb = dx; + s % dxb; + if(dx) { + int *w = const_cast(dx); + while(n--) + s / *w++; + } +} + +Drawing DrawingDraw::GetResult() +{ + Drawing out; + out.size = size; + LLOG("GetResult size: " << size); + out.data = drawing.GetResult(); + out.val = val; + return out; +} + +// ------------------------------ + +struct Draw::DrawingPos : StringStream { + Size source; + Size target; + Point srcoff; + Point trgoff; + + Vector stk; + + bool Identity() const { return source == target; } + + int GetX(int x) const; + int GetY(int y) const; + int GetCx(int cx) const; + int GetCy(int cy) const; + int GetW(int w) const; + Point Get(int x, int y) const; + Point Get(Point p) const; + Rect Get(const Rect& r) const; + Rect Get(int x, int y, int cx, int cy) const; + + Point operator()(int x, int y) const { return Get(x, y); } + Point operator()(Point p) const { return Get(p); } + Rect operator()(const Rect& r) const { return Get(r); } + Rect operator()(int x, int y, int cx, int cy) const { return Get(x, y, cx, cy); } + + void TransformX(int& x) const { x = GetX(x); } + void TransformY(int& y) const { y = GetY(y); } + void TransformW(int& w) const { w = GetW(w); } + void Transform(int& x, int& y) const { TransformX(x); TransformY(y); } + void Transform(Point& p) const { p = Get(p); } + void Transform(Rect& r) const { r = Get(r); } + + Rect GetRect(); + + void Push(); + void Pop(); + + DrawingPos(const String& src) : StringStream(src) {} +}; + +void Draw::DrawingPos::Push() +{ + stk.Add(srcoff); + stk.Add(trgoff); +} + +void Draw::DrawingPos::Pop() +{ + trgoff = stk.Pop(); + srcoff = stk.Pop(); +} + +Rect Draw::DrawingPos::GetRect() +{ + Rect r; + *this % r; + return Get(r); +} + +int Draw::DrawingPos::GetX(int x) const { + return iscale(x + srcoff.x, target.cx, source.cx) - trgoff.x; +} + +int Draw::DrawingPos::GetY(int y) const { + return iscale(y + srcoff.y, target.cy, source.cy) - trgoff.y; +} + +int Draw::DrawingPos::GetCx(int cx) const { + return iscale(cx, target.cx, source.cx); +} + +int Draw::DrawingPos::GetCy(int cy) const { + return iscale(cy, target.cy, source.cy); +} + +int Draw::DrawingPos::GetW(int w) const { + return iscale(w, target.cx + target.cy, source.cx + source.cy); +} + +Point Draw::DrawingPos::Get(int x, int y) const { + return Point(GetX(x), GetY(y)); +} + +Point Draw::DrawingPos::Get(Point p) const { + return Get(p.x, p.y); +} + +Rect Draw::DrawingPos::Get(const Rect& r) const { + return Rect(GetX(r.left), GetY(r.top), GetX(r.right), GetY(r.bottom)); +} + +Rect Draw::DrawingPos::Get(int x, int y, int cx, int cy) const { + return Get(RectC(x, y, cx, cy)); +} + +void Draw::DrawDrawingOp(const Rect& target, const Drawing& w) { +#ifdef _DEBUG + int cl = GetCloffLevel(); +#endif + DrawingPos ps(w.data); + ps.srcoff = ps.trgoff = Point(0, 0); + ps.target = target.Size(); + ps.source = w.size; + LLOG("DrawDrawingOp size: " << w.size); + if(ps.target.cx == 0 || ps.target.cy == 0 || ps.source.cx == 0 || ps.source.cy == 0) + return; + Clipoff(target); + Rect r, r1; + int x, y, cx, cy, width, vertex_count, count_count; + Color color, pencolor, doxor; + Image img; + Drawing dw; + Painting sw; + Point p, p1; + int vi = 0; + while(!ps.IsEof()) { + int code; + ps / code; + switch(code) { + case DRAWING_BEGIN: + Begin(); + ps.Push(); + break; + case DRAWING_OFFSET: + ps % p; + p1 = ps(p); + Offset(p1); + ps.Push(); + ps.srcoff += p; + ps.trgoff += p1; + break; + case DRAWING_CLIP: + Clip(ps.GetRect()); + ps.Push(); + break; + case DRAWING_CLIPOFF: + ps % r; + r1 = ps(r); + Clipoff(r1); + ps.Push(); + ps.srcoff += r.TopLeft(); + ps.trgoff += r1.TopLeft(); + break; + case DRAWING_EXCLUDECLIP: + ExcludeClip(ps.GetRect()); + break; + case DRAWING_INTERSECTCLIP: + IntersectClip(ps.GetRect()); + break; + case DRAWING_END: + End(); + ps.Pop(); + break; + case DRAWING_DRAWRECT: + ps % x % y % cx % cy % color; + DrawRect(ps(x, y, cx, cy), color); + break; + case DRAWING_DRAWIMAGE: + ps % x % y % cx % cy; + if(w.val.GetCount()) + img = w.val[vi++]; + else + ps % img; + ps % r % color; + DrawImageOp(ps.GetX(x), ps.GetY(y), ps.GetCx(cx), ps.GetCy(cy), img, r, color); + break; + case DRAWING_DRAWDATA: + { + String data, id; + ps % x % y % cx % cy % id; + if(w.val.GetCount()) + data = w.val[vi++]; + else + ps % data; + DrawData(ps(x, y, cx, cy), data, id); + } + break; + case DRAWING_DRAWDRAWING: + if(w.val.GetCount()) + dw = w.val[vi++]; + else + ps % dw; + DrawDrawing(ps.GetRect(), dw); + break; + case DRAWING_DRAWPAINTING: + if(w.val.GetCount()) + sw = w.val[vi++]; + else + ps % sw; + DrawPainting(ps.GetRect(), sw); + break; + case DRAWING_DRAWLINE: + ps % x % y % cx % cy % width % color; + DrawLine(ps(x, y), ps(cx, cy), width > 0 ? ps.GetW(width) : width, color); + break; + case DRAWING_DRAWELLIPSE: + r = ps.GetRect(); + ps % color / width % pencolor; + DrawEllipse(r, color, width > 0 ? ps.GetW(width) : width, pencolor); + break; +#ifndef PLATFORM_WINCE + case DRAWING_DRAWARC: + r = ps.GetRect(); + ps % p % p1 % color % width; + DrawArc(r, ps(p), ps(p1), width > 0 ? ps.GetW(width) : width, color); + break; + case DRAWING_DRAWPOLYPOLYLINE: + { + int version; + ps / version; + ps % width % color % doxor; + ps % vertex_count % count_count; + Buffer vertices(vertex_count); + Buffer counts(count_count); + StreamUnpackPoints(ps, vertices, vertex_count); + StreamUnpackInts(ps, counts, count_count); + if(!ps.Identity()) { + for(Point *p = vertices, *e = p + vertex_count; p < e; p++) + ps.Transform(*p); + if(width > 0) + ps.TransformW(width); + } + DrawPolyPolyline(vertices, vertex_count, counts, count_count, width, color, doxor); + } + break; + case DRAWING_DRAWPOLYPOLYPOLYGON: + { + Color outline; + uint64 pattern; + int subpolygon_count_count, disjunct_polygon_count_count; + int version = 2; + ps / version; + ps % color % width % outline % pattern % doxor; + ps % vertex_count % subpolygon_count_count % disjunct_polygon_count_count; + Buffer vertices(vertex_count); + Buffer subpolygon_counts(subpolygon_count_count); + Buffer disjunct_polygon_counts(disjunct_polygon_count_count); + StreamUnpackPoints(ps, vertices, vertex_count); + StreamUnpackInts(ps, subpolygon_counts, subpolygon_count_count); + StreamUnpackInts(ps, disjunct_polygon_counts, disjunct_polygon_count_count); + if(!ps.Identity()) { + for(Point *p = vertices, *e = p + vertex_count; p < e; p++) + ps.Transform(*p); + ps.TransformW(width); + } + DrawPolyPolyPolygon(vertices, vertex_count, + subpolygon_counts, subpolygon_count_count, + disjunct_polygon_counts, disjunct_polygon_count_count, + color, width, outline, pattern, doxor); + } + break; +#endif + case DRAWING_DRAWTEXT: + { + int n, angle; + Font font; + Color ink; + byte cs; + ps % x % y % angle % font % ink / n % cs; + if(font.GetHeight() == 0) { + FontInfo fi = font.Info(); + font.Height(fi.GetHeight() - fi.GetInternal()); + } + bool unicode = cs == CHARSET_UNICODE; + WString text; + if(unicode) { + Buffer txt(n); + ps.Stream::GetW(txt, n); + text = WString(txt, n); + } + else { + Buffer txt(n); + ps.Stream::Get(txt, n); + text = ToUnicode(txt, n, cs); + } + LLOG("wsDrawText \"" << WString(text, n) + << "\" at: (" << x << ", " << y << ", " << angle << ")"); + bool dxb; + ps % dxb; + Buffer dx(n); + int *wd = dx; + int nn = n; + angle %= 3600; + if(ps.Identity()) { + if(dxb) { + while(nn--) + ps / *wd++; + DrawText(x, y, angle, text, font, ink, dx); + } + else + DrawText(x, y, angle, text, font, ink); + } + else { + const wchar *wp = ~text; + double q = Length((Sizef)ps.target) / Length((Sizef)ps.source); + double px = 0; + while(nn--) { + int cx; + if(dxb) + ps / cx; + else + cx = font[*wp++]; + double npx = q * cx + px; + *wd++ = (int)npx - (int)px; + px = npx; + } + int ht = (int)(font.GetHeight() * min(double(ps.target.cx) / ps.source.cx, double(ps.target.cy) / ps.source.cy)); + font.Width((int)q * font.GetWidth()).Height(ht ? ht : 1); + DrawText(ps.GetX(x), ps.GetY(y), angle, text, font, ink, dx); + /* + FontInfo fi = font.Info(); + const wchar *wp = ~text; + int odd = (angle / 900) & 1; + double ang = (double) (angle % 900) * M_2PI / 3600; + double sx = (double) ps.target.cx / ps.source.cx; + double sy = (double) ps.target.cy / ps.source.cy; + double ang2 = atan((odd ? sx / sy : sy / sx) * tan(ang)); + DDUMP(odd); + DDUMP(cx); + DDUMP(sy); + DDUMP(sin(ang)); + DDUMP(sin(ang2)); + double q = (odd ? sx : sy) * sin(ang) / sin(ang2); + double error = 0; + while(nn--) { + int cx; + if(dxb) { + ps / cx; + DDUMP(cx); + } + else + cx = fi[*wp++]; + double ncx = q * cx + error; + DDUMP(q * cx); + *wd++ = cx = (int) ncx; + error = ncx - cx; + } + int ht = (int)(fi.GetFontHeight() * (sx * sin(ang) * sin(ang2) + sy * cos(ang) * cos(ang2))); + font.Width(int(q * fi.GetAveWidth())).Height(ht ? ht : 1); + DrawText(ps.GetX(x), ps.GetY(y), int(ang2 * 3600 / M_2PI) + (angle / 900) * 900, + text, font, ink, dx); + */ + } + } + } + } +// LOGEND(); + End(); +#ifdef _DEBUG + ASSERT(GetCloffLevel() == cl); +#endif +} + +void Draw::DrawDrawing(int x, int y, int cx, int cy, const Drawing& w) { + DrawDrawing(RectC(x, y, cx, cy), w); +} + +void DrawingDraw::Create(int cx, int cy, bool dots_) { + Create(Size(cx, cy), dots_); +} + +void DrawingDraw::Create(Size sz, bool dots_) { + drawing.Create(); + size = sz; + dots = dots_; + val.Clear(); + LLOG("DrawingDraw::Create, sz = " << sz << ", dots = " << dots << " -> clip = " << GetClip()); +} + +DrawingDraw::DrawingDraw() +{ +} + +DrawingDraw::DrawingDraw(Size sz, bool dots_) { + Create(sz, dots_); +} + +DrawingDraw::DrawingDraw(int cx, int cy, bool dots_) { + Create(cx, cy, dots_); +} + +Size Drawing::RatioSize(int cx, int cy) const { + return GetRatioSize(GetSize(), cx, cy); +} + +void Drawing::Append(Drawing& dw) +{ + if(IsNull(size)) + size = dw.size; + data << dw.data; + for(int i = 0; i < dw.val.GetCount(); i++) + val.Add(dw.val[i]); +} + +Drawing::Drawing(const Value& src) +{ + if(IsNull(src)) + size = Null; + else + *this = RichValue::Extract(src); +} + +void Drawing::Serialize(Stream& s) +{ + if(val.GetCount()) + size.cy |= 0x80000000; + s % size; + s % data; + if(size.cy & 0x80000000) { + size.cy &= ~0x80000000; + s % val; + } +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Draw/FontCR.cpp b/uppsrc/Draw/FontCR.cpp index cf8a0a141..726610419 100644 --- a/uppsrc/Draw/FontCR.cpp +++ b/uppsrc/Draw/FontCR.cpp @@ -1,313 +1,313 @@ -#include "Draw.h" - -NAMESPACE_UPP - -enum { - CG_NONE, - CG_CAPITAL, - CG_SMALL -}; - -enum { - CG_GRAVE = 0x60, - CG_ACUTE = 0xb4, - CG_CEDILLA = 0xb8, - CG_MACRON = 175, - CG_CIRCUMFLEX = 0x2c6, - CG_TILDE = 0x2dc, - CG_DOT_ABOVE = 0x2d9, - CG_OGONEK = 0x2db, - CG_STROKE = '-', - CG_BREVE = 0x2d8, - CG_CARON = 0x2c7, - CG_MIDDLE_DOT = 0xb7, - CG_DOUBLE_ACUTE = 0x2dd, - CG_DIAERESIS = 0xa8, - CG_RING_ABOVE = 0x2da, - CG_COMMA_T = ',', - CG_COMMA_UR = 1, - CG_COMMA_URI, -}; - -struct CGInfo { - byte type; - char ascii; - wchar mark; -} -gc_info[128] = { - { CG_CAPITAL, 'A', CG_MACRON }, - { CG_SMALL, 'a', CG_MACRON }, - { CG_CAPITAL, 'A', CG_BREVE }, - { CG_SMALL, 'a', CG_BREVE }, - { CG_CAPITAL, 'A', CG_OGONEK }, - { CG_SMALL, 'a', CG_OGONEK }, - { CG_CAPITAL, 'C', CG_ACUTE }, - { CG_SMALL, 'c', CG_ACUTE }, - { CG_CAPITAL, 'C', CG_CIRCUMFLEX }, - { CG_SMALL, 'c', CG_CIRCUMFLEX }, - { CG_CAPITAL, 'C', CG_DOT_ABOVE }, - { CG_SMALL, 'c', CG_DOT_ABOVE }, - { CG_CAPITAL, 'C', CG_CARON }, - { CG_SMALL, 'c', CG_CARON }, - { CG_CAPITAL, 'D', CG_CARON }, - { CG_SMALL, 'd', CG_COMMA_UR }, - { CG_CAPITAL, 'D', CG_STROKE }, - { CG_SMALL, 'd', CG_STROKE }, - { CG_CAPITAL, 'E', CG_MACRON }, - { CG_SMALL, 'e', CG_MACRON }, - { CG_CAPITAL, 'E', CG_BREVE }, - { CG_SMALL, 'e', CG_BREVE }, - { CG_CAPITAL, 'E', CG_DOT_ABOVE }, - { CG_SMALL, 'e', CG_DOT_ABOVE }, - { CG_CAPITAL, 'E', CG_OGONEK }, - { CG_SMALL, 'e', CG_OGONEK }, - { CG_CAPITAL, 'E', CG_CARON }, - { CG_SMALL, 'e', CG_CARON }, - { CG_CAPITAL, 'G', CG_CIRCUMFLEX }, - { CG_SMALL, 'g', CG_CIRCUMFLEX }, - { CG_CAPITAL, 'G', CG_BREVE }, - { CG_SMALL, 'g', CG_BREVE }, - { CG_CAPITAL, 'G', CG_DOT_ABOVE }, - { CG_SMALL, 'g', CG_DOT_ABOVE }, - { CG_CAPITAL, 'G', CG_CEDILLA }, - { CG_SMALL, 'g', CG_CEDILLA }, - { CG_CAPITAL, 'H', CG_CIRCUMFLEX }, - { CG_SMALL, 'h', CG_CIRCUMFLEX }, - { CG_CAPITAL, 'H', CG_STROKE }, - { CG_SMALL, 'h', CG_STROKE }, - { CG_CAPITAL, 'I', CG_TILDE }, - { CG_SMALL, 'i', CG_TILDE }, - { CG_CAPITAL, 'I', CG_MACRON }, - { CG_SMALL, 'i', CG_MACRON }, - { CG_CAPITAL, 'I', CG_BREVE }, - { CG_SMALL, 'i', CG_BREVE }, - { CG_CAPITAL, 'I', CG_OGONEK }, - { CG_SMALL, 'i', CG_OGONEK }, - { CG_CAPITAL, 'I', CG_DOT_ABOVE }, - { CG_NONE, 0, 0 }, // , CG_SMALL, 'DOTLESS I - { CG_NONE, 0, 0 }, // LATIN CAPITAL LIGATURE IJ - { CG_NONE, 0, 0 }, // LATIN SMALL LIGATURE IJ - { CG_CAPITAL, 'J', CG_CIRCUMFLEX }, - { CG_SMALL, 'j', CG_CIRCUMFLEX }, - { CG_CAPITAL, 'K', CG_CEDILLA }, - { CG_SMALL, 'k', CG_CEDILLA }, - { CG_NONE, 0, 0 }, // CG_SMALL, 'KRA - { CG_CAPITAL, 'L', CG_ACUTE }, - { CG_SMALL, 'l', CG_ACUTE }, - { CG_CAPITAL, 'L', CG_CEDILLA }, - { CG_SMALL, 'l', CG_CEDILLA }, - { CG_CAPITAL, 'L', CG_COMMA_URI }, - { CG_SMALL, 'l', CG_COMMA_UR }, - { CG_CAPITAL, 'L', CG_MIDDLE_DOT }, - { CG_SMALL, 'l', CG_MIDDLE_DOT }, - { CG_CAPITAL, 'L', CG_STROKE }, - { CG_SMALL, 'l', CG_STROKE }, - { CG_CAPITAL, 'N', CG_ACUTE }, - { CG_SMALL, 'n', CG_ACUTE }, - { CG_CAPITAL, 'N', CG_CEDILLA }, - { CG_SMALL, 'n', CG_CEDILLA }, - { CG_CAPITAL, 'N', CG_CARON }, - { CG_SMALL, 'n', CG_CARON }, - { CG_NONE, 0, 0 }, // CG_SMALL, 'N PRECEDED BY APOSTROPHE - { CG_NONE, 0, 0 }, //CG_CAPITAL, 'ENG - { CG_NONE, 0, 0 }, //CG_SMALL, 'ENG - { CG_CAPITAL, 'O', CG_MACRON }, - { CG_SMALL, 'o', CG_MACRON }, - { CG_CAPITAL, 'O', CG_BREVE }, - { CG_SMALL, 'o', CG_BREVE }, - { CG_CAPITAL, 'O', CG_DOUBLE_ACUTE }, - { CG_SMALL, 'o', CG_DOUBLE_ACUTE }, - { CG_NONE, 0, 0 }, // LATIN CAPITAL LIGATURE OE - { CG_NONE, 0, 0 }, // LATIN SMALL LIGATURE OE - { CG_CAPITAL, 'R', CG_ACUTE }, - { CG_SMALL, 'r', CG_ACUTE }, - { CG_CAPITAL, 'R', CG_CEDILLA }, - { CG_SMALL, 'r', CG_CEDILLA }, - { CG_CAPITAL, 'R', CG_CARON }, - { CG_SMALL, 'r', CG_CARON }, - { CG_CAPITAL, 'S', CG_ACUTE }, - { CG_SMALL, 's', CG_ACUTE }, - { CG_CAPITAL, 'S', CG_CIRCUMFLEX }, - { CG_SMALL, 's', CG_CIRCUMFLEX }, - { CG_CAPITAL, 'S', CG_CEDILLA }, - { CG_SMALL, 's', CG_CEDILLA }, - { CG_CAPITAL, 'S', CG_CARON }, - { CG_SMALL, 's', CG_CARON }, - { CG_CAPITAL, 'T', CG_CEDILLA }, - { CG_SMALL, 't', CG_CEDILLA }, - { CG_CAPITAL, 'T', CG_CARON }, - { CG_SMALL, 't', CG_COMMA_T }, - { CG_CAPITAL, 'T', CG_STROKE }, - { CG_SMALL, 't', CG_STROKE }, - { CG_CAPITAL, 'U', CG_TILDE }, - { CG_SMALL, 'u', CG_TILDE }, - { CG_CAPITAL, 'U', CG_MACRON }, - { CG_SMALL, 'u', CG_MACRON }, - { CG_CAPITAL, 'U', CG_BREVE }, - { CG_SMALL, 'u', CG_BREVE }, - { CG_CAPITAL, 'U', CG_RING_ABOVE }, - { CG_SMALL, 'u', CG_RING_ABOVE }, - { CG_CAPITAL, 'U', CG_DOUBLE_ACUTE }, - { CG_SMALL, 'u', CG_DOUBLE_ACUTE }, - { CG_CAPITAL, 'U', CG_OGONEK }, - { CG_SMALL, 'u', CG_OGONEK }, - { CG_CAPITAL, 'W', CG_CIRCUMFLEX }, - { CG_SMALL, 'w', CG_CIRCUMFLEX }, - { CG_CAPITAL, 'Y', CG_CIRCUMFLEX }, - { CG_SMALL, 'y', CG_CIRCUMFLEX }, - { CG_CAPITAL, 'Y', CG_DIAERESIS }, - { CG_CAPITAL, 'Z', CG_ACUTE }, - { CG_SMALL, 'z', CG_ACUTE }, - { CG_CAPITAL, 'Z', CG_DOT_ABOVE }, - { CG_SMALL, 'z', CG_DOT_ABOVE }, - { CG_CAPITAL, 'Z', CG_CARON }, - { CG_SMALL, 'z', CG_CARON }, - { CG_NONE, 0, 0 } // CG_SMALL, 'LONG S -}; - -bool Compose(Font font, int chr, ComposedGlyph& cg) -{ - if(chr < 256 || chr > 256 + 128) - return false; - CGInfo f = gc_info[chr - 256]; - if(f.type == CG_NONE) - return false; - GlyphInfo gi = GetGlyphInfo(font, f.ascii); - if(!gi.IsNormal()) - return false; - int cw = gi.width; - CommonFontInfo fi = GetFontInfo(font); - gi = GetGlyphInfo(font, f.mark); - if(!gi.IsNormal()) - return false; - int mw = gi.width; - cg.mark_font = font; - cg.mark_pos.x = cg.mark_pos.y = 0; - cg.basic_char = f.ascii; - cg.mark_char = f.mark; - if(cg.mark_char == CG_COMMA_UR && fi.fixedpitch) - cg.mark_char = CG_CARON; - if(cg.mark_char == CG_COMMA_T) { - cg.mark_pos.y -= 3 * font.GetHeight() / 4; - cg.mark_pos.x += 4 * cw / 10; - if(font.IsItalic()) - cg.mark_pos.x += mw / 2; - } - else - if(cg.mark_char == CG_COMMA_UR) { - cg.mark_pos.y -= 2 * font.GetHeight() / 3; - cg.mark_pos.x += cw - mw / 4; - cg.mark_char = ','; - if(font.IsItalic()) - cg.mark_pos.x += mw / 3; - } - else - if(cg.mark_char == CG_COMMA_URI) { - cg.mark_pos.y -= 2 * font.GetHeight() / 3; - cg.mark_pos.x += cw - mw / 2; - cg.mark_char = ','; - if(font.IsItalic()) - cg.mark_pos.x += mw / 3; - } - else - if(cg.mark_char != CG_STROKE) { - if(cg.mark_char != CG_OGONEK && cg.mark_char != CG_CEDILLA && f.type == CG_CAPITAL) { - cg.mark_font = font(9 * font.GetHeight() / 10); - mw = GetGlyphInfo(cg.mark_font, f.mark).width; - cg.mark_pos.y -= cg.mark_char == CG_RING_ABOVE ? font.GetHeight() / 19 - : font.GetHeight() / 10; - } - cg.mark_pos.x += (cw - mw) / 2; - if(font.IsItalic()) - cg.mark_pos.x += mw / 5; - } - return true; -} - -struct sRFace { - const char *name; - dword l, h; -} sFontReplacements[] = { - { "sans-serif", 0xffee0008, 0xdc000801 }, - { "Arial", 0xfffe0000, 0x9c000801 }, - {"\346\226\260\345\256\213\344\275\223", 0xfd800000, 0x9ffff00d },//SimSun (or New Song Ti) - {"SimSun", 0xfd800000, 0x9ffff00d },//SimSun (or New Song Ti) - {"\345\256\213\344\275\223", 0xfd800000, 0x9ffff00d }, // Song Ti - {"\345\276\256\350\275\257\351\233\205\351\273\221", 0xfd800000, 0x9ffff00f }, //MS Ya Hei - {"Microsoft YaHei", 0xfd800000, 0x9ffff00f }, //MS Ya Hei -// {"\351\273\221\344\275\223", 0xfd800000, 0x09ffff00 }, // Hei Ti -// {"\346\226\207\346\263\211\351\251\277\346\255\243\351\273\221", 0xfd800000, 0x09ffff00 }, //WenQuanYi Zheng Hi -// {"\346\226\207\346\263\211\351\251\277\347\255\211\345\256\275\345\276\256\347\261\263\351\273\221", 0xfd800000, 0x09ffff00 },//WenQuanYi Wei Hei -// {"\344\273\277\345\256\213", 0xfd800000, 0x09ffff00 }, //Fang Song -// {"\346\245\267\344\275\223", 0xfd800000, 0x09ffff00 }, // Kai Ti - { "Arial Unicode MS", 0xfffc3fef, 0xfa7ff7ef }, - { "MS UI Gothic", 0xffc01008, 0xfffff001 }, - { "MS Mincho", 0xffc01008, 0xfffff001 }, - { "VL Gothic", 0xfd800000, 0x9a7ff80f }, - { "VL PGothic", 0xffe00008, 0xde7ff80f }, - { "UnDotum", 0xe5800000, 0xaa7ff7ef }, - { "UnBatang", 0xe5800000, 0xaa7ff7ef }, - { "DejaVu Sans Mono", 0xffec0004, 0xfc00080f }, - { "DejaVu Sans", 0xfffd000c, 0xfc40080f }, - { "AlArabiyaFreeSerif", 0xffdc0008, 0xd800000f }, - { "Kochi Mincho", 0xffdc0008, 0xd800000f }, - { "Kochi Gothic", 0xffdc0008, 0xd800000f }, - { "Sazanami Mincho", 0xffdc0008, 0xd800000f }, - { "Sazanami Gothic", 0xffdc0008, 0xd800000f }, - { "Gulim", 0xf7c00000, 0xba7ff7e1 }, - { "PMingLiU", 0xff800000, 0x9ffff001 }, // <--- SHOULD MOVE UP - { "FreeSans", 0xfff23d00, 0xfc00000f }, - { "FreeSerif", 0xfffd3938, 0xfc00080f }, - { "Symbol", 0xe4000000, 0x8800000f }, -}; - -struct sFontMetricsReplacement { - Font src; - Font dst; - Font mdst; -}; - -bool Replace(Font fnt, int chr, Font& rfnt) -{ - static Vector rface; - static Vector l, h; - ONCELOCK { - for(int i = 0; i < __countof(sFontReplacements) && rface.GetCount() < 20; i++) { - int q = Font::FindFaceNameIndex(sFontReplacements[i].name); - if(q > 0) { - rface.Add(q); - l.Add(sFontReplacements[i].l); - h.Add(sFontReplacements[i].h); - } - } - } - - Font f = fnt; - dword tl = chr < 4096 ? 0x80000000 >> (chr >> 7) : 0; - dword th = 0x80000000 >> ((dword)chr >> 11); - for(int i = 0; i < rface.GetCount(); i++) { - if(/*((l[i] & tl) || (h[i] & th)) && */IsNormal(f.Face(rface[i]), chr)) { - int a = fnt.GetAscent(); - int d = fnt.GetDescent(); - if(f.GetAscent() > a || f.GetDescent() > d) { - static sFontMetricsReplacement cache[256]; - int q = CombineHash(fnt, f) & 255; - if(cache[q].src != fnt || cache[q].dst != f) { - cache[q].src = fnt; - cache[q].dst = f; - while((f.GetAscent() > a || f.GetDescent() > d) && f.GetHeight() > 1) { - f.Height(max(1, min(f.GetHeight() - 1, f.GetHeight() * 9 / 10))); - } - cache[q].mdst = f; - } - else - f = cache[q].mdst; - } - rfnt = f; - return true; - } - } - return false; -} - -END_UPP_NAMESPACE +#include "Draw.h" + +NAMESPACE_UPP + +enum { + CG_NONE, + CG_CAPITAL, + CG_SMALL +}; + +enum { + CG_GRAVE = 0x60, + CG_ACUTE = 0xb4, + CG_CEDILLA = 0xb8, + CG_MACRON = 175, + CG_CIRCUMFLEX = 0x2c6, + CG_TILDE = 0x2dc, + CG_DOT_ABOVE = 0x2d9, + CG_OGONEK = 0x2db, + CG_STROKE = '-', + CG_BREVE = 0x2d8, + CG_CARON = 0x2c7, + CG_MIDDLE_DOT = 0xb7, + CG_DOUBLE_ACUTE = 0x2dd, + CG_DIAERESIS = 0xa8, + CG_RING_ABOVE = 0x2da, + CG_COMMA_T = ',', + CG_COMMA_UR = 1, + CG_COMMA_URI, +}; + +struct CGInfo { + byte type; + char ascii; + wchar mark; +} +gc_info[128] = { + { CG_CAPITAL, 'A', CG_MACRON }, + { CG_SMALL, 'a', CG_MACRON }, + { CG_CAPITAL, 'A', CG_BREVE }, + { CG_SMALL, 'a', CG_BREVE }, + { CG_CAPITAL, 'A', CG_OGONEK }, + { CG_SMALL, 'a', CG_OGONEK }, + { CG_CAPITAL, 'C', CG_ACUTE }, + { CG_SMALL, 'c', CG_ACUTE }, + { CG_CAPITAL, 'C', CG_CIRCUMFLEX }, + { CG_SMALL, 'c', CG_CIRCUMFLEX }, + { CG_CAPITAL, 'C', CG_DOT_ABOVE }, + { CG_SMALL, 'c', CG_DOT_ABOVE }, + { CG_CAPITAL, 'C', CG_CARON }, + { CG_SMALL, 'c', CG_CARON }, + { CG_CAPITAL, 'D', CG_CARON }, + { CG_SMALL, 'd', CG_COMMA_UR }, + { CG_CAPITAL, 'D', CG_STROKE }, + { CG_SMALL, 'd', CG_STROKE }, + { CG_CAPITAL, 'E', CG_MACRON }, + { CG_SMALL, 'e', CG_MACRON }, + { CG_CAPITAL, 'E', CG_BREVE }, + { CG_SMALL, 'e', CG_BREVE }, + { CG_CAPITAL, 'E', CG_DOT_ABOVE }, + { CG_SMALL, 'e', CG_DOT_ABOVE }, + { CG_CAPITAL, 'E', CG_OGONEK }, + { CG_SMALL, 'e', CG_OGONEK }, + { CG_CAPITAL, 'E', CG_CARON }, + { CG_SMALL, 'e', CG_CARON }, + { CG_CAPITAL, 'G', CG_CIRCUMFLEX }, + { CG_SMALL, 'g', CG_CIRCUMFLEX }, + { CG_CAPITAL, 'G', CG_BREVE }, + { CG_SMALL, 'g', CG_BREVE }, + { CG_CAPITAL, 'G', CG_DOT_ABOVE }, + { CG_SMALL, 'g', CG_DOT_ABOVE }, + { CG_CAPITAL, 'G', CG_CEDILLA }, + { CG_SMALL, 'g', CG_CEDILLA }, + { CG_CAPITAL, 'H', CG_CIRCUMFLEX }, + { CG_SMALL, 'h', CG_CIRCUMFLEX }, + { CG_CAPITAL, 'H', CG_STROKE }, + { CG_SMALL, 'h', CG_STROKE }, + { CG_CAPITAL, 'I', CG_TILDE }, + { CG_SMALL, 'i', CG_TILDE }, + { CG_CAPITAL, 'I', CG_MACRON }, + { CG_SMALL, 'i', CG_MACRON }, + { CG_CAPITAL, 'I', CG_BREVE }, + { CG_SMALL, 'i', CG_BREVE }, + { CG_CAPITAL, 'I', CG_OGONEK }, + { CG_SMALL, 'i', CG_OGONEK }, + { CG_CAPITAL, 'I', CG_DOT_ABOVE }, + { CG_NONE, 0, 0 }, // , CG_SMALL, 'DOTLESS I + { CG_NONE, 0, 0 }, // LATIN CAPITAL LIGATURE IJ + { CG_NONE, 0, 0 }, // LATIN SMALL LIGATURE IJ + { CG_CAPITAL, 'J', CG_CIRCUMFLEX }, + { CG_SMALL, 'j', CG_CIRCUMFLEX }, + { CG_CAPITAL, 'K', CG_CEDILLA }, + { CG_SMALL, 'k', CG_CEDILLA }, + { CG_NONE, 0, 0 }, // CG_SMALL, 'KRA + { CG_CAPITAL, 'L', CG_ACUTE }, + { CG_SMALL, 'l', CG_ACUTE }, + { CG_CAPITAL, 'L', CG_CEDILLA }, + { CG_SMALL, 'l', CG_CEDILLA }, + { CG_CAPITAL, 'L', CG_COMMA_URI }, + { CG_SMALL, 'l', CG_COMMA_UR }, + { CG_CAPITAL, 'L', CG_MIDDLE_DOT }, + { CG_SMALL, 'l', CG_MIDDLE_DOT }, + { CG_CAPITAL, 'L', CG_STROKE }, + { CG_SMALL, 'l', CG_STROKE }, + { CG_CAPITAL, 'N', CG_ACUTE }, + { CG_SMALL, 'n', CG_ACUTE }, + { CG_CAPITAL, 'N', CG_CEDILLA }, + { CG_SMALL, 'n', CG_CEDILLA }, + { CG_CAPITAL, 'N', CG_CARON }, + { CG_SMALL, 'n', CG_CARON }, + { CG_NONE, 0, 0 }, // CG_SMALL, 'N PRECEDED BY APOSTROPHE + { CG_NONE, 0, 0 }, //CG_CAPITAL, 'ENG + { CG_NONE, 0, 0 }, //CG_SMALL, 'ENG + { CG_CAPITAL, 'O', CG_MACRON }, + { CG_SMALL, 'o', CG_MACRON }, + { CG_CAPITAL, 'O', CG_BREVE }, + { CG_SMALL, 'o', CG_BREVE }, + { CG_CAPITAL, 'O', CG_DOUBLE_ACUTE }, + { CG_SMALL, 'o', CG_DOUBLE_ACUTE }, + { CG_NONE, 0, 0 }, // LATIN CAPITAL LIGATURE OE + { CG_NONE, 0, 0 }, // LATIN SMALL LIGATURE OE + { CG_CAPITAL, 'R', CG_ACUTE }, + { CG_SMALL, 'r', CG_ACUTE }, + { CG_CAPITAL, 'R', CG_CEDILLA }, + { CG_SMALL, 'r', CG_CEDILLA }, + { CG_CAPITAL, 'R', CG_CARON }, + { CG_SMALL, 'r', CG_CARON }, + { CG_CAPITAL, 'S', CG_ACUTE }, + { CG_SMALL, 's', CG_ACUTE }, + { CG_CAPITAL, 'S', CG_CIRCUMFLEX }, + { CG_SMALL, 's', CG_CIRCUMFLEX }, + { CG_CAPITAL, 'S', CG_CEDILLA }, + { CG_SMALL, 's', CG_CEDILLA }, + { CG_CAPITAL, 'S', CG_CARON }, + { CG_SMALL, 's', CG_CARON }, + { CG_CAPITAL, 'T', CG_CEDILLA }, + { CG_SMALL, 't', CG_CEDILLA }, + { CG_CAPITAL, 'T', CG_CARON }, + { CG_SMALL, 't', CG_COMMA_T }, + { CG_CAPITAL, 'T', CG_STROKE }, + { CG_SMALL, 't', CG_STROKE }, + { CG_CAPITAL, 'U', CG_TILDE }, + { CG_SMALL, 'u', CG_TILDE }, + { CG_CAPITAL, 'U', CG_MACRON }, + { CG_SMALL, 'u', CG_MACRON }, + { CG_CAPITAL, 'U', CG_BREVE }, + { CG_SMALL, 'u', CG_BREVE }, + { CG_CAPITAL, 'U', CG_RING_ABOVE }, + { CG_SMALL, 'u', CG_RING_ABOVE }, + { CG_CAPITAL, 'U', CG_DOUBLE_ACUTE }, + { CG_SMALL, 'u', CG_DOUBLE_ACUTE }, + { CG_CAPITAL, 'U', CG_OGONEK }, + { CG_SMALL, 'u', CG_OGONEK }, + { CG_CAPITAL, 'W', CG_CIRCUMFLEX }, + { CG_SMALL, 'w', CG_CIRCUMFLEX }, + { CG_CAPITAL, 'Y', CG_CIRCUMFLEX }, + { CG_SMALL, 'y', CG_CIRCUMFLEX }, + { CG_CAPITAL, 'Y', CG_DIAERESIS }, + { CG_CAPITAL, 'Z', CG_ACUTE }, + { CG_SMALL, 'z', CG_ACUTE }, + { CG_CAPITAL, 'Z', CG_DOT_ABOVE }, + { CG_SMALL, 'z', CG_DOT_ABOVE }, + { CG_CAPITAL, 'Z', CG_CARON }, + { CG_SMALL, 'z', CG_CARON }, + { CG_NONE, 0, 0 } // CG_SMALL, 'LONG S +}; + +bool Compose(Font font, int chr, ComposedGlyph& cg) +{ + if(chr < 256 || chr > 256 + 128) + return false; + CGInfo f = gc_info[chr - 256]; + if(f.type == CG_NONE) + return false; + GlyphInfo gi = GetGlyphInfo(font, f.ascii); + if(!gi.IsNormal()) + return false; + int cw = gi.width; + CommonFontInfo fi = GetFontInfo(font); + gi = GetGlyphInfo(font, f.mark); + if(!gi.IsNormal()) + return false; + int mw = gi.width; + cg.mark_font = font; + cg.mark_pos.x = cg.mark_pos.y = 0; + cg.basic_char = f.ascii; + cg.mark_char = f.mark; + if(cg.mark_char == CG_COMMA_UR && fi.fixedpitch) + cg.mark_char = CG_CARON; + if(cg.mark_char == CG_COMMA_T) { + cg.mark_pos.y -= 3 * font.GetHeight() / 4; + cg.mark_pos.x += 4 * cw / 10; + if(font.IsItalic()) + cg.mark_pos.x += mw / 2; + } + else + if(cg.mark_char == CG_COMMA_UR) { + cg.mark_pos.y -= 2 * font.GetHeight() / 3; + cg.mark_pos.x += cw - mw / 4; + cg.mark_char = ','; + if(font.IsItalic()) + cg.mark_pos.x += mw / 3; + } + else + if(cg.mark_char == CG_COMMA_URI) { + cg.mark_pos.y -= 2 * font.GetHeight() / 3; + cg.mark_pos.x += cw - mw / 2; + cg.mark_char = ','; + if(font.IsItalic()) + cg.mark_pos.x += mw / 3; + } + else + if(cg.mark_char != CG_STROKE) { + if(cg.mark_char != CG_OGONEK && cg.mark_char != CG_CEDILLA && f.type == CG_CAPITAL) { + cg.mark_font = font(9 * font.GetHeight() / 10); + mw = GetGlyphInfo(cg.mark_font, f.mark).width; + cg.mark_pos.y -= cg.mark_char == CG_RING_ABOVE ? font.GetHeight() / 19 + : font.GetHeight() / 10; + } + cg.mark_pos.x += (cw - mw) / 2; + if(font.IsItalic()) + cg.mark_pos.x += mw / 5; + } + return true; +} + +struct sRFace { + const char *name; + dword l, h; +} sFontReplacements[] = { + { "sans-serif", 0xffee0008, 0xdc000801 }, + { "Arial", 0xfffe0000, 0x9c000801 }, + {"\346\226\260\345\256\213\344\275\223", 0xfd800000, 0x9ffff00d },//SimSun (or New Song Ti) + {"SimSun", 0xfd800000, 0x9ffff00d },//SimSun (or New Song Ti) + {"\345\256\213\344\275\223", 0xfd800000, 0x9ffff00d }, // Song Ti + {"\345\276\256\350\275\257\351\233\205\351\273\221", 0xfd800000, 0x9ffff00f }, //MS Ya Hei + {"Microsoft YaHei", 0xfd800000, 0x9ffff00f }, //MS Ya Hei +// {"\351\273\221\344\275\223", 0xfd800000, 0x09ffff00 }, // Hei Ti +// {"\346\226\207\346\263\211\351\251\277\346\255\243\351\273\221", 0xfd800000, 0x09ffff00 }, //WenQuanYi Zheng Hi +// {"\346\226\207\346\263\211\351\251\277\347\255\211\345\256\275\345\276\256\347\261\263\351\273\221", 0xfd800000, 0x09ffff00 },//WenQuanYi Wei Hei +// {"\344\273\277\345\256\213", 0xfd800000, 0x09ffff00 }, //Fang Song +// {"\346\245\267\344\275\223", 0xfd800000, 0x09ffff00 }, // Kai Ti + { "Arial Unicode MS", 0xfffc3fef, 0xfa7ff7ef }, + { "MS UI Gothic", 0xffc01008, 0xfffff001 }, + { "MS Mincho", 0xffc01008, 0xfffff001 }, + { "VL Gothic", 0xfd800000, 0x9a7ff80f }, + { "VL PGothic", 0xffe00008, 0xde7ff80f }, + { "UnDotum", 0xe5800000, 0xaa7ff7ef }, + { "UnBatang", 0xe5800000, 0xaa7ff7ef }, + { "DejaVu Sans Mono", 0xffec0004, 0xfc00080f }, + { "DejaVu Sans", 0xfffd000c, 0xfc40080f }, + { "AlArabiyaFreeSerif", 0xffdc0008, 0xd800000f }, + { "Kochi Mincho", 0xffdc0008, 0xd800000f }, + { "Kochi Gothic", 0xffdc0008, 0xd800000f }, + { "Sazanami Mincho", 0xffdc0008, 0xd800000f }, + { "Sazanami Gothic", 0xffdc0008, 0xd800000f }, + { "Gulim", 0xf7c00000, 0xba7ff7e1 }, + { "PMingLiU", 0xff800000, 0x9ffff001 }, // <--- SHOULD MOVE UP + { "FreeSans", 0xfff23d00, 0xfc00000f }, + { "FreeSerif", 0xfffd3938, 0xfc00080f }, + { "Symbol", 0xe4000000, 0x8800000f }, +}; + +struct sFontMetricsReplacement { + Font src; + Font dst; + Font mdst; +}; + +bool Replace(Font fnt, int chr, Font& rfnt) +{ + static Vector rface; + static Vector l, h; + ONCELOCK { + for(int i = 0; i < __countof(sFontReplacements) && rface.GetCount() < 20; i++) { + int q = Font::FindFaceNameIndex(sFontReplacements[i].name); + if(q > 0) { + rface.Add(q); + l.Add(sFontReplacements[i].l); + h.Add(sFontReplacements[i].h); + } + } + } + + Font f = fnt; +// dword tl = chr < 4096 ? 0x80000000 >> (chr >> 7) : 0; +// dword th = 0x80000000 >> ((dword)chr >> 11); + for(int i = 0; i < rface.GetCount(); i++) { + if(/*((l[i] & tl) || (h[i] & th)) && */IsNormal(f.Face(rface[i]), chr)) { + int a = fnt.GetAscent(); + int d = fnt.GetDescent(); + if(f.GetAscent() > a || f.GetDescent() > d) { + static sFontMetricsReplacement cache[256]; + int q = CombineHash(fnt, f) & 255; + if(cache[q].src != fnt || cache[q].dst != f) { + cache[q].src = fnt; + cache[q].dst = f; + while((f.GetAscent() > a || f.GetDescent() > d) && f.GetHeight() > 1) { + f.Height(max(1, min(f.GetHeight() - 1, f.GetHeight() * 9 / 10))); + } + cache[q].mdst = f; + } + else + f = cache[q].mdst; + } + rfnt = f; + return true; + } + } + return false; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Draw/FontFc.cpp b/uppsrc/Draw/FontFc.cpp index b2fad1f46..97f7289c4 100644 --- a/uppsrc/Draw/FontFc.cpp +++ b/uppsrc/Draw/FontFc.cpp @@ -1,242 +1,240 @@ -#include "Draw.h" - -#ifdef PLATFORM_POSIX -#include -#include -#endif - -NAMESPACE_UPP - -#ifdef PLATFORM_POSIX - -#define LLOG(x) // LOG(x) -#define LTIMING(x) // TIMING(x) - -void GetStdFontSys(String& name, int& height) -{ - name = "xxxx"; -} - -static FT_Library sFTlib; - -bool sInitFt(void) -{ - if(sFTlib) - return true; - return FT_Init_FreeType(&sFTlib) == 0; -} - -FcPattern *CreateFcPattern(Font font) -{ - LTIMING("CreateXftFont"); - double sina, cosa; - int hg = abs(font.GetHeight()); - if(hg == 0) hg = 10; - String face = font.GetFaceName(); - FcPattern *p = FcPatternCreate(); - FcPatternAddString(p, FC_FAMILY, (FcChar8*)~face); - FcPatternAddInteger(p, FC_SLANT, font.IsItalic() ? 110 : 0); - FcPatternAddInteger(p, FC_PIXEL_SIZE, hg); - FcPatternAddInteger(p, FC_WEIGHT, font.IsBold() ? 200 : 100); - FcPatternAddBool(p, FC_MINSPACE, 1); - FcConfigSubstitute(0, p, FcMatchPattern); - FcDefaultSubstitute(p); - FcResult result; - FcPattern *m = FcFontMatch(0, p, &result); - FcPatternDestroy(p); - return m; -} - -FT_Face CreateFTFace(const FcPattern *pattern, String *rpath) { - FT_Face face = NULL; - - int id; - double dsize; - double aspect; - FcChar8 *filename; - - if(!sInitFt()) - return NULL; - - if(FcPatternGetString(pattern, FC_FILE, 0, &filename) != FcResultMatch) - return NULL; - if(rpath) - *rpath = (const char *)filename; - - if(FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch) - dsize = 16; - - if (FcPatternGetDouble(pattern, FC_ASPECT, 0, &aspect) != FcResultMatch) - aspect = 1.0; - - FT_F26Dot6 ysize = (FT_F26Dot6) (dsize * 64.0); - FT_F26Dot6 xsize = (FT_F26Dot6) (dsize * aspect * 64.0); - - if(FT_New_Face(sFTlib, (const char *)filename, 0, &face)) - return NULL; - - FT_Set_Char_Size(face, xsize, ysize, 0, 0); - return face; -} - -#define FONTCACHE 37 - -struct FtFaceEntry { - Font font; - FT_Face face; - String path; -}; - -static FtFaceEntry ft_cache[FONTCACHE]; - -void ClearFtFaceCache() -{ - for(int i = 0; i < FONTCACHE; i++) - ft_cache[i].font.Height(-30000); -} - -FT_Face (*FTFaceXft)(Font fnt, String *rpath); - -FT_Face FTFace(Font fnt, String *rpath = NULL) -{ - LTIMING("FTFace"); - if(FTFaceXft) - return (*FTFaceXft)(fnt, rpath); - ONCELOCK { - ClearFtFaceCache(); - } - FtFaceEntry be; - be = ft_cache[0]; - for(int i = 0; i < FONTCACHE; i++) { - FtFaceEntry e = ft_cache[i]; - if(i) - ft_cache[i] = be; - if(e.font == fnt) { - if(rpath) - *rpath = e.path; - if(i) - ft_cache[0] = e; - return e.face; - } - be = e; - } - LTIMING("FTFace2"); - if(be.face) { - LLOG("Removing " << be.font << " - " << (void *)be.face); - FT_Done_Face(be.face); - } - be.font = fnt; - FcPattern *p = CreateFcPattern(fnt); - be.face = CreateFTFace(p, &be.path); - FcPatternDestroy(p); - ft_cache[0] = be; - if(rpath) - *rpath = be.path; - return be.face; -} - -CommonFontInfo (*GetFontInfoSysXft)(Font font); - -CommonFontInfo GetFontInfoSys(Font font) -{ - sInitFt(); - if(GetFontInfoSysXft) - return (*GetFontInfoSysXft)(font); - CommonFontInfo fi; - String path; - FT_Face face = FTFace(font, &path); - if(face) { - fi.ascent = face->size->metrics.ascender >> 6; - fi.descent = -(face->size->metrics.descender >> 6); - fi.height = fi.ascent + fi.descent; - fi.lineheight = face->size->metrics.height >> 6; - fi.external = 0; - fi.internal = 0; - fi.overhang = 0; - fi.maxwidth = face->size->metrics.max_advance >> 6; - fi.avewidth = fi.maxwidth; - fi.default_char = '?'; - fi.fixedpitch = font.GetFaceInfo() & Font::FIXEDPITCH; - if(path.GetCount() < 250) - strcpy(fi.path, ~path); - else - *fi.path = 0; - } - return fi; -} - -#define FLOOR(x) ((x) & -64) -#define CEIL(x) (((x)+63) & -64) -#define TRUNC(x) ((x) >> 6) -#define ROUND(x) (((x)+32) & -64) - -GlyphInfo (*GetGlyphInfoSysXft)(Font font, int chr); - -GlyphInfo GetGlyphInfoSys(Font font, int chr) -{ - LTIMING("GetGlyphInfoSys"); - GlyphInfo gi; - FT_Face face = FTFace(font, NULL); - gi.lspc = gi.rspc = 0; - gi.width = 0x8000; - if(face) { - LTIMING("GetGlyphInfoSys 2"); - int glyph_index = FT_Get_Char_Index(face, chr); - if(glyph_index) { - if(GetGlyphInfoSysXft) - return (*GetGlyphInfoSysXft)(font, chr); - if(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP) == 0 || - FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT) == 0) { - FT_Glyph_Metrics& m = face->glyph->metrics; - int left = FLOOR(m.horiBearingX); - int width = TRUNC(CEIL(m.horiBearingX + m.width) - left); - gi.width = TRUNC(ROUND(face->glyph->advance.x)); - gi.lspc = TRUNC(left); - gi.rspc = gi.width - width - gi.lspc; - } - } - } - return gi; -} - -Vector GetAllFacesSys() -{ - static const char *basic_fonts[] = { - "sans-serif", - "serif", - "sans-serif", - "monospace", - }; - - Vector list; - for(int i = 0; i < __countof(basic_fonts); i++) { - FaceInfo& fi = list.Add(); - fi.name = basic_fonts[i]; - fi.info = (i == 3) ? Font::SCALEABLE|Font::FIXEDPITCH : Font::SCALEABLE; - } - FcPattern *p = FcPatternCreate(); - FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, FC_SPACING, FC_SCALABLE, (void *)0); - FcFontSet *fs = FcFontList(NULL, p, os); - FcPatternDestroy(p); - FcObjectSetDestroy(os); - for(int i = 0; i < fs->nfont; i++) { - FcChar8 *family = NULL; - if(FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == 0 && family) { - FaceInfo& fi = list.Add(); - fi.name = (const char *)family; - fi.info = 0; - int iv; - if(FcPatternGetInteger(fs->fonts[i], FC_SPACING, 0, &iv) == 0 && iv == FC_MONO) - fi.info |= Font::FIXEDPITCH; - FcBool bv; - if(FcPatternGetBool(fs->fonts[i], FC_SCALABLE, 0, &bv) == 0 && bv) - fi.info |= Font::SCALEABLE; - } - } - FcFontSetDestroy(fs); - return list; -} - -#endif - -END_UPP_NAMESPACE +#include "Draw.h" + +#ifdef PLATFORM_POSIX +#include +#include +#endif + +NAMESPACE_UPP + +#ifdef PLATFORM_POSIX + +#define LLOG(x) // LOG(x) +#define LTIMING(x) // TIMING(x) + +void GetStdFontSys(String& name, int& height) +{ + name = "xxxx"; +} + +static FT_Library sFTlib; + +bool sInitFt(void) +{ + if(sFTlib) + return true; + return FT_Init_FreeType(&sFTlib) == 0; +} + +FcPattern *CreateFcPattern(Font font) +{ + LTIMING("CreateXftFont"); + int hg = abs(font.GetHeight()); + if(hg == 0) hg = 10; + String face = font.GetFaceName(); + FcPattern *p = FcPatternCreate(); + FcPatternAddString(p, FC_FAMILY, (FcChar8*)~face); + FcPatternAddInteger(p, FC_SLANT, font.IsItalic() ? 110 : 0); + FcPatternAddInteger(p, FC_PIXEL_SIZE, hg); + FcPatternAddInteger(p, FC_WEIGHT, font.IsBold() ? 200 : 100); + FcPatternAddBool(p, FC_MINSPACE, 1); + FcConfigSubstitute(0, p, FcMatchPattern); + FcDefaultSubstitute(p); + FcResult result; + FcPattern *m = FcFontMatch(0, p, &result); + FcPatternDestroy(p); + return m; +} + +FT_Face CreateFTFace(const FcPattern *pattern, String *rpath) { + FT_Face face = NULL; + + double dsize; + double aspect; + FcChar8 *filename; + + if(!sInitFt()) + return NULL; + + if(FcPatternGetString(pattern, FC_FILE, 0, &filename) != FcResultMatch) + return NULL; + if(rpath) + *rpath = (const char *)filename; + + if(FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch) + dsize = 16; + + if (FcPatternGetDouble(pattern, FC_ASPECT, 0, &aspect) != FcResultMatch) + aspect = 1.0; + + FT_F26Dot6 ysize = (FT_F26Dot6) (dsize * 64.0); + FT_F26Dot6 xsize = (FT_F26Dot6) (dsize * aspect * 64.0); + + if(FT_New_Face(sFTlib, (const char *)filename, 0, &face)) + return NULL; + + FT_Set_Char_Size(face, xsize, ysize, 0, 0); + return face; +} + +#define FONTCACHE 37 + +struct FtFaceEntry { + Font font; + FT_Face face; + String path; +}; + +static FtFaceEntry ft_cache[FONTCACHE]; + +void ClearFtFaceCache() +{ + for(int i = 0; i < FONTCACHE; i++) + ft_cache[i].font.Height(-30000); +} + +FT_Face (*FTFaceXft)(Font fnt, String *rpath); + +FT_Face FTFace(Font fnt, String *rpath = NULL) +{ + LTIMING("FTFace"); + if(FTFaceXft) + return (*FTFaceXft)(fnt, rpath); + ONCELOCK { + ClearFtFaceCache(); + } + FtFaceEntry be; + be = ft_cache[0]; + for(int i = 0; i < FONTCACHE; i++) { + FtFaceEntry e = ft_cache[i]; + if(i) + ft_cache[i] = be; + if(e.font == fnt) { + if(rpath) + *rpath = e.path; + if(i) + ft_cache[0] = e; + return e.face; + } + be = e; + } + LTIMING("FTFace2"); + if(be.face) { + LLOG("Removing " << be.font << " - " << (void *)be.face); + FT_Done_Face(be.face); + } + be.font = fnt; + FcPattern *p = CreateFcPattern(fnt); + be.face = CreateFTFace(p, &be.path); + FcPatternDestroy(p); + ft_cache[0] = be; + if(rpath) + *rpath = be.path; + return be.face; +} + +CommonFontInfo (*GetFontInfoSysXft)(Font font); + +CommonFontInfo GetFontInfoSys(Font font) +{ + sInitFt(); + if(GetFontInfoSysXft) + return (*GetFontInfoSysXft)(font); + CommonFontInfo fi; + String path; + FT_Face face = FTFace(font, &path); + if(face) { + fi.ascent = face->size->metrics.ascender >> 6; + fi.descent = -(face->size->metrics.descender >> 6); + fi.height = fi.ascent + fi.descent; + fi.lineheight = face->size->metrics.height >> 6; + fi.external = 0; + fi.internal = 0; + fi.overhang = 0; + fi.maxwidth = face->size->metrics.max_advance >> 6; + fi.avewidth = fi.maxwidth; + fi.default_char = '?'; + fi.fixedpitch = font.GetFaceInfo() & Font::FIXEDPITCH; + if(path.GetCount() < 250) + strcpy(fi.path, ~path); + else + *fi.path = 0; + } + return fi; +} + +#define FLOOR(x) ((x) & -64) +#define CEIL(x) (((x)+63) & -64) +#define TRUNC(x) ((x) >> 6) +#define ROUND(x) (((x)+32) & -64) + +GlyphInfo (*GetGlyphInfoSysXft)(Font font, int chr); + +GlyphInfo GetGlyphInfoSys(Font font, int chr) +{ + LTIMING("GetGlyphInfoSys"); + GlyphInfo gi; + FT_Face face = FTFace(font, NULL); + gi.lspc = gi.rspc = 0; + gi.width = 0x8000; + if(face) { + LTIMING("GetGlyphInfoSys 2"); + int glyph_index = FT_Get_Char_Index(face, chr); + if(glyph_index) { + if(GetGlyphInfoSysXft) + return (*GetGlyphInfoSysXft)(font, chr); + if(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP) == 0 || + FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT) == 0) { + FT_Glyph_Metrics& m = face->glyph->metrics; + int left = FLOOR(m.horiBearingX); + int width = TRUNC(CEIL(m.horiBearingX + m.width) - left); + gi.width = TRUNC(ROUND(face->glyph->advance.x)); + gi.lspc = TRUNC(left); + gi.rspc = gi.width - width - gi.lspc; + } + } + } + return gi; +} + +Vector GetAllFacesSys() +{ + static const char *basic_fonts[] = { + "sans-serif", + "serif", + "sans-serif", + "monospace", + }; + + Vector list; + for(int i = 0; i < __countof(basic_fonts); i++) { + FaceInfo& fi = list.Add(); + fi.name = basic_fonts[i]; + fi.info = (i == 3) ? Font::SCALEABLE|Font::FIXEDPITCH : Font::SCALEABLE; + } + FcPattern *p = FcPatternCreate(); + FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, FC_SPACING, FC_SCALABLE, (void *)0); + FcFontSet *fs = FcFontList(NULL, p, os); + FcPatternDestroy(p); + FcObjectSetDestroy(os); + for(int i = 0; i < fs->nfont; i++) { + FcChar8 *family = NULL; + if(FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == 0 && family) { + FaceInfo& fi = list.Add(); + fi.name = (const char *)family; + fi.info = 0; + int iv; + if(FcPatternGetInteger(fs->fonts[i], FC_SPACING, 0, &iv) == 0 && iv == FC_MONO) + fi.info |= Font::FIXEDPITCH; + FcBool bv; + if(FcPatternGetBool(fs->fonts[i], FC_SCALABLE, 0, &bv) == 0 && bv) + fi.info |= Font::SCALEABLE; + } + } + FcFontSetDestroy(fs); + return list; +} + +#endif + +END_UPP_NAMESPACE diff --git a/uppsrc/Draw/ImageOp.cpp b/uppsrc/Draw/ImageOp.cpp index 586835491..7ba5bad47 100644 --- a/uppsrc/Draw/ImageOp.cpp +++ b/uppsrc/Draw/ImageOp.cpp @@ -1,750 +1,746 @@ -#include "Draw.h" - -NAMESPACE_UPP - -Image WithHotSpots(const Image& m, int x1, int y1, int x2, int y2) -{ - Image h = m; - ImageBuffer b(h); - b.SetHotSpot(Point(x1, y1)); - b.Set2ndSpot(Point(x2, y2)); - return b; -} - -Image WithHotSpot(const Image& m, int x1, int y1) -{ - Image h = m; - ImageBuffer b(h); - b.SetHotSpot(Point(x1, y1)); - return b; -} - -Image CreateImage(Size sz, const RGBA& rgba) -{ - ImageBuffer ib(sz); - Fill(~ib, rgba, ib.GetLength()); - return ib; -} - -Image CreateImage(Size sz, Color color) -{ - return CreateImage(sz, (RGBA)color); -} - -Size DstSrc(ImageBuffer& dest, Point& p, const Image& src, Rect& sr) -{ - if(p.x < 0) { - sr.left += -p.x; - p.x = 0; - } - if(p.y < 0) { - sr.top += -p.y; - p.y = 0; - } - sr = sr & src.GetSize(); - Size sz = dest.GetSize() - p; - sz.cx = min(sz.cx, sr.GetWidth()); - sz.cy = min(sz.cy, sr.GetHeight()); - return sz; -} - -void DstSrcOp(ImageBuffer& dest, Point p, const Image& src, const Rect& srect, - void (*op)(RGBA *t, const RGBA *s, int n)) -{ - Rect sr = srect; - Size sz = DstSrc(dest, p, src, sr); - if(sz.cx > 0) - while(--sz.cy >= 0) - (*op)(dest[p.y++] + p.x, src[sr.top++] + sr.left, sz.cx); -} - -void Copy(ImageBuffer& dest, Point p, const Image& src, const Rect& srect) -{ - DstSrcOp(dest, p, src, srect, Copy); -} - -void Over(ImageBuffer& dest, Point p, const Image& src, const Rect& srect) -{ - DstSrcOp(dest, p, src, srect, AlphaBlend); -} - -void OverStraightOpaque(ImageBuffer& dest, Point p, const Image& src, const Rect& srect) -{ - DstSrcOp(dest, p, src, srect, AlphaBlendStraightOpaque); -} - -void Copy(Image& dest, Point p, const Image& _src, const Rect& srect) -{ - Image src = _src; - ImageBuffer b(dest); - Copy(b, p, src, srect); - dest = b; -} - -void Over(Image& dest, Point p, const Image& _src, const Rect& srect) -{ - Image src = _src; - ImageBuffer b(dest); - Over(b, p, src, srect); - dest = b; -} - -void OverStraightOpaque(Image& dest, Point p, const Image& _src, const Rect& srect) -{ - Image src = _src; - ImageBuffer b(dest); - OverStraightOpaque(b, p, src, srect); - dest = b; -} - -void Crop(RasterEncoder& tgt, Raster& img, const Rect& rc) -{ - Rect r = rc & img.GetSize(); - tgt.Create(r.Size(), img); - for(int y = r.top; y < r.bottom; y++) - tgt.WriteLine(~img[y] + r.left); -} - -Image Crop(const Image& img, const Rect& rc) -{ - if(rc.left == 0 && rc.top == 0 && rc.Size() == img.GetSize()) - return img; - if((rc & img.GetSize()).IsEmpty()) - return Image(); - ImageRaster src(img); - ImageEncoder tgt; - Crop(tgt, src, rc); - return tgt; -} - -Image Crop(const Image& img, int x, int y, int cx, int cy) -{ - return Crop(img, RectC(x, y, cx, cy)); -} - -Image ColorMask(const Image& src, Color key) -{ - ImageBuffer ib(src.GetSize()); - const RGBA *s = src; - const RGBA *e = src + src.GetLength(); - RGBA *t = ~ib; - byte kr = key.GetR(); - byte kg = key.GetG(); - byte kb = key.GetB(); - while(s < e) { - if(s->r == kr && s->g == kg && s->b == kb) - *t++ = RGBAZero(); - else - *t++ = *s; - s++; - } - ib.SetHotSpots(src); - return ib; -} - -void CanvasSize(RasterEncoder& tgt, Raster& img, int cx, int cy) -{ - tgt.Create(cx, cy, img); - int ccx = min(img.GetWidth(), cx); - int ccy = min(img.GetHeight(), cy); - for(int y = 0; y < ccy; y++) { - memcpy(~tgt, img[y], ccx * sizeof(RGBA)); - memset(~tgt + ccx, 0, (cx - ccx) * sizeof(RGBA)); - tgt.WriteLine(); - } - for(int y = cy - ccy; --y >= 0;) { - memset(~tgt, 0, cx * sizeof(RGBA)); - tgt.WriteLine(); - } -} - -Image CanvasSize(const Image& img, int cx, int cy) -{ - ImageRaster src(img); - ImageEncoder tgt; - CanvasSize(tgt, src, cx, cy); - return tgt; -} - -Image AssignAlpha(const Image& img, const Image& alpha) -{ - Size sz = Size(min(img.GetWidth(), alpha.GetWidth()), - min(img.GetHeight(), alpha.GetHeight())); - if(sz.cx == 0 || sz.cy == 0) - return Image(); - ImageBuffer ib(sz); - for(int y = 0; y < sz.cy; y++) { - const RGBA *s = img[y]; - const RGBA *e = s + sz.cx; - const RGBA *a = alpha[y]; - RGBA *t = ib[y]; - while(s < e) { - *t = *s++; - (t++)->a = (a++)->a; - } - } - ib.SetHotSpots(img); - return ib; -} - -int EqualightCh(int c, int l, int h) -{ - return Saturate255((c - l) * 255 / (h - l) + l); -} - -Image Equalight(const Image& img, int thold) -{ - int histogram[256]; - ZeroArray(histogram); - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - while(s < e) { - histogram[Grayscale(*s)]++; - s++; - } - int n = (thold * img.GetLength()) >> 8; - int h = 255; - int l = 0; - while(l < h) { - if(n < 0) - break; - n -= histogram[l++]; - if(n < 0) - break; - n -= histogram[h--]; - } - if(l >= h) - return img; - ImageBuffer w(img.GetSize()); - RGBA *t = w; - s = ~img; - while(s < e) { - t->r = EqualightCh(s->r, l, h); - t->g = EqualightCh(s->g, l, h); - t->b = EqualightCh(s->b, l, h); - t->a = s->a; - s++; - t++; - } - w.SetHotSpots(img); - return w; -} - -Image Grayscale(const Image& img) -{ - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - ImageBuffer w(img.GetSize()); - RGBA *t = w; - while(s < e) { - int q = Grayscale(*s); - t->r = q; - t->g = q; - t->b = q; - t->a = s->a; - t++; - s++; - } - w.SetHotSpots(img); - return w; -} - -Image Grayscale(const Image& img, int amount) -{ - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - ImageBuffer w(img.GetSize()); - RGBA *t = w; - int na = 256 - amount; - while(s < e) { - int q = Grayscale(*s); - t->r = Saturate255((amount * q + na * s->r) >> 8); - t->g = Saturate255((amount * q + na * s->g) >> 8); - t->b = Saturate255((amount * q + na * s->b) >> 8); - t->a = s->a; - t++; - s++; - } - w.SetHotSpots(img); - return w; -} - -Image Colorize(const Image& img, Color color, int alpha) -{ - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - ImageBuffer w(img.GetSize()); - Unmultiply(w); - RGBA *t = w; - byte r = color.GetR(); - byte g = color.GetG(); - byte b = color.GetB(); - alpha = alpha + (alpha >> 7); - while(s < e) { - int ga = Grayscale(*s); - ga = ga + (ga >> 7); - t->r = (alpha * (((ga * r) >> 8) - s->r) >> 8) + s->r; - t->g = (alpha * (((ga * g) >> 8) - s->g) >> 8) + s->g; - t->b = (alpha * (((ga * b) >> 8) - s->b) >> 8) + s->b; - t->a = s->a; - t++; - s++; - } - Premultiply(w); - w.SetHotSpots(img); - return w; -} - -Image AdjustForDarkBk(const Image& img) -{ - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - ImageBuffer w(img.GetSize()); - RGBA *t = w; - while(s < e) { - RGBA h = *s; - Unmultiply(&h, &h, 1); - int q = Grayscale(*s); - if(q < 40) { - int lvl0 = max(s->r, max(s->g, s->b)); - int l = 255 - lvl0; - t->r = Saturate255(l + s->r); - t->g = Saturate255(l + s->g); - t->b = Saturate255(l + s->b); - } - else - if(q > 216) { - int lvl0 = min(s->r, min(s->g, s->b)); - int l = 255 - lvl0; - t->r = Saturate255(l + s->r - lvl0); - t->g = Saturate255(l + s->g - lvl0); - t->b = Saturate255(l + s->b - lvl0); - } - else { - t->r = s->r; - t->g = s->g; - t->b = s->b; - } - t->a = s->a; - t++; - s++; - } - Premultiply(w); - w.SetHotSpots(img); - return w; -} - -inline -byte ContrastCh(int amount, int ch) -{ - return Saturate255(128 + (amount * (ch - 128) >> 8)); -} - -Image Contrast(const Image& img, int amount) -{ - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - ImageBuffer w(img.GetSize()); - Unmultiply(w); - RGBA *t = w; - while(s < e) { - t->r = ContrastCh(amount, s->r); - t->g = ContrastCh(amount, s->g); - t->b = ContrastCh(amount, s->b); - t->a = s->a; - t++; - s++; - } - Premultiply(w); - w.SetHotSpots(img); - return w; -} - -void sLine(RGBA *t, int cx, const RasterLine l[3], ImageFilter9& filter) -{ - RGBA h[3][3]; - const RGBA *x[3]; - x[0] = h[0]; - x[1] = h[1]; - x[2] = h[2]; - if(cx == 1) { - h[0][0] = l[0][0]; h[0][1] = l[0][0]; h[0][2] = l[0][0]; - h[1][0] = l[1][0]; h[1][1] = l[1][0]; h[1][2] = l[1][0]; - h[2][0] = l[2][0]; h[2][1] = l[2][0]; h[2][2] = l[2][0]; - *t = filter(x); - return; - } - h[0][0] = l[0][0]; h[0][1] = l[0][0]; h[0][2] = l[0][1]; - h[1][0] = l[1][0]; h[1][1] = l[1][0]; h[1][2] = l[1][1]; - h[2][0] = l[2][0]; h[2][1] = l[2][0]; h[2][2] = l[2][1]; - *t++ = filter(x); - for(int i = 1; i < cx - 1; i++) { - x[0] = ~l[0] + i - 1; - x[1] = ~l[1] + i - 1; - x[2] = ~l[2] + i - 1; - *t++ = filter(x); - } - h[0][0] = l[0][cx - 2]; h[0][1] = l[0][cx - 1]; h[0][2] = l[0][cx - 1]; - h[1][0] = l[1][cx - 2]; h[1][1] = l[1][cx - 1]; h[1][2] = l[1][cx - 1]; - h[2][0] = l[2][cx - 2]; h[2][1] = l[2][cx - 1]; h[2][2] = l[2][cx - 1]; - x[0] = h[0]; - x[1] = h[1]; - x[2] = h[2]; - *t++ = filter(x); -} - -void Filter(RasterEncoder& target, Raster& src, ImageFilter9& filter) -{ - Size sz = src.GetSize(); - target.Create(sz, src); - if(sz.cy < 1) - return; - RasterLine l[3]; - if(sz.cy == 1) { - l[0] = src[0]; - l[1] = src[0]; - l[2] = src[0]; - sLine(target, sz.cx, l, filter); - return; - } - l[0] = src[0]; - l[1] = src[0]; - l[2] = src[1]; - sLine(target, sz.cx, l, filter); - target.WriteLine(); - for(int y = 1; y < sz.cy - 1; y++) { - l[0] = l[1]; - l[1] = l[2]; - l[2] = src[y + 1]; - sLine(target, sz.cx, l, filter); - target.WriteLine(); - } - l[0] = l[1]; - l[1] = l[2]; - l[2] = src[sz.cy - 1]; - sLine(target, sz.cx, l, filter); - target.WriteLine(); -} - -Image Filter(const Image& img, ImageFilter9& filter) -{ - ImageEncoder tgt; - ImageRaster src(img); - Filter(tgt, src, filter); - return tgt; -} - -struct RGBAI { - int r, g, b, a; - - RGBAI() { r = g = b = a= 0; } -}; - -static void sGetS(RGBA q, RGBAI& p, int mul) -{ - p.r += mul * q.r; - p.g += mul * q.g; - p.b += mul * q.b; - p.a += mul * q.a; -} - -struct sSharpenFilter : ImageFilter9 { - int amount; - - virtual RGBA operator()(const RGBA **mx); -}; - -RGBA sSharpenFilter::operator()(const RGBA **mx) -{ - RGBAI q; - sGetS(mx[0][0], q, 7); - sGetS(mx[0][1], q, 9); - sGetS(mx[0][2], q, 7); - sGetS(mx[1][0], q, 9); - sGetS(mx[1][2], q, 9); - sGetS(mx[2][0], q, 7); - sGetS(mx[2][1], q, 9); - sGetS(mx[2][2], q, 7); - const RGBA& s = mx[1][1]; - RGBA t; - int na = 256 + amount; - t.b = Saturate255((na * (s.b << 6) - amount * q.b) >> 14); - t.g = Saturate255((na * (s.g << 6) - amount * q.g) >> 14); - t.r = Saturate255((na * (s.r << 6) - amount * q.r) >> 14); - t.a = Saturate255((na * (s.a << 6) - amount * q.a) >> 14); - return t; -} - -void Sharpen(RasterEncoder& target, Raster& src, int amount) -{ - Size sz = src.GetSize(); - target.Create(sz, src); - sSharpenFilter f; - f.amount = amount; - Filter(target, src, f); -} - -Image Sharpen(const Image& img, int amount) -{ - ImageEncoder tgt; - ImageRaster src(img); - Sharpen(tgt, src, amount); - return tgt; -} - -struct sEtchFilter : ImageFilter9 { - virtual RGBA operator()(const RGBA **mx); -}; - -RGBA sEtchFilter::operator()(const RGBA **mx) -{ - RGBA t; - RGBA s = mx[1][1]; - if(s.a > 0x80 && s.r + s.g + s.b < 500) { - t.r = t.g = t.b = 128; - t.a = s.a; - return t; - } - s = mx[0][0]; - if(s.a > 0x80 && s.r + s.g + s.b < 500) { - t.r = t.g = t.b = 255; - t.a = s.a; - return t; - } - Zero(t); - return t; -} - -Image Etched(const Image& img) -{ - sEtchFilter ef; - return Premultiply(Filter(Unmultiply(img), ef)); -} - -Image SetColorKeepAlpha(const Image& img, Color c) -{ - RGBA rgba = c; - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - ImageBuffer w(img.GetSize()); - RGBA *t = w; - while(s < e) { - *t = rgba; - (t++)->a = (s++)->a; - } - Premultiply(w); - w.SetHotSpots(img); - return w; -} - -Image CreateHorzFadeOut(Size sz, Color color) -{ - ImageBuffer ib(sz); - RGBA c = color; - for(int q = 0; q < sz.cx; q++) { - c.a = q * 255 / sz.cx; - RGBA *t = ~ib + q; - for(int n = sz.cy; n > 0; n--) { - *t = c; - t += sz.cx; - } - } - Premultiply(ib); - return ib; -} - -struct FadeOutMaker : ImageMaker { - Size sz; - Color color; - - virtual String Key() const { - char h[sizeof(Size) + sizeof(Color)]; - memcpy(h, &sz, sizeof(sz)); - memcpy(h + sizeof(Size), &color, sizeof(Color)); - return String(h, sizeof(h)); - } - - virtual Image Make() const { - return CreateHorzFadeOut(sz, color); - } -}; - -Image HorzFadeOut(Size sz, Color color) -{ - FadeOutMaker m; - m.sz = sz; - m.color = color; - return MakeImage(m); -} - -Image HorzFadeOut(int cx, int cy, Color color) -{ - return HorzFadeOut(Size(cx, cy), color); -} - -void SetNormalizedHotSpots(ImageBuffer& ib, int x1, int y1, int x2, int y2) -{ - Rect r(x1, y1, x2, y2); - r.Normalize(); - ib.SetHotSpot(r.TopLeft()); - ib.Set2ndSpot(r.BottomRight()); -} - -Image RotateClockwise(const Image& img) -{ - Size sz = img.GetSize(); - ImageBuffer ib(sz.cy, sz.cx); - for(int x = 0; x < sz.cx; x++) - for(int y = 0; y < sz.cy; y++) - ib[x][y] = img[sz.cy - y - 1][x]; - Point p1 = img.GetHotSpot(); - Point p2 = img.Get2ndSpot(); - SetNormalizedHotSpots(ib, sz.cy - p1.y - 1, p1.x, sz.cy - p2.y - 1, p2.x); - return ib; -} - -Image RotateAntiClockwise(const Image& img) -{ - Size sz = img.GetSize(); - ImageBuffer ib(sz.cy, sz.cx); - for(int x = 0; x < sz.cx; x++) - for(int y = 0; y < sz.cy; y++) - ib[x][y] = img[y][sz.cx - x - 1]; - - Point p1 = img.GetHotSpot(); - Point p2 = img.Get2ndSpot(); - SetNormalizedHotSpots(ib, p1.y, sz.cx - p1.x - 1, p2.y, sz.cx - p2.x - 1); - return ib; -} - -Image Rotate180(const Image& orig) -{ - Size sz = orig.GetSize(); - ImageBuffer dest(sz); - for(int rw = 0; rw < sz.cy; rw++) - for(int cl = 0; cl < sz.cx; cl++) - dest[rw][cl] = orig[sz.cy - rw - 1][sz.cx - cl - 1]; - Point p1 = orig.GetHotSpot(); - Point p2 = orig.Get2ndSpot(); - SetNormalizedHotSpots(dest, sz.cy - p1.y - 1, sz.cx - p1.x - 1, sz.cy - p2.y - 1, sz.cx - p2.x - 1); - return dest; -} - -Image MirrorHorz(const Image& img) -{ - Size sz = img.GetSize(); - Image h = img; - ImageBuffer ib(h); - for(int y = 0; y < sz.cy; y++) { - RGBA *b = ib[y] + 0; - RGBA *e = b + sz.cx - 1; - while(b < e) { - Swap(*b, *e); - b++; - e--; - } - } - Point p1 = img.GetHotSpot(); - Point p2 = img.Get2ndSpot(); - SetNormalizedHotSpots(ib, sz.cx - p1.x - 1, p1.y, sz.cx - p2.x - 1, p2.y); - return ib; -} - -Image MirrorVert(const Image& img) -{ - Size sz = img.GetSize(); - Image h = img; - ImageBuffer ib(h); - - for(int y = 0; y < sz.cy / 2; y++) { - RGBA *b = ib[y]; - RGBA *e = ib[sz.cy - y - 1]; - for(int x = 0; x < sz.cx; x++) { - Swap(*b, *e); - b++; - e++; - } - } - Point p1 = img.GetHotSpot(); - Point p2 = img.Get2ndSpot(); - SetNormalizedHotSpots(ib, p1.x, sz.cy - p1.y - 1, p2.x, sz.cy - p2.y - 1); - return ib; -} - -Image Magnify(const Image& img, int nx, int ny) -{ - if(nx == 1 && ny == 1) - return img; - if(nx == 0 || ny == 0) - return Image(); - Size sz = img.GetSize(); - bool xdown = nx < 0; - nx = abs(nx); - int ncx = xdown ? sz.cx / nx : sz.cx * nx; - ImageBuffer b(ncx, sz.cy * ny); - const RGBA *s = ~img; - const RGBA *e = s + img.GetLength(); - RGBA *t = ~b; - while(s < e) { - RGBA *q = t; - const RGBA *le = s + sz.cx; - while(s < le) { - Fill(q, *s, nx); - q += nx; - s++; - } - for(int n = ny - 1; n--;) { - memcpy(q, t, ncx * sizeof(RGBA)); - q += ncx; - } - t = q; - } - return b; -} - -static Pointf Cvp(double x, double y, double sina, double cosa) -{ - return Pointf(x * cosa + y * sina, -x * sina + y * cosa); -} - -Image Rotate(const Image& m, int angle) -{ - Size isz = m.GetSize(); - Point center = isz / 2; - Pointf centerf = Pointf(Point(isz)) / 2.0; - double sina, cosa; - Draw::SinCos(-angle, sina, cosa); - Pointf p1 = Cvp(-centerf.x, -centerf.y, sina, cosa); - Pointf p2 = Cvp(centerf.x, -centerf.y, sina, cosa); - Size sz2 = Size(2 * (int)max(tabs(p1.x), tabs(p2.x)), - 2 * (int)max(tabs(p1.y), tabs(p2.y))); - Pointf dcenterf = Sizef(sz2) / 2.0; - Point dcenter = sz2 / 2; - - ImageBuffer ib(sz2); - Fill(~ib, RGBAZero(), ib.GetLength()); - RGBA *t = ~ib; - Draw::SinCos(angle, sina, cosa); - int sini = int(sina * 128); - int cosi = int(cosa * 128); - Buffer xmx(sz2.cx); - Buffer xmy(sz2.cx); - for(int x = 0; x < sz2.cx; x++) { - int xx = x + x - sz2.cx; - xmx[x] = int(xx * cosi); - xmy[x] = -int(xx * sini); - } - for(int y = 0; y < sz2.cy; y++) { - int yy = y + y - sz2.cy; - int ymx = int(yy * sini) + (isz.cx << 7); - int ymy = int(yy * cosi) + (isz.cy << 7); - for(int x = 0; x < sz2.cx; x++) { - int xs = (xmx[x] + ymx) >> 8; - int ys = (xmy[x] + ymy) >> 8; - *t++ = xs >= 0 && xs < isz.cx && ys >= 0 && ys < isz.cy ? m[ys][xs] : RGBAZero(); - } - } - return ib; -} - -END_UPP_NAMESPACE +#include "Draw.h" + +NAMESPACE_UPP + +Image WithHotSpots(const Image& m, int x1, int y1, int x2, int y2) +{ + Image h = m; + ImageBuffer b(h); + b.SetHotSpot(Point(x1, y1)); + b.Set2ndSpot(Point(x2, y2)); + return b; +} + +Image WithHotSpot(const Image& m, int x1, int y1) +{ + Image h = m; + ImageBuffer b(h); + b.SetHotSpot(Point(x1, y1)); + return b; +} + +Image CreateImage(Size sz, const RGBA& rgba) +{ + ImageBuffer ib(sz); + Fill(~ib, rgba, ib.GetLength()); + return ib; +} + +Image CreateImage(Size sz, Color color) +{ + return CreateImage(sz, (RGBA)color); +} + +Size DstSrc(ImageBuffer& dest, Point& p, const Image& src, Rect& sr) +{ + if(p.x < 0) { + sr.left += -p.x; + p.x = 0; + } + if(p.y < 0) { + sr.top += -p.y; + p.y = 0; + } + sr = sr & src.GetSize(); + Size sz = dest.GetSize() - p; + sz.cx = min(sz.cx, sr.GetWidth()); + sz.cy = min(sz.cy, sr.GetHeight()); + return sz; +} + +void DstSrcOp(ImageBuffer& dest, Point p, const Image& src, const Rect& srect, + void (*op)(RGBA *t, const RGBA *s, int n)) +{ + Rect sr = srect; + Size sz = DstSrc(dest, p, src, sr); + if(sz.cx > 0) + while(--sz.cy >= 0) + (*op)(dest[p.y++] + p.x, src[sr.top++] + sr.left, sz.cx); +} + +void Copy(ImageBuffer& dest, Point p, const Image& src, const Rect& srect) +{ + DstSrcOp(dest, p, src, srect, Copy); +} + +void Over(ImageBuffer& dest, Point p, const Image& src, const Rect& srect) +{ + DstSrcOp(dest, p, src, srect, AlphaBlend); +} + +void OverStraightOpaque(ImageBuffer& dest, Point p, const Image& src, const Rect& srect) +{ + DstSrcOp(dest, p, src, srect, AlphaBlendStraightOpaque); +} + +void Copy(Image& dest, Point p, const Image& _src, const Rect& srect) +{ + Image src = _src; + ImageBuffer b(dest); + Copy(b, p, src, srect); + dest = b; +} + +void Over(Image& dest, Point p, const Image& _src, const Rect& srect) +{ + Image src = _src; + ImageBuffer b(dest); + Over(b, p, src, srect); + dest = b; +} + +void OverStraightOpaque(Image& dest, Point p, const Image& _src, const Rect& srect) +{ + Image src = _src; + ImageBuffer b(dest); + OverStraightOpaque(b, p, src, srect); + dest = b; +} + +void Crop(RasterEncoder& tgt, Raster& img, const Rect& rc) +{ + Rect r = rc & img.GetSize(); + tgt.Create(r.Size(), img); + for(int y = r.top; y < r.bottom; y++) + tgt.WriteLine(~img[y] + r.left); +} + +Image Crop(const Image& img, const Rect& rc) +{ + if(rc.left == 0 && rc.top == 0 && rc.Size() == img.GetSize()) + return img; + if((rc & img.GetSize()).IsEmpty()) + return Image(); + ImageRaster src(img); + ImageEncoder tgt; + Crop(tgt, src, rc); + return tgt; +} + +Image Crop(const Image& img, int x, int y, int cx, int cy) +{ + return Crop(img, RectC(x, y, cx, cy)); +} + +Image ColorMask(const Image& src, Color key) +{ + ImageBuffer ib(src.GetSize()); + const RGBA *s = src; + const RGBA *e = src + src.GetLength(); + RGBA *t = ~ib; + byte kr = key.GetR(); + byte kg = key.GetG(); + byte kb = key.GetB(); + while(s < e) { + if(s->r == kr && s->g == kg && s->b == kb) + *t++ = RGBAZero(); + else + *t++ = *s; + s++; + } + ib.SetHotSpots(src); + return ib; +} + +void CanvasSize(RasterEncoder& tgt, Raster& img, int cx, int cy) +{ + tgt.Create(cx, cy, img); + int ccx = min(img.GetWidth(), cx); + int ccy = min(img.GetHeight(), cy); + for(int y = 0; y < ccy; y++) { + memcpy(~tgt, img[y], ccx * sizeof(RGBA)); + memset(~tgt + ccx, 0, (cx - ccx) * sizeof(RGBA)); + tgt.WriteLine(); + } + for(int y = cy - ccy; --y >= 0;) { + memset(~tgt, 0, cx * sizeof(RGBA)); + tgt.WriteLine(); + } +} + +Image CanvasSize(const Image& img, int cx, int cy) +{ + ImageRaster src(img); + ImageEncoder tgt; + CanvasSize(tgt, src, cx, cy); + return tgt; +} + +Image AssignAlpha(const Image& img, const Image& alpha) +{ + Size sz = Size(min(img.GetWidth(), alpha.GetWidth()), + min(img.GetHeight(), alpha.GetHeight())); + if(sz.cx == 0 || sz.cy == 0) + return Image(); + ImageBuffer ib(sz); + for(int y = 0; y < sz.cy; y++) { + const RGBA *s = img[y]; + const RGBA *e = s + sz.cx; + const RGBA *a = alpha[y]; + RGBA *t = ib[y]; + while(s < e) { + *t = *s++; + (t++)->a = (a++)->a; + } + } + ib.SetHotSpots(img); + return ib; +} + +int EqualightCh(int c, int l, int h) +{ + return Saturate255((c - l) * 255 / (h - l) + l); +} + +Image Equalight(const Image& img, int thold) +{ + int histogram[256]; + ZeroArray(histogram); + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + while(s < e) { + histogram[Grayscale(*s)]++; + s++; + } + int n = (thold * img.GetLength()) >> 8; + int h = 255; + int l = 0; + while(l < h) { + if(n < 0) + break; + n -= histogram[l++]; + if(n < 0) + break; + n -= histogram[h--]; + } + if(l >= h) + return img; + ImageBuffer w(img.GetSize()); + RGBA *t = w; + s = ~img; + while(s < e) { + t->r = EqualightCh(s->r, l, h); + t->g = EqualightCh(s->g, l, h); + t->b = EqualightCh(s->b, l, h); + t->a = s->a; + s++; + t++; + } + w.SetHotSpots(img); + return w; +} + +Image Grayscale(const Image& img) +{ + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + ImageBuffer w(img.GetSize()); + RGBA *t = w; + while(s < e) { + int q = Grayscale(*s); + t->r = q; + t->g = q; + t->b = q; + t->a = s->a; + t++; + s++; + } + w.SetHotSpots(img); + return w; +} + +Image Grayscale(const Image& img, int amount) +{ + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + ImageBuffer w(img.GetSize()); + RGBA *t = w; + int na = 256 - amount; + while(s < e) { + int q = Grayscale(*s); + t->r = Saturate255((amount * q + na * s->r) >> 8); + t->g = Saturate255((amount * q + na * s->g) >> 8); + t->b = Saturate255((amount * q + na * s->b) >> 8); + t->a = s->a; + t++; + s++; + } + w.SetHotSpots(img); + return w; +} + +Image Colorize(const Image& img, Color color, int alpha) +{ + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + ImageBuffer w(img.GetSize()); + Unmultiply(w); + RGBA *t = w; + byte r = color.GetR(); + byte g = color.GetG(); + byte b = color.GetB(); + alpha = alpha + (alpha >> 7); + while(s < e) { + int ga = Grayscale(*s); + ga = ga + (ga >> 7); + t->r = (alpha * (((ga * r) >> 8) - s->r) >> 8) + s->r; + t->g = (alpha * (((ga * g) >> 8) - s->g) >> 8) + s->g; + t->b = (alpha * (((ga * b) >> 8) - s->b) >> 8) + s->b; + t->a = s->a; + t++; + s++; + } + Premultiply(w); + w.SetHotSpots(img); + return w; +} + +Image AdjustForDarkBk(const Image& img) +{ + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + ImageBuffer w(img.GetSize()); + RGBA *t = w; + while(s < e) { + RGBA h = *s; + Unmultiply(&h, &h, 1); + int q = Grayscale(*s); + if(q < 40) { + int lvl0 = max(s->r, max(s->g, s->b)); + int l = 255 - lvl0; + t->r = Saturate255(l + s->r); + t->g = Saturate255(l + s->g); + t->b = Saturate255(l + s->b); + } + else + if(q > 216) { + int lvl0 = min(s->r, min(s->g, s->b)); + int l = 255 - lvl0; + t->r = Saturate255(l + s->r - lvl0); + t->g = Saturate255(l + s->g - lvl0); + t->b = Saturate255(l + s->b - lvl0); + } + else { + t->r = s->r; + t->g = s->g; + t->b = s->b; + } + t->a = s->a; + t++; + s++; + } + Premultiply(w); + w.SetHotSpots(img); + return w; +} + +inline +byte ContrastCh(int amount, int ch) +{ + return Saturate255(128 + (amount * (ch - 128) >> 8)); +} + +Image Contrast(const Image& img, int amount) +{ + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + ImageBuffer w(img.GetSize()); + Unmultiply(w); + RGBA *t = w; + while(s < e) { + t->r = ContrastCh(amount, s->r); + t->g = ContrastCh(amount, s->g); + t->b = ContrastCh(amount, s->b); + t->a = s->a; + t++; + s++; + } + Premultiply(w); + w.SetHotSpots(img); + return w; +} + +void sLine(RGBA *t, int cx, const RasterLine l[3], ImageFilter9& filter) +{ + RGBA h[3][3]; + const RGBA *x[3]; + x[0] = h[0]; + x[1] = h[1]; + x[2] = h[2]; + if(cx == 1) { + h[0][0] = l[0][0]; h[0][1] = l[0][0]; h[0][2] = l[0][0]; + h[1][0] = l[1][0]; h[1][1] = l[1][0]; h[1][2] = l[1][0]; + h[2][0] = l[2][0]; h[2][1] = l[2][0]; h[2][2] = l[2][0]; + *t = filter(x); + return; + } + h[0][0] = l[0][0]; h[0][1] = l[0][0]; h[0][2] = l[0][1]; + h[1][0] = l[1][0]; h[1][1] = l[1][0]; h[1][2] = l[1][1]; + h[2][0] = l[2][0]; h[2][1] = l[2][0]; h[2][2] = l[2][1]; + *t++ = filter(x); + for(int i = 1; i < cx - 1; i++) { + x[0] = ~l[0] + i - 1; + x[1] = ~l[1] + i - 1; + x[2] = ~l[2] + i - 1; + *t++ = filter(x); + } + h[0][0] = l[0][cx - 2]; h[0][1] = l[0][cx - 1]; h[0][2] = l[0][cx - 1]; + h[1][0] = l[1][cx - 2]; h[1][1] = l[1][cx - 1]; h[1][2] = l[1][cx - 1]; + h[2][0] = l[2][cx - 2]; h[2][1] = l[2][cx - 1]; h[2][2] = l[2][cx - 1]; + x[0] = h[0]; + x[1] = h[1]; + x[2] = h[2]; + *t++ = filter(x); +} + +void Filter(RasterEncoder& target, Raster& src, ImageFilter9& filter) +{ + Size sz = src.GetSize(); + target.Create(sz, src); + if(sz.cy < 1) + return; + RasterLine l[3]; + if(sz.cy == 1) { + l[0] = src[0]; + l[1] = src[0]; + l[2] = src[0]; + sLine(target, sz.cx, l, filter); + return; + } + l[0] = src[0]; + l[1] = src[0]; + l[2] = src[1]; + sLine(target, sz.cx, l, filter); + target.WriteLine(); + for(int y = 1; y < sz.cy - 1; y++) { + l[0] = l[1]; + l[1] = l[2]; + l[2] = src[y + 1]; + sLine(target, sz.cx, l, filter); + target.WriteLine(); + } + l[0] = l[1]; + l[1] = l[2]; + l[2] = src[sz.cy - 1]; + sLine(target, sz.cx, l, filter); + target.WriteLine(); +} + +Image Filter(const Image& img, ImageFilter9& filter) +{ + ImageEncoder tgt; + ImageRaster src(img); + Filter(tgt, src, filter); + return tgt; +} + +struct RGBAI { + int r, g, b, a; + + RGBAI() { r = g = b = a= 0; } +}; + +static void sGetS(RGBA q, RGBAI& p, int mul) +{ + p.r += mul * q.r; + p.g += mul * q.g; + p.b += mul * q.b; + p.a += mul * q.a; +} + +struct sSharpenFilter : ImageFilter9 { + int amount; + + virtual RGBA operator()(const RGBA **mx); +}; + +RGBA sSharpenFilter::operator()(const RGBA **mx) +{ + RGBAI q; + sGetS(mx[0][0], q, 7); + sGetS(mx[0][1], q, 9); + sGetS(mx[0][2], q, 7); + sGetS(mx[1][0], q, 9); + sGetS(mx[1][2], q, 9); + sGetS(mx[2][0], q, 7); + sGetS(mx[2][1], q, 9); + sGetS(mx[2][2], q, 7); + const RGBA& s = mx[1][1]; + RGBA t; + int na = 256 + amount; + t.b = Saturate255((na * (s.b << 6) - amount * q.b) >> 14); + t.g = Saturate255((na * (s.g << 6) - amount * q.g) >> 14); + t.r = Saturate255((na * (s.r << 6) - amount * q.r) >> 14); + t.a = Saturate255((na * (s.a << 6) - amount * q.a) >> 14); + return t; +} + +void Sharpen(RasterEncoder& target, Raster& src, int amount) +{ + Size sz = src.GetSize(); + target.Create(sz, src); + sSharpenFilter f; + f.amount = amount; + Filter(target, src, f); +} + +Image Sharpen(const Image& img, int amount) +{ + ImageEncoder tgt; + ImageRaster src(img); + Sharpen(tgt, src, amount); + return tgt; +} + +struct sEtchFilter : ImageFilter9 { + virtual RGBA operator()(const RGBA **mx); +}; + +RGBA sEtchFilter::operator()(const RGBA **mx) +{ + RGBA t; + RGBA s = mx[1][1]; + if(s.a > 0x80 && s.r + s.g + s.b < 500) { + t.r = t.g = t.b = 128; + t.a = s.a; + return t; + } + s = mx[0][0]; + if(s.a > 0x80 && s.r + s.g + s.b < 500) { + t.r = t.g = t.b = 255; + t.a = s.a; + return t; + } + Zero(t); + return t; +} + +Image Etched(const Image& img) +{ + sEtchFilter ef; + return Premultiply(Filter(Unmultiply(img), ef)); +} + +Image SetColorKeepAlpha(const Image& img, Color c) +{ + RGBA rgba = c; + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + ImageBuffer w(img.GetSize()); + RGBA *t = w; + while(s < e) { + *t = rgba; + (t++)->a = (s++)->a; + } + Premultiply(w); + w.SetHotSpots(img); + return w; +} + +Image CreateHorzFadeOut(Size sz, Color color) +{ + ImageBuffer ib(sz); + RGBA c = color; + for(int q = 0; q < sz.cx; q++) { + c.a = q * 255 / sz.cx; + RGBA *t = ~ib + q; + for(int n = sz.cy; n > 0; n--) { + *t = c; + t += sz.cx; + } + } + Premultiply(ib); + return ib; +} + +struct FadeOutMaker : ImageMaker { + Size sz; + Color color; + + virtual String Key() const { + char h[sizeof(Size) + sizeof(Color)]; + memcpy(h, &sz, sizeof(sz)); + memcpy(h + sizeof(Size), &color, sizeof(Color)); + return String(h, sizeof(h)); + } + + virtual Image Make() const { + return CreateHorzFadeOut(sz, color); + } +}; + +Image HorzFadeOut(Size sz, Color color) +{ + FadeOutMaker m; + m.sz = sz; + m.color = color; + return MakeImage(m); +} + +Image HorzFadeOut(int cx, int cy, Color color) +{ + return HorzFadeOut(Size(cx, cy), color); +} + +void SetNormalizedHotSpots(ImageBuffer& ib, int x1, int y1, int x2, int y2) +{ + Rect r(x1, y1, x2, y2); + r.Normalize(); + ib.SetHotSpot(r.TopLeft()); + ib.Set2ndSpot(r.BottomRight()); +} + +Image RotateClockwise(const Image& img) +{ + Size sz = img.GetSize(); + ImageBuffer ib(sz.cy, sz.cx); + for(int x = 0; x < sz.cx; x++) + for(int y = 0; y < sz.cy; y++) + ib[x][y] = img[sz.cy - y - 1][x]; + Point p1 = img.GetHotSpot(); + Point p2 = img.Get2ndSpot(); + SetNormalizedHotSpots(ib, sz.cy - p1.y - 1, p1.x, sz.cy - p2.y - 1, p2.x); + return ib; +} + +Image RotateAntiClockwise(const Image& img) +{ + Size sz = img.GetSize(); + ImageBuffer ib(sz.cy, sz.cx); + for(int x = 0; x < sz.cx; x++) + for(int y = 0; y < sz.cy; y++) + ib[x][y] = img[y][sz.cx - x - 1]; + + Point p1 = img.GetHotSpot(); + Point p2 = img.Get2ndSpot(); + SetNormalizedHotSpots(ib, p1.y, sz.cx - p1.x - 1, p2.y, sz.cx - p2.x - 1); + return ib; +} + +Image Rotate180(const Image& orig) +{ + Size sz = orig.GetSize(); + ImageBuffer dest(sz); + for(int rw = 0; rw < sz.cy; rw++) + for(int cl = 0; cl < sz.cx; cl++) + dest[rw][cl] = orig[sz.cy - rw - 1][sz.cx - cl - 1]; + Point p1 = orig.GetHotSpot(); + Point p2 = orig.Get2ndSpot(); + SetNormalizedHotSpots(dest, sz.cy - p1.y - 1, sz.cx - p1.x - 1, sz.cy - p2.y - 1, sz.cx - p2.x - 1); + return dest; +} + +Image MirrorHorz(const Image& img) +{ + Size sz = img.GetSize(); + Image h = img; + ImageBuffer ib(h); + for(int y = 0; y < sz.cy; y++) { + RGBA *b = ib[y] + 0; + RGBA *e = b + sz.cx - 1; + while(b < e) { + Swap(*b, *e); + b++; + e--; + } + } + Point p1 = img.GetHotSpot(); + Point p2 = img.Get2ndSpot(); + SetNormalizedHotSpots(ib, sz.cx - p1.x - 1, p1.y, sz.cx - p2.x - 1, p2.y); + return ib; +} + +Image MirrorVert(const Image& img) +{ + Size sz = img.GetSize(); + Image h = img; + ImageBuffer ib(h); + + for(int y = 0; y < sz.cy / 2; y++) { + RGBA *b = ib[y]; + RGBA *e = ib[sz.cy - y - 1]; + for(int x = 0; x < sz.cx; x++) { + Swap(*b, *e); + b++; + e++; + } + } + Point p1 = img.GetHotSpot(); + Point p2 = img.Get2ndSpot(); + SetNormalizedHotSpots(ib, p1.x, sz.cy - p1.y - 1, p2.x, sz.cy - p2.y - 1); + return ib; +} + +Image Magnify(const Image& img, int nx, int ny) +{ + if(nx == 1 && ny == 1) + return img; + if(nx == 0 || ny == 0) + return Image(); + Size sz = img.GetSize(); + bool xdown = nx < 0; + nx = abs(nx); + int ncx = xdown ? sz.cx / nx : sz.cx * nx; + ImageBuffer b(ncx, sz.cy * ny); + const RGBA *s = ~img; + const RGBA *e = s + img.GetLength(); + RGBA *t = ~b; + while(s < e) { + RGBA *q = t; + const RGBA *le = s + sz.cx; + while(s < le) { + Fill(q, *s, nx); + q += nx; + s++; + } + for(int n = ny - 1; n--;) { + memcpy(q, t, ncx * sizeof(RGBA)); + q += ncx; + } + t = q; + } + return b; +} + +static Pointf Cvp(double x, double y, double sina, double cosa) +{ + return Pointf(x * cosa + y * sina, -x * sina + y * cosa); +} + +Image Rotate(const Image& m, int angle) +{ + Size isz = m.GetSize(); + Pointf centerf = Pointf(Point(isz)) / 2.0; + double sina, cosa; + Draw::SinCos(-angle, sina, cosa); + Pointf p1 = Cvp(-centerf.x, -centerf.y, sina, cosa); + Pointf p2 = Cvp(centerf.x, -centerf.y, sina, cosa); + Size sz2 = Size(2 * (int)max(tabs(p1.x), tabs(p2.x)), + 2 * (int)max(tabs(p1.y), tabs(p2.y))); + ImageBuffer ib(sz2); + Fill(~ib, RGBAZero(), ib.GetLength()); + RGBA *t = ~ib; + Draw::SinCos(angle, sina, cosa); + int sini = int(sina * 128); + int cosi = int(cosa * 128); + Buffer xmx(sz2.cx); + Buffer xmy(sz2.cx); + for(int x = 0; x < sz2.cx; x++) { + int xx = x + x - sz2.cx; + xmx[x] = int(xx * cosi); + xmy[x] = -int(xx * sini); + } + for(int y = 0; y < sz2.cy; y++) { + int yy = y + y - sz2.cy; + int ymx = int(yy * sini) + (isz.cx << 7); + int ymy = int(yy * cosi) + (isz.cy << 7); + for(int x = 0; x < sz2.cx; x++) { + int xs = (xmx[x] + ymx) >> 8; + int ys = (xmy[x] + ymy) >> 8; + *t++ = xs >= 0 && xs < isz.cx && ys >= 0 && ys < isz.cy ? m[ys][xs] : RGBAZero(); + } + } + return ib; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/RichEdit/Cursor.cpp b/uppsrc/RichEdit/Cursor.cpp index a0561b380..664ec855a 100644 --- a/uppsrc/RichEdit/Cursor.cpp +++ b/uppsrc/RichEdit/Cursor.cpp @@ -113,7 +113,6 @@ void RichEdit::Move(int newpos, bool select) void RichEdit::MoveUpDown(int dir, bool select, int pg) { Rect page = pagesz; - PageY h = text.GetHeight(pagesz); if(dir > 0 && cursor >= GetLength() && select) { Move(GetLength() + 1, true); return; diff --git a/uppsrc/RichEdit/Mouse.cpp b/uppsrc/RichEdit/Mouse.cpp index 71adf8235..d50ce089d 100644 --- a/uppsrc/RichEdit/Mouse.cpp +++ b/uppsrc/RichEdit/Mouse.cpp @@ -47,7 +47,6 @@ void RichEdit::SetObjectPos(int pos) Rect rr = r.Offseted(GetTextRect().left, -sb); objectrect = GetObjectRect(pos); objectpos = cursor; - PageRect pr = text.GetCaret(cursor, pagesz); PlaceCaret(); Refresh(rr); ReadFormat(); @@ -185,7 +184,6 @@ void RichEdit::MouseMove(Point p, dword flags) void RichEdit::StdBar(Bar& menu) { int l, h; - int fieldpos = -1; Id field; String fieldparam; String ofieldparam; @@ -228,7 +226,6 @@ void RichEdit::StdBar(Bar& menu) bar_fieldparam = p.fieldparam; RichPara::FieldType *ft = RichPara::fieldtype().Get(field, NULL); if(ft) { - fieldpos = cursor; ft->Menu(menu, &bar_fieldparam); if(!menu.IsEmpty()) menu.Separator(); @@ -257,7 +254,6 @@ void RichEdit::RightDown(Point p, dword flags) MenuBar menu; int l, h; Rect ocr = GetCaretRect(); - int c = GetMousePos(p); int fieldpos = -1; Id field; String ofieldparam; diff --git a/uppsrc/RichEdit/UnitEdit.cpp b/uppsrc/RichEdit/UnitEdit.cpp index 0e6957a1e..04c98621e 100644 --- a/uppsrc/RichEdit/UnitEdit.cpp +++ b/uppsrc/RichEdit/UnitEdit.cpp @@ -112,7 +112,7 @@ void UnitEdit::Spin(int delta) if(IsNull(q)) q = 0; else { - double h; + double h = 10; switch(u) { case UNIT_DOT: h = 10; break; case UNIT_POINT: h = 0.5; break; diff --git a/uppsrc/RichEdit/init b/uppsrc/RichEdit/init index 1dfc09bbd..40ef1c5f6 100644 --- a/uppsrc/RichEdit/init +++ b/uppsrc/RichEdit/init @@ -1,7 +1,7 @@ #ifndef _RichEdit_icpp_init_stub #define _RichEdit_icpp_init_stub #include "CtrlLib/init" -#define BLITZ_INDEX__ F7B84A33358140089DF60DD41168411CA +#define BLITZ_INDEX__ F8db56c659ae621fee9530fc849c1b0ec #include "RichEdit.icpp" #undef BLITZ_INDEX__ #endif diff --git a/uppsrc/RichText/ParaPaint.cpp b/uppsrc/RichText/ParaPaint.cpp index b71298117..95238048b 100644 --- a/uppsrc/RichText/ParaPaint.cpp +++ b/uppsrc/RichText/ParaPaint.cpp @@ -46,8 +46,8 @@ void RichPara::Flush(Draw& draw, const PaintInfo& pi, wchar *text, draw.DrawRect(zx0, zy0, width, 2, pi.indexentry); Font fnt = f; int zht = z * tabs(f.GetHeight()); - int ssa; - int ssd; + int ssa = 0; + int ssd = 0; if(f.sscript) { FontInfo fi = fnt(zht).Info(); ssa = fi.GetAscent(); @@ -174,8 +174,8 @@ void RichPara::Paint(PageDraw& pw, const Rect& page, PageY py, const PaintInfo& } opy = py; int oi = 0; - int x; - int y0; + int x = 0; + int y0 = 0; int lineascent = 0; for(int lni = 0; lni < pl.GetCount(); lni++) { const Line& li = pl[lni]; diff --git a/uppsrc/RichText/ParaType.cpp b/uppsrc/RichText/ParaType.cpp index 2d366f4f0..626c437ad 100644 --- a/uppsrc/RichText/ParaType.cpp +++ b/uppsrc/RichText/ParaType.cpp @@ -304,7 +304,7 @@ RichPara::Lines RichPara::FormatLines(int acx) const int cx = lines.first_indent; int rcx = lines.cx - format.lm - format.rm; bool withtabs = false; - int scx; + int scx = cx; while(s < end) { Tab t; if(*s == ' ') { diff --git a/uppsrc/RichText/RichText.h b/uppsrc/RichText/RichText.h index 7a21b5ab6..1f5e31f9d 100644 --- a/uppsrc/RichText/RichText.h +++ b/uppsrc/RichText/RichText.h @@ -247,7 +247,7 @@ struct RichHotPos { int left, cx; int textleft, textcx; - RichHotPos() { table = 0; column = Null; } + RichHotPos() { table = 0; column = Null; left = cx = 0; } }; struct RichValPos : Moveable { diff --git a/uppsrc/RichText/TablePaint.cpp b/uppsrc/RichText/TablePaint.cpp index f0bdc8b1a..7ed6d224e 100644 --- a/uppsrc/RichText/TablePaint.cpp +++ b/uppsrc/RichText/TablePaint.cpp @@ -108,7 +108,6 @@ void RichTable::Paint(PageDraw& pw, RichContext rc, const PaintInfo& _pi) const pw.tracer->Table(rc.page, rc.py, *this); const TabLayout& tab = Realize(rc); if(!tab.page.IsEmpty()) { - Rect p = tab.page; PaintInfo pi = _pi; int frameln = LineZoom(pi.zoom, format.frame); int gridln = LineZoom(pi.zoom, format.grid); @@ -152,7 +151,6 @@ void RichTable::Paint(PageDraw& pw, RichContext rc, const PaintInfo& _pi) const } } for(int i = 0; i < frr.GetCount(); i++) { - PaintInfo _pi = pi; pi.tablesel = 0; pi.sell = pi.selh = -1; int pgi = frr.GetKey(i); diff --git a/uppsrc/RichText/TextPaint.cpp b/uppsrc/RichText/TextPaint.cpp index 640cdd25c..f0dce3ef7 100644 --- a/uppsrc/RichText/TextPaint.cpp +++ b/uppsrc/RichText/TextPaint.cpp @@ -80,7 +80,7 @@ void RichText::Validate() bool RichText::GetInvalid(PageY& top, PageY& bottom, const Rect& page, int sell, int selh, int osell, int oselh) const { - int spi; + int spi = 0; int rtype = r_type; if(sell != selh || osell != oselh) { if(sell != osell) { diff --git a/uppsrc/RichText/TxtPaint.cpp b/uppsrc/RichText/TxtPaint.cpp index b580f386f..8b2223425 100644 --- a/uppsrc/RichText/TxtPaint.cpp +++ b/uppsrc/RichText/TxtPaint.cpp @@ -122,7 +122,6 @@ PageY RichTxt::GetPartPageY(int parti, RichContext rc) const bool IsPainting(PageDraw& pw, Zoom z, const Rect& page, PageY top, PageY bottom) { - int t = top.y; for(int pi = top.page; pi <= bottom.page; pi++) if(pw.Page(pi).IsPainting(Rect(z * page.left, z * (pi == top.page ? top.y : page.top), z * page.right, z * (pi == bottom.page ? bottom.y : page.bottom)))) diff --git a/uppsrc/TabBar/TabBar.cpp b/uppsrc/TabBar/TabBar.cpp index 0f4c21687..181a35e57 100644 --- a/uppsrc/TabBar/TabBar.cpp +++ b/uppsrc/TabBar/TabBar.cpp @@ -1,2496 +1,2496 @@ -#include "TabBar.h" - -#define TFILE -#include - -#define IMAGECLASS TabBarImg -#define IMAGEFILE -#include - -NAMESPACE_UPP - -// AlignedFrame -void AlignedFrame::FrameLayout(Rect &r) -{ - switch(layout) - { - case LEFT: - LayoutFrameLeft(r, this, framesize); - break; - case TOP: - LayoutFrameTop(r, this, framesize); - break; - case RIGHT: - LayoutFrameRight(r, this, framesize); - break; - case BOTTOM: - LayoutFrameBottom(r, this, framesize); - break; - } - r.top += border; - r.left += border; - r.right -= border; - r.bottom -= border; -} - -void AlignedFrame::FrameAddSize(Size& sz) -{ - sz += border * 2; - IsVert() ? sz.cx += framesize : sz.cy += framesize; -} - -void AlignedFrame::FramePaint(Draw& w, const Rect& r) -{ - if(border > 0) - { - Rect n = r; - switch(layout) - { - case LEFT: - n.left += framesize; - break; - case TOP: - n.top += framesize; - break; - case RIGHT: - n.right -= framesize; - break; - case BOTTOM: - n.bottom -= framesize; - break; - } - ViewFrame().FramePaint(w, n); - } - else - FrameCtrl::FramePaint(w, r); -} - -AlignedFrame& AlignedFrame::SetFrameSize(int sz, bool refresh) -{ - framesize = sz; - if (refresh) RefreshParentLayout(); - return *this; -} - -void AlignedFrame::Fix(Size& sz) -{ - if(IsVert()) - Swap(sz.cx, sz.cy); -} - -void AlignedFrame::Fix(Point& p) -{ - if(IsVert()) - Swap(p.x, p.y); -} - -Size AlignedFrame::Fixed(const Size& sz) -{ - return IsVert() ? Size(sz.cy, sz.cx) : Size(sz.cx, sz.cy); -} - -Point AlignedFrame::Fixed(const Point& p) -{ - return IsVert() ? Point(p.y, p.x) : Point(p.x, p.y); -} - -// TabScrollBar -TabScrollBar::TabScrollBar() -{ - Clear(); -} - -void TabScrollBar::Clear() -{ - total = 0; - pos = 0; - ps = 0; - start_pos = 0; - new_pos = 0; - old_pos = 0; - sz.Clear(); - ready = false; -} - -void TabScrollBar::UpdatePos(bool update) -{ - sz = GetSize(); - Fix(sz); - if(total <= 0 || sz.cx <= 0) - cs = ics = 0; - else - { - cs = sz.cx / ((double) total + 0.5); - ics = total / ((double) sz.cx); - } - size = sz.cx * cs; - if(update) - pos = new_pos - start_pos; - if(pos < 0) - pos = 0; - else if(pos + size > sz.cx) - pos = sz.cx - size; - - ps = total > sz.cx ? pos * ics : 0; -} - -void TabScrollBar::Paint(Draw &w) -{ - if(!ready) - { - UpdatePos(); - ready = true; - } - Size rsz = GetSize(); - #ifdef TABBAR_DEBUG - w.DrawRect(rsz, Red); - #else - w.DrawRect(rsz, White); - #endif - Point p; - - if(total > sz.cx) { - p = Point(ffloor(pos), 1); - rsz = Size(fceil(size), (IsVert() ? rsz.cx : rsz.cy) - 2); - } - else { - p = Point(0, 1); - rsz = Size(sz.cx, (IsVert() ? rsz.cx : rsz.cy) - 2); - } - Fix(p); - Fix(rsz); - w.DrawRect(p.x, p.y, rsz.cx, rsz.cy, Blue); -} - -void TabScrollBar::Layout() -{ - UpdatePos(false); -} - -void TabScrollBar::LeftDown(Point p, dword keyflags) -{ - SetCapture(); - Fix(p); - old_pos = new_pos = p.x; - if(p.x < pos || p.x > pos + size) - start_pos = size / 2; - else - start_pos = tabs(p.x - pos); - UpdatePos(); - UpdateActionRefresh(); -} - -void TabScrollBar::LeftUp(Point p, dword keyflags) -{ - ReleaseCapture(); - Fix(p); - old_pos = p.x; -} - -void TabScrollBar::MouseMove(Point p, dword keyflags) -{ - if(!HasCapture()) - return; - - Fix(p); - new_pos = p.x; - UpdatePos(); - UpdateActionRefresh(); -} - -void TabScrollBar::MouseWheel(Point p, int zdelta, dword keyflags) -{ - AddPos(-zdelta / 4, true); - UpdateActionRefresh(); -} - -int TabScrollBar::GetPos() const -{ - return ffloor(ps); -} - -void TabScrollBar::SetPos(int p, bool dontscale) -{ - pos = total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0; - UpdatePos(false); - Refresh(); -} - -void TabScrollBar::AddPos(int p, bool dontscale) -{ - pos += total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0; - UpdatePos(false); - Refresh(); -} - -int TabScrollBar::GetTotal() const -{ - return total; -} - -void TabScrollBar::SetTotal(int t) -{ - bool upd = total < t; - total = t; - UpdatePos(upd); - Refresh(); -} - -void TabScrollBar::AddTotal(int t) -{ - sz = GetSize(); - Fix(sz); - total += t; - if(total <= 0 || sz.cx <= 0) - cs = ics = 0; - else - cs = sz.cx / ((double) total + 0.5); - size = sz.cx * cs; - ps = min(ps, (double)(total-sz.cx)); - pos = (int)(ps * cs); - old_pos = new_pos = (int)(pos - start_pos); - - Refresh(); -} - -void TabScrollBar::GoEnd() -{ - pos = total; - UpdatePos(false); - Refresh(); -} - -void TabScrollBar::GoBegin() -{ - pos = 0; - UpdatePos(false); - Refresh(); -} - -void TabScrollBar::Set(const TabScrollBar& t) -{ - total = t.total; - pos = t.pos; - ps = t.ps; - Refresh(); -} - -bool TabScrollBar::IsScrollable() const -{ - // Note: sz already 'fixed' - return total > sz.cx && sz.cx > 0; -} - -// Group - -void TabBar::Group::Serialize(Stream& s) -{ - s % name % active % count % first % last; -} - - -// TabBar - -TabBar::TabBar() -{ - Clear(); - - id = 0; - display = NULL; - crosses = true; - crosses_side = RIGHT; - grouping = true; - isctrl = false; - isdrag = false; - inactivedisabled = false; - autoscrollhide = true; - stacking = false; - groupseps = false; - allownullcursor = false; - icons = true; - mintabcount = 1; - scrollbar_sz = TB_SBHEIGHT; - allowreorder = true; - - // Init sorting - groupsort = false; - tabsort = false; - stacksort = true; - contextmenu = true; - keysorter_inst.vo = &Single(); - valuesorter_inst.vo = &Single(); - stacksorter_inst.vo = &Single(); - tabsorter = &keysorter_inst; - groupsorter = &Single(); - stacksorter = &stacksorter_inst; - - SetAlign(TOP); - SetFrameSize(GetHeight(false)); - BackPaint(); -} - -void TabBar::Set(const TabBar& t) -{ - CopyBaseSettings(t); - - id = t.id; - - tabs.Clear(); - tabs <<= t.tabs; - groups.Clear(); - groups <<= t.groups; - separators.Clear(); - separators <<= t.separators; - - group = t.group; - stackcount = t.stackcount; - - active = t.active; - cross = -1; - highlight = -1; - target = -1; - - mouse.Clear(); - oldp.Clear(); - - sc.Set(t.sc); - SetAlign(t.GetAlign()); -} - -void TabBar::CloseAll(int exception) -{ - Vector vv; - for(int i = 0; i < tabs.GetCount(); i++) - if(i != exception) - vv.Add(tabs[i].key); - - if (exception < 0 && CancelCloseAll()) - return; - else if (exception >= 0 && CancelCloseSome(Vector(vv,0))) - return; - - for(int i = tabs.GetCount() - 1; i >= 0; i--) - if(i != exception) - tabs.Remove(i); - - SetCursor(0); - - if (exception >= 0) - WhenCloseSome(vv); - else - WhenCloseAll(); - - MakeGroups(); - Repos(); - Refresh(); -} - -int TabBar::GetNextId() -{ - return id++; -} - -void TabBar::ContextMenu(Bar& bar) -{ - if (highlight >= 0 && crosses) { - bar.Add(tabs.GetCount() > mintabcount, t_("Close"), THISBACK2(Close, highlight, true)); - bar.Separator(); - } - int cnt = groups.GetCount(); - for(int i = 0; i < cnt; i++) - { - String name = Format("%s (%d)", groups[i].name, groups[i].count); - Bar::Item &it = i > 0 ? bar.Add(name, THISBACK1(GroupMenu, i)) - : bar.Add(name, THISBACK1(DoGrouping, i)); - if(i == group) - it.Image(TabBarImg::CHK); - if(i == 0 && cnt > 1) - bar.Separator(); - } - bool sep = true; - if (GetCursor() >= 0 && crosses) { - bar.Separator(); - sep = false; - bar.Add(t_("Close others"), THISBACK1(CloseAll, GetCursor())); - } - if (mintabcount <= 0 && crosses) { - if (sep) bar.Separator(); - bar.Add(t_("Close all"), THISBACK1(CloseAll, -1)); - } -} - -void TabBar::GroupMenu(Bar &bar, int n) -{ - bar.Add(t_("Set active"), THISBACK1(DoGrouping, n)); - bar.Add(t_("Close"), THISBACK1(DoCloseGroup, n)); -} - -void TabBar::Tab::Set(const Tab& t) -{ - id = t.id; - - img = t.img; - col = t.col; - key = t.key; - value = t.value; - group = t.group; - - stackid = t.stackid; - stack = t.stack; - - visible = t.visible; - - pos = t.pos; - size = t.size; - - cross_pos = t.cross_pos; - cross_size = t.cross_size; - - tab_pos = t.tab_pos; - tab_size = t.tab_size; - - items <<= t.items; -} - -void TabBar::Tab::Serialize(Stream& s) -{ - s % id % key % value % group % stackid % stack % visible; -} - -bool TabBar::Tab::HasMouse(const Point& p) const -{ - if(!visible) - return false; - - return p.x >= tab_pos.x && p.x < tab_pos.x + tab_size.cx && - p.y >= tab_pos.y && p.y < tab_pos.y + tab_size.cy; -} - -bool TabBar::Tab::HasMouseCross(const Point& p) const -{ - if(!visible) - return false; - - return p.x >= cross_pos.x && p.x < cross_pos.x + cross_size.cx && - p.y >= cross_pos.y && p.y < cross_pos.y + cross_size.cy; -} - -int TabBar::FindGroup(const String& g) const -{ - for(int i = 0; i < groups.GetCount(); i++) - if(groups[i].name == g) - return i; - return -1; -} - -void TabBar::DoStacking() -{ - Value v = GetData(); - - // Reset stack info - for (int i = 0; i < tabs.GetCount(); i++) { - Tab &t = tabs[i]; - t.stack = -1; - t.stackid = GetStackId(t); - } - // Create stacks - Vector< Vector > tstack; - for (int i = 0; i < tabs.GetCount(); i++) { - Tab &ti = tabs[i]; - if (ti.stack < 0) { - ti.stack = tstack.GetCount(); - Vector &ttabs = tstack.Add(); - ttabs.Add(ti); - for (int j = i + 1; j < tabs.GetCount(); j++) { - Tab &tj = tabs[j]; - if (tj.stack < 0 && tj.stackid == ti.stackid && (!grouping || tj.group == ti.group)) { - tj.stack = ti.stack; - ttabs.Add(tj); - } - } - } - } - stackcount = tstack.GetCount(); - // Recombine - tabs.SetCount(0); - for (int i = 0; i < tstack.GetCount(); i++) { - if (stacksort) - StableSort(tstack[i], *stacksorter); - tabs.AppendPick(tstack[i]); - } - highlight = -1; - SetData(v); - MakeGroups(); - Repos(); -} - -void TabBar::DoUnstacking() -{ - stackcount = 0; - for (int i = 0; i < tabs.GetCount(); i++) - tabs[i].stack = -1; - highlight = -1; - MakeGroups(); - Repos(); - if (HasCursor()) - SetCursor(-1); - else - Refresh(); -} - -void TabBar::SortStack(int stackix) -{ - if (!stacksort) return; - - int head = FindStackHead(stackix); - int tail = head; - while (tail < tabs.GetCount() && tabs[tail].stack == stackix) - ++tail; - SortStack(stackix, head, tail-1); -} - -void TabBar::SortStack(int stackix, int head, int tail) -{ - if (!stacksort) return; - - int headid = tabs[head].id; - StableSort(tabs.GetIter(head), tabs.GetIter(tail), *stacksorter); - while (tabs[head].id != headid) - CycleTabStack(head, stackix); -} - -void TabBar::MakeGroups() -{ - groups[0].count = tabs.GetCount(); - groups[0].first = 0; - groups[0].last = tabs.GetCount() - 1; - - if (groupsort) - StableSort(tabs, *groupsorter); - - for(int i = 1; i < groups.GetCount(); i++) - { - groups[i].count = 0; - groups[i].first = 10000000; - groups[i].last = 0; - } - - for(int i = 0; i < tabs.GetCount(); i++) - { - Tab &tab = tabs[i]; - int n = FindGroup(tab.group); - ASSERT(n >= 0); - if (n > 0) { - if(groups[n].active < 0) - groups[n].active = tab.id; - groups[n].count++; - groups[n].last = i; - - if(i < groups[n].first) - groups[n].first = i; - if(i > groups[n].last) - groups[n].last = i; - } - } - - int cnt = groups.GetCount() - 1; - for(int i = cnt; i > 0; i--) - if(groups[i].count == 0) - groups.Remove(i); - - if(group > groups.GetCount() - 1 && group > 0) - group--; -} - -void TabBar::DoGrouping(int n) -{ - group = n; - Repos(); - SyncScrollBar(); - SetCursor(-1); -} - -void TabBar::DoCloseGroup(int n) -{ - int cnt = groups.GetCount(); - if(cnt <= 0) - return; - - String groupName = groups[n].name; - - /* - do WhenCloseSome()/CancelCloseSome() checking - before WhenClose()/CancelClose() stuff - (that code must be reviewed anyways...) - In order to leave existing code as it is, following - changes have effect *ONLY* if WhenCloseSome()/CancelCloseSome() - callbacks are used, otherwise previous path is taken. - I think we should anyways review some parts of it later - */ - - if(WhenCloseSome || CancelCloseSome) - { - Vectorvv; - int nTabs = 0; - for(int i = 0; i < tabs.GetCount(); i++) - if(groupName == tabs[i].group) { - vv.Add(tabs[i].key); - nTabs++; - } - // at first, we check for CancelCloseSome() - if(vv.GetCount() && !CancelCloseSome(Vector(vv,0))) { - // we didn't cancel globally, now we check CancelClose() - // for each tab -- group gets removed ONLY if ALL of - // group tabs are closed - for(int i = tabs.GetCount() - 1; i >= 0; i--) { - if(groupName == tabs[i].group && tabs.GetCount() > 1) { - Value v = tabs[i].key; - if(!CancelClose(v)) - { - nTabs--; - WhenClose(v); - tabs.Remove(i); - } - } - // remove group if all of its tabs get closed - if(!nTabs) { - if(cnt == n) - group--; - if(cnt > 1) - groups.Remove(n); - } - MakeGroups(); - Repos(); - SetCursor(-1); - } - } - return; - } - - // previous code path, taken if WhenCancelSome()/WhenCloseSome() - for(int i = tabs.GetCount() - 1; i >= 0; i--) - { - if(groupName == tabs[i].group && tabs.GetCount() > 1) { - Value v = tabs[i].value; // should be key ?? - if (!CancelClose(v)) { - WhenClose(v); - tabs.Remove(i); - } - } - } - - if (cnt == n) - group--; - - if(cnt > 1) // what if CancelClose suppressed some tab closing ? - groups.Remove(n); - MakeGroups(); - Repos(); - SetCursor(-1); -} - -void TabBar::NewGroup(const String &name) -{ - Group &g = groups.Add(); - g.name = name; - g.count = 0; - g.first = 10000000; - g.last = 0; - g.active = -1; -} - -Image TabBar::AlignImage(int align, const Image& img) -{ - switch(align) { - case AlignedFrame::LEFT: - return RotateAntiClockwise(img); - case AlignedFrame::RIGHT: - return RotateClockwise(img); - case AlignedFrame::BOTTOM: - return MirrorVert(img); - default: - return img; - } -} - -Value TabBar::AlignValue(int align, const Value &v, const Size &sz) -{ - Size isz = sz; - if(align == AlignedFrame::LEFT || align == AlignedFrame::RIGHT) - Swap(isz.cx, isz.cy); - - ImageDraw w(isz.cx, isz.cy); - w.DrawRect(isz, SColorFace()); - ChPaint(w, isz, v); - ImageBuffer img; - return AlignImage(align, (Image)w); -} - -void TabBar::TabItem::Clear() -{ - text.Clear(); - ink = Null; - img = Null; - side = LEFT; - clickable = false; - cross = false; - stacked_tab = -1; -} - -TabBar::TabItem& TabBar::Tab::AddItem() -{ - if(itn < items.GetCount()) - { - TabItem& ti = items[itn++]; - ti.Clear(); - return ti; - } - else - { - ++itn; - return items.Add(); - } -} - -void TabBar::Tab::Clear() -{ - itn = 0; -} - -TabBar::TabItem& TabBar::Tab::AddImage(const Image& img, int side) -{ - TabItem& ti = AddItem(); - ti.img = img; - ti.size = img.GetSize(); - ti.side = side; - return ti; -} - -TabBar::TabItem& TabBar::Tab::AddValue(const Value& q, const Font& font, const Color& ink) -{ - TabItem& ti = AddItem(); - - ti.font = font; - ti.ink = ink; - - if(IsType(q)) { - const AttrText& t = ValueTo(q); - ti.text = t.text; - if(!IsNull(t.font)) - ti.font = t.font; - - if(!IsNull(t.ink)) - ti.ink = t.ink; - } - else - ti.text = IsString(q) ? q : StdConvert().Format(q); - - ti.size = GetTextSize(ti.text, ti.font); - return ti; -} - -TabBar::TabItem& TabBar::Tab::AddText(const WString& s, const Font& font, const Color& ink) -{ - TabItem& ti = AddItem(); - - ti.font = font; - ti.ink = ink; - ti.text = s; - ti.size = GetTextSize(ti.text, ti.font); - return ti; -} - -TabBar::TabItem& TabBar::Tab::AddSpace(int space, int side) -{ - TabItem& ti = AddItem(); - - ti.size.cx = space; - ti.size.cy = 0; - ti.side = side; - return ti; -} - -void TabBar::ComposeTab(Tab& tab, const Font &font, Color ink, int style) -{ - if(PaintIcons() && tab.HasIcon()) - { - tab.AddImage(tab.img); - tab.AddSpace(TB_SPACEICON); - } - tab.AddValue(tab.value, font, ink).Clickable(); -} - -void TabBar::ComposeStackedTab(Tab& tab, const Tab& stacked_tab, const Font& font, Color ink, int style) -{ - tab.AddImage(stacked_tab.img); - tab.AddText("|...", font, ink); -} - -int TabBar::GetTextAngle() -{ - return AlignedFrame::IsVert() ? (GetAlign() == LEFT ? 900 : 2700) : 0; -} - -Point TabBar::GetTextPosition(int align, const Rect& r, int cy, int space) const -{ - Point p; - - if(align == LEFT) - { - p.y = r.bottom - space; - p.x = r.left + (r.GetWidth() - cy) / 2; - } - else if(align == RIGHT) - { - p.y = r.top + space; - p.x = r.right - (r.GetWidth() - cy) / 2; - } - else - { - p.x = r.left + space; - p.y = r.top + (r.GetHeight() - cy) / 2; - } - return p; -} - -Point TabBar::GetImagePosition(int align, const Rect& r, int cx, int cy, int space, int side, int offset) const -{ - Point p; - - if (align == LEFT) - { - p.x = r.left + (r.GetWidth() - cy) / 2 + offset; - p.y = side == LEFT ? r.bottom - space - cx : r.top + space; - } - else if (align == RIGHT) - { - p.x = r.right - (r.GetWidth() + cy) / 2 - offset; - p.y = side == LEFT ? r.top + space : r.bottom - space - cx; - } - else if (align == TOP) - { - p.x = side == LEFT ? r.left + space : r.right - cx - space; - p.y = r.top + (r.GetHeight() - cy) / 2 + offset; - } - else if (align == BOTTOM) - { - p.x = side == LEFT ? r.left + space : r.right - cx - space; - p.y = r.bottom - (r.GetHeight() + cy) / 2 - offset; - } - return p; -} - -void TabBar::PaintTabItems(Tab& t, Draw &w, const Rect& rn, int align) -{ - int pos_left = TB_MARGIN; - int pos_right = (IsVert() ? rn.GetHeight() : rn.GetWidth()) - TB_MARGIN; - - for(int i = 0; i < t.itn; i++) - { - const TabItem& ti = t.items[i]; - - Point p; - int pos = ti.side == LEFT ? pos_left : pos_right - ti.size.cx; - - if(!IsNull(ti.img)) - { - p = GetImagePosition(align, rn, ti.size.cx, ti.size.cy, pos, LEFT); - w.DrawImage(p.x, p.y, IsVert() ? AlignImage(align, ti.img) : ti.img); - } - - if(!IsNull(ti.text)) - { - p = GetTextPosition(align, rn, ti.size.cy, pos); - w.DrawText(p.x, p.y, GetTextAngle(), ti.text, ti.font, ti.ink); - } - - if(ti.cross) - { - t.cross_size = ti.size; - t.cross_pos = p; - } - - if(ti.stacked_tab >= 0 && ti.clickable) - { - Tab& st = tabs[ti.stacked_tab]; - - if(align == RIGHT) - { - st.tab_pos = Point(rn.left, rn.top + pos); - st.tab_size = Size(rn.GetWidth(), ti.size.cx); - } - else if(align == LEFT) - { - st.tab_pos = Point(rn.left, rn.bottom - pos - ti.size.cx); - st.tab_size = Size(rn.GetWidth(), ti.size.cx); - } - else - { - st.tab_pos = Point(rn.left + pos, rn.top); - st.tab_size = Size(ti.size.cx, rn.GetHeight()); - } - - #ifdef TABBAR_DEBUG - DrawFrame(w, Rect(st.tab_pos, st.tab_size), Red); - #endif - } - - if(ti.side == LEFT) - pos_left += ti.size.cx; - else - pos_right -= ti.size.cx; - } -} - -void TabBar::PaintTab(Draw &w, const Size &sz, int n, bool enable, bool dragsample) -{ - TabBar::Tab &t = tabs[n]; - const Style& s = GetStyle(); - int align = GetAlign(); - int cnt = dragsample ? 1 : tabs.GetCount(); - - bool ac = (n == active && enable); - bool hl = (n == highlight && enable) || (stacking && highlight >= 0 && tabs[highlight].stack == t.stack); - - int ndx = !enable ? CTRL_DISABLED : - ac ? CTRL_PRESSED : - hl ? CTRL_HOT : CTRL_NORMAL; - - int c = align == LEFT ? cnt - n : n; - int lx = n > 0 ? s.extendleft : 0; - int x = t.pos.x - sc.GetPos() - lx + s.margin; - - int dy = -s.sel.top * ac; - int sel = s.sel.top; - - int df = 0; - - if (IsBR()) - { - dy = -dy; - sel = s.sel.bottom; - df = Fixed(sz).cy; - } - - Size sa = Size(t.size.cx + lx + s.sel.right + s.sel.left, t.size.cy + s.sel.bottom); - Point pa = Point(x - s.sel.left, IsBR() ? df - sa.cy : 0); - - Size sn = Size(t.size.cx + lx, t.size.cy - s.sel.top); - Point pn = Point(x, IsBR() ? df - sn.cy - s.sel.top : s.sel.top); - - Rect ra(Fixed(pa), Fixed(sa)); - Rect rn(Fixed(pn), Fixed(sn)); - - t.tab_pos = (ac ? ra : rn).TopLeft(); - t.tab_size = (ac ? ra : rn).GetSize(); - - const Value& sv = (cnt == 1 ? s.both : c == 0 ? s.first : c == cnt - 1 ? s.last : s.normal)[ndx]; - - Image img = AlignValue(align, sv, t.tab_size); - - if(!IsNull(t.col)) - { - img = Colorize(img, t.col); - } - - if(dragsample) - { - w.DrawImage(Rect(Point(0, 0), t.tab_size), img); - rn = Rect(Fixed(Point(s.sel.left * ac, sel * ac + dy)), Fixed(sn)); - } - else - { - w.DrawImage(Rect(t.tab_pos, t.tab_size), img); - rn = Rect(Fixed(Point(pn.x, pn.y + dy)), Fixed(sn)); - } - - #ifdef TABBAR_DEBUG - DrawFrame(w, rn, Green); - #endif - - if (display) - display->Paint(w, rn, t.value, s.text_color[ndx], SColorDisabled(), ndx); - - t.Clear(); - - if(crosses && cnt > mintabcount && !dragsample) { - TabItem& ti = t.AddItem(); - ti.img = s.crosses[cross == n ? 2 : ac || hl ? 1 : 0]; - ti.side = crosses_side; - ti.cross = true; - ti.size = s.crosses[0].GetSize(); - t.AddSpace(3, crosses_side); - } - - ComposeTab(t, s.font, s.text_color[ndx], ndx); - - if (stacking) { - int ix = n + 1; - - while (ix < tabs.GetCount() && tabs[ix].stack == t.stack) { - Tab &q = tabs[ix]; - int ndx = !enable ? CTRL_DISABLED : - highlight == ix ? CTRL_HOT : CTRL_NORMAL; - - int sn = t.itn; - ComposeStackedTab(t, q, s.font, s.text_color[ndx], ndx); - if(t.itn > sn) - for(; sn < t.itn; sn++) - t.items[sn].stacked_tab = ix; - - ix++; - } - } - - PaintTabItems(t, w, rn, align); -} - -void TabBar::Paint(Draw &w) -{ - int align = GetAlign(); - const Style &st = StyleDefault(); - Size ctrlsz = GetSize(); - Size sz = GetBarSize(ctrlsz); - - if (align == BOTTOM || align == RIGHT) - w.Offset(ctrlsz.cx - sz.cx, ctrlsz.cy - sz.cy); - - #ifdef TABBAR_DEBUG - w.DrawRect(sz, Yellow); - #else - w.DrawRect(sz, SColorFace()); - #endif - - IsVert() ? w.DrawRect(align == LEFT ? sz.cx - 1 : 0, 0, 1, sz.cy, Blend(SColorDkShadow, SColorShadow)): - w.DrawRect(0, align == TOP ? sz.cy - 1 : 0, sz.cx, 1, Blend(SColorDkShadow, SColorShadow)); - - if (!tabs.GetCount()) return; - - int limt = sc.GetPos() + (IsVert() ? sz.cy : sz.cx); - int first = 0; - int last = tabs.GetCount() - 1; - // Find first visible tab - for(int i = 0; i < tabs.GetCount(); i++) { - Tab &tab = tabs[i]; - if (tab.pos.x + tab.size.cx > sc.GetPos()) { - first = i; - break; - } - } - // Find last visible tab - for(int i = first + 1; i < tabs.GetCount(); i++) { - if (tabs[i].visible && tabs[i].pos.x > limt) { - last = i; - break; - } - } - // Draw active group - for (int i = first; i <= last; i++) { - if(tabs[i].visible && i != active) - PaintTab(w, sz, i, IsEnabled()); - } - // Clear tab_size for non-visible tabs to prevent mouse handling bugs - for (int i = 0; i < first; i++) - tabs[i].tab_size = Size(0, 0); - for (int i = last + 1; i < tabs.GetCount(); i++) - tabs[i].tab_size = Size(0, 0); - // Draw inactive groups - if (inactivedisabled) - for (int i = first; i <= last; i++) { - if(!tabs[i].visible && i != active && (!stacking || IsStackHead(i))) - PaintTab(w, sz, i, !IsEnabled()); - } - - // Draw selected tab - if(active >= first && active <= last) - PaintTab(w, sz, active, true); - - // Separators - if (grouping && groupseps) { - int cy = IsVert() ? sz.cx : sz.cy; - for (int i = 0; i < separators.GetCount(); i++) { - int x = separators[i]; - if (x > sc.GetPos() && x < limt) { - // Paint separator - ChPaint(w, Rect(Fixed(Point(x - sc.GetPos() + GetStyle().sel.left, 0)), - Fixed(Size(TB_SPACE - GetStyle().sel.left, cy-1))), - st.group_separators[IsVert() ? 1 : 0]); - } - } - } - - // Draw drag highlights - if(target >= 0) - { - // Draw target marker - int drag = isctrl ? highlight : active; - if(target != drag && target != GetNext(drag, true)) - { - last = GetLast(); - first = GetFirst(); - int x = (target == last + 1 ? tabs[last].Right() : tabs[target].pos.x) - - sc.GetPos() - (target <= first ? 1 : 2) - + st.margin - (target > 0 ? st.extendleft : 0); - - if (IsHorz()) - DrawVertDrop(w, x + 1, 0, sz.cy); - else - DrawHorzDrop(w, 0, x + 1, sz.cx); - } - // Draw transparent drag image - Point mouse = GetMousePos() - GetScreenRect().TopLeft(); - Size isz = dragtab.GetSize(); - int p = 0; - int sep = TB_SBSEPARATOR * sc.IsVisible(); - - int top = drag == active ? st.sel.bottom : st.sel.top; - if (align == BOTTOM || align == RIGHT) - p = int(drag == active) * -top + sep; - else - p = int(drag != active) * top; - - if (IsHorz()) - w.DrawImage(mouse.x - isz.cx / 2, p, isz.cx, isz.cy, dragtab); - else - w.DrawImage(p, mouse.y - isz.cy / 2, isz.cx, isz.cy, dragtab); - } - - // If not in a frame fill any spare area - if (!InFrame()) - w.DrawRect(GetClientArea(), SColorFace()); - - if (align == BOTTOM || align == RIGHT) - w.EndOp(); -} - -Image TabBar::GetDragSample() -{ - int h = drag_highlight; - if(stacking) - h = FindStackHead(tabs[h].stack); - return GetDragSample(h); -} - -Image TabBar::GetDragSample(int n) -{ - if (n < 0) return Image(); - Tab &t = tabs[n]; - - Size tsz(t.tab_size); - ImageDraw iw(tsz); - iw.DrawRect(tsz, SColorFace()); //this need to be fixed - if inactive tab is dragged gray edges are visible - - PaintTab(iw, tsz, n, true, true); - - Image img = iw; - ImageBuffer ib(img); - Unmultiply(ib); - RGBA *s = ~ib; - RGBA *e = s + ib.GetLength(); - while(s < e) { - s->a = 180; - s++; - } - Premultiply(ib); - return ib; -} - -void TabBar::Scroll() -{ - Refresh(); -} - -int TabBar::GetWidth(int n) -{ - return GetStdSize(tabs[n]).cx + GetExtraWidth(); -} - -int TabBar::GetExtraWidth() -{ - return TB_MARGIN * 2 + (TB_SPACE + GetStyle().crosses[0].GetSize().cx) * crosses; -} - -Size TabBar::GetStdSize(const Value &q) -{ - if (display) - return display->GetStdSize(q); - else if (q.GetType() == STRING_V || q.GetType() == WSTRING_V) - return GetTextSize(WString(q), GetStyle().font); - else - return GetTextSize("A Tab", GetStyle().font); -} - -Size TabBar::GetStackedSize(const Tab &t) -{ - if (!IsNull(t.img)) - return t.img.GetSize(); - return GetTextSize("...", GetStyle().font, 3); -} - -Size TabBar::GetStdSize(const Tab &t) -{ - return (PaintIcons() && t.HasIcon()) ? (GetStdSize(t.value) + Size(TB_ICON + 2, 0)) : GetStdSize(t.value); -} - -TabBar& TabBar::Add(const Value &value, Image icon, String group, bool make_active) -{ - return InsertKey(tabs.GetCount(), value, value, icon, group, make_active); -} - -TabBar& TabBar::Insert(int ix, const Value &value, Image icon, String group, bool make_active) -{ - return InsertKey(tabs.GetCount(), value, value, icon, group, make_active); -} - -TabBar& TabBar::AddKey(const Value &key, const Value &value, Image icon, String group, bool make_active) -{ - return InsertKey(tabs.GetCount(), key, value, icon, group, make_active); -} - -TabBar& TabBar::InsertKey(int ix, const Value &key, const Value &value, Image icon, String group, bool make_active) -{ - int id = InsertKey0(ix, key, value, icon, group); - - SortTabs0(); - MakeGroups(); - Repos(); - active = -1; - if (make_active || (!allownullcursor && active < 0)) - SetCursor((groupsort || stacking) ? FindId(id) : ( minmax(ix, 0, tabs.GetCount() - 1))); - return *this; -} - -int TabBar::InsertKey0(int ix, const Value &key, const Value &value, Image icon, String group) -{ - ASSERT(ix >= 0); - int g = 0; - if (!group.IsEmpty()) { - g = FindGroup(group); - if (g < 0) { - NewGroup(group); - g = groups.GetCount() - 1; - } - } - - group = groups[g].name; - Tab t; - t.value = value; - t.key = key; - t.img = icon; - t.id = GetNextId(); - t.group = Nvl(TrimBoth(group), "Unnamed Group"); - if (stacking) { - t.stackid = GetStackId(t); - - // Override index - int tail = -1; - for (int i = 0; i < tabs.GetCount(); i++) { - if (tabs[i].stackid == t.stackid && (!grouping || tabs[i].group == t.group)) { - tail = FindStackTail(tabs[i].stack); - break; - } - } - if (tail >= 0) { - ix = tail+1; - t.stack = tabs[tail].stack; - tail++; - } - else { - ix = (ix < tabs.GetCount()) ? FindStackHead(tabs[ix].stack) : ix; - t.stack = stackcount++; - } - tabs.Insert(ix, t); - if (tail >= 0) - SortStack(t.stack, FindStackHead(t.stack), ix); - - } - else - tabs.Insert(ix, t); - return t.id; -} - -int TabBar::GetWidth() const -{ - if (!tabs.GetCount()) return 0; - int ix = GetLast(); - const Style& s = StyleDefault(); - if (IsStackHead(ix)) - return tabs[ix].Right() + s.margin * 2; - int stack = tabs[ix].stack; - ix--; - while (ix >= 0 && tabs[ix].stack == stack) - ix--; - return tabs[ix + 1].Right() + s.margin * 2; - -} - -int TabBar::GetHeight(bool scrollbar) const -{ - return TabBar::GetStyleHeight() + TB_SBSEPARATOR * int(scrollbar); -} - -int TabBar::GetStyleHeight() -{ - const Style& s = GetStyle(); - return s.tabheight + s.sel.top; -} - -void TabBar::Repos() -{ - if(!tabs.GetCount()) - return; - - String g = GetGroupName(); - - int j; - bool first = true; - j = 0; - separators.Clear(); - for(int i = 0; i < tabs.GetCount(); i++) - j = TabPos(g, first, i, j, false); - if (inactivedisabled) - for(int i = 0; i < tabs.GetCount(); i++) - if (!tabs[i].visible) - j = TabPos(g, first, i, j, true); - SyncScrollBar(); -} - -Size TabBar::GetBarSize(Size ctrlsz) const -{ - return IsVert() ? Size(GetFrameSize() - scrollbar_sz * int(sc.IsShown()), ctrlsz.cy) - : Size(ctrlsz.cx, GetFrameSize() - scrollbar_sz * int(sc.IsShown())); -} - -Rect TabBar::GetClientArea() const -{ - Rect rect = GetSize(); - switch (GetAlign()) { - case TOP: - rect.top += GetFrameSize(); - break; - case BOTTOM: - rect.bottom -= GetFrameSize(); - break; - case LEFT: - rect.left += GetFrameSize(); - break; - case RIGHT: - rect.right -= GetFrameSize(); - break; - }; - return rect; -} - -int TabBar::TabPos(const String &g, bool &first, int i, int j, bool inactive) -{ - bool ishead = IsStackHead(i); - bool v = IsNull(g) ? true : g == tabs[i].group; - Tab& t = tabs[i]; - - if(ishead && (v || inactive)) - { - t.visible = v; - t.pos.y = 0; - t.size.cy = GetStyleHeight(); - - // Normal visible or inactive but greyed out tabs - t.pos.x = first ? 0 : tabs[j].Right(); - - // Separators - if (groupseps && grouping && !first && t.group != tabs[j].group) { - separators.Add(t.pos.x); - t.pos.x += TB_SPACE; - } - - int cx = GetStdSize(t).cx; - - // Stacked/shortened tabs - if (stacking) { - for(int n = i + 1; n < tabs.GetCount() && tabs[n].stack == t.stack; n++) - cx += GetStackedSize(tabs[n]).cx; - } - - t.size.cx = cx + GetExtraWidth(); - - if (stacking) { - for(int n = i + 1; n < tabs.GetCount() && tabs[n].stack == t.stack; n++) { - Tab &q = tabs[n]; - q.visible = false; - q.pos = t.pos; - q.size = t.size; - } - } - - j = i; - first = false; - } - else if (!(v || inactive)) { - t.visible = false; - t.pos.x = sc.GetTotal() + GetBarSize(GetSize()).cx; - } - return j; -} - -void TabBar::ShowScrollbarFrame(bool b) -{ - SetFrameSize((b ? sc.GetFrameSize() : TB_SBSEPARATOR) + GetHeight(b), false); - sc.Show(b); - RefreshParentLayout(); -} - -void TabBar::SyncScrollBar(bool synctotal) -{ - if (synctotal) - sc.SetTotal(GetWidth()); - if (autoscrollhide) { - bool v = sc.IsScrollable(); - if (sc.IsShown() != v) { - PostCallback(THISBACK1(ShowScrollbarFrame, v)); - } - } - else { - SetFrameSize(sc.GetFrameSize() + GetHeight(true), false); - sc.Show(); - } -} - -int TabBar::FindId(int id) const -{ - for(int i = 0; i < tabs.GetCount(); i++) - if(tabs[i].id == id) - return i; - return -1; -} - -int TabBar::GetNext(int n, bool drag) const -{ - for(int i = n + 1; i < tabs.GetCount(); i++) - if(tabs[i].visible) - return i; - return drag ? tabs.GetCount() : -1; -} - -int TabBar::GetPrev(int n, bool drag) const -{ - for(int i = n - 1; i >= 0; i--) - if(tabs[i].visible) - return i; - return -1; -} - -void TabBar::Clear() -{ - highlight = -1; - drag_highlight = -1; - active = -1; - target = -1; - cross = -1; - stackcount = 0; - tabs.Clear(); - groups.Clear(); - NewGroup(t_("TabBarGroupAll\aAll")); - group = 0; - Refresh(); -} - -TabBar& TabBar::Crosses(bool b, int side) -{ - crosses = b; - crosses_side = side; - Repos(); - Refresh(); - return *this; -} - -TabBar& TabBar::SortTabs(bool b) -{ - tabsort = b; - if (b) - DoTabSort(*tabsorter); - return *this; -} - -TabBar& TabBar::SortTabsOnce() -{ - DoTabSort(*tabsorter); - return *this; -} - -TabBar& TabBar::SortTabsOnce(TabSort &sort) -{ - DoTabSort(sort); - return *this; -} - -TabBar& TabBar::SortTabs(TabSort &sort) -{ - tabsorter = &sort; - return SortTabs(true); -} - -TabBar& TabBar::SortTabValues(ValueOrder &sort) -{ - valuesorter_inst.vo = &sort; - tabsorter = &valuesorter_inst; - return SortTabs(true); -} - -TabBar& TabBar::SortTabValuesOnce(ValueOrder &sort) -{ - TabValueSort q; - q.vo = &sort; - DoTabSort(q); - return *this; -} - -TabBar& TabBar::SortTabKeys(ValueOrder &sort) -{ - keysorter_inst.vo = &sort; - tabsorter = &keysorter_inst; - return SortTabs(true); -} - -TabBar& TabBar::SortTabKeysOnce(ValueOrder &sort) -{ - TabKeySort q; - q.vo = &sort; - DoTabSort(q); - return *this; -} - -TabBar& TabBar::SortGroups(bool b) -{ - groupsort = b; - if (!b) return *this;; - - Value v = GetData(); - MakeGroups(); - Repos(); - if (!IsNull(v)) - SetData(v); - Refresh(); - return *this; -} - -TabBar& TabBar::SortGroupsOnce() -{ - if (!grouping) return *this;; - - Value v = GetData(); - MakeGroups(); - Repos(); - if (!IsNull(v)) - SetData(v); - Refresh(); - return *this; -} - -TabBar& TabBar::SortGroupsOnce(TabSort &sort) -{ - TabSort *current = groupsorter; - groupsorter = &sort; - SortGroupsOnce(); - groupsorter = current; - return *this; -} - -TabBar& TabBar::SortGroups(TabSort &sort) -{ - groupsorter = &sort; - return SortGroups(true); -} - -TabBar& TabBar::SortStacks(bool b) -{ - stacksort = b; - if (stacking) { - DoStacking(); - Refresh(); - } - return *this; -} - -TabBar& TabBar::SortStacksOnce() -{ - if (stacking) { - DoStacking(); - Refresh(); - } - return *this; -} - -TabBar& TabBar::SortStacksOnce(TabSort &sort) -{ - TabSort *current = stacksorter; - stacksorter = &sort; - SortStacksOnce(); - stacksorter = current; - return *this; -} - -TabBar& TabBar::SortStacks(TabSort &sort) -{ - stacksorter = &sort; - return SortStacks(true); -} - -TabBar& TabBar::SortStacks(ValueOrder &sort) -{ - stacksorter_inst.vo = &sort; - stacksorter = &stacksorter_inst; - return SortStacks(true); -} - -void TabBar::DoTabSort(TabSort &sort) -{ - Value v = GetData(); - StableSort(tabs, sort); - Repos(); - if (!IsNull(v)) - SetData(v); - Refresh(); -} - -void TabBar::SortTabs0() -{ - if (tabsort) - StableSort(tabs, *tabsorter); -} - -TabBar& TabBar::Grouping(bool b) -{ - grouping = b; - Repos(); - Refresh(); - return *this; -} - -TabBar& TabBar::ContextMenu(bool b) -{ - contextmenu = b; - return *this; -} - -TabBar& TabBar::GroupSeparators(bool b) -{ - groupseps = b; - Repos(); - Refresh(); - return *this; -} - -TabBar& TabBar::AutoScrollHide(bool b) -{ - autoscrollhide = b; - sc.Hide(); - SetFrameSize(GetHeight(false), false); - SyncScrollBar(GetWidth()); - return *this; -} - -TabBar& TabBar::InactiveDisabled(bool b) -{ - inactivedisabled = b; - Repos(); - Refresh(); - return *this; -} - -TabBar& TabBar::AllowNullCursor(bool b) -{ - allownullcursor = b; - return *this; -} - -TabBar& TabBar::Icons(bool v) -{ - icons = v; - Repos(); - Refresh(); - return *this; -} - -TabBar& TabBar::Stacking(bool b) -{ - stacking = b; - if (b) - DoStacking(); - else - DoUnstacking(); - Refresh(); - return *this; -} - -void TabBar::FrameSet() -{ - int al = GetAlign(); - Ctrl::ClearFrames(); - sc.Clear(); - sc.SetFrameSize(scrollbar_sz).SetAlign((al >= 2) ? al - 2 : al + 2); - sc <<= THISBACK(Scroll); - sc.Hide(); - - if (sc.IsChild()) sc.Remove(); - switch (al) { - case LEFT: - Ctrl::Add(sc.LeftPos(GetHeight(), scrollbar_sz).VSizePos()); - break; - case RIGHT: - Ctrl::Add(sc.RightPos(GetHeight(), scrollbar_sz).VSizePos()); - break; - case TOP: - Ctrl::Add(sc.TopPos(GetHeight(), scrollbar_sz).HSizePos()); - break; - case BOTTOM: - Ctrl::Add(sc.BottomPos(GetHeight(), scrollbar_sz).HSizePos()); - break; - }; - - SyncScrollBar(true); -} - -TabBar& TabBar::SetScrollThickness(int sz) -{ - scrollbar_sz = max(sz + 2, 3); - FrameSet(); - RefreshLayout(); - return *this; -} - -void TabBar::Layout() -{ - if (autoscrollhide && tabs.GetCount()) - SyncScrollBar(false); -} - -int TabBar::FindValue(const Value &v) const -{ - for (int i = 0; i < tabs.GetCount(); i++) - if (tabs[i].value == v) - return i; - return -1; -} - -int TabBar::FindKey(const Value &v) const -{ - for (int i = 0; i < tabs.GetCount(); i++) - if (tabs[i].key == v) - return i; - return -1; -} - -bool TabBar::IsStackHead(int n) const -{ - return tabs[n].stack < 0 - || n == 0 - || (n > 0 && tabs[n - 1].stack != tabs[n].stack); -} - -bool TabBar::IsStackTail(int n) const -{ - return tabs[n].stack < 0 - || n >= tabs.GetCount() - 1 - || (n < tabs.GetCount() && tabs[n + 1].stack != tabs[n].stack); -} - -int TabBar::FindStackHead(int stackix) const -{ - int i = 0; - while (tabs[i].stack != stackix) - i++; - return i; -} - -int TabBar::FindStackTail(int stackix) const -{ - int i = tabs.GetCount() - 1; - while (tabs[i].stack != stackix) - i--; - return i; -} - -int TabBar::SetStackHead(Tab &t) -// Returns index of stack head -{ - ASSERT(stacking); - int id = t.id; - int stack = t.stack; - int head = FindStackHead(stack); - while (tabs[head].id != id) - CycleTabStack(head, stack); - return head; -} - -int TabBar::CycleTabStack(int n) -// Returns index of stack head -{ - int head = FindStackHead(n); - CycleTabStack(head, n); - return head; -} - -void TabBar::CycleTabStack(int head, int n) -{ - // Swap tab to end of stack - int ix = head; - while (!IsStackTail(ix)) { - tabs.Swap(ix, ix + 1); - ++ix; - } -} - -Value TabBar::GetData() const -{ - return (HasCursor() && active < GetCount()) - ? GetKey(active) - : Value(); -} - -void TabBar::SetData(const Value &key) -{ - int n = FindKey(key); - if (n >= 0) { - if (stacking && tabs[n].stack >= 0) - n = SetStackHead(tabs[n]); - SetCursor(n); - } -} - -void TabBar::Set(int n, const Value &newkey, const Value &newvalue) -{ - Set(n, newkey, newvalue, tabs[n].img); -} - -void TabBar::Set(int n, const Value &newkey, const Value &newvalue, Image icon) -{ - ASSERT(n >= 0 && n < tabs.GetCount()); - tabs[n].key = newkey; - tabs[n].value = newvalue; - tabs[n].img = icon; - if (stacking) { - String id = tabs[n].stackid; - tabs[n].stackid = GetStackId(tabs[n]); - if (tabs[n].stackid != id) { - tabs.Remove(n); - InsertKey0(GetCount(), newkey, newvalue, tabs[n].img, tabs[n].group); - } - } - Repos(); - Refresh(); -} - -void TabBar::SetValue(const Value &key, const Value &newvalue) -{ - Set(FindKey(key), key, newvalue); -} - -void TabBar::SetValue(int n, const Value &newvalue) -{ - Set(n, tabs[n].key, newvalue); -} - -void TabBar::SetKey(int n, const Value &newkey) -{ - Set(n, newkey, tabs[n].value); -} - -void TabBar::SetIcon(int n, Image icon) -{ - ASSERT(n >= 0 && n < tabs.GetCount()); - tabs[n].img = icon; - Repos(); - Refresh(); -} - -void TabBar::LeftDown(Point p, dword keyflags) -{ - SetCapture(); - - if(keyflags & K_SHIFT) - { - highlight = -1; - Refresh(); - Fix(p); - oldp = p; - return; - } - - drag_highlight = highlight; - - isctrl = keyflags & K_CTRL; - if(isctrl) - return; - - if(cross != -1) { - Value v = tabs[cross].key; - Vectorvv; - vv.Add(v); - int ix = cross; - if (!CancelClose(v) && !CancelCloseSome(Vector(vv, 0))) { - Close(ix); - WhenClose(v); - WhenCloseSome(vv); - } - } - else if(highlight >= 0) { - if (stacking && highlight == active) { - CycleTabStack(tabs[active].stack); - Repos(); - CursorChanged(); - UpdateActionRefresh(); - } - else - SetCursor0(highlight, true); - } -} - -void TabBar::LeftUp(Point p, dword keyflags) -{ - ReleaseCapture(); -} - -void TabBar::LeftDouble(Point p, dword keysflags) -{ - WhenLeftDouble(); -} - -void TabBar::RightDown(Point p, dword keyflags) -{ - if (contextmenu) - MenuBar::Execute(THISBACK(ContextMenu), GetMousePos()); -} - -void TabBar::MiddleDown(Point p, dword keyflags) -{ - if (highlight >= 0) - { - Value v = tabs[highlight].key; - Vectorvv; - vv.Add(v); - if (!CancelClose(v) && ! CancelCloseSome(Vector(vv, 0))) { - Value v = tabs[highlight].key; - Close(highlight); - WhenClose(v); - WhenCloseSome(vv); - } - } -} - -void TabBar::MiddleUp(Point p, dword keyflags) -{ -} - -int TabBar::GetTargetTab(Point p) -{ - p.x += sc.GetPos(); - - int f = GetFirst(); - int l = GetLast(); - - if(tabs[f].visible && p.x < tabs[f].pos.x + tabs[f].size.cx / 2) - return f; - - int t = -1; - - for(int i = l; i >= f; i--) - if(tabs[i].visible && p.x >= tabs[i].pos.x + tabs[i].size.cx / 2) - { - t = i; - break; - } - - if(stacking) - l = FindStackHead(tabs[l].stack); - - if(t == l) - t = tabs.GetCount(); - else - t = GetNext(t); - - return t; -} - -void TabBar::MouseWheel(Point p, int zdelta, dword keyflags) -{ - sc.AddPos(-zdelta / 4, true); - Scroll(); - MouseMove(p, 0); -} - -bool TabBar::ProcessMouse(int i, const Point& p) -{ - if(i >= 0 && i < tabs.GetCount() && tabs[i].HasMouse(p)) - { - if (stacking && ProcessStackMouse(i, p)) - return true; - bool iscross = crosses ? tabs[i].HasMouseCross(p) : false; - if(highlight != i || (iscross && cross != i || !iscross && cross == i)) - { - cross = iscross ? i : -1; - SetHighlight(i); - } - return true; - } - return false; -} - -bool TabBar::ProcessStackMouse(int i, const Point& p) -{ - int j = i + 1; - while (j < tabs.GetCount() && tabs[j].stack == tabs[i].stack) { - if (Rect(tabs[j].tab_pos, tabs[j].tab_size).Contains(p)) { - cross = -1; - if (highlight != j) - SetHighlight(j); - return true; - } - j++; - } - return false; -} - -void TabBar::SetHighlight(int n) -{ - highlight = n; - WhenHighlight(); - Refresh(); -} - -void TabBar::SetColor(int n, Color c) -{ - tabs[n].col = c; - Refresh(); -} - -void TabBar::MouseMove(Point p, dword keyflags) -{ - if(HasCapture() && (keyflags & K_SHIFT)) - { - Fix(p); - sc.AddPos(p.x - oldp.x, true); - oldp = p; - Refresh(); - return; - } - - if(HasCapture()) - return; - - if(ProcessMouse(active, p)) - return; - - for(int i = 0; i < tabs.GetCount(); i++) - { - if(i == active) - continue; - - if(ProcessMouse(i, p)) - return; - } - - if(highlight >= 0 || cross >= 0) - { - highlight = cross = -1; - WhenHighlight(); - Refresh(); - } -} - -void TabBar::MouseLeave() -{ - if(isdrag) - return; - highlight = cross = -1; - WhenHighlight(); - Refresh(); -} - -void TabBar::DragAndDrop(Point p, PasteClip& d) -{ - Fix(p); - int c = GetTargetTab(p); - int tab = isctrl ? drag_highlight : active; - - if (&GetInternal(d) != this || tabsort || c < 0 || !allowreorder) return; - - if (stacking) { - tab = FindStackHead(tabs[tab].stack); - if(c < tabs.GetCount()) - c = FindStackHead(tabs[c].stack); - } - - bool sametab = c == tab || c == GetNext(tab, true); - bool internal = AcceptInternal(d, "tabs"); - - if(!sametab && internal && d.IsAccepted()) - { - int id = active >= 0 ? tabs[active].id : -1; - - // Count stack - int count = 1; - if (stacking) { - int ix = tab + 1; - int stack = tabs[tab].stack; - while (ix < tabs.GetCount() && tabs[ix].stack == stack) - ix++; - count = ix - tab; - } - // Copy tabs - Vector stacktemp; - stacktemp.SetCount(count); - //Copy(&stacktemp[0], &tabs[tab], count); - for(int i = 0; i < count; i++) - stacktemp[i].Set(tabs[tab + i]); - // Remove - tabs.Remove(tab, count); - if (tab < c) - c -= count; - // Re-insert - tabs.InsertPick(c, stacktemp); - - active = id >= 0 ? FindId(id) : -1; - isdrag = false; - target = -1; - MakeGroups(); - Repos(); - Refresh(); - Sync(); - MouseMove(p, 0); - } - else if(isdrag) - { - if(internal) - { - target = -1; - isdrag = false; - } - else - target = c; - Refresh(); - } -} - -void TabBar::CancelMode() -{ - isdrag = false; - target = -1; - Refresh(); -} - -void TabBar::LeftDrag(Point p, dword keyflags) -{ - if(keyflags & K_SHIFT) - return; - if(highlight < 0) - return; - - Sync(); - isdrag = true; - dragtab = GetDragSample(); - DoDragAndDrop(InternalClip(*this, "tabs")); -} - -void TabBar::DragEnter() -{ -} - -void TabBar::DragLeave() -{ - target = -1; - Refresh(); -} - -void TabBar::DragRepeat(Point p) -{ - if(target >= 0) - { - Point dx = GetDragScroll(this, p, 16); - Fix(dx); - if(dx.x != 0) - sc.AddPos(dx.x); - } -} - -bool TabBar::SetCursor0(int n, bool action) -{ - if(tabs.GetCount() == 0) - return false; - - if(n < 0) - { - n = max(0, FindId(GetGroupActive())); - active = -1; - highlight = -1; - drag_highlight = -1; - if (allownullcursor) - return true; - } - - bool is_all = IsGroupAll(); - bool same_group = tabs[n].group == GetGroupName(); - - if((same_group || is_all) && active == n) - return false; - - bool repos = false; - - if (!IsStackHead(n)) - { - n = SetStackHead(tabs[n]); - repos = true; - } - - active = n; - - if(!is_all && !same_group) - { - SetGroup(tabs[n].group); - repos = true; - } - if (repos) - Repos(); - - SetGroupActive(tabs[n].id); - - int cx = tabs[n].pos.x - sc.GetPos(); - if(cx < 0) - sc.AddPos(cx - 10); - else - { - Size sz = Ctrl::GetSize(); - Fix(sz); - cx = tabs[n].pos.x + tabs[n].size.cx - sz.cx - sc.GetPos(); - if(cx > 0) - sc.AddPos(cx + 10); - } - - if(action) - { - CursorChanged(); - UpdateAction(); - } - - Refresh(); - - if(Ctrl::HasMouse()) - { - Sync(); - MouseMove(GetMouseViewPos(), 0); - } - return true; -} - -void TabBar::SetCursor(int n) -{ - SetCursor0(n, true); -} - -void TabBar::SetTabGroup(int n, const String &group) -{ - ASSERT(n >= 0 && n < tabs.GetCount()); - int g = FindGroup(group); - if (g <= 0) - NewGroup(group); - else if (groups[g].active == tabs[n].id) - SetGroupActive(tabs[n].id); - tabs[n].group = group; - MakeGroups(); - Repos(); -} - -void TabBar::CloseForce(int n, bool action) -{ - if(n == active) - { - int c = FindId(tabs[n].id); - int nc = GetNext(c); - if(nc < 0) - nc = max(0, GetPrev(c)); - SetGroupActive(tabs[nc].id); - } - sc.AddTotal(-tabs[n].size.cx); - tabs.Remove(n); - MakeGroups(); - Repos(); - - if(n == active) - SetCursor0(-1, action); - else { - if (n < active) - active--; - Refresh(); - if (n == highlight && Ctrl::HasMouse()) { - //TODO: That must be refactored - highlight = -1; - drag_highlight = -1; - Sync(); - MouseMove(GetMouseViewPos(), 0); - } - } -} - -void TabBar::Close(int n, bool action) -{ - if(tabs.GetCount() <= mintabcount) - return; - - CloseForce(n, action); -} - -void TabBar::CloseKey(const Value &key) -{ - int tabix = FindKey(key); - if (tabix < 0) return; - Close(tabix); -} - -TabBar::Style& TabBar::Style::DefaultCrosses() -{ - crosses[0] = TabBarImg::CR0(); - crosses[1] = TabBarImg::CR1(); - crosses[2] = TabBarImg::CR2(); - return *this; -} - -TabBar::Style& TabBar::Style::Variant1Crosses() -{ - crosses[0] = TabBarImg::VARIANT1_CR0(); - crosses[1] = TabBarImg::VARIANT1_CR1(); - crosses[2] = TabBarImg::VARIANT1_CR2(); - return *this; -} - -TabBar::Style& TabBar::Style::Variant2Crosses() -{ - crosses[0] = TabBarImg::VARIANT2_CR0(); - crosses[1] = TabBarImg::VARIANT2_CR1(); - crosses[2] = TabBarImg::VARIANT2_CR2(); - return *this; -} - -TabBar::Style& TabBar::Style::GroupSeparators(Value horz, Value vert) -{ - group_separators[0] = horz; - group_separators[0] = vert; - return *this; -} - -TabBar::Style& TabBar::Style::DefaultGroupSeparators() -{ - return GroupSeparators(TabBarImg::SEP(), TabBarImg::SEPV()); -} - -Vector TabBar::GetKeys() const -{ - Vector keys; - keys.SetCount(tabs.GetCount()); - for (int i = 0; i < tabs.GetCount(); i++) - keys[i] = tabs[i].key; - return keys; -} - -Vector TabBar::GetIcons() const -{ - Vector img; - img.SetCount(tabs.GetCount()); - for (int i = 0; i < tabs.GetCount(); i++) - img[i] = tabs[i].img; - return img; -} - -TabBar& TabBar::CopyBaseSettings(const TabBar& src) -{ - crosses = src.crosses; - crosses_side = src.crosses_side; - grouping = src.grouping; - contextmenu = src.contextmenu; - autoscrollhide = src.autoscrollhide; - nosel = src.nosel; - nohl = src.nohl; - inactivedisabled = src.inactivedisabled; - stacking = src.stacking; - groupsort = src.groupsort; - groupseps = src.groupseps; - tabsort = src.tabsort; - allownullcursor = src.allownullcursor; - icons = src.icons; - mintabcount = src.mintabcount; - return *this; -} - -TabBar& TabBar::CopySettings(const TabBar &src) -{ - - CopyBaseSettings(src); - - if (stacking != src.stacking) - Stacking(src.stacking); - else { - MakeGroups(); - Repos(); - Refresh(); - } - return *this; -} - -void TabBar::Serialize(Stream& s) -{ - int version = 1; - s / version; - - s % id; - s % crosses; - s % crosses_side; - s % grouping; - s % autoscrollhide; - s % nosel; - s % nohl; - s % inactivedisabled; - s % stacking; - s % groupsort; - s % groupseps; - s % tabsort; - s % allownullcursor; - s % icons; - s % mintabcount; - s % active; - - cross = -1; - highlight = -1; - drag_highlight = -1; - target = -1; - - int n = groups.GetCount(); - s % n; - groups.SetCount(n); - - for(int i = 0; i < groups.GetCount(); i++) - s % groups[i]; - - n = tabs.GetCount(); - s % n; - tabs.SetCount(n); - - for(int i = 0; i < tabs.GetCount(); i++) - s % tabs[i]; - - int g = GetGroup(); - s % g; - group = g; -} - -CH_STYLE(TabBar, Style, StyleDefault) -{ - Assign(TabCtrl::StyleDefault()); -#ifdef PLATFORM_WIN32 - if(IsWinVista()) - Variant2Crosses(); - else - DefaultCrosses(); -#else - DefaultCrosses(); -#endif - DefaultGroupSeparators(); -} - -END_UPP_NAMESPACE +#include "TabBar.h" + +#define TFILE +#include + +#define IMAGECLASS TabBarImg +#define IMAGEFILE +#include + +NAMESPACE_UPP + +// AlignedFrame +void AlignedFrame::FrameLayout(Rect &r) +{ + switch(layout) + { + case LEFT: + LayoutFrameLeft(r, this, framesize); + break; + case TOP: + LayoutFrameTop(r, this, framesize); + break; + case RIGHT: + LayoutFrameRight(r, this, framesize); + break; + case BOTTOM: + LayoutFrameBottom(r, this, framesize); + break; + } + r.top += border; + r.left += border; + r.right -= border; + r.bottom -= border; +} + +void AlignedFrame::FrameAddSize(Size& sz) +{ + sz += border * 2; + IsVert() ? sz.cx += framesize : sz.cy += framesize; +} + +void AlignedFrame::FramePaint(Draw& w, const Rect& r) +{ + if(border > 0) + { + Rect n = r; + switch(layout) + { + case LEFT: + n.left += framesize; + break; + case TOP: + n.top += framesize; + break; + case RIGHT: + n.right -= framesize; + break; + case BOTTOM: + n.bottom -= framesize; + break; + } + ViewFrame().FramePaint(w, n); + } + else + FrameCtrl::FramePaint(w, r); +} + +AlignedFrame& AlignedFrame::SetFrameSize(int sz, bool refresh) +{ + framesize = sz; + if (refresh) RefreshParentLayout(); + return *this; +} + +void AlignedFrame::Fix(Size& sz) +{ + if(IsVert()) + Swap(sz.cx, sz.cy); +} + +void AlignedFrame::Fix(Point& p) +{ + if(IsVert()) + Swap(p.x, p.y); +} + +Size AlignedFrame::Fixed(const Size& sz) +{ + return IsVert() ? Size(sz.cy, sz.cx) : Size(sz.cx, sz.cy); +} + +Point AlignedFrame::Fixed(const Point& p) +{ + return IsVert() ? Point(p.y, p.x) : Point(p.x, p.y); +} + +// TabScrollBar +TabScrollBar::TabScrollBar() +{ + Clear(); +} + +void TabScrollBar::Clear() +{ + total = 0; + pos = 0; + ps = 0; + start_pos = 0; + new_pos = 0; + old_pos = 0; + sz.Clear(); + ready = false; +} + +void TabScrollBar::UpdatePos(bool update) +{ + sz = GetSize(); + Fix(sz); + if(total <= 0 || sz.cx <= 0) + cs = ics = 0; + else + { + cs = sz.cx / ((double) total + 0.5); + ics = total / ((double) sz.cx); + } + size = sz.cx * cs; + if(update) + pos = new_pos - start_pos; + if(pos < 0) + pos = 0; + else if(pos + size > sz.cx) + pos = sz.cx - size; + + ps = total > sz.cx ? pos * ics : 0; +} + +void TabScrollBar::Paint(Draw &w) +{ + if(!ready) + { + UpdatePos(); + ready = true; + } + Size rsz = GetSize(); + #ifdef TABBAR_DEBUG + w.DrawRect(rsz, Red); + #else + w.DrawRect(rsz, White); + #endif + Point p; + + if(total > sz.cx) { + p = Point(ffloor(pos), 1); + rsz = Size(fceil(size), (IsVert() ? rsz.cx : rsz.cy) - 2); + } + else { + p = Point(0, 1); + rsz = Size(sz.cx, (IsVert() ? rsz.cx : rsz.cy) - 2); + } + Fix(p); + Fix(rsz); + w.DrawRect(p.x, p.y, rsz.cx, rsz.cy, Blue); +} + +void TabScrollBar::Layout() +{ + UpdatePos(false); +} + +void TabScrollBar::LeftDown(Point p, dword keyflags) +{ + SetCapture(); + Fix(p); + old_pos = new_pos = p.x; + if(p.x < pos || p.x > pos + size) + start_pos = size / 2; + else + start_pos = tabs(p.x - pos); + UpdatePos(); + UpdateActionRefresh(); +} + +void TabScrollBar::LeftUp(Point p, dword keyflags) +{ + ReleaseCapture(); + Fix(p); + old_pos = p.x; +} + +void TabScrollBar::MouseMove(Point p, dword keyflags) +{ + if(!HasCapture()) + return; + + Fix(p); + new_pos = p.x; + UpdatePos(); + UpdateActionRefresh(); +} + +void TabScrollBar::MouseWheel(Point p, int zdelta, dword keyflags) +{ + AddPos(-zdelta / 4, true); + UpdateActionRefresh(); +} + +int TabScrollBar::GetPos() const +{ + return ffloor(ps); +} + +void TabScrollBar::SetPos(int p, bool dontscale) +{ + pos = total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0; + UpdatePos(false); + Refresh(); +} + +void TabScrollBar::AddPos(int p, bool dontscale) +{ + pos += total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0; + UpdatePos(false); + Refresh(); +} + +int TabScrollBar::GetTotal() const +{ + return total; +} + +void TabScrollBar::SetTotal(int t) +{ + bool upd = total < t; + total = t; + UpdatePos(upd); + Refresh(); +} + +void TabScrollBar::AddTotal(int t) +{ + sz = GetSize(); + Fix(sz); + total += t; + if(total <= 0 || sz.cx <= 0) + cs = ics = 0; + else + cs = sz.cx / ((double) total + 0.5); + size = sz.cx * cs; + ps = min(ps, (double)(total-sz.cx)); + pos = (int)(ps * cs); + old_pos = new_pos = (int)(pos - start_pos); + + Refresh(); +} + +void TabScrollBar::GoEnd() +{ + pos = total; + UpdatePos(false); + Refresh(); +} + +void TabScrollBar::GoBegin() +{ + pos = 0; + UpdatePos(false); + Refresh(); +} + +void TabScrollBar::Set(const TabScrollBar& t) +{ + total = t.total; + pos = t.pos; + ps = t.ps; + Refresh(); +} + +bool TabScrollBar::IsScrollable() const +{ + // Note: sz already 'fixed' + return total > sz.cx && sz.cx > 0; +} + +// Group + +void TabBar::Group::Serialize(Stream& s) +{ + s % name % active % count % first % last; +} + + +// TabBar + +TabBar::TabBar() +{ + Clear(); + + id = 0; + display = NULL; + crosses = true; + crosses_side = RIGHT; + grouping = true; + isctrl = false; + isdrag = false; + inactivedisabled = false; + autoscrollhide = true; + stacking = false; + groupseps = false; + allownullcursor = false; + icons = true; + mintabcount = 1; + scrollbar_sz = TB_SBHEIGHT; + allowreorder = true; + + // Init sorting + groupsort = false; + tabsort = false; + stacksort = true; + contextmenu = true; + keysorter_inst.vo = &Single(); + valuesorter_inst.vo = &Single(); + stacksorter_inst.vo = &Single(); + tabsorter = &keysorter_inst; + groupsorter = &Single(); + stacksorter = &stacksorter_inst; + + SetAlign(TOP); + SetFrameSize(GetHeight(false)); + BackPaint(); +} + +void TabBar::Set(const TabBar& t) +{ + CopyBaseSettings(t); + + id = t.id; + + tabs.Clear(); + tabs <<= t.tabs; + groups.Clear(); + groups <<= t.groups; + separators.Clear(); + separators <<= t.separators; + + group = t.group; + stackcount = t.stackcount; + + active = t.active; + cross = -1; + highlight = -1; + target = -1; + + mouse.Clear(); + oldp.Clear(); + + sc.Set(t.sc); + SetAlign(t.GetAlign()); +} + +void TabBar::CloseAll(int exception) +{ + Vector vv; + for(int i = 0; i < tabs.GetCount(); i++) + if(i != exception) + vv.Add(tabs[i].key); + + if (exception < 0 && CancelCloseAll()) + return; + else if (exception >= 0 && CancelCloseSome(Vector(vv,0))) + return; + + for(int i = tabs.GetCount() - 1; i >= 0; i--) + if(i != exception) + tabs.Remove(i); + + SetCursor(0); + + if (exception >= 0) + WhenCloseSome(vv); + else + WhenCloseAll(); + + MakeGroups(); + Repos(); + Refresh(); +} + +int TabBar::GetNextId() +{ + return id++; +} + +void TabBar::ContextMenu(Bar& bar) +{ + if (highlight >= 0 && crosses) { + bar.Add(tabs.GetCount() > mintabcount, t_("Close"), THISBACK2(Close, highlight, true)); + bar.Separator(); + } + int cnt = groups.GetCount(); + for(int i = 0; i < cnt; i++) + { + String name = Format("%s (%d)", groups[i].name, groups[i].count); + Bar::Item &it = i > 0 ? bar.Add(name, THISBACK1(GroupMenu, i)) + : bar.Add(name, THISBACK1(DoGrouping, i)); + if(i == group) + it.Image(TabBarImg::CHK); + if(i == 0 && cnt > 1) + bar.Separator(); + } + bool sep = true; + if (GetCursor() >= 0 && crosses) { + bar.Separator(); + sep = false; + bar.Add(t_("Close others"), THISBACK1(CloseAll, GetCursor())); + } + if (mintabcount <= 0 && crosses) { + if (sep) bar.Separator(); + bar.Add(t_("Close all"), THISBACK1(CloseAll, -1)); + } +} + +void TabBar::GroupMenu(Bar &bar, int n) +{ + bar.Add(t_("Set active"), THISBACK1(DoGrouping, n)); + bar.Add(t_("Close"), THISBACK1(DoCloseGroup, n)); +} + +void TabBar::Tab::Set(const Tab& t) +{ + id = t.id; + + img = t.img; + col = t.col; + key = t.key; + value = t.value; + group = t.group; + + stackid = t.stackid; + stack = t.stack; + + visible = t.visible; + + pos = t.pos; + size = t.size; + + cross_pos = t.cross_pos; + cross_size = t.cross_size; + + tab_pos = t.tab_pos; + tab_size = t.tab_size; + + items <<= t.items; +} + +void TabBar::Tab::Serialize(Stream& s) +{ + s % id % key % value % group % stackid % stack % visible; +} + +bool TabBar::Tab::HasMouse(const Point& p) const +{ + if(!visible) + return false; + + return p.x >= tab_pos.x && p.x < tab_pos.x + tab_size.cx && + p.y >= tab_pos.y && p.y < tab_pos.y + tab_size.cy; +} + +bool TabBar::Tab::HasMouseCross(const Point& p) const +{ + if(!visible) + return false; + + return p.x >= cross_pos.x && p.x < cross_pos.x + cross_size.cx && + p.y >= cross_pos.y && p.y < cross_pos.y + cross_size.cy; +} + +int TabBar::FindGroup(const String& g) const +{ + for(int i = 0; i < groups.GetCount(); i++) + if(groups[i].name == g) + return i; + return -1; +} + +void TabBar::DoStacking() +{ + Value v = GetData(); + + // Reset stack info + for (int i = 0; i < tabs.GetCount(); i++) { + Tab &t = tabs[i]; + t.stack = -1; + t.stackid = GetStackId(t); + } + // Create stacks + Vector< Vector > tstack; + for (int i = 0; i < tabs.GetCount(); i++) { + Tab &ti = tabs[i]; + if (ti.stack < 0) { + ti.stack = tstack.GetCount(); + Vector &ttabs = tstack.Add(); + ttabs.Add(ti); + for (int j = i + 1; j < tabs.GetCount(); j++) { + Tab &tj = tabs[j]; + if (tj.stack < 0 && tj.stackid == ti.stackid && (!grouping || tj.group == ti.group)) { + tj.stack = ti.stack; + ttabs.Add(tj); + } + } + } + } + stackcount = tstack.GetCount(); + // Recombine + tabs.SetCount(0); + for (int i = 0; i < tstack.GetCount(); i++) { + if (stacksort) + StableSort(tstack[i], *stacksorter); + tabs.AppendPick(tstack[i]); + } + highlight = -1; + SetData(v); + MakeGroups(); + Repos(); +} + +void TabBar::DoUnstacking() +{ + stackcount = 0; + for (int i = 0; i < tabs.GetCount(); i++) + tabs[i].stack = -1; + highlight = -1; + MakeGroups(); + Repos(); + if (HasCursor()) + SetCursor(-1); + else + Refresh(); +} + +void TabBar::SortStack(int stackix) +{ + if (!stacksort) return; + + int head = FindStackHead(stackix); + int tail = head; + while (tail < tabs.GetCount() && tabs[tail].stack == stackix) + ++tail; + SortStack(stackix, head, tail-1); +} + +void TabBar::SortStack(int stackix, int head, int tail) +{ + if (!stacksort) return; + + int headid = tabs[head].id; + StableSort(tabs.GetIter(head), tabs.GetIter(tail), *stacksorter); + while (tabs[head].id != headid) + CycleTabStack(head, stackix); +} + +void TabBar::MakeGroups() +{ + groups[0].count = tabs.GetCount(); + groups[0].first = 0; + groups[0].last = tabs.GetCount() - 1; + + if (groupsort) + StableSort(tabs, *groupsorter); + + for(int i = 1; i < groups.GetCount(); i++) + { + groups[i].count = 0; + groups[i].first = 10000000; + groups[i].last = 0; + } + + for(int i = 0; i < tabs.GetCount(); i++) + { + Tab &tab = tabs[i]; + int n = FindGroup(tab.group); + ASSERT(n >= 0); + if (n > 0) { + if(groups[n].active < 0) + groups[n].active = tab.id; + groups[n].count++; + groups[n].last = i; + + if(i < groups[n].first) + groups[n].first = i; + if(i > groups[n].last) + groups[n].last = i; + } + } + + int cnt = groups.GetCount() - 1; + for(int i = cnt; i > 0; i--) + if(groups[i].count == 0) + groups.Remove(i); + + if(group > groups.GetCount() - 1 && group > 0) + group--; +} + +void TabBar::DoGrouping(int n) +{ + group = n; + Repos(); + SyncScrollBar(); + SetCursor(-1); +} + +void TabBar::DoCloseGroup(int n) +{ + int cnt = groups.GetCount(); + if(cnt <= 0) + return; + + String groupName = groups[n].name; + + /* + do WhenCloseSome()/CancelCloseSome() checking + before WhenClose()/CancelClose() stuff + (that code must be reviewed anyways...) + In order to leave existing code as it is, following + changes have effect *ONLY* if WhenCloseSome()/CancelCloseSome() + callbacks are used, otherwise previous path is taken. + I think we should anyways review some parts of it later + */ + + if(WhenCloseSome || CancelCloseSome) + { + Vectorvv; + int nTabs = 0; + for(int i = 0; i < tabs.GetCount(); i++) + if(groupName == tabs[i].group) { + vv.Add(tabs[i].key); + nTabs++; + } + // at first, we check for CancelCloseSome() + if(vv.GetCount() && !CancelCloseSome(Vector(vv,0))) { + // we didn't cancel globally, now we check CancelClose() + // for each tab -- group gets removed ONLY if ALL of + // group tabs are closed + for(int i = tabs.GetCount() - 1; i >= 0; i--) { + if(groupName == tabs[i].group && tabs.GetCount() > 1) { + Value v = tabs[i].key; + if(!CancelClose(v)) + { + nTabs--; + WhenClose(v); + tabs.Remove(i); + } + } + // remove group if all of its tabs get closed + if(!nTabs) { + if(cnt == n) + group--; + if(cnt > 1) + groups.Remove(n); + } + MakeGroups(); + Repos(); + SetCursor(-1); + } + } + return; + } + + // previous code path, taken if WhenCancelSome()/WhenCloseSome() + for(int i = tabs.GetCount() - 1; i >= 0; i--) + { + if(groupName == tabs[i].group && tabs.GetCount() > 1) { + Value v = tabs[i].value; // should be key ?? + if (!CancelClose(v)) { + WhenClose(v); + tabs.Remove(i); + } + } + } + + if (cnt == n) + group--; + + if(cnt > 1) // what if CancelClose suppressed some tab closing ? + groups.Remove(n); + MakeGroups(); + Repos(); + SetCursor(-1); +} + +void TabBar::NewGroup(const String &name) +{ + Group &g = groups.Add(); + g.name = name; + g.count = 0; + g.first = 10000000; + g.last = 0; + g.active = -1; +} + +Image TabBar::AlignImage(int align, const Image& img) +{ + switch(align) { + case AlignedFrame::LEFT: + return RotateAntiClockwise(img); + case AlignedFrame::RIGHT: + return RotateClockwise(img); + case AlignedFrame::BOTTOM: + return MirrorVert(img); + default: + return img; + } +} + +Value TabBar::AlignValue(int align, const Value &v, const Size &sz) +{ + Size isz = sz; + if(align == AlignedFrame::LEFT || align == AlignedFrame::RIGHT) + Swap(isz.cx, isz.cy); + + ImageDraw w(isz.cx, isz.cy); + w.DrawRect(isz, SColorFace()); + ChPaint(w, isz, v); + ImageBuffer img; + return AlignImage(align, (Image)w); +} + +void TabBar::TabItem::Clear() +{ + text.Clear(); + ink = Null; + img = Null; + side = LEFT; + clickable = false; + cross = false; + stacked_tab = -1; +} + +TabBar::TabItem& TabBar::Tab::AddItem() +{ + if(itn < items.GetCount()) + { + TabItem& ti = items[itn++]; + ti.Clear(); + return ti; + } + else + { + ++itn; + return items.Add(); + } +} + +void TabBar::Tab::Clear() +{ + itn = 0; +} + +TabBar::TabItem& TabBar::Tab::AddImage(const Image& img, int side) +{ + TabItem& ti = AddItem(); + ti.img = img; + ti.size = img.GetSize(); + ti.side = side; + return ti; +} + +TabBar::TabItem& TabBar::Tab::AddValue(const Value& q, const Font& font, const Color& ink) +{ + TabItem& ti = AddItem(); + + ti.font = font; + ti.ink = ink; + + if(IsType(q)) { + const AttrText& t = ValueTo(q); + ti.text = t.text; + if(!IsNull(t.font)) + ti.font = t.font; + + if(!IsNull(t.ink)) + ti.ink = t.ink; + } + else + ti.text = IsString(q) ? q : StdConvert().Format(q); + + ti.size = GetTextSize(ti.text, ti.font); + return ti; +} + +TabBar::TabItem& TabBar::Tab::AddText(const WString& s, const Font& font, const Color& ink) +{ + TabItem& ti = AddItem(); + + ti.font = font; + ti.ink = ink; + ti.text = s; + ti.size = GetTextSize(ti.text, ti.font); + return ti; +} + +TabBar::TabItem& TabBar::Tab::AddSpace(int space, int side) +{ + TabItem& ti = AddItem(); + + ti.size.cx = space; + ti.size.cy = 0; + ti.side = side; + return ti; +} + +void TabBar::ComposeTab(Tab& tab, const Font &font, Color ink, int style) +{ + if(PaintIcons() && tab.HasIcon()) + { + tab.AddImage(tab.img); + tab.AddSpace(TB_SPACEICON); + } + tab.AddValue(tab.value, font, ink).Clickable(); +} + +void TabBar::ComposeStackedTab(Tab& tab, const Tab& stacked_tab, const Font& font, Color ink, int style) +{ + tab.AddImage(stacked_tab.img); + tab.AddText("|...", font, ink); +} + +int TabBar::GetTextAngle() +{ + return AlignedFrame::IsVert() ? (GetAlign() == LEFT ? 900 : 2700) : 0; +} + +Point TabBar::GetTextPosition(int align, const Rect& r, int cy, int space) const +{ + Point p; + + if(align == LEFT) + { + p.y = r.bottom - space; + p.x = r.left + (r.GetWidth() - cy) / 2; + } + else if(align == RIGHT) + { + p.y = r.top + space; + p.x = r.right - (r.GetWidth() - cy) / 2; + } + else + { + p.x = r.left + space; + p.y = r.top + (r.GetHeight() - cy) / 2; + } + return p; +} + +Point TabBar::GetImagePosition(int align, const Rect& r, int cx, int cy, int space, int side, int offset) const +{ + Point p; + + if (align == LEFT) + { + p.x = r.left + (r.GetWidth() - cy) / 2 + offset; + p.y = side == LEFT ? r.bottom - space - cx : r.top + space; + } + else if (align == RIGHT) + { + p.x = r.right - (r.GetWidth() + cy) / 2 - offset; + p.y = side == LEFT ? r.top + space : r.bottom - space - cx; + } + else if (align == TOP) + { + p.x = side == LEFT ? r.left + space : r.right - cx - space; + p.y = r.top + (r.GetHeight() - cy) / 2 + offset; + } + else if (align == BOTTOM) + { + p.x = side == LEFT ? r.left + space : r.right - cx - space; + p.y = r.bottom - (r.GetHeight() + cy) / 2 - offset; + } + return p; +} + +void TabBar::PaintTabItems(Tab& t, Draw &w, const Rect& rn, int align) +{ + int pos_left = TB_MARGIN; + int pos_right = (IsVert() ? rn.GetHeight() : rn.GetWidth()) - TB_MARGIN; + + for(int i = 0; i < t.itn; i++) + { + const TabItem& ti = t.items[i]; + + Point p(0, 0); + int pos = ti.side == LEFT ? pos_left : pos_right - ti.size.cx; + + if(!IsNull(ti.img)) + { + p = GetImagePosition(align, rn, ti.size.cx, ti.size.cy, pos, LEFT); + w.DrawImage(p.x, p.y, IsVert() ? AlignImage(align, ti.img) : ti.img); + } + + if(!IsNull(ti.text)) + { + p = GetTextPosition(align, rn, ti.size.cy, pos); + w.DrawText(p.x, p.y, GetTextAngle(), ti.text, ti.font, ti.ink); + } + + if(ti.cross) + { + t.cross_size = ti.size; + t.cross_pos = p; + } + + if(ti.stacked_tab >= 0 && ti.clickable) + { + Tab& st = tabs[ti.stacked_tab]; + + if(align == RIGHT) + { + st.tab_pos = Point(rn.left, rn.top + pos); + st.tab_size = Size(rn.GetWidth(), ti.size.cx); + } + else if(align == LEFT) + { + st.tab_pos = Point(rn.left, rn.bottom - pos - ti.size.cx); + st.tab_size = Size(rn.GetWidth(), ti.size.cx); + } + else + { + st.tab_pos = Point(rn.left + pos, rn.top); + st.tab_size = Size(ti.size.cx, rn.GetHeight()); + } + + #ifdef TABBAR_DEBUG + DrawFrame(w, Rect(st.tab_pos, st.tab_size), Red); + #endif + } + + if(ti.side == LEFT) + pos_left += ti.size.cx; + else + pos_right -= ti.size.cx; + } +} + +void TabBar::PaintTab(Draw &w, const Size &sz, int n, bool enable, bool dragsample) +{ + TabBar::Tab &t = tabs[n]; + const Style& s = GetStyle(); + int align = GetAlign(); + int cnt = dragsample ? 1 : tabs.GetCount(); + + bool ac = (n == active && enable); + bool hl = (n == highlight && enable) || (stacking && highlight >= 0 && tabs[highlight].stack == t.stack); + + int ndx = !enable ? CTRL_DISABLED : + ac ? CTRL_PRESSED : + hl ? CTRL_HOT : CTRL_NORMAL; + + int c = align == LEFT ? cnt - n : n; + int lx = n > 0 ? s.extendleft : 0; + int x = t.pos.x - sc.GetPos() - lx + s.margin; + + int dy = -s.sel.top * ac; + int sel = s.sel.top; + + int df = 0; + + if (IsBR()) + { + dy = -dy; + sel = s.sel.bottom; + df = Fixed(sz).cy; + } + + Size sa = Size(t.size.cx + lx + s.sel.right + s.sel.left, t.size.cy + s.sel.bottom); + Point pa = Point(x - s.sel.left, IsBR() ? df - sa.cy : 0); + + Size sn = Size(t.size.cx + lx, t.size.cy - s.sel.top); + Point pn = Point(x, IsBR() ? df - sn.cy - s.sel.top : s.sel.top); + + Rect ra(Fixed(pa), Fixed(sa)); + Rect rn(Fixed(pn), Fixed(sn)); + + t.tab_pos = (ac ? ra : rn).TopLeft(); + t.tab_size = (ac ? ra : rn).GetSize(); + + const Value& sv = (cnt == 1 ? s.both : c == 0 ? s.first : c == cnt - 1 ? s.last : s.normal)[ndx]; + + Image img = AlignValue(align, sv, t.tab_size); + + if(!IsNull(t.col)) + { + img = Colorize(img, t.col); + } + + if(dragsample) + { + w.DrawImage(Rect(Point(0, 0), t.tab_size), img); + rn = Rect(Fixed(Point(s.sel.left * ac, sel * ac + dy)), Fixed(sn)); + } + else + { + w.DrawImage(Rect(t.tab_pos, t.tab_size), img); + rn = Rect(Fixed(Point(pn.x, pn.y + dy)), Fixed(sn)); + } + + #ifdef TABBAR_DEBUG + DrawFrame(w, rn, Green); + #endif + + if (display) + display->Paint(w, rn, t.value, s.text_color[ndx], SColorDisabled(), ndx); + + t.Clear(); + + if(crosses && cnt > mintabcount && !dragsample) { + TabItem& ti = t.AddItem(); + ti.img = s.crosses[cross == n ? 2 : ac || hl ? 1 : 0]; + ti.side = crosses_side; + ti.cross = true; + ti.size = s.crosses[0].GetSize(); + t.AddSpace(3, crosses_side); + } + + ComposeTab(t, s.font, s.text_color[ndx], ndx); + + if (stacking) { + int ix = n + 1; + + while (ix < tabs.GetCount() && tabs[ix].stack == t.stack) { + Tab &q = tabs[ix]; + int ndx = !enable ? CTRL_DISABLED : + highlight == ix ? CTRL_HOT : CTRL_NORMAL; + + int sn = t.itn; + ComposeStackedTab(t, q, s.font, s.text_color[ndx], ndx); + if(t.itn > sn) + for(; sn < t.itn; sn++) + t.items[sn].stacked_tab = ix; + + ix++; + } + } + + PaintTabItems(t, w, rn, align); +} + +void TabBar::Paint(Draw &w) +{ + int align = GetAlign(); + const Style &st = StyleDefault(); + Size ctrlsz = GetSize(); + Size sz = GetBarSize(ctrlsz); + + if (align == BOTTOM || align == RIGHT) + w.Offset(ctrlsz.cx - sz.cx, ctrlsz.cy - sz.cy); + + #ifdef TABBAR_DEBUG + w.DrawRect(sz, Yellow); + #else + w.DrawRect(sz, SColorFace()); + #endif + + IsVert() ? w.DrawRect(align == LEFT ? sz.cx - 1 : 0, 0, 1, sz.cy, Blend(SColorDkShadow, SColorShadow)): + w.DrawRect(0, align == TOP ? sz.cy - 1 : 0, sz.cx, 1, Blend(SColorDkShadow, SColorShadow)); + + if (!tabs.GetCount()) return; + + int limt = sc.GetPos() + (IsVert() ? sz.cy : sz.cx); + int first = 0; + int last = tabs.GetCount() - 1; + // Find first visible tab + for(int i = 0; i < tabs.GetCount(); i++) { + Tab &tab = tabs[i]; + if (tab.pos.x + tab.size.cx > sc.GetPos()) { + first = i; + break; + } + } + // Find last visible tab + for(int i = first + 1; i < tabs.GetCount(); i++) { + if (tabs[i].visible && tabs[i].pos.x > limt) { + last = i; + break; + } + } + // Draw active group + for (int i = first; i <= last; i++) { + if(tabs[i].visible && i != active) + PaintTab(w, sz, i, IsEnabled()); + } + // Clear tab_size for non-visible tabs to prevent mouse handling bugs + for (int i = 0; i < first; i++) + tabs[i].tab_size = Size(0, 0); + for (int i = last + 1; i < tabs.GetCount(); i++) + tabs[i].tab_size = Size(0, 0); + // Draw inactive groups + if (inactivedisabled) + for (int i = first; i <= last; i++) { + if(!tabs[i].visible && i != active && (!stacking || IsStackHead(i))) + PaintTab(w, sz, i, !IsEnabled()); + } + + // Draw selected tab + if(active >= first && active <= last) + PaintTab(w, sz, active, true); + + // Separators + if (grouping && groupseps) { + int cy = IsVert() ? sz.cx : sz.cy; + for (int i = 0; i < separators.GetCount(); i++) { + int x = separators[i]; + if (x > sc.GetPos() && x < limt) { + // Paint separator + ChPaint(w, Rect(Fixed(Point(x - sc.GetPos() + GetStyle().sel.left, 0)), + Fixed(Size(TB_SPACE - GetStyle().sel.left, cy-1))), + st.group_separators[IsVert() ? 1 : 0]); + } + } + } + + // Draw drag highlights + if(target >= 0) + { + // Draw target marker + int drag = isctrl ? highlight : active; + if(target != drag && target != GetNext(drag, true)) + { + last = GetLast(); + first = GetFirst(); + int x = (target == last + 1 ? tabs[last].Right() : tabs[target].pos.x) + - sc.GetPos() - (target <= first ? 1 : 2) + + st.margin - (target > 0 ? st.extendleft : 0); + + if (IsHorz()) + DrawVertDrop(w, x + 1, 0, sz.cy); + else + DrawHorzDrop(w, 0, x + 1, sz.cx); + } + // Draw transparent drag image + Point mouse = GetMousePos() - GetScreenRect().TopLeft(); + Size isz = dragtab.GetSize(); + int p = 0; + int sep = TB_SBSEPARATOR * sc.IsVisible(); + + int top = drag == active ? st.sel.bottom : st.sel.top; + if (align == BOTTOM || align == RIGHT) + p = int(drag == active) * -top + sep; + else + p = int(drag != active) * top; + + if (IsHorz()) + w.DrawImage(mouse.x - isz.cx / 2, p, isz.cx, isz.cy, dragtab); + else + w.DrawImage(p, mouse.y - isz.cy / 2, isz.cx, isz.cy, dragtab); + } + + // If not in a frame fill any spare area + if (!InFrame()) + w.DrawRect(GetClientArea(), SColorFace()); + + if (align == BOTTOM || align == RIGHT) + w.EndOp(); +} + +Image TabBar::GetDragSample() +{ + int h = drag_highlight; + if(stacking) + h = FindStackHead(tabs[h].stack); + return GetDragSample(h); +} + +Image TabBar::GetDragSample(int n) +{ + if (n < 0) return Image(); + Tab &t = tabs[n]; + + Size tsz(t.tab_size); + ImageDraw iw(tsz); + iw.DrawRect(tsz, SColorFace()); //this need to be fixed - if inactive tab is dragged gray edges are visible + + PaintTab(iw, tsz, n, true, true); + + Image img = iw; + ImageBuffer ib(img); + Unmultiply(ib); + RGBA *s = ~ib; + RGBA *e = s + ib.GetLength(); + while(s < e) { + s->a = 180; + s++; + } + Premultiply(ib); + return ib; +} + +void TabBar::Scroll() +{ + Refresh(); +} + +int TabBar::GetWidth(int n) +{ + return GetStdSize(tabs[n]).cx + GetExtraWidth(); +} + +int TabBar::GetExtraWidth() +{ + return TB_MARGIN * 2 + (TB_SPACE + GetStyle().crosses[0].GetSize().cx) * crosses; +} + +Size TabBar::GetStdSize(const Value &q) +{ + if (display) + return display->GetStdSize(q); + else if (q.GetType() == STRING_V || q.GetType() == WSTRING_V) + return GetTextSize(WString(q), GetStyle().font); + else + return GetTextSize("A Tab", GetStyle().font); +} + +Size TabBar::GetStackedSize(const Tab &t) +{ + if (!IsNull(t.img)) + return t.img.GetSize(); + return GetTextSize("...", GetStyle().font, 3); +} + +Size TabBar::GetStdSize(const Tab &t) +{ + return (PaintIcons() && t.HasIcon()) ? (GetStdSize(t.value) + Size(TB_ICON + 2, 0)) : GetStdSize(t.value); +} + +TabBar& TabBar::Add(const Value &value, Image icon, String group, bool make_active) +{ + return InsertKey(tabs.GetCount(), value, value, icon, group, make_active); +} + +TabBar& TabBar::Insert(int ix, const Value &value, Image icon, String group, bool make_active) +{ + return InsertKey(tabs.GetCount(), value, value, icon, group, make_active); +} + +TabBar& TabBar::AddKey(const Value &key, const Value &value, Image icon, String group, bool make_active) +{ + return InsertKey(tabs.GetCount(), key, value, icon, group, make_active); +} + +TabBar& TabBar::InsertKey(int ix, const Value &key, const Value &value, Image icon, String group, bool make_active) +{ + int id = InsertKey0(ix, key, value, icon, group); + + SortTabs0(); + MakeGroups(); + Repos(); + active = -1; + if (make_active || (!allownullcursor && active < 0)) + SetCursor((groupsort || stacking) ? FindId(id) : ( minmax(ix, 0, tabs.GetCount() - 1))); + return *this; +} + +int TabBar::InsertKey0(int ix, const Value &key, const Value &value, Image icon, String group) +{ + ASSERT(ix >= 0); + int g = 0; + if (!group.IsEmpty()) { + g = FindGroup(group); + if (g < 0) { + NewGroup(group); + g = groups.GetCount() - 1; + } + } + + group = groups[g].name; + Tab t; + t.value = value; + t.key = key; + t.img = icon; + t.id = GetNextId(); + t.group = Nvl(TrimBoth(group), "Unnamed Group"); + if (stacking) { + t.stackid = GetStackId(t); + + // Override index + int tail = -1; + for (int i = 0; i < tabs.GetCount(); i++) { + if (tabs[i].stackid == t.stackid && (!grouping || tabs[i].group == t.group)) { + tail = FindStackTail(tabs[i].stack); + break; + } + } + if (tail >= 0) { + ix = tail+1; + t.stack = tabs[tail].stack; + tail++; + } + else { + ix = (ix < tabs.GetCount()) ? FindStackHead(tabs[ix].stack) : ix; + t.stack = stackcount++; + } + tabs.Insert(ix, t); + if (tail >= 0) + SortStack(t.stack, FindStackHead(t.stack), ix); + + } + else + tabs.Insert(ix, t); + return t.id; +} + +int TabBar::GetWidth() const +{ + if (!tabs.GetCount()) return 0; + int ix = GetLast(); + const Style& s = StyleDefault(); + if (IsStackHead(ix)) + return tabs[ix].Right() + s.margin * 2; + int stack = tabs[ix].stack; + ix--; + while (ix >= 0 && tabs[ix].stack == stack) + ix--; + return tabs[ix + 1].Right() + s.margin * 2; + +} + +int TabBar::GetHeight(bool scrollbar) const +{ + return TabBar::GetStyleHeight() + TB_SBSEPARATOR * int(scrollbar); +} + +int TabBar::GetStyleHeight() +{ + const Style& s = GetStyle(); + return s.tabheight + s.sel.top; +} + +void TabBar::Repos() +{ + if(!tabs.GetCount()) + return; + + String g = GetGroupName(); + + int j; + bool first = true; + j = 0; + separators.Clear(); + for(int i = 0; i < tabs.GetCount(); i++) + j = TabPos(g, first, i, j, false); + if (inactivedisabled) + for(int i = 0; i < tabs.GetCount(); i++) + if (!tabs[i].visible) + j = TabPos(g, first, i, j, true); + SyncScrollBar(); +} + +Size TabBar::GetBarSize(Size ctrlsz) const +{ + return IsVert() ? Size(GetFrameSize() - scrollbar_sz * int(sc.IsShown()), ctrlsz.cy) + : Size(ctrlsz.cx, GetFrameSize() - scrollbar_sz * int(sc.IsShown())); +} + +Rect TabBar::GetClientArea() const +{ + Rect rect = GetSize(); + switch (GetAlign()) { + case TOP: + rect.top += GetFrameSize(); + break; + case BOTTOM: + rect.bottom -= GetFrameSize(); + break; + case LEFT: + rect.left += GetFrameSize(); + break; + case RIGHT: + rect.right -= GetFrameSize(); + break; + }; + return rect; +} + +int TabBar::TabPos(const String &g, bool &first, int i, int j, bool inactive) +{ + bool ishead = IsStackHead(i); + bool v = IsNull(g) ? true : g == tabs[i].group; + Tab& t = tabs[i]; + + if(ishead && (v || inactive)) + { + t.visible = v; + t.pos.y = 0; + t.size.cy = GetStyleHeight(); + + // Normal visible or inactive but greyed out tabs + t.pos.x = first ? 0 : tabs[j].Right(); + + // Separators + if (groupseps && grouping && !first && t.group != tabs[j].group) { + separators.Add(t.pos.x); + t.pos.x += TB_SPACE; + } + + int cx = GetStdSize(t).cx; + + // Stacked/shortened tabs + if (stacking) { + for(int n = i + 1; n < tabs.GetCount() && tabs[n].stack == t.stack; n++) + cx += GetStackedSize(tabs[n]).cx; + } + + t.size.cx = cx + GetExtraWidth(); + + if (stacking) { + for(int n = i + 1; n < tabs.GetCount() && tabs[n].stack == t.stack; n++) { + Tab &q = tabs[n]; + q.visible = false; + q.pos = t.pos; + q.size = t.size; + } + } + + j = i; + first = false; + } + else if (!(v || inactive)) { + t.visible = false; + t.pos.x = sc.GetTotal() + GetBarSize(GetSize()).cx; + } + return j; +} + +void TabBar::ShowScrollbarFrame(bool b) +{ + SetFrameSize((b ? sc.GetFrameSize() : TB_SBSEPARATOR) + GetHeight(b), false); + sc.Show(b); + RefreshParentLayout(); +} + +void TabBar::SyncScrollBar(bool synctotal) +{ + if (synctotal) + sc.SetTotal(GetWidth()); + if (autoscrollhide) { + bool v = sc.IsScrollable(); + if (sc.IsShown() != v) { + PostCallback(THISBACK1(ShowScrollbarFrame, v)); + } + } + else { + SetFrameSize(sc.GetFrameSize() + GetHeight(true), false); + sc.Show(); + } +} + +int TabBar::FindId(int id) const +{ + for(int i = 0; i < tabs.GetCount(); i++) + if(tabs[i].id == id) + return i; + return -1; +} + +int TabBar::GetNext(int n, bool drag) const +{ + for(int i = n + 1; i < tabs.GetCount(); i++) + if(tabs[i].visible) + return i; + return drag ? tabs.GetCount() : -1; +} + +int TabBar::GetPrev(int n, bool drag) const +{ + for(int i = n - 1; i >= 0; i--) + if(tabs[i].visible) + return i; + return -1; +} + +void TabBar::Clear() +{ + highlight = -1; + drag_highlight = -1; + active = -1; + target = -1; + cross = -1; + stackcount = 0; + tabs.Clear(); + groups.Clear(); + NewGroup(t_("TabBarGroupAll\aAll")); + group = 0; + Refresh(); +} + +TabBar& TabBar::Crosses(bool b, int side) +{ + crosses = b; + crosses_side = side; + Repos(); + Refresh(); + return *this; +} + +TabBar& TabBar::SortTabs(bool b) +{ + tabsort = b; + if (b) + DoTabSort(*tabsorter); + return *this; +} + +TabBar& TabBar::SortTabsOnce() +{ + DoTabSort(*tabsorter); + return *this; +} + +TabBar& TabBar::SortTabsOnce(TabSort &sort) +{ + DoTabSort(sort); + return *this; +} + +TabBar& TabBar::SortTabs(TabSort &sort) +{ + tabsorter = &sort; + return SortTabs(true); +} + +TabBar& TabBar::SortTabValues(ValueOrder &sort) +{ + valuesorter_inst.vo = &sort; + tabsorter = &valuesorter_inst; + return SortTabs(true); +} + +TabBar& TabBar::SortTabValuesOnce(ValueOrder &sort) +{ + TabValueSort q; + q.vo = &sort; + DoTabSort(q); + return *this; +} + +TabBar& TabBar::SortTabKeys(ValueOrder &sort) +{ + keysorter_inst.vo = &sort; + tabsorter = &keysorter_inst; + return SortTabs(true); +} + +TabBar& TabBar::SortTabKeysOnce(ValueOrder &sort) +{ + TabKeySort q; + q.vo = &sort; + DoTabSort(q); + return *this; +} + +TabBar& TabBar::SortGroups(bool b) +{ + groupsort = b; + if (!b) return *this;; + + Value v = GetData(); + MakeGroups(); + Repos(); + if (!IsNull(v)) + SetData(v); + Refresh(); + return *this; +} + +TabBar& TabBar::SortGroupsOnce() +{ + if (!grouping) return *this;; + + Value v = GetData(); + MakeGroups(); + Repos(); + if (!IsNull(v)) + SetData(v); + Refresh(); + return *this; +} + +TabBar& TabBar::SortGroupsOnce(TabSort &sort) +{ + TabSort *current = groupsorter; + groupsorter = &sort; + SortGroupsOnce(); + groupsorter = current; + return *this; +} + +TabBar& TabBar::SortGroups(TabSort &sort) +{ + groupsorter = &sort; + return SortGroups(true); +} + +TabBar& TabBar::SortStacks(bool b) +{ + stacksort = b; + if (stacking) { + DoStacking(); + Refresh(); + } + return *this; +} + +TabBar& TabBar::SortStacksOnce() +{ + if (stacking) { + DoStacking(); + Refresh(); + } + return *this; +} + +TabBar& TabBar::SortStacksOnce(TabSort &sort) +{ + TabSort *current = stacksorter; + stacksorter = &sort; + SortStacksOnce(); + stacksorter = current; + return *this; +} + +TabBar& TabBar::SortStacks(TabSort &sort) +{ + stacksorter = &sort; + return SortStacks(true); +} + +TabBar& TabBar::SortStacks(ValueOrder &sort) +{ + stacksorter_inst.vo = &sort; + stacksorter = &stacksorter_inst; + return SortStacks(true); +} + +void TabBar::DoTabSort(TabSort &sort) +{ + Value v = GetData(); + StableSort(tabs, sort); + Repos(); + if (!IsNull(v)) + SetData(v); + Refresh(); +} + +void TabBar::SortTabs0() +{ + if (tabsort) + StableSort(tabs, *tabsorter); +} + +TabBar& TabBar::Grouping(bool b) +{ + grouping = b; + Repos(); + Refresh(); + return *this; +} + +TabBar& TabBar::ContextMenu(bool b) +{ + contextmenu = b; + return *this; +} + +TabBar& TabBar::GroupSeparators(bool b) +{ + groupseps = b; + Repos(); + Refresh(); + return *this; +} + +TabBar& TabBar::AutoScrollHide(bool b) +{ + autoscrollhide = b; + sc.Hide(); + SetFrameSize(GetHeight(false), false); + SyncScrollBar(GetWidth()); + return *this; +} + +TabBar& TabBar::InactiveDisabled(bool b) +{ + inactivedisabled = b; + Repos(); + Refresh(); + return *this; +} + +TabBar& TabBar::AllowNullCursor(bool b) +{ + allownullcursor = b; + return *this; +} + +TabBar& TabBar::Icons(bool v) +{ + icons = v; + Repos(); + Refresh(); + return *this; +} + +TabBar& TabBar::Stacking(bool b) +{ + stacking = b; + if (b) + DoStacking(); + else + DoUnstacking(); + Refresh(); + return *this; +} + +void TabBar::FrameSet() +{ + int al = GetAlign(); + Ctrl::ClearFrames(); + sc.Clear(); + sc.SetFrameSize(scrollbar_sz).SetAlign((al >= 2) ? al - 2 : al + 2); + sc <<= THISBACK(Scroll); + sc.Hide(); + + if (sc.IsChild()) sc.Remove(); + switch (al) { + case LEFT: + Ctrl::Add(sc.LeftPos(GetHeight(), scrollbar_sz).VSizePos()); + break; + case RIGHT: + Ctrl::Add(sc.RightPos(GetHeight(), scrollbar_sz).VSizePos()); + break; + case TOP: + Ctrl::Add(sc.TopPos(GetHeight(), scrollbar_sz).HSizePos()); + break; + case BOTTOM: + Ctrl::Add(sc.BottomPos(GetHeight(), scrollbar_sz).HSizePos()); + break; + }; + + SyncScrollBar(true); +} + +TabBar& TabBar::SetScrollThickness(int sz) +{ + scrollbar_sz = max(sz + 2, 3); + FrameSet(); + RefreshLayout(); + return *this; +} + +void TabBar::Layout() +{ + if (autoscrollhide && tabs.GetCount()) + SyncScrollBar(false); +} + +int TabBar::FindValue(const Value &v) const +{ + for (int i = 0; i < tabs.GetCount(); i++) + if (tabs[i].value == v) + return i; + return -1; +} + +int TabBar::FindKey(const Value &v) const +{ + for (int i = 0; i < tabs.GetCount(); i++) + if (tabs[i].key == v) + return i; + return -1; +} + +bool TabBar::IsStackHead(int n) const +{ + return tabs[n].stack < 0 + || n == 0 + || (n > 0 && tabs[n - 1].stack != tabs[n].stack); +} + +bool TabBar::IsStackTail(int n) const +{ + return tabs[n].stack < 0 + || n >= tabs.GetCount() - 1 + || (n < tabs.GetCount() && tabs[n + 1].stack != tabs[n].stack); +} + +int TabBar::FindStackHead(int stackix) const +{ + int i = 0; + while (tabs[i].stack != stackix) + i++; + return i; +} + +int TabBar::FindStackTail(int stackix) const +{ + int i = tabs.GetCount() - 1; + while (tabs[i].stack != stackix) + i--; + return i; +} + +int TabBar::SetStackHead(Tab &t) +// Returns index of stack head +{ + ASSERT(stacking); + int id = t.id; + int stack = t.stack; + int head = FindStackHead(stack); + while (tabs[head].id != id) + CycleTabStack(head, stack); + return head; +} + +int TabBar::CycleTabStack(int n) +// Returns index of stack head +{ + int head = FindStackHead(n); + CycleTabStack(head, n); + return head; +} + +void TabBar::CycleTabStack(int head, int n) +{ + // Swap tab to end of stack + int ix = head; + while (!IsStackTail(ix)) { + tabs.Swap(ix, ix + 1); + ++ix; + } +} + +Value TabBar::GetData() const +{ + return (HasCursor() && active < GetCount()) + ? GetKey(active) + : Value(); +} + +void TabBar::SetData(const Value &key) +{ + int n = FindKey(key); + if (n >= 0) { + if (stacking && tabs[n].stack >= 0) + n = SetStackHead(tabs[n]); + SetCursor(n); + } +} + +void TabBar::Set(int n, const Value &newkey, const Value &newvalue) +{ + Set(n, newkey, newvalue, tabs[n].img); +} + +void TabBar::Set(int n, const Value &newkey, const Value &newvalue, Image icon) +{ + ASSERT(n >= 0 && n < tabs.GetCount()); + tabs[n].key = newkey; + tabs[n].value = newvalue; + tabs[n].img = icon; + if (stacking) { + String id = tabs[n].stackid; + tabs[n].stackid = GetStackId(tabs[n]); + if (tabs[n].stackid != id) { + tabs.Remove(n); + InsertKey0(GetCount(), newkey, newvalue, tabs[n].img, tabs[n].group); + } + } + Repos(); + Refresh(); +} + +void TabBar::SetValue(const Value &key, const Value &newvalue) +{ + Set(FindKey(key), key, newvalue); +} + +void TabBar::SetValue(int n, const Value &newvalue) +{ + Set(n, tabs[n].key, newvalue); +} + +void TabBar::SetKey(int n, const Value &newkey) +{ + Set(n, newkey, tabs[n].value); +} + +void TabBar::SetIcon(int n, Image icon) +{ + ASSERT(n >= 0 && n < tabs.GetCount()); + tabs[n].img = icon; + Repos(); + Refresh(); +} + +void TabBar::LeftDown(Point p, dword keyflags) +{ + SetCapture(); + + if(keyflags & K_SHIFT) + { + highlight = -1; + Refresh(); + Fix(p); + oldp = p; + return; + } + + drag_highlight = highlight; + + isctrl = keyflags & K_CTRL; + if(isctrl) + return; + + if(cross != -1) { + Value v = tabs[cross].key; + Vectorvv; + vv.Add(v); + int ix = cross; + if (!CancelClose(v) && !CancelCloseSome(Vector(vv, 0))) { + Close(ix); + WhenClose(v); + WhenCloseSome(vv); + } + } + else if(highlight >= 0) { + if (stacking && highlight == active) { + CycleTabStack(tabs[active].stack); + Repos(); + CursorChanged(); + UpdateActionRefresh(); + } + else + SetCursor0(highlight, true); + } +} + +void TabBar::LeftUp(Point p, dword keyflags) +{ + ReleaseCapture(); +} + +void TabBar::LeftDouble(Point p, dword keysflags) +{ + WhenLeftDouble(); +} + +void TabBar::RightDown(Point p, dword keyflags) +{ + if (contextmenu) + MenuBar::Execute(THISBACK(ContextMenu), GetMousePos()); +} + +void TabBar::MiddleDown(Point p, dword keyflags) +{ + if (highlight >= 0) + { + Value v = tabs[highlight].key; + Vectorvv; + vv.Add(v); + if (!CancelClose(v) && ! CancelCloseSome(Vector(vv, 0))) { + Value v = tabs[highlight].key; + Close(highlight); + WhenClose(v); + WhenCloseSome(vv); + } + } +} + +void TabBar::MiddleUp(Point p, dword keyflags) +{ +} + +int TabBar::GetTargetTab(Point p) +{ + p.x += sc.GetPos(); + + int f = GetFirst(); + int l = GetLast(); + + if(tabs[f].visible && p.x < tabs[f].pos.x + tabs[f].size.cx / 2) + return f; + + int t = -1; + + for(int i = l; i >= f; i--) + if(tabs[i].visible && p.x >= tabs[i].pos.x + tabs[i].size.cx / 2) + { + t = i; + break; + } + + if(stacking) + l = FindStackHead(tabs[l].stack); + + if(t == l) + t = tabs.GetCount(); + else + t = GetNext(t); + + return t; +} + +void TabBar::MouseWheel(Point p, int zdelta, dword keyflags) +{ + sc.AddPos(-zdelta / 4, true); + Scroll(); + MouseMove(p, 0); +} + +bool TabBar::ProcessMouse(int i, const Point& p) +{ + if(i >= 0 && i < tabs.GetCount() && tabs[i].HasMouse(p)) + { + if (stacking && ProcessStackMouse(i, p)) + return true; + bool iscross = crosses ? tabs[i].HasMouseCross(p) : false; + if(highlight != i || (iscross && cross != i || !iscross && cross == i)) + { + cross = iscross ? i : -1; + SetHighlight(i); + } + return true; + } + return false; +} + +bool TabBar::ProcessStackMouse(int i, const Point& p) +{ + int j = i + 1; + while (j < tabs.GetCount() && tabs[j].stack == tabs[i].stack) { + if (Rect(tabs[j].tab_pos, tabs[j].tab_size).Contains(p)) { + cross = -1; + if (highlight != j) + SetHighlight(j); + return true; + } + j++; + } + return false; +} + +void TabBar::SetHighlight(int n) +{ + highlight = n; + WhenHighlight(); + Refresh(); +} + +void TabBar::SetColor(int n, Color c) +{ + tabs[n].col = c; + Refresh(); +} + +void TabBar::MouseMove(Point p, dword keyflags) +{ + if(HasCapture() && (keyflags & K_SHIFT)) + { + Fix(p); + sc.AddPos(p.x - oldp.x, true); + oldp = p; + Refresh(); + return; + } + + if(HasCapture()) + return; + + if(ProcessMouse(active, p)) + return; + + for(int i = 0; i < tabs.GetCount(); i++) + { + if(i == active) + continue; + + if(ProcessMouse(i, p)) + return; + } + + if(highlight >= 0 || cross >= 0) + { + highlight = cross = -1; + WhenHighlight(); + Refresh(); + } +} + +void TabBar::MouseLeave() +{ + if(isdrag) + return; + highlight = cross = -1; + WhenHighlight(); + Refresh(); +} + +void TabBar::DragAndDrop(Point p, PasteClip& d) +{ + Fix(p); + int c = GetTargetTab(p); + int tab = isctrl ? drag_highlight : active; + + if (&GetInternal(d) != this || tabsort || c < 0 || !allowreorder) return; + + if (stacking) { + tab = FindStackHead(tabs[tab].stack); + if(c < tabs.GetCount()) + c = FindStackHead(tabs[c].stack); + } + + bool sametab = c == tab || c == GetNext(tab, true); + bool internal = AcceptInternal(d, "tabs"); + + if(!sametab && internal && d.IsAccepted()) + { + int id = active >= 0 ? tabs[active].id : -1; + + // Count stack + int count = 1; + if (stacking) { + int ix = tab + 1; + int stack = tabs[tab].stack; + while (ix < tabs.GetCount() && tabs[ix].stack == stack) + ix++; + count = ix - tab; + } + // Copy tabs + Vector stacktemp; + stacktemp.SetCount(count); + //Copy(&stacktemp[0], &tabs[tab], count); + for(int i = 0; i < count; i++) + stacktemp[i].Set(tabs[tab + i]); + // Remove + tabs.Remove(tab, count); + if (tab < c) + c -= count; + // Re-insert + tabs.InsertPick(c, stacktemp); + + active = id >= 0 ? FindId(id) : -1; + isdrag = false; + target = -1; + MakeGroups(); + Repos(); + Refresh(); + Sync(); + MouseMove(p, 0); + } + else if(isdrag) + { + if(internal) + { + target = -1; + isdrag = false; + } + else + target = c; + Refresh(); + } +} + +void TabBar::CancelMode() +{ + isdrag = false; + target = -1; + Refresh(); +} + +void TabBar::LeftDrag(Point p, dword keyflags) +{ + if(keyflags & K_SHIFT) + return; + if(highlight < 0) + return; + + Sync(); + isdrag = true; + dragtab = GetDragSample(); + DoDragAndDrop(InternalClip(*this, "tabs")); +} + +void TabBar::DragEnter() +{ +} + +void TabBar::DragLeave() +{ + target = -1; + Refresh(); +} + +void TabBar::DragRepeat(Point p) +{ + if(target >= 0) + { + Point dx = GetDragScroll(this, p, 16); + Fix(dx); + if(dx.x != 0) + sc.AddPos(dx.x); + } +} + +bool TabBar::SetCursor0(int n, bool action) +{ + if(tabs.GetCount() == 0) + return false; + + if(n < 0) + { + n = max(0, FindId(GetGroupActive())); + active = -1; + highlight = -1; + drag_highlight = -1; + if (allownullcursor) + return true; + } + + bool is_all = IsGroupAll(); + bool same_group = tabs[n].group == GetGroupName(); + + if((same_group || is_all) && active == n) + return false; + + bool repos = false; + + if (!IsStackHead(n)) + { + n = SetStackHead(tabs[n]); + repos = true; + } + + active = n; + + if(!is_all && !same_group) + { + SetGroup(tabs[n].group); + repos = true; + } + if (repos) + Repos(); + + SetGroupActive(tabs[n].id); + + int cx = tabs[n].pos.x - sc.GetPos(); + if(cx < 0) + sc.AddPos(cx - 10); + else + { + Size sz = Ctrl::GetSize(); + Fix(sz); + cx = tabs[n].pos.x + tabs[n].size.cx - sz.cx - sc.GetPos(); + if(cx > 0) + sc.AddPos(cx + 10); + } + + if(action) + { + CursorChanged(); + UpdateAction(); + } + + Refresh(); + + if(Ctrl::HasMouse()) + { + Sync(); + MouseMove(GetMouseViewPos(), 0); + } + return true; +} + +void TabBar::SetCursor(int n) +{ + SetCursor0(n, true); +} + +void TabBar::SetTabGroup(int n, const String &group) +{ + ASSERT(n >= 0 && n < tabs.GetCount()); + int g = FindGroup(group); + if (g <= 0) + NewGroup(group); + else if (groups[g].active == tabs[n].id) + SetGroupActive(tabs[n].id); + tabs[n].group = group; + MakeGroups(); + Repos(); +} + +void TabBar::CloseForce(int n, bool action) +{ + if(n == active) + { + int c = FindId(tabs[n].id); + int nc = GetNext(c); + if(nc < 0) + nc = max(0, GetPrev(c)); + SetGroupActive(tabs[nc].id); + } + sc.AddTotal(-tabs[n].size.cx); + tabs.Remove(n); + MakeGroups(); + Repos(); + + if(n == active) + SetCursor0(-1, action); + else { + if (n < active) + active--; + Refresh(); + if (n == highlight && Ctrl::HasMouse()) { + //TODO: That must be refactored + highlight = -1; + drag_highlight = -1; + Sync(); + MouseMove(GetMouseViewPos(), 0); + } + } +} + +void TabBar::Close(int n, bool action) +{ + if(tabs.GetCount() <= mintabcount) + return; + + CloseForce(n, action); +} + +void TabBar::CloseKey(const Value &key) +{ + int tabix = FindKey(key); + if (tabix < 0) return; + Close(tabix); +} + +TabBar::Style& TabBar::Style::DefaultCrosses() +{ + crosses[0] = TabBarImg::CR0(); + crosses[1] = TabBarImg::CR1(); + crosses[2] = TabBarImg::CR2(); + return *this; +} + +TabBar::Style& TabBar::Style::Variant1Crosses() +{ + crosses[0] = TabBarImg::VARIANT1_CR0(); + crosses[1] = TabBarImg::VARIANT1_CR1(); + crosses[2] = TabBarImg::VARIANT1_CR2(); + return *this; +} + +TabBar::Style& TabBar::Style::Variant2Crosses() +{ + crosses[0] = TabBarImg::VARIANT2_CR0(); + crosses[1] = TabBarImg::VARIANT2_CR1(); + crosses[2] = TabBarImg::VARIANT2_CR2(); + return *this; +} + +TabBar::Style& TabBar::Style::GroupSeparators(Value horz, Value vert) +{ + group_separators[0] = horz; + group_separators[0] = vert; + return *this; +} + +TabBar::Style& TabBar::Style::DefaultGroupSeparators() +{ + return GroupSeparators(TabBarImg::SEP(), TabBarImg::SEPV()); +} + +Vector TabBar::GetKeys() const +{ + Vector keys; + keys.SetCount(tabs.GetCount()); + for (int i = 0; i < tabs.GetCount(); i++) + keys[i] = tabs[i].key; + return keys; +} + +Vector TabBar::GetIcons() const +{ + Vector img; + img.SetCount(tabs.GetCount()); + for (int i = 0; i < tabs.GetCount(); i++) + img[i] = tabs[i].img; + return img; +} + +TabBar& TabBar::CopyBaseSettings(const TabBar& src) +{ + crosses = src.crosses; + crosses_side = src.crosses_side; + grouping = src.grouping; + contextmenu = src.contextmenu; + autoscrollhide = src.autoscrollhide; + nosel = src.nosel; + nohl = src.nohl; + inactivedisabled = src.inactivedisabled; + stacking = src.stacking; + groupsort = src.groupsort; + groupseps = src.groupseps; + tabsort = src.tabsort; + allownullcursor = src.allownullcursor; + icons = src.icons; + mintabcount = src.mintabcount; + return *this; +} + +TabBar& TabBar::CopySettings(const TabBar &src) +{ + + CopyBaseSettings(src); + + if (stacking != src.stacking) + Stacking(src.stacking); + else { + MakeGroups(); + Repos(); + Refresh(); + } + return *this; +} + +void TabBar::Serialize(Stream& s) +{ + int version = 1; + s / version; + + s % id; + s % crosses; + s % crosses_side; + s % grouping; + s % autoscrollhide; + s % nosel; + s % nohl; + s % inactivedisabled; + s % stacking; + s % groupsort; + s % groupseps; + s % tabsort; + s % allownullcursor; + s % icons; + s % mintabcount; + s % active; + + cross = -1; + highlight = -1; + drag_highlight = -1; + target = -1; + + int n = groups.GetCount(); + s % n; + groups.SetCount(n); + + for(int i = 0; i < groups.GetCount(); i++) + s % groups[i]; + + n = tabs.GetCount(); + s % n; + tabs.SetCount(n); + + for(int i = 0; i < tabs.GetCount(); i++) + s % tabs[i]; + + int g = GetGroup(); + s % g; + group = g; +} + +CH_STYLE(TabBar, Style, StyleDefault) +{ + Assign(TabCtrl::StyleDefault()); +#ifdef PLATFORM_WIN32 + if(IsWinVista()) + Variant2Crosses(); + else + DefaultCrosses(); +#else + DefaultCrosses(); +#endif + DefaultGroupSeparators(); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Web/sproc.cpp b/uppsrc/Web/sproc.cpp index 40f95eb92..fd6bf432c 100644 --- a/uppsrc/Web/sproc.cpp +++ b/uppsrc/Web/sproc.cpp @@ -441,7 +441,9 @@ void LocalSlaveProcess::Write(String s) WriteFile(hInputWrite, s, s.GetLength(), &n, NULL); #endif #ifdef PLATFORM_POSIX - write(rpipe[1], s, s.GetLength()); + IGNORE_RESULT( + write(rpipe[1], s, s.GetLength()) + ); #endif } diff --git a/uppsrc/ide/Assist.cpp b/uppsrc/ide/Assist.cpp index b883c00c6..675067278 100644 --- a/uppsrc/ide/Assist.cpp +++ b/uppsrc/ide/Assist.cpp @@ -512,7 +512,6 @@ void AssistEditor::PopUpAssist(bool auto_insert) Point p = GetCaretPoint() + GetScreenView().TopLeft(); Rect wa = GetWorkArea(); int cx = min(wa.Width() - 100, HorzLayoutZoom(600)); - Rect r = RectC(p.x, p.y, cx, cy); if(p.x + cx > wa.right) p.x = wa.right - cx; if(p.y + cy + GetFontSize().cy < wa.bottom) diff --git a/uppsrc/ide/Builders/CppBuilder.cpp b/uppsrc/ide/Builders/CppBuilder.cpp index 6574b8caa..d185c3464 100644 --- a/uppsrc/ide/Builders/CppBuilder.cpp +++ b/uppsrc/ide/Builders/CppBuilder.cpp @@ -307,7 +307,6 @@ Blitz CppBuilder::BlitzStep(Vector& sfile, Vector& soptions, Vector& optimize) { Blitz b; - Time now = GetSysTime(); Vector excluded; Vector excludedoptions; Vector excludedoptimize; diff --git a/uppsrc/ide/Builders/OwcBuilder.icpp b/uppsrc/ide/Builders/OwcBuilder.icpp index 7e4abe9c7..93fd02910 100644 --- a/uppsrc/ide/Builders/OwcBuilder.icpp +++ b/uppsrc/ide/Builders/OwcBuilder.icpp @@ -1,422 +1,422 @@ -#include "Builders.h" - -#include - -void OwcBuilder::AddFlags(Index& cfg) -{ - -} - -bool OwcBuilder::BuildPackage(const String& package, Vector& linkfile, String& linkoptions, const Vector& all_uses, const Vector& all_libraries, int opt) -{ - String packagepath = PackagePath(package); - Package pkg; - pkg.Load(packagepath); - String packagedir = GetFileFolder(packagepath); - ChDir(packagedir); - PutVerbose("cd " + packagedir); - IdeConsoleBeginGroup(package); - Vector obj; - - bool is_shared = HasFlag("SO"); - - String cc = CmdLine(package, pkg), - clc = CompilerName(false), clcpp = CompilerName(true); - - if (HasFlag("MT")) - cc << " -bm"; - - if (HasFlag("WIN32")) - cc << " -bt=nt"; - - String ccoptions = Gather(pkg.option, config.GetKeys()); - if (!ccoptions.IsEmpty()) - cc << ' ' << ccoptions; - - String cc_speed = cc; - bool release = false; - - if (HasFlag("DEBUG")) - cc << " -d_DEBUG " << debug_options; - else { - release = true; - cc << ' ' << release_size_options; - cc_speed << ' ' << release_options; - if (opt == R_SPEED || pkg.optimize_speed) - cc = cc_speed; - } - - Vector sfile, isfile; - Vector soptions, isoptions; - Vector optimize, ioptimize; - bool error = false; - - for (int i = 0; i < pkg.GetCount(); i++) { - if (!IdeIsBuilding()) - return false; - if (!pkg[i].separator) { - String gop = Gather(pkg[i].option, config.GetKeys()); - Vector srcfile = CustomStep(pkg[i], package, error); - if (srcfile.GetCount() == 0) - error = true; - for (int j = 0; j < srcfile.GetCount(); j++) { - String fn = srcfile[j]; - String ext = ToLower(GetFileExt(fn)); - if (ext == ".c" || ext == ".cpp" || ext == ".cc" || ext == ".cxx" || ext == ".rc" || ext == ".brc") { - sfile.Add(fn); - soptions.Add(gop); - optimize.Add(release && pkg[i].optimize_speed && opt == R_OPTIMAL); - } - else - if (ext == ".icpp") { - isfile.Add(fn); - isoptions.Add(gop); - ioptimize.Add(release && pkg[i].optimize_speed && opt == R_OPTIMAL); - } - else - if (ext == ".obj") - obj.Add(fn); - else - if (ext == ".lib") - linkfile.Add(fn); - } - } - } - - if (HasFlag("BLITZ")) { - Blitz b = BlitzStep(sfile, soptions, obj, ".obj", optimize); - if (b.build) { - PutConsole("BLITZ:" + b.info); - int slot = AllocSlot(); - if (slot < 0 || !Run(String().Cat() << clcpp << cc << ' ' - << GetHostPathQ(b.path) << " -fo=" << GetHostPathQ(b.object), slot, GetHostPath(b.object), b.count)) - error = true; - } - } - - int first_ifile = sfile.GetCount(); - sfile.AppendPick(isfile); - soptions.AppendPick(isoptions); - optimize.AppendPick(ioptimize); - - int ccount = 0; - - for (int i = 0; i < sfile.GetCount(); ++i) { - if (!IdeIsBuilding()) - return false; - String fn = sfile[i]; - String ext = ToLower(GetFileExt(fn)); - bool rc = (ext == ".rc"); - bool brc = (ext == ".brc"); - bool init = (i >= first_ifile); - String objfile = CatAnyPath(outdir, GetFileName(fn) + ".obj"); - if (HdependFileTime(fn) > GetFileTime(objfile)) { - int time = GetTickCount(); - bool execerr = false; - if (rc) { - PutConsole(GetFileNamePos(fn)); - objfile = ForceExt(objfile, ".res"); - int slot = AllocSlot(); - if (slot < 0 || !Run("wrc -q -r -x -fo=" + GetHostPathQ(objfile) + Includes(" -i=", package, pkg) - + ' ' + GetHostPathQ(fn), slot, GetHostPath(objfile), 1)) - execerr = true; - } - else - if (brc) { - PutConsole(GetFileNamePos(fn)); - try { - String hfn = GetHostPath(fn); - String brcdata = LoadFile(hfn); - if(brcdata.IsVoid()) - throw Exc(NFormat("error reading file '%s'", hfn)); - CParser parser(brcdata, hfn); - BinaryToObject(GetHostPath(objfile), parser, GetFileDirectory(hfn), THISBACK(BinObjConsole)); - } - catch (Exc e) { - PutConsole(e); - execerr = true; - } - } - else { - String c = cc; - if (optimize[i]) - c = cc_speed; - int slot = AllocSlot(); - - if (slot < 0 || !Run((ext == ".c" ? clc : clcpp + " -xr -xs") + c + soptions[i] + ' ' - + GetHostPathQ(fn) + " -fo=" + GetHostPathQ(objfile), slot, GetHostPath(objfile), 1)) - execerr = true; - } - - if (execerr) - DeleteFile(objfile); - error |= execerr; - PutVerbose("compiled in " + GetPrintTime(time)); - ccount++; - } - if (init) - linkfile.Add(objfile); - else - obj.Add(objfile); - } - - if (error) { - IdeConsoleEndGroup(); - return false; - } - - Vector libs = Split(Gather(pkg.library, config.GetKeys()), ' '); - for (int i = 0, n = libs.GetCount(); i < n; ++i) - linkfile.Add(AppendExt(libs[i], ".lib")); - - linkoptions << Gather(pkg.link, config.GetKeys()); - - int linktime = GetTickCount(); - if (!HasFlag("MAIN")) { - if (HasFlag("BLITZ") || HasFlag("NOLIB")) { - obj.Append(linkfile); - linkfile = obj; - IdeConsoleEndGroup(); - return true; - } - - String product; - if (is_shared) - product = GetSharedLibPath(package); - else - product = CatAnyPath(outdir, GetAnyFileName(package) + ".lib"); - - Time producttime = GetFileTime(product); - linkfile.Add(ForceExt(product, ".lib")); - - Vector files, res; - for (int i = 0, n = obj.GetCount(); i < n; ++i) - if (obj[i].EndsWith("j")) // ".obj" - files.Add(obj[i]); - else // ".res" - res.Add(obj[i]); - - if (!is_shared && !res.IsEmpty()) - linkfile.Append(res); - - if (!Wait()) { - IdeConsoleEndGroup(); - return false; - } - - for (int i = 0, n = obj.GetCount(); i < n; ++i) - if (GetFileTime(obj[i]) > producttime) { - String lib; - - if (is_shared) { - lib << LinkerName() << " option quiet"; - - if (HasFlag("DEBUG")) - lib << " debug all option incremental"; - - if (HasFlag("WIN32")) - lib << " system nt_dll"; - - if (!linkoptions.IsEmpty()) - lib << ' ' << linkoptions; - - if (!libpath.IsEmpty()) - lib << " libpath \"" << Join(libpath, "\";\"") << '\"'; - - if (!files.IsEmpty()) - lib << " file '" << Join(files, "', '") << '\''; - - if (!res.IsEmpty()) - lib << " resource '" << Join(res, "', '") << '\''; - - const int all_usesCount = all_uses.GetCount(), - all_librariesCount = all_libraries.GetCount(); - - if (all_usesCount > 0 || all_librariesCount > 0) { - lib << " library "; - - for (int j = 0, k = all_usesCount - 1; j < all_usesCount; ++j) { - lib << '\'' << GetHostPath(ForceExt(GetSharedLibPath(all_uses[j]), ".lib")) << '\''; - - if (j < k) - lib << ", "; - } - - if (all_usesCount > 0 && all_librariesCount > 0) - lib << ", "; - - for (int j = 0, k = all_librariesCount - 1; j < all_librariesCount; ++j) { - lib << '\'' << AppendExt(all_libraries[j], ".lib") << '\''; - - if (j < k) - lib << ", "; - } - } - - lib << " name '" << product << '\''; - } - else { - lib << "wlib -q -b -n -pa "; - - if (!linkoptions.IsEmpty()) - lib << linkoptions; - - lib << '\'' << product << '\''; - - for (int j = 0, m = files.GetCount(); j < m; ++j) - lib << " '" << GetHostPath(files[j]) << '\''; - } - - PutConsole("Creating library..."); - IdeConsoleEndGroup(); - DeleteFile(product); - - if (Execute(lib)) { - DeleteFile(product); - return false; - } - - PutConsole(String().Cat() << product << " (" << GetFileInfo(product).length - << " B) created in " << GetPrintTime(linktime)); - - break; - } - - return true; - } - - obj.Append(linkfile); - linkfile = obj; - return true; -} - -bool OwcBuilder::Link(const Vector& linkfile, const String& linkoptions, bool createmap) -{ - int time = GetTickCount(); - if (!Wait()) - return false; - - const int linkfileCount = linkfile.GetCount(); - - for (int i = 0; i < linkfileCount; i++) - if (GetFileTime(linkfile[i]) >= targettime) { - String link; - link << LinkerName() << " option quiet"; - - if (HasFlag("DEBUG")) - link << " debug all option incremental"; - - if (HasFlag("WIN32")) - if (HasFlag("GUI")) - link << " system nt_win"; - else if (HasFlag("DLL")) - link << " system nt_dll"; - else link << " system nt"; - - if (createmap) - link << " option map"; - - if (!linkoptions.IsEmpty()) - link << ' ' << linkoptions; - - if (!libpath.IsEmpty()) - link << " libpath \"" << Join(libpath, "\";\"") << '\"'; - - Vector files, libs, res; - - for (int j = 0; j < linkfileCount; ++j) { - const String ext(linkfile[j].Right(1)); - if (ext == "j") // ".obj" - files.Add(linkfile[j]); - else if (ext == "b") // ".lib" - libs.Add(linkfile[j]); - else // ".res" - res.Add(linkfile[j]); - } - - if (!files.IsEmpty()) - link << " file '" << Join(files, "', '") << '\''; - - if (!res.IsEmpty()) - link << " resource '" << Join(res, "', '") << '\''; - - if (!libs.IsEmpty()) - link << " library '" << Join(libs, "', '") << '\''; - - link << " name '" << GetHostPath(target) << '\''; - - PutConsole("Linking..."); - bool error = false; - CustomStep(".pre-link", Null, error); - if (!error, Execute(link) == 0) { - CustomStep(".post-link", Null, error); - PutConsole(String().Cat() << GetHostPath(target) << " (" << GetFileInfo(target).length - << " B) linked in " << GetPrintTime(time)); - return !error; - } - else { - DeleteFile(target); - return false; - } - } - - PutConsole(String().Cat() << GetHostPath(target) << " (" << GetFileInfo(target).length - << " B) is up to date."); - return true; -} - -bool OwcBuilder::Preprocess(const String& package, const String& file, const String& target, bool asmout) -{ - FileOut out(target); - const String packagepath = PackagePath(package); - Package pkg; - pkg.Load(packagepath); - - return Execute((GetFileExt(file) == ".c" ? CompilerName(false) : CompilerName(true)) + CmdLine(package, pkg) + " -pl " + file, out); -} - -String OwcBuilder::IncludesDefinesTargetTime(const String& package, const Package& pkg) -{ - String cc = Includes(" -i=", package, pkg); - - for (int i = 0; i < config.GetCount(); i++) - cc << " -Dflag" << config[i]; - - Time t = GetSysTime(); - cc << " -DbmYEAR=" << (int)t.year; - cc << " -DbmMONTH=" << (int)t.month; - cc << " -DbmDAY=" << (int)t.day; - cc << " -DbmHOUR=" << (int)t.hour; - cc << " -DbmMINUTE=" << (int)t.minute; - cc << " -DbmSECOND=" << (int)t.second; - targettime = GetFileTime(target); - - return cc; -} - -String OwcBuilder::CompilerName(bool isCpp) const -{ - if (!IsNull(compiler)) return compiler; - return isCpp ? "wpp386" : "wcc386"; // C++ or C compiler -} - -String OwcBuilder::LinkerName() const -{ - return "wlink"; -} - -String OwcBuilder::CmdLine(const String& package, const Package& pkg) -{ - return String().Cat() << " -zq -fr=" << IncludesDefinesTargetTime(package, pkg); - // "-fr=" - do not create error files -} - -Builder *CreateOwcBuilder() -{ - return new OwcBuilder; -} - -INITBLOCK -{ - RegisterBuilder("OWC", CreateOwcBuilder); -} +#include "Builders.h" + +#include + +void OwcBuilder::AddFlags(Index& cfg) +{ + +} + +bool OwcBuilder::BuildPackage(const String& package, Vector& linkfile, String& linkoptions, const Vector& all_uses, const Vector& all_libraries, int opt) +{ + String packagepath = PackagePath(package); + Package pkg; + pkg.Load(packagepath); + String packagedir = GetFileFolder(packagepath); + ChDir(packagedir); + PutVerbose("cd " + packagedir); + IdeConsoleBeginGroup(package); + Vector obj; + + bool is_shared = HasFlag("SO"); + + String cc = CmdLine(package, pkg), + clc = CompilerName(false), clcpp = CompilerName(true); + + if (HasFlag("MT")) + cc << " -bm"; + + if (HasFlag("WIN32")) + cc << " -bt=nt"; + + String ccoptions = Gather(pkg.option, config.GetKeys()); + if (!ccoptions.IsEmpty()) + cc << ' ' << ccoptions; + + String cc_speed = cc; + bool release = false; + + if (HasFlag("DEBUG")) + cc << " -d_DEBUG " << debug_options; + else { + release = true; + cc << ' ' << release_size_options; + cc_speed << ' ' << release_options; + if (opt == R_SPEED || pkg.optimize_speed) + cc = cc_speed; + } + + Vector sfile, isfile; + Vector soptions, isoptions; + Vector optimize, ioptimize; + bool error = false; + + for (int i = 0; i < pkg.GetCount(); i++) { + if (!IdeIsBuilding()) + return false; + if (!pkg[i].separator) { + String gop = Gather(pkg[i].option, config.GetKeys()); + Vector srcfile = CustomStep(pkg[i], package, error); + if (srcfile.GetCount() == 0) + error = true; + for (int j = 0; j < srcfile.GetCount(); j++) { + String fn = srcfile[j]; + String ext = ToLower(GetFileExt(fn)); + if (ext == ".c" || ext == ".cpp" || ext == ".cc" || ext == ".cxx" || ext == ".rc" || ext == ".brc") { + sfile.Add(fn); + soptions.Add(gop); + optimize.Add(release && pkg[i].optimize_speed && opt == R_OPTIMAL); + } + else + if (ext == ".icpp") { + isfile.Add(fn); + isoptions.Add(gop); + ioptimize.Add(release && pkg[i].optimize_speed && opt == R_OPTIMAL); + } + else + if (ext == ".obj") + obj.Add(fn); + else + if (ext == ".lib") + linkfile.Add(fn); + } + } + } + + if (HasFlag("BLITZ")) { + Blitz b = BlitzStep(sfile, soptions, obj, ".obj", optimize); + if (b.build) { + PutConsole("BLITZ:" + b.info); + int slot = AllocSlot(); + if (slot < 0 || !Run(String().Cat() << clcpp << cc << ' ' + << GetHostPathQ(b.path) << " -fo=" << GetHostPathQ(b.object), slot, GetHostPath(b.object), b.count)) + error = true; + } + } + + int first_ifile = sfile.GetCount(); + sfile.AppendPick(isfile); + soptions.AppendPick(isoptions); + optimize.AppendPick(ioptimize); + + int ccount = 0; + + for (int i = 0; i < sfile.GetCount(); ++i) { + if (!IdeIsBuilding()) + return false; + String fn = sfile[i]; + String ext = ToLower(GetFileExt(fn)); + bool rc = (ext == ".rc"); + bool brc = (ext == ".brc"); + bool init = (i >= first_ifile); + String objfile = CatAnyPath(outdir, GetFileName(fn) + ".obj"); + if (HdependFileTime(fn) > GetFileTime(objfile)) { + int time = GetTickCount(); + bool execerr = false; + if (rc) { + PutConsole(GetFileNamePos(fn)); + objfile = ForceExt(objfile, ".res"); + int slot = AllocSlot(); + if (slot < 0 || !Run("wrc -q -r -x -fo=" + GetHostPathQ(objfile) + Includes(" -i=", package, pkg) + + ' ' + GetHostPathQ(fn), slot, GetHostPath(objfile), 1)) + execerr = true; + } + else + if (brc) { + PutConsole(GetFileNamePos(fn)); + try { + String hfn = GetHostPath(fn); + String brcdata = LoadFile(hfn); + if(brcdata.IsVoid()) + throw Exc(NFormat("error reading file '%s'", hfn)); + CParser parser(brcdata, hfn); + BinaryToObject(GetHostPath(objfile), parser, GetFileDirectory(hfn), THISBACK(BinObjConsole)); + } + catch (Exc e) { + PutConsole(e); + execerr = true; + } + } + else { + String c = cc; + if (optimize[i]) + c = cc_speed; + int slot = AllocSlot(); + + if (slot < 0 || !Run((ext == ".c" ? clc : clcpp + " -xr -xs") + c + soptions[i] + ' ' + + GetHostPathQ(fn) + " -fo=" + GetHostPathQ(objfile), slot, GetHostPath(objfile), 1)) + execerr = true; + } + + if (execerr) + DeleteFile(objfile); + error |= execerr; + PutVerbose("compiled in " + GetPrintTime(time)); + ccount++; + } + if (init) + linkfile.Add(objfile); + else + obj.Add(objfile); + } + + if (error) { + IdeConsoleEndGroup(); + return false; + } + + Vector libs = Split(Gather(pkg.library, config.GetKeys()), ' '); + for (int i = 0, n = libs.GetCount(); i < n; ++i) + linkfile.Add(AppendExt(libs[i], ".lib")); + + linkoptions << Gather(pkg.link, config.GetKeys()); + + int linktime = GetTickCount(); + if (!HasFlag("MAIN")) { + if (HasFlag("BLITZ") || HasFlag("NOLIB")) { + obj.Append(linkfile); + linkfile = obj; + IdeConsoleEndGroup(); + return true; + } + + String product; + if (is_shared) + product = GetSharedLibPath(package); + else + product = CatAnyPath(outdir, GetAnyFileName(package) + ".lib"); + + Time producttime = GetFileTime(product); + linkfile.Add(ForceExt(product, ".lib")); + + Vector files, res; + for (int i = 0, n = obj.GetCount(); i < n; ++i) + if (obj[i].EndsWith("j")) // ".obj" + files.Add(obj[i]); + else // ".res" + res.Add(obj[i]); + + if (!is_shared && !res.IsEmpty()) + linkfile.Append(res); + + if (!Wait()) { + IdeConsoleEndGroup(); + return false; + } + + for (int i = 0, n = obj.GetCount(); i < n; ++i) + if (GetFileTime(obj[i]) > producttime) { + String lib; + + if (is_shared) { + lib << LinkerName() << " option quiet"; + + if (HasFlag("DEBUG")) + lib << " debug all option incremental"; + + if (HasFlag("WIN32")) + lib << " system nt_dll"; + + if (!linkoptions.IsEmpty()) + lib << ' ' << linkoptions; + + if (!libpath.IsEmpty()) + lib << " libpath \"" << Join(libpath, "\";\"") << '\"'; + + if (!files.IsEmpty()) + lib << " file '" << Join(files, "', '") << '\''; + + if (!res.IsEmpty()) + lib << " resource '" << Join(res, "', '") << '\''; + + const int all_usesCount = all_uses.GetCount(), + all_librariesCount = all_libraries.GetCount(); + + if (all_usesCount > 0 || all_librariesCount > 0) { + lib << " library "; + + for (int j = 0, k = all_usesCount - 1; j < all_usesCount; ++j) { + lib << '\'' << GetHostPath(ForceExt(GetSharedLibPath(all_uses[j]), ".lib")) << '\''; + + if (j < k) + lib << ", "; + } + + if (all_usesCount > 0 && all_librariesCount > 0) + lib << ", "; + + for (int j = 0, k = all_librariesCount - 1; j < all_librariesCount; ++j) { + lib << '\'' << AppendExt(all_libraries[j], ".lib") << '\''; + + if (j < k) + lib << ", "; + } + } + + lib << " name '" << product << '\''; + } + else { + lib << "wlib -q -b -n -pa "; + + if (!linkoptions.IsEmpty()) + lib << linkoptions; + + lib << '\'' << product << '\''; + + for (int j = 0, m = files.GetCount(); j < m; ++j) + lib << " '" << GetHostPath(files[j]) << '\''; + } + + PutConsole("Creating library..."); + IdeConsoleEndGroup(); + DeleteFile(product); + + if (Execute(lib)) { + DeleteFile(product); + return false; + } + + PutConsole(String().Cat() << product << " (" << GetFileInfo(product).length + << " B) created in " << GetPrintTime(linktime)); + + break; + } + + return true; + } + + obj.Append(linkfile); + linkfile = obj; + return true; +} + +bool OwcBuilder::Link(const Vector& linkfile, const String& linkoptions, bool createmap) +{ + int time = GetTickCount(); + if (!Wait()) + return false; + + const int linkfileCount = linkfile.GetCount(); + + for (int i = 0; i < linkfileCount; i++) + if (GetFileTime(linkfile[i]) >= targettime) { + String link; + link << LinkerName() << " option quiet"; + + if (HasFlag("DEBUG")) + link << " debug all option incremental"; + + if (HasFlag("WIN32")) + if (HasFlag("GUI")) + link << " system nt_win"; + else if (HasFlag("DLL")) + link << " system nt_dll"; + else link << " system nt"; + + if (createmap) + link << " option map"; + + if (!linkoptions.IsEmpty()) + link << ' ' << linkoptions; + + if (!libpath.IsEmpty()) + link << " libpath \"" << Join(libpath, "\";\"") << '\"'; + + Vector files, libs, res; + + for (int j = 0; j < linkfileCount; ++j) { + const String ext(linkfile[j].Right(1)); + if (ext == "j") // ".obj" + files.Add(linkfile[j]); + else if (ext == "b") // ".lib" + libs.Add(linkfile[j]); + else // ".res" + res.Add(linkfile[j]); + } + + if (!files.IsEmpty()) + link << " file '" << Join(files, "', '") << '\''; + + if (!res.IsEmpty()) + link << " resource '" << Join(res, "', '") << '\''; + + if (!libs.IsEmpty()) + link << " library '" << Join(libs, "', '") << '\''; + + link << " name '" << GetHostPath(target) << '\''; + + PutConsole("Linking..."); + bool error = false; + CustomStep(".pre-link", Null, error); + if (!error && Execute(link) == 0) { + CustomStep(".post-link", Null, error); + PutConsole(String().Cat() << GetHostPath(target) << " (" << GetFileInfo(target).length + << " B) linked in " << GetPrintTime(time)); + return !error; + } + else { + DeleteFile(target); + return false; + } + } + + PutConsole(String().Cat() << GetHostPath(target) << " (" << GetFileInfo(target).length + << " B) is up to date."); + return true; +} + +bool OwcBuilder::Preprocess(const String& package, const String& file, const String& target, bool asmout) +{ + FileOut out(target); + const String packagepath = PackagePath(package); + Package pkg; + pkg.Load(packagepath); + + return Execute((GetFileExt(file) == ".c" ? CompilerName(false) : CompilerName(true)) + CmdLine(package, pkg) + " -pl " + file, out); +} + +String OwcBuilder::IncludesDefinesTargetTime(const String& package, const Package& pkg) +{ + String cc = Includes(" -i=", package, pkg); + + for (int i = 0; i < config.GetCount(); i++) + cc << " -Dflag" << config[i]; + + Time t = GetSysTime(); + cc << " -DbmYEAR=" << (int)t.year; + cc << " -DbmMONTH=" << (int)t.month; + cc << " -DbmDAY=" << (int)t.day; + cc << " -DbmHOUR=" << (int)t.hour; + cc << " -DbmMINUTE=" << (int)t.minute; + cc << " -DbmSECOND=" << (int)t.second; + targettime = GetFileTime(target); + + return cc; +} + +String OwcBuilder::CompilerName(bool isCpp) const +{ + if (!IsNull(compiler)) return compiler; + return isCpp ? "wpp386" : "wcc386"; // C++ or C compiler +} + +String OwcBuilder::LinkerName() const +{ + return "wlink"; +} + +String OwcBuilder::CmdLine(const String& package, const Package& pkg) +{ + return String().Cat() << " -zq -fr=" << IncludesDefinesTargetTime(package, pkg); + // "-fr=" - do not create error files +} + +Builder *CreateOwcBuilder() +{ + return new OwcBuilder; +} + +INITBLOCK +{ + RegisterBuilder("OWC", CreateOwcBuilder); +} diff --git a/uppsrc/ide/Common/ComDlg.cpp b/uppsrc/ide/Common/ComDlg.cpp index 8567fee4f..0cfc0d559 100644 --- a/uppsrc/ide/Common/ComDlg.cpp +++ b/uppsrc/ide/Common/ComDlg.cpp @@ -154,8 +154,12 @@ void ShellOpenFolder(const String& dir) #if defined(PLATFORM_WIN32) LaunchWebBrowser(dir); #elif __APPLE__ - system("open " + dir + " &"); + IGNORE_RESULT( + system("open " + dir + " &") + ); #else - system("xdg-open " + dir + " &"); + IGNORE_RESULT( + system("xdg-open " + dir + " &") + ); #endif } diff --git a/uppsrc/ide/Core/Host.cpp b/uppsrc/ide/Core/Host.cpp index a91b25f20..eadefc3ff 100644 --- a/uppsrc/ide/Core/Host.cpp +++ b/uppsrc/ide/Core/Host.cpp @@ -62,7 +62,7 @@ void LocalHost::ChDir(const String& path) SetCurrentDirectory(path); #endif #ifdef PLATFORM_POSIX - chdir(path); + IGNORE_RESULT( chdir(path) ); #endif if(cmdout) *cmdout << "cd \"" << GetHostPath(path) << "\"\n"; diff --git a/uppsrc/ide/FindInFiles.cpp b/uppsrc/ide/FindInFiles.cpp index c0fbe1aad..b9c58cbfa 100644 --- a/uppsrc/ide/FindInFiles.cpp +++ b/uppsrc/ide/FindInFiles.cpp @@ -185,23 +185,6 @@ static bool RawFileMatch(const char *pattern, const char *file, const char *& en return true; } -static const char *FindFileMatch(const char *pattern, const char *file, String& cont) -{ - const char *endptr; - if(!RawFileMatch(pattern, file, endptr)) - return endptr; - if(cont.IsVoid()) - { - cont = endptr; - return NULL; - } - const char *p = cont; - while(*p && ToLower(*p) == ToLower(*endptr++)) - p++; - cont.Trim(int(p - cont.Begin())); - return NULL; -} - int CharFilterFindFileMask(int c) { return ToUpper(ToAscii(c)); diff --git a/uppsrc/ide/Help.cpp b/uppsrc/ide/Help.cpp index 250b1c578..dcf0ac830 100644 --- a/uppsrc/ide/Help.cpp +++ b/uppsrc/ide/Help.cpp @@ -142,7 +142,7 @@ void TopicCtrl::SyncDocTree() String hdx = sTopicHome.Mid(1); if(idelink.GetCount() == 0) GatherLinks(idelink, hdx); - int ide; + int ide = 0; bool idefirst = true; if(MatchTopicLink(hdx, sdx)) { ide = AddTree(0, IdeImg::Package(), "\3" + hdx, s_idehelp); @@ -163,9 +163,9 @@ void TopicCtrl::SyncDocTree() for(int i = 0; i < wspc.GetCount(); i++) used.Add(wspc[i]); - int usid; + int usid = 0; bool usedfirst = true; - int otid; + int otid = 0; bool otherfirst = true; String lng = ~lang; @@ -173,7 +173,7 @@ void TopicCtrl::SyncDocTree() TopicLink tl; tl.package = map.GetKey(i); bool packagefirst = true; - int pid; + int pid = 0; VectorMap >& group = map[i]; for(int i = 0; i < group.GetCount(); i++) { tl.group = group.GetKey(i); @@ -185,7 +185,7 @@ void TopicCtrl::SyncDocTree() n = s_documents; if(n == "srcimp") n = s_implementation; - int gid; + int gid = 0; bool groupfirst = true; const Index& topic = group[i]; for(int i = 0; i < topic.GetCount(); i++) { diff --git a/uppsrc/ide/LayDes/laylib.cpp b/uppsrc/ide/LayDes/laylib.cpp index efe1840fd..b5857d0b3 100644 --- a/uppsrc/ide/LayDes/laylib.cpp +++ b/uppsrc/ide/LayDes/laylib.cpp @@ -368,9 +368,9 @@ void EscDraw::DrawSmartText(EscEscape& e) int x = e.Int(0); int y = e.Int(1); int ii = 2; - int cx = INT_MAX; - if(e[ii].IsInt()) - cx = e.Int(ii++); +// int cx = INT_MAX; +// if(e[ii].IsInt()) +// cx = e.Int(ii++); String text; if(ii < e.GetCount() && e[ii].IsArray()) text = ToUtf8((WString)e[ii++]); diff --git a/uppsrc/ide/SrcUpdater/Wizard.h b/uppsrc/ide/SrcUpdater/Wizard.h index 130dfa23c..20ee55803 100644 --- a/uppsrc/ide/SrcUpdater/Wizard.h +++ b/uppsrc/ide/SrcUpdater/Wizard.h @@ -10,7 +10,8 @@ struct NullStep : public ParentCtrl { } }; -static void InitLayout(Upp::Ctrl& parent, NullStep& layout, NullStep& uts, NullStep&){ +inline +void InitLayout(Upp::Ctrl& parent, NullStep& layout, NullStep& uts, NullStep&){ parent.LayoutId("NullStepLayout"); }; diff --git a/uppsrc/ide/UppWspc.cpp b/uppsrc/ide/UppWspc.cpp index 5ba4bb89d..292eaa5be 100644 --- a/uppsrc/ide/UppWspc.cpp +++ b/uppsrc/ide/UppWspc.cpp @@ -278,7 +278,6 @@ void WorkspaceWork::TouchFile(const String& path) { if(!showtime) return; - Time tm = GetSysTime(); String n = GetFileName(path); for(int i = 0; i < filelist.GetCount(); i++) { FileList::File f = filelist[i]; @@ -647,11 +646,14 @@ void WorkspaceWork::RemoveFile() int fx = fileindex[i]; separator = actual.file[fx].separator; if(separator && closed.Find(GetActiveSepfo()) >= 0) { - int px = fx, c; + int px = fx; while(--px >= 0 && !actual.file[fx].separator) ; - if(px >= 0 && (c = closed.Find(Sepfo(GetActivePackage(), actual.file[px]))) >= 0) - closed.Unlink(c); + if(px >= 0) { + int c = closed.Find(Sepfo(GetActivePackage(), actual.file[px])); + if(c >= 0) + closed.Unlink(c); + } } actual.file.Remove(fx); }