diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 7a45aba0..ac1a8bd7 100755 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "higan"; - static const char Version[] = "092.09"; + static const char Version[] = "092.10"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/higan/fc/cheat/cheat.cpp b/higan/fc/cheat/cheat.cpp index 148e4b76..b023ff96 100755 --- a/higan/fc/cheat/cheat.cpp +++ b/higan/fc/cheat/cheat.cpp @@ -28,7 +28,7 @@ bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) code.upper(); unsigned length = code.length(), bits = 0; - if(code.wildcard("????:??")) { + if(code.match("????:??")) { code = {substr(code, 0, 4), substr(code, 5, 2)}; for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false; bits = hex(code); @@ -38,7 +38,7 @@ bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) return true; } - if(code.wildcard("????:??:??")) { + if(code.match("????:??:??")) { code = {substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2)}; for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false; bits = hex(code); diff --git a/higan/gb/cheat/cheat.cpp b/higan/gb/cheat/cheat.cpp index e972c520..cb4bcf7b 100755 --- a/higan/gb/cheat/cheat.cpp +++ b/higan/gb/cheat/cheat.cpp @@ -28,7 +28,7 @@ bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) code.upper(); unsigned length = code.length(), bits = 0; - if(code.wildcard("????:??")) { + if(code.match("????:??")) { code = {substr(code, 0, 4), substr(code, 5, 2)}; for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false; bits = hex(code); @@ -38,7 +38,7 @@ bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) return true; } - if(code.wildcard("????:??:??")) { + if(code.match("????:??:??")) { code = {substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2)}; for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false; bits = hex(code); @@ -48,7 +48,7 @@ bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) return true; } - if(code.wildcard("???" "-" "???")) { + if(code.match("???" "-" "???")) { code = {substr(code, 0, 3), substr(code, 4, 3)}; for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false; for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4); @@ -62,7 +62,7 @@ bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) return true; } - if(code.wildcard("???" "-" "???" "-" "???")) { + if(code.match("???" "-" "???" "-" "???")) { code = {substr(code, 0, 3), substr(code, 4, 3), substr(code, 8, 1), substr(code, 10, 1)}; for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false; for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4); diff --git a/higan/nall/any.hpp b/higan/nall/any.hpp index 6d3be3b2..27a07ef2 100755 --- a/higan/nall/any.hpp +++ b/higan/nall/any.hpp @@ -8,7 +8,11 @@ namespace nall { struct any { bool empty() const { return container; } - const std::type_info& type() const { return container ? container->type() : typeid(void); } + void reset() { if(container) { delete container; container = nullptr; } } + + const std::type_info& type() const { + return container ? container->type() : typeid(void); + } template any& operator=(const T& value) { typedef typename type_if< @@ -27,20 +31,37 @@ struct any { return *this; } + any& operator=(const any& source) { + if(container) { delete container; container = nullptr; } + if(source.container) container = source.container->copy(); + return *this; + } + + any& operator=(any&& source) { + if(container) delete container; + container = source.container; + source.container = nullptr; + return *this; + } + any() = default; + any(const any& source) { operator=(source); } + any(any&& source) { operator=(std::move(source)); } template any(const T& value) { operator=(value); } - ~any() { if(container) delete container; } + ~any() { reset(); } private: struct placeholder { virtual const std::type_info& type() const = 0; + virtual placeholder* copy() const = 0; + virtual ~placeholder() {} }; - placeholder* container = nullptr; template struct holder : placeholder { T value; const std::type_info& type() const { return typeid(T); } + placeholder* copy() const { return new holder(value); } holder(const T& value) : value(value) {} }; diff --git a/higan/nall/atoi.hpp b/higan/nall/atoi.hpp index 63395b8e..37998627 100755 --- a/higan/nall/atoi.hpp +++ b/higan/nall/atoi.hpp @@ -48,6 +48,8 @@ constexpr inline uintmax_t binary(const char* s) { constexpr inline uintmax_t octal(const char* s) { return ( + *s == '0' && *(s + 1) == 'O' ? octal_(s + 2) : + *s == '0' && *(s + 1) == 'o' ? octal_(s + 2) : octal_(s) ); } @@ -88,7 +90,7 @@ constexpr inline intmax_t numeral(const char* s) { ); } -inline double fp(const char* s) { +inline double real(const char* s) { return atof(s); } diff --git a/higan/nall/beat/archive.hpp b/higan/nall/beat/archive.hpp index d0b3ad4f..0205f352 100644 --- a/higan/nall/beat/archive.hpp +++ b/higan/nall/beat/archive.hpp @@ -57,7 +57,7 @@ struct beatArchive : beatBase { while(fp.offset() < fp.size() - 4) { unsigned data = readNumber(); string name = readString((data >> 1) + 1); - if(name.position("\\") || name.position("../")) return false; //block path exploits + if(name.find("\\") || name.find("../")) return false; //block path exploits if((data & 1) == 0) { directory::create({pathname, name}); diff --git a/higan/nall/config.hpp b/higan/nall/config.hpp index dc579181..ce4b812c 100755 --- a/higan/nall/config.hpp +++ b/higan/nall/config.hpp @@ -35,7 +35,7 @@ struct Node { case Type::Bool: *(bool*)data = (value == "true"); break; case Type::Signed: *(signed*)data = integer(value); break; case Type::Unsigned: *(unsigned*)data = decimal(value); break; - case Type::Double: *(double*)data = fp(value); break; + case Type::Double: *(double*)data = real(value); break; case Type::String: *(string*)data = value; break; } } diff --git a/higan/nall/directory.hpp b/higan/nall/directory.hpp index a6db78fd..b0c36f6d 100755 --- a/higan/nall/directory.hpp +++ b/higan/nall/directory.hpp @@ -112,14 +112,14 @@ private: if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { string name = (const char*)utf8_t(data.cFileName); - if(wildcard(name, pattern)) list.append(name); + if(name.match(pattern)) list.append(name); } } while(FindNextFile(handle, &data) != false) { if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { string name = (const char*)utf8_t(data.cFileName); - if(wildcard(name, pattern)) list.append(name); + if(name.match(pattern)) list.append(name); } } } @@ -141,12 +141,12 @@ private: if(handle != INVALID_HANDLE_VALUE) { if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { string name = (const char*)utf8_t(data.cFileName); - if(wildcard(name, pattern)) list.append(name); + if(name.match(pattern)) list.append(name); } while(FindNextFile(handle, &data) != false) { if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { string name = (const char*)utf8_t(data.cFileName); - if(wildcard(name, pattern)) list.append(name); + if(name.match(pattern)) list.append(name); } } FindClose(handle); @@ -190,8 +190,14 @@ private: while(ep = readdir(dp)) { if(!strcmp(ep->d_name, ".")) continue; if(!strcmp(ep->d_name, "..")) continue; - if(ep->d_type & DT_DIR) { - if(wildcard(ep->d_name, pattern)) list.append(ep->d_name); + bool is_directory = ep->d_type & DT_DIR; + if(ep->d_type & DT_UNKNOWN) { + struct stat sp = {0}; + stat(string{pathname, ep->d_name}, &sp); + is_directory = S_ISDIR(sp.st_mode); + } + if(is_directory) { + if(strmatch(ep->d_name, pattern)) list.append(ep->d_name); } } closedir(dp); @@ -210,7 +216,7 @@ private: if(!strcmp(ep->d_name, ".")) continue; if(!strcmp(ep->d_name, "..")) continue; if((ep->d_type & DT_DIR) == 0) { - if(wildcard(ep->d_name, pattern)) list.append(ep->d_name); + if(strmatch(ep->d_name, pattern)) list.append(ep->d_name); } } closedir(dp); diff --git a/higan/nall/hashset.hpp b/higan/nall/hashset.hpp new file mode 100644 index 00000000..3f51199c --- /dev/null +++ b/higan/nall/hashset.hpp @@ -0,0 +1,137 @@ +#ifndef NALL_HASHSET_HPP +#define NALL_HASHSET_HPP + +//hashset +// +//search: O(1) average; O(n) worst +//insert: O(1) average; O(n) worst +//remove: O(1) average; O(n) worst +// +//requirements: +// unsigned T::hash() const; +// bool T::operator==(const T&) const; + +namespace nall { + +template +struct hashset { +protected: + T** pool = nullptr; + unsigned length = 8; //length of pool + unsigned count = 0; //number of objects inside of the pool + +public: + hashset() {} + hashset(unsigned length) : length(bit::round(length)) {} + hashset(const hashset& source) { operator=(source); } + hashset(hashset&& source) { operator=(std::move(source)); } + ~hashset() { reset(); } + + hashset& operator=(const hashset& source) { + reset(); + if(source.pool) { + for(unsigned n = 0; n < source.count; n++) { + insert(*source.pool[n]); + } + } + return *this; + } + + hashset& operator=(hashset&& source) { + reset(); + pool = source.pool; + length = source.length; + count = source.count; + source.pool = nullptr; + source.length = 8; + source.count = 0; + return *this; + } + + unsigned capacity() const { return length; } + unsigned size() const { return count; } + bool empty() const { return count == 0; } + + void reset() { + if(pool) { + for(unsigned n = 0; n < length; n++) { + if(pool[n]) { + delete pool[n]; + pool[n] = nullptr; + } + } + delete pool; + pool = nullptr; + } + length = 8; + count = 0; + } + + void reserve(unsigned size) { + //ensure all items will fit into pool (with <= 50% load) and amortize growth + size = bit::round(max(size, count << 1)); + T** copy = new T*[size](); + + if(pool) { + for(unsigned n = 0; n < length; n++) { + if(pool[n]) { + unsigned hash = (*pool[n]).hash() & (size - 1); + while(copy[hash]) if(++hash >= size) hash = 0; + copy[hash] = pool[n]; + pool[n] = nullptr; + } + } + } + + delete pool; + pool = copy; + length = size; + } + + optional find(const T& value) { + if(!pool) return false; + + unsigned hash = value.hash() & (length - 1); + while(pool[hash]) { + if(value == *pool[hash]) return {true, *pool[hash]}; + if(++hash >= length) hash = 0; + } + + return false; + } + + optional insert(const T& value) { + if(!pool) pool = new T*[length](); + + //double pool size when load is >= 50% + if(count >= (length >> 1)) reserve(length << 1); + count++; + + unsigned hash = value.hash() & (length - 1); + while(pool[hash]) if(++hash >= length) hash = 0; + pool[hash] = new T(value); + + return {true, *pool[hash]}; + } + + bool remove(const T& value) { + if(!pool) return false; + + unsigned hash = value.hash() & (length - 1); + while(pool[hash]) { + if(value == *pool[hash]) { + delete pool[hash]; + pool[hash] = nullptr; + count--; + return true; + } + if(++hash >= length) hash = 0; + } + + return false; + } +}; + +} + +#endif diff --git a/higan/nall/http.hpp b/higan/nall/http.hpp index cf294486..5fc4875f 100755 --- a/higan/nall/http.hpp +++ b/higan/nall/http.hpp @@ -100,7 +100,7 @@ struct http { inline void downloadContent(uint8_t*& data, unsigned& size) { unsigned capacity = 0; - if(header.iposition("\r\nTransfer-Encoding: chunked\r\n")) { + if(header.ifind("\r\nTransfer-Encoding: chunked\r\n")) { while(true) { unsigned length = hex(downloadChunkLength()); if(length == 0) break; @@ -116,7 +116,7 @@ struct http { length -= packetlength; } } - } else if(auto position = header.iposition("\r\nContent-Length: ")) { + } else if(auto position = header.ifind("\r\nContent-Length: ")) { unsigned length = decimal((const char*)header + position() + 18); while(length) { char buffer[256]; diff --git a/higan/nall/invoke.hpp b/higan/nall/invoke.hpp index ed77fc38..74e7d8b2 100755 --- a/higan/nall/invoke.hpp +++ b/higan/nall/invoke.hpp @@ -22,7 +22,7 @@ namespace nall { template inline void invoke(const string& name, Args&&... args) { lstring argl(std::forward(args)...); - for(auto& arg : argl) if(arg.position(" ")) arg = {"\"", arg, "\""}; + for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""}; string arguments = argl.concatenate(" "); ShellExecuteW(NULL, NULL, utf16_t(name), utf16_t(arguments), NULL, SW_SHOWNORMAL); } diff --git a/higan/nall/mosaic/context.hpp b/higan/nall/mosaic/context.hpp index a0fe91d5..b48f343f 100755 --- a/higan/nall/mosaic/context.hpp +++ b/higan/nall/mosaic/context.hpp @@ -47,9 +47,8 @@ struct context { } unsigned eval(const string& expression) { - intmax_t result; - if(fixedpoint::eval(expression, result) == false) return 0u; - return result; + if(auto result = Eval::integer(expression)) return result(); + return 0u; } void eval(vector& buffer, const string& expression_) { @@ -64,7 +63,7 @@ struct context { lstring list = expression.split(","); for(auto& item : list) { item.trim(); - if(item.wildcard("f(?*) ?*")) { + if(item.match("f(?*) ?*")) { item.ltrim<1>("f("); lstring part = item.split<1>(") "); lstring args = part[0].split<3>(";"); @@ -85,10 +84,10 @@ struct context { buffer[offset] = eval(fn); offset += stride; } - } else if(item.wildcard("base64*")) { + } else if(item.match("base64*")) { unsigned offset = 0; item.ltrim<1>("base64"); - if(item.wildcard("(?*) *")) { + if(item.match("(?*) *")) { item.ltrim<1>("("); lstring part = item.split<1>(") "); offset = eval(part[0]); @@ -102,7 +101,7 @@ struct context { if(c == '-') buffer.append(offset + 62); if(c == '_') buffer.append(offset + 63); } - } else if(item.wildcard("file *")) { + } else if(item.match("file *")) { item.ltrim<1>("file "); item.trim(); //... @@ -159,8 +158,8 @@ struct context { } bool load(const string& filename) { - string filedata; - if(filedata.readfile(filename) == false) return false; + string filedata = string::read(filename); + if(filedata.empty()) return false; parse(filedata); return true; } diff --git a/higan/nall/nall.hpp b/higan/nall/nall.hpp index ad571038..3895c916 100755 --- a/higan/nall/nall.hpp +++ b/higan/nall/nall.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/higan/nall/platform.hpp b/higan/nall/platform.hpp index 3326f4c4..180a07af 100755 --- a/higan/nall/platform.hpp +++ b/higan/nall/platform.hpp @@ -13,6 +13,7 @@ //========================= #include +#include #include #include diff --git a/higan/nall/serializer.hpp b/higan/nall/serializer.hpp index 0e5f95e8..5b2c8ef6 100755 --- a/higan/nall/serializer.hpp +++ b/higan/nall/serializer.hpp @@ -10,7 +10,7 @@ // //caveats: //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); -//- floating-point usage is not portable across platforms +//- floating-point usage is not portable across different implementations #include #include @@ -19,122 +19,130 @@ namespace nall { +struct serializer; + +template +struct has_serialize { + template static char test(decltype(std::declval().serialize(std::declval()))*); + template static long test(...); + static const bool value = sizeof(test(0)) == sizeof(char); +}; + struct serializer { enum mode_t { Load, Save, Size }; mode_t mode() const { - return imode; + return _mode; } const uint8_t* data() const { - return idata; + return _data; } unsigned size() const { - return isize; + return _size; } unsigned capacity() const { - return icapacity; + return _capacity; } - template void floatingpoint(T& value) { + template serializer& floatingpoint(T& value) { enum { size = sizeof(T) }; //this is rather dangerous, and not cross-platform safe; //but there is no standardized way to export FP-values uint8_t* p = (uint8_t*)&value; - if(imode == Save) { - for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; - } else if(imode == Load) { - for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + if(_mode == Save) { + for(unsigned n = 0; n < size; n++) _data[_size++] = p[n]; + } else if(_mode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = _data[_size++]; } else { - isize += size; + _size += size; } + return *this; } - template void integer(T& value) { + template serializer& integer(T& value) { enum { size = std::is_same::value ? 1 : sizeof(T) }; - if(imode == Save) { - for(unsigned n = 0; n < size; n++) idata[isize++] = (uintmax_t)value >> (n << 3); - } else if(imode == Load) { + if(_mode == Save) { + for(unsigned n = 0; n < size; n++) _data[_size++] = (uintmax_t)value >> (n << 3); + } else if(_mode == Load) { value = 0; - for(unsigned n = 0; n < size; n++) value |= (uintmax_t)idata[isize++] << (n << 3); - } else if(imode == Size) { - isize += size; + for(unsigned n = 0; n < size; n++) value |= (uintmax_t)_data[_size++] << (n << 3); + } else if(_mode == Size) { + _size += size; } + return *this; } - template void array(T& array) { - enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; - for(unsigned n = 0; n < size; n++) integer(array[n]); + template serializer& array(T (&array)[N]) { + for(unsigned n = 0; n < N; n++) operator()(array[n]); + return *this; } - template void array(T array, unsigned size) { - for(unsigned n = 0; n < size; n++) integer(array[n]); + template serializer& array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) operator()(array[n]); + return *this; } - //copy + template serializer& operator()(T& value, typename std::enable_if::value>::type* = 0) { value.serialize(*this); return *this; } + template serializer& operator()(T& value, typename std::enable_if::value>::type* = 0) { return integer(value); } + template serializer& operator()(T& value, typename std::enable_if::value>::type* = 0) { return floatingpoint(value); } + template serializer& operator()(T& value, typename std::enable_if::value>::type* = 0) { return array(value); } + template serializer& operator()(T& value, unsigned size, typename std::enable_if::value>::type* = 0) { return array(value, size); } + serializer& operator=(const serializer& s) { - if(idata) delete[] idata; + if(_data) delete[] _data; - imode = s.imode; - idata = new uint8_t[s.icapacity]; - isize = s.isize; - icapacity = s.icapacity; + _mode = s._mode; + _data = new uint8_t[s._capacity]; + _size = s._size; + _capacity = s._capacity; - memcpy(idata, s.idata, s.icapacity); + memcpy(_data, s._data, s._capacity); return *this; } - serializer(const serializer& s) { - operator=(s); - } - - //move serializer& operator=(serializer&& s) { - if(idata) delete[] idata; + if(_data) delete[] _data; - imode = s.imode; - idata = s.idata; - isize = s.isize; - icapacity = s.icapacity; + _mode = s._mode; + _data = s._data; + _size = s._size; + _capacity = s._capacity; - s.idata = nullptr; + s._data = nullptr; return *this; } - serializer(serializer&& s) { - operator=(std::move(s)); - } - - //construction serializer() = default; + serializer(const serializer& s) { operator=(s); } + serializer(serializer&& s) { operator=(std::move(s)); } serializer(unsigned capacity) { - imode = Save; - idata = new uint8_t[capacity](); - isize = 0; - icapacity = capacity; + _mode = Save; + _data = new uint8_t[capacity](); + _size = 0; + _capacity = capacity; } serializer(const uint8_t* data, unsigned capacity) { - imode = Load; - idata = new uint8_t[capacity]; - isize = 0; - icapacity = capacity; - memcpy(idata, data, capacity); + _mode = Load; + _data = new uint8_t[capacity]; + _size = 0; + _capacity = capacity; + memcpy(_data, data, capacity); } - //destruction ~serializer() { - if(idata) delete[] idata; + if(_data) delete[] _data; } private: - mode_t imode = Size; - uint8_t* idata = nullptr; - unsigned isize = 0; - unsigned icapacity = 0; + mode_t _mode = Size; + uint8_t* _data = nullptr; + unsigned _size = 0; + unsigned _capacity = 0; }; }; diff --git a/higan/nall/set.hpp b/higan/nall/set.hpp index 9215e840..c8003ec0 100644 --- a/higan/nall/set.hpp +++ b/higan/nall/set.hpp @@ -3,9 +3,14 @@ //set //implementation: red-black tree +// //search: O(log n) average; O(log n) worst //insert: O(log n) average; O(log n) worst //remove: O(log n) average; O(log n) worst +// +//requirements: +// bool T::operator==(const T&) const; +// bool T::operator< (const T&) const; #include #include @@ -50,16 +55,18 @@ template struct set { return false; } - bool insert(const T& value) { + optional insert(const T& value) { unsigned count = size(); - insert(root, value); + node_t* v = insert(root, value); root->red = 0; - return size() > count; + if(size() == count) return false; + return {true, v->value}; } template bool insert(const T& value, Args&&... args) { bool result = insert(value); - return insert(std::forward(args)...) | result; + insert(std::forward(args)...) | result; + return result; } bool remove(const T& value) { @@ -175,13 +182,13 @@ private: rotate(node, dir); } - void insert(node_t*& node, const T& value) { - if(!node) { nodes++; node = new node_t(value); return; } - if(node->value == value) { node->value = value; return; } //prevent duplicate entries + node_t* insert(node_t*& node, const T& value) { + if(!node) { nodes++; node = new node_t(value); return node; } + if(node->value == value) { node->value = value; return node; } //prevent duplicate entries bool dir = node->value < value; - insert(node->link[dir], value); - if(black(node->link[dir])) return; + node_t* v = insert(node->link[dir], value); + if(black(node->link[dir])) return v; if(red(node->link[!dir])) { node->red = 1; @@ -192,6 +199,8 @@ private: } else if(red(node->link[dir]->link[!dir])) { rotateTwice(node, !dir); } + + return v; } void balance(node_t*& node, bool dir, bool& done) { diff --git a/higan/nall/stream/zip.hpp b/higan/nall/stream/zip.hpp index 39175084..dfb4767b 100755 --- a/higan/nall/stream/zip.hpp +++ b/higan/nall/stream/zip.hpp @@ -19,7 +19,7 @@ struct zipstream : memorystream { delete[] data; for(auto& file : archive.file) { - if(file.name.wildcard(filter)) { + if(file.name.match(filter)) { auto buffer = archive.extract(file); psize = buffer.size(); pdata = buffer.move(); diff --git a/higan/nall/string.hpp b/higan/nall/string.hpp index e4dd1d0d..adf86b31 100755 --- a/higan/nall/string.hpp +++ b/higan/nall/string.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include diff --git a/higan/nall/string/allocator/copy-on-write.hpp b/higan/nall/string/allocator/copy-on-write.hpp new file mode 100644 index 00000000..4b20b669 --- /dev/null +++ b/higan/nall/string/allocator/copy-on-write.hpp @@ -0,0 +1,98 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +/* +copy on write (COW) allocator +sizeof(string) == 24 (amd64) + +utilizes a shared_ptr to reference count strings +allows string copies to execute as fast as string moves +requires extra computations, which will be slower for all string sizes + +pros: +* lower memory usage +* pass-by-value does not require heap allocation; obviates pass-by-const-reference + +cons: +* added overhead to fetch data() +* added heap allocation for reference-count pool +* no potential for in-place resize (always copies) +* larger sizeof(string) + +*/ + +namespace nall { + +char* string::data() { + if(!_data.unique()) _copy(); + return _data.get(); +} + +const char* string::data() const { + if(!_data) return ""; + return _data.get(); +} + +//copy _data (to make unique or to grow in size) +void string::_copy() { + auto copy = new char[_capacity + 1]; + if(_data.get()) memcpy(copy, _data.get(), min(_capacity, _size)); + copy[_size] = 0; + copy[_capacity] = 0; + _data.reset(copy); +} + +//amortize growth to O(log n) +//allocate one extra byte to always store null-terminator for libc usage +void string::reserve(unsigned capacity) { + if(capacity > _capacity) { + _capacity = bit::round(capacity + 1) - 1; + _copy(); + } +} + +void string::resize(unsigned size) { + reserve(size); + data()[_size = size] = 0; +} + +void string::reset() { + _data.reset(); + _capacity = 0; + _size = 0; +} + +string& string::operator=(const string& source) { + if(&source == this) return *this; + _data = source._data; + _capacity = source._capacity; + _size = source._size; + return *this; +} + +string& string::operator=(string&& source) { + if(&source == this) return *this; + _data = std::move(source._data); + _capacity = source._capacity; + _size = source._size; + source._capacity = 0; + source._size = 0; + return *this; +} + +template string::string(T&& source, Args&&... args) { + _capacity = 0; + _size = 0; + sprint(*this, std::forward(source), std::forward(args)...); +} + +string::string() { + _capacity = 0; + _size = 0; +} + +string::~string() { +} + +} + +#endif diff --git a/higan/nall/string/allocator/small-string-optimization.hpp b/higan/nall/string/allocator/small-string-optimization.hpp new file mode 100644 index 00000000..a5a9cf7c --- /dev/null +++ b/higan/nall/string/allocator/small-string-optimization.hpp @@ -0,0 +1,107 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +/* +small string optimization (SSO) allocator +sizeof(string) == 16 (amd64) + +utilizes a union to store small strings directly into text pointer +bypasses the need to allocate heap memory for small strings +requires extra computations, which can be slower for large strings + +pros: +* potential for in-place resize +* no heap allocation when (capacity < 8) + +cons: +* added overhead to fetch data() +* 32-bit platforms limited to (capacity < 4) +* pass-by-value requires heap allocation + +*/ + +namespace nall { + +char* string::data() { + if(_capacity < SSO) return _text; + return _data; +} + +const char* string::data() const { + if(_capacity < SSO) return _text; + return _data; +} + +void string::reserve(unsigned capacity) { + if(capacity > _capacity) { + if(capacity >= SSO) { + capacity = bit::round(capacity + 1) - 1; + if(_capacity < SSO) { + char temp[SSO]; + memcpy(temp, _text, SSO); + _data = (char*)malloc(capacity + 1); + memcpy(_data, temp, SSO); + } else { + _data = (char*)realloc(_data, capacity + 1); + } + } + _capacity = capacity; + data()[_capacity] = 0; + } +} + +void string::resize(unsigned size) { + reserve(size); + data()[_size = size] = 0; +} + +void string::reset() { + if(_capacity >= SSO) free(_data); + _data = nullptr; + _capacity = SSO - 1; + _size = 0; +} + +string& string::operator=(const string& source) { + if(&source == this) return *this; + if(source._capacity >= SSO) { + _data = (char*)malloc(source._capacity + 1); + _capacity = source._capacity; + _size = source._size; + memcpy(_data, source.data(), source.size() + 1); + } else { + memcpy(_text, source._text, SSO); + _capacity = SSO - 1; + _size = strlen(_text); + } + return *this; +} + +string& string::operator=(string&& source) { + if(&source == this) return *this; + memcpy(this, &source, sizeof(string)); + source._data = nullptr; + source._capacity = SSO - 1; + source._size = 0; + return *this; +} + +template string::string(T&& source, Args&&... args) { + _data = nullptr; + _capacity = SSO - 1; + _size = 0; + sprint(*this, std::forward(source), std::forward(args)...); +} + +string::string() { + _data = nullptr; + _capacity = SSO - 1; + _size = 0; +} + +string::~string() { + if(_capacity >= SSO) free(_data); +} + +} + +#endif diff --git a/higan/nall/string/allocator/vector.hpp b/higan/nall/string/allocator/vector.hpp new file mode 100644 index 00000000..c8252b50 --- /dev/null +++ b/higan/nall/string/allocator/vector.hpp @@ -0,0 +1,90 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +/* +vector allocator +sizeof(string) == 16 (amd64) + +utilizes a raw string pointer +always allocates memory onto the heap when string is not empty + +pros: +* potential for in-place resize +* simplicity + +cons: +* always allocates heap memory on (capacity > 0) +* pass-by-value requires heap allocation + +*/ + +namespace nall { + +char* string::data() { + if(_capacity == 0) reserve(1); + return _data; +} + +const char* string::data() const { + if(_capacity == 0) return ""; + return _data; +} + +void string::reserve(unsigned capacity) { + if(capacity > _capacity) { + _capacity = bit::round(capacity + 1) - 1; + _data = (char*)realloc(_data, _capacity + 1); + _data[_capacity] = 0; + } +} + +void string::resize(unsigned size) { + reserve(size); + data()[_size = size] = 0; +} + +void string::reset() { + if(_data) { free(_data); _data = nullptr; } + _capacity = 0; + _size = 0; +} + +string& string::operator=(const string& source) { + if(&source == this) return *this; + _data = (char*)malloc(source._size + 1); + _capacity = source._size; + _size = source._size; + memcpy(_data, source.data(), source.size() + 1); + return *this; +} + +string& string::operator=(string&& source) { + if(&source == this) return *this; + _data = source._data; + _capacity = source._capacity; + _size = source._size; + source._data = nullptr; + source._capacity = 0; + source._size = 0; + return *this; +} + +template string::string(T&& source, Args&&... args) { + _data = nullptr; + _capacity = 0; + _size = 0; + sprint(*this, std::forward(source), std::forward(args)...); +} + +string::string() { + _data = nullptr; + _capacity = 0; + _size = 0; +} + +string::~string() { + if(_data) free(_data); +} + +} + +#endif diff --git a/higan/nall/string/base.hpp b/higan/nall/string/base.hpp index 0085f7cc..322a5d09 100755 --- a/higan/nall/string/base.hpp +++ b/higan/nall/string/base.hpp @@ -7,9 +7,29 @@ struct stringref; struct lstring; typedef const stringref& rstring; +//#define NALL_STRING_ALLOCATOR_COPY_ON_WRITE +#define NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION +//#define NALL_STRING_ALLOCATOR_VECTOR + struct string { protected: + #if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE) + inline void _copy(); std::shared_ptr _data; + #endif + + #if defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION) + enum : unsigned { SSO = 24 }; + union { + char* _data; + char _text[SSO]; + }; + #endif + + #if defined(NALL_STRING_ALLOCATOR_VECTOR) + char* _data; + #endif + unsigned _capacity; unsigned _size; @@ -27,12 +47,13 @@ public: inline void resize(unsigned); inline void clear(char); + inline unsigned hash() const; + template inline string& assign(Args&&... args); template inline string& append(Args&&... args); //file.hpp - inline static string read(rstring filename); - inline bool readfile(rstring); + inline static string read(const string& filename); //datetime.hpp inline static string date(); @@ -51,17 +72,22 @@ public: template inline lstring qsplit(rstring) const; template inline lstring iqsplit(rstring) const; + inline signed compare(rstring) const; + inline signed icompare(rstring) const; + inline bool equals(rstring) const; inline bool iequals(rstring) const; - inline bool wildcard(rstring) const; - inline bool iwildcard(rstring) const; + inline bool match(rstring) const; + inline bool imatch(rstring) const; inline bool beginswith(rstring) const; inline bool ibeginswith(rstring) const; inline bool endswith(rstring) const; inline bool iendswith(rstring) const; + inline string slice(unsigned offset, unsigned length = ~0u) const; + inline string& lower(); inline string& upper(); inline string& qlower(); @@ -69,21 +95,28 @@ public: inline string& transform(rstring before, rstring after); inline string& reverse(); - template inline string& ltrim(rstring key = " "); - template inline string& rtrim(rstring key = " "); - template inline string& trim(rstring key = " ", rstring rkey = ""); + template inline string& ltrim() { return ltrim(" "); } + template inline string& ltrim(rstring key); + + template inline string& rtrim() { return rtrim(" "); } + template inline string& rtrim(rstring key); + + template inline string& trim() { return trim(" "); } + template inline string& trim(rstring key); + template inline string& trim(rstring key, rstring rkey); + inline string& strip(); - inline optional position(rstring key) const; - inline optional iposition(rstring key) const; - inline optional qposition(rstring key) const; - inline optional iqposition(rstring key) const; + inline optional find(rstring key) const; + inline optional ifind(rstring key) const; + inline optional qfind(rstring key) const; + inline optional iqfind(rstring key) const; //core.hpp inline explicit operator bool() const; inline operator const char*() const; - inline char& operator[](unsigned); - inline const char& operator[](unsigned) const; + inline char& operator[](signed); + inline const char& operator[](signed) const; inline bool operator==(const char*) const; inline bool operator!=(const char*) const; @@ -109,8 +142,6 @@ public: //protected: struct exception_out_of_bounds{}; template inline string& ureplace(rstring, rstring); - inline void _unique(); - inline void _copy(); inline string& _append(const char*); #if defined(QSTRING_H) @@ -179,8 +210,8 @@ inline bool tokenize(lstring& list, const char* s, const char* p); inline char* integer(char* result, intmax_t value); inline char* decimal(char* result, uintmax_t value); -inline unsigned fp(char* str, long double value); -inline string fp(long double value); +inline unsigned real(char* str, long double value); +inline string real(long double value); //variadic.hpp inline void sprint(string& output); diff --git a/higan/nall/string/cast.hpp b/higan/nall/string/cast.hpp index c5a60993..48405970 100755 --- a/higan/nall/string/cast.hpp +++ b/higan/nall/string/cast.hpp @@ -101,19 +101,19 @@ template struct stringify> { template<> struct stringify { char data[256]; operator const char*() const { return data; } - stringify(float value) { fp(data, value); } + stringify(float value) { real(data, value); } }; template<> struct stringify { char data[256]; operator const char*() const { return data; } - stringify(double value) { fp(data, value); } + stringify(double value) { real(data, value); } }; template<> struct stringify { char data[256]; operator const char*() const { return data; } - stringify(long double value) { fp(data, value); } + stringify(long double value) { real(data, value); } }; // arrays diff --git a/higan/nall/string/char.hpp b/higan/nall/string/char.hpp index 3cfdddea..29ac2c77 100644 --- a/higan/nall/string/char.hpp +++ b/higan/nall/string/char.hpp @@ -3,13 +3,11 @@ #include #include #include -#include -#include +#include #include #include #include #include #include -#include #endif diff --git a/higan/nall/string/char/base.hpp b/higan/nall/string/char/base.hpp index cb52d775..1a053c05 100644 --- a/higan/nall/string/char/base.hpp +++ b/higan/nall/string/char/base.hpp @@ -23,21 +23,10 @@ inline char* qstrlower(char* str); inline char* qstrupper(char* str); inline char* strtr(char* dest, const char* before, const char* after); -//math-fixed-point.hpp -namespace fixedpoint { -inline intmax_t eval_integer(const char*& s); -inline intmax_t eval(const char*& s, int depth = 0); -inline bool eval(const char* s, intmax_t& result); -inline intmax_t parse(const char* s); -} - -//math-floating-point.hpp -namespace floatingpoint { -inline double eval_integer(const char*& s); -inline double eval(const char*& s, int depth = 0); -inline bool eval(const char* s, double& result); -inline double parse(const char* s); -} +//match.hpp +inline bool strmatch(const char* str, const char* pattern); +inline bool istrmatch(const char* str, const char* pattern); +inline bool tokenize(const char* s, const char* p); //strm.hpp inline unsigned strmcpy(char* target, const char* source, unsigned length); @@ -56,7 +45,8 @@ template inline optional inline char* ltrim(char* str, const char* key = " "); template inline char* rtrim(char* str, const char* key = " "); -template inline char* trim(char* str, const char* key = " ", const char* rkey = nullptr); +template inline char* trim(char* str, const char* key = " "); +template inline char* trim(char* str, const char* lkey, const char* rkey); inline char* strip(char* s); //utf8.hpp @@ -73,11 +63,6 @@ template alwaysinline bool chrequal(char x, char y); template alwaysinline bool quoteskip(T*& p); template alwaysinline bool quotecopy(char*& t, T*& p); -//wildcard.hpp -inline bool wildcard(const char* str, const char* pattern); -inline bool iwildcard(const char* str, const char* pattern); -inline bool tokenize(const char* s, const char* p); - } #endif diff --git a/higan/nall/string/char/wildcard.hpp b/higan/nall/string/char/match.hpp similarity index 92% rename from higan/nall/string/char/wildcard.hpp rename to higan/nall/string/char/match.hpp index e1b644aa..2471d1ce 100644 --- a/higan/nall/string/char/wildcard.hpp +++ b/higan/nall/string/char/match.hpp @@ -2,7 +2,7 @@ namespace nall { -bool wildcard(const char* s, const char* p) { +bool strmatch(const char* s, const char* p) { const char* cp = nullptr; const char* mp = nullptr; while(*s && *p != '*') { @@ -23,7 +23,7 @@ bool wildcard(const char* s, const char* p) { return !*p; } -bool iwildcard(const char* s, const char* p) { +bool istrmatch(const char* s, const char* p) { const char* cp = nullptr; const char* mp = nullptr; while(*s && *p != '*') { diff --git a/higan/nall/string/char/math-fixed-point.hpp b/higan/nall/string/char/math-fixed-point.hpp deleted file mode 100644 index 839c5370..00000000 --- a/higan/nall/string/char/math-fixed-point.hpp +++ /dev/null @@ -1,168 +0,0 @@ -#ifdef NALL_STRING_INTERNAL_HPP - -namespace nall { -namespace fixedpoint { - -static nall::function eval_fallback; - -intmax_t eval_integer(const char*& s) { - if(!*s) throw "unrecognized integer"; - intmax_t value = 0, x = *s, y = *(s + 1); - - //hexadecimal - if(x == '0' && (y == 'X' || y == 'x')) { - s += 2; - while(true) { - if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } - if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } - if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } - return value; - } - } - - //binary - if(x == '0' && (y == 'B' || y == 'b')) { - s += 2; - while(true) { - if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } - return value; - } - } - - //octal (or decimal '0') - if(x == '0') { - s += 1; - while(true) { - if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } - return value; - } - } - - //decimal - if(x >= '0' && x <= '9') { - while(true) { - if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } - return value; - } - } - - //char - if(x == '\'' && y != '\'') { - s += 1; - while(true) { - value = value * 256 + *s++; - if(*s == '\'') { s += 1; return value; } - if(!*s) throw "mismatched char"; - } - } - - throw "unrecognized integer"; -} - -intmax_t eval(const char*& s, int depth) { - while(*s == ' ' || *s == '\t') s++; //trim whitespace - if(!*s) throw "unrecognized token"; - intmax_t value = 0, x = *s, y = *(s + 1); - - if(*s == '(') { - value = eval(++s, 1); - if(*s++ != ')') throw "mismatched group"; - } - - else if(x == '!') value = !eval(++s, 13); - else if(x == '~') value = ~eval(++s, 13); - else if(x == '+') value = +eval(++s, 13); - else if(x == '-') value = -eval(++s, 13); - - else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); - - else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing - - else throw "unrecognized token"; - - while(true) { - while(*s == ' ' || *s == '\t') s++; //trim whitespace - if(!*s) break; - x = *s, y = *(s + 1); - - if(depth >= 13) break; - if(x == '*') { value *= eval(++s, 13); continue; } - if(x == '/') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value /= result; continue; } - if(x == '%') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value %= result; continue; } - - if(depth >= 12) break; - if(x == '+') { value += eval(++s, 12); continue; } - if(x == '-') { value -= eval(++s, 12); continue; } - - if(depth >= 11) break; - if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } - if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } - - if(depth >= 10) break; - if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } - if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } - if(x == '<') { value = value < eval(++s, 10); continue; } - if(x == '>') { value = value > eval(++s, 10); continue; } - - if(depth >= 9) break; - if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } - if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } - - if(depth >= 8) break; - if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } - - if(depth >= 7) break; - if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } - - if(depth >= 6) break; - if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } - - if(depth >= 5) break; - if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } - - if(depth >= 4) break; - if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } - - if(depth >= 3) break; - if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } - - if(x == '?') { - intmax_t lhs = eval(++s, 2); - if(*s != ':') throw "mismatched ternary"; - intmax_t rhs = eval(++s, 2); - value = value ? lhs : rhs; - continue; - } - if(depth >= 2) break; - - if(depth > 0 && x == ')') break; - - throw "unrecognized token"; - } - - return value; -} - -bool eval(const char* s, intmax_t& result) { - try { - result = eval(s); - return true; - } catch(const char*) { - result = 0; - return false; - } -} - -intmax_t parse(const char* s) { - try { - intmax_t result = eval(s); - return result; - } catch(const char*) { - return 0; - } -} - -} -} - -#endif diff --git a/higan/nall/string/char/math-floating-point.hpp b/higan/nall/string/char/math-floating-point.hpp deleted file mode 100644 index 66b65dd2..00000000 --- a/higan/nall/string/char/math-floating-point.hpp +++ /dev/null @@ -1,160 +0,0 @@ -#ifdef NALL_STRING_INTERNAL_HPP - -namespace nall { -namespace floatingpoint { - -static nall::function eval_fallback; - -double eval_integer(const char*& s) { - if(!*s) throw "unrecognized integer"; - intmax_t value = 0, radix = 0, x = *s, y = *(s + 1); - - //hexadecimal - if(x == '0' && (y == 'X' || y == 'x')) { - s += 2; - while(true) { - if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } - if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } - if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } - return value; - } - } - - //binary - if(x == '0' && (y == 'B' || y == 'b')) { - s += 2; - while(true) { - if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } - return value; - } - } - - //octal (or decimal '0') - if(x == '0' && y != '.') { - s += 1; - while(true) { - if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } - return value; - } - } - - //decimal - if(x >= '0' && x <= '9') { - while(true) { - if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } - if(*s == '.') { s++; break; } - return value; - } - //floating-point - unsigned divisor = 1; - while(true) { - if(*s >= '0' && *s <= '9') { radix = radix * 10 + (*s++ - '0'); divisor *= 10; continue; } - return (double)value + (double)radix / (double)divisor; - } - } - - //char - if(x == '\'' && y != '\'') { - s += 1; - while(true) { - value = value * 256 + *s++; - if(*s == '\'') { s += 1; return value; } - if(!*s) throw "mismatched char"; - } - } - - throw "unrecognized integer"; -} - -double eval(const char*& s, int depth) { - while(*s == ' ' || *s == '\t') s++; //trim whitespace - if(!*s) throw "unrecognized token"; - double value = 0, x = *s, y = *(s + 1); - - if(*s == '(') { - value = eval(++s, 1); - if(*s++ != ')') throw "mismatched group"; - } - - else if(x == '!') value = !eval(++s, 9); - else if(x == '+') value = +eval(++s, 9); - else if(x == '-') value = -eval(++s, 9); - - else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); - - else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing - - else throw "unrecognized token"; - - while(true) { - while(*s == ' ' || *s == '\t') s++; //trim whitespace - if(!*s) break; - x = *s, y = *(s + 1); - - if(depth >= 9) break; - if(x == '*') { value *= eval(++s, 9); continue; } - if(x == '/') { double result = eval(++s, 9); if(result == 0.0) throw "division by zero"; value /= result; continue; } - - if(depth >= 8) break; - if(x == '+') { value += eval(++s, 8); continue; } - if(x == '-') { value -= eval(++s, 8); continue; } - - if(depth >= 7) break; - if(x == '<' && y == '=') { value = value <= eval(++++s, 7); continue; } - if(x == '>' && y == '=') { value = value >= eval(++++s, 7); continue; } - if(x == '<') { value = value < eval(++s, 7); continue; } - if(x == '>') { value = value > eval(++s, 7); continue; } - - if(depth >= 6) break; - if(x == '=' && y == '=') { value = value == eval(++++s, 6); continue; } - if(x == '!' && y == '=') { value = value != eval(++++s, 6); continue; } - - if(depth >= 5) break; - if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } - - if(depth >= 4) break; - if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } - - if(depth >= 3) break; - if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } - - if(x == '?') { - double lhs = eval(++s, 2); - if(*s != ':') throw "mismatched ternary"; - double rhs = eval(++s, 2); - value = value ? lhs : rhs; - continue; - } - if(depth >= 2) break; - - if(depth > 0 && x == ')') break; - - throw "unrecognized token"; - } - - return value; -} - -bool eval(const char* s, double& result) { - try { - result = eval(s); - return true; - } catch(const char*) { - result = 0; - return false; - } -} - -double parse(const char* s) { - try { - double result = eval(s); - return result; - } catch(const char*) { - return 0; - } -} - -} -} - -#endif diff --git a/higan/nall/string/char/trim.hpp b/higan/nall/string/char/trim.hpp index e9ca2c7d..969b6926 100644 --- a/higan/nall/string/char/trim.hpp +++ b/higan/nall/string/char/trim.hpp @@ -29,11 +29,14 @@ template char* rtrim(char* str, const char* key) { return str; } -template char* trim(char* str, const char* key, const char* rkey) { - if(rkey) return ltrim(rtrim(str, rkey), key); +template char* trim(char* str, const char* key) { return ltrim(rtrim(str, key), key); } +template char* trim(char* str, const char* lkey, const char* rkey) { + return ltrim(rtrim(str, rkey), lkey); +} + //remove whitespace characters from both left and right sides of string char* strip(char* s) { if(!s) return nullptr; diff --git a/higan/nall/string/core.hpp b/higan/nall/string/core.hpp index 82016aab..1830fae9 100755 --- a/higan/nall/string/core.hpp +++ b/higan/nall/string/core.hpp @@ -1,59 +1,36 @@ #ifdef NALL_STRING_INTERNAL_HPP -//core functionality -//only this header file may access _data, _size, _capacity directly -//all other headers must use data(), size(), capacity() +//only allocators may access _data or modify _size and _capacity +//all other functions must use data(), size(), capacity() + +#if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE) + #include +#elif defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION) + #include +#elif defined(NALL_STRING_ALLOCATOR_VECTOR) + #include +#endif namespace nall { -char* string::data() { _unique(); return _data.get(); } -const char* string::data() const { if(!_data) return ""; return _data.get(); } unsigned string::length() const { return strlen(data()); } unsigned string::size() const { return _size; } unsigned string::capacity() const { return _capacity; } -bool string::empty() const { return size() == 0; } - -//ensure _data is unique -void string::_unique() { - if(_data.unique()) return; - _copy(); -} - -//copy _data (to make unique or to grow in size) -void string::_copy() { - auto copy = new char[_capacity + 1]; - if(_data.get()) memcpy(copy, _data.get(), min(_capacity, _size)); - copy[_size] = 0; - copy[_capacity] = 0; - _data.reset(copy); -} - -//amortize growth to O(log n) -//allocate one extra byte to always store null-terminator for libc usage -void string::reserve(unsigned capacity) { - if(capacity > _capacity) { - _capacity = bit::round(capacity + 1) - 1; - _copy(); - } -} - -void string::resize(unsigned size) { - reserve(size); - data()[_size = size] = 0; -} - -void string::reset() { - _data.reset(); - _capacity = 0; - _size = 0; -} +bool string::empty() const { return _size == 0; } void string::clear(char c) { for(unsigned n = 0; n < size(); n++) data()[n] = c; } +unsigned string::hash() const { + const char* p = data(); + unsigned result = 5381; + while(*p) result = (result << 5) + result + *p++; + return result; +} + template string& string::assign(Args&&... args) { - reset(); + resize(0); sprint(*this, std::forward(args)...); return *this; } @@ -80,12 +57,12 @@ string::operator const char*() const { return data(); } -char& string::operator[](unsigned position) { +char& string::operator[](signed position) { if(position > size() + 1) throw exception_out_of_bounds{}; return data()[position]; } -const char& string::operator[](unsigned position) const { +const char& string::operator[](signed position) const { if(position > size() + 1) throw exception_out_of_bounds{}; return data()[position]; } @@ -97,24 +74,6 @@ bool string::operator<=(const char* str) const { return strcmp(data(), str) <= 0 bool string::operator> (const char* str) const { return strcmp(data(), str) > 0; } bool string::operator>=(const char* str) const { return strcmp(data(), str) >= 0; } -string& string::operator=(const string& source) { - if(&source == this) return *this; - _data = source._data; - _capacity = source._capacity; - _size = source._size; - return *this; -} - -string& string::operator=(string&& source) { - if(&source == this) return *this; - _data = std::move(source._data); - _capacity = source._capacity; - _size = source._size; - source._capacity = 0; - source._size = 0; - return *this; -} - string::string(const string& source) { operator=(source); } @@ -123,20 +82,6 @@ string::string(string&& source) { operator=(std::move(source)); } -template string::string(T&& source, Args&&... args) { - _capacity = 0; - _size = 0; - sprint(*this, std::forward(source), std::forward(args)...); -} - -string::string() { - _capacity = 0; - _size = 0; -} - -string::~string() { -} - } #endif diff --git a/higan/nall/string/eval/evaluator.hpp b/higan/nall/string/eval/evaluator.hpp new file mode 100644 index 00000000..7af578ee --- /dev/null +++ b/higan/nall/string/eval/evaluator.hpp @@ -0,0 +1,150 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Eval { + +inline string evaluateExpression(Node* node) { + #define p(n) evaluateExpression(node->link[n]) + switch(node->type) { + case Node::Type::Null: return "Null"; + case Node::Type::Literal: return {"Literal:", node->literal}; + case Node::Type::Function: return {"Function(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::Subscript: return {"Subscript(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::Member: return {"Member(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::SuffixIncrement: return {"SuffixIncrement(0:", p(0), ")"}; + case Node::Type::SuffixDecrement: return {"SuffixDecrement(0:", p(0), ")"}; + case Node::Type::Reference: return {"Reference(0:", p(0), ")"}; + case Node::Type::Dereference: return {"Dereference(0:", p(0), ")"}; + case Node::Type::BitwiseNot: return {"Complement(0:", p(0), ")"}; + case Node::Type::PrefixIncrement: return {"PrefixIncrement(0:", p(0), ")"}; + case Node::Type::PrefixDecrement: return {"PrefixDecrement(0:", p(0), ")"}; + case Node::Type::Add: return {"Add(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::Multiply: return {"Multiply(0:", p(0), ", 1:", p(1), ")"}; + case Node::Type::Concatenate: return {"Concatenate(0:", p(0), ", ", p(1), ")"}; + case Node::Type::Coalesce: return {"Coalesce(0:", p(0), ", ", p(1), ")"}; + case Node::Type::Condition: return {"Condition(0:", p(0), ", ", p(1), ", ", p(2), ")"}; + case Node::Type::Assign: return {"Assign(0:", p(0), ", ", p(1), ")"}; + case Node::Type::Separator: { + string result = "Separator("; + for(auto& link : node->link) { + result.append(evaluateExpression(link), ", "); + } + return result.rtrim<1>(", ").append(")"); + } + } + #undef p + + throw "invalid operator"; +} + +inline int64_t evaluateInteger(Node* node) { + if(node->type == Node::Type::Literal) return nall::integer(node->literal); + + #define p(n) evaluateInteger(node->link[n]) + switch(node->type) { + case Node::Type::SuffixIncrement: return p(0); + case Node::Type::SuffixDecrement: return p(0); + case Node::Type::LogicalNot: return !p(0); + case Node::Type::BitwiseNot: return ~p(0); + case Node::Type::Positive: return +p(0); + case Node::Type::Negative: return -p(0); + case Node::Type::PrefixIncrement: return p(0) + 1; + case Node::Type::PrefixDecrement: return p(0) - 1; + case Node::Type::Multiply: return p(0) * p(1); + case Node::Type::Divide: return p(0) / p(1); + case Node::Type::Modulo: return p(0) % p(1); + case Node::Type::Add: return p(0) + p(1); + case Node::Type::Subtract: return p(0) - p(1); + case Node::Type::ShiftLeft: return p(0) << p(1); + case Node::Type::ShiftRight: return p(0) >> p(1); + case Node::Type::BitwiseAnd: return p(0) & p(1); + case Node::Type::BitwiseOr: return p(0) | p(1); + case Node::Type::BitwiseXor: return p(0) ^ p(1); + case Node::Type::Equal: return p(0) == p(1); + case Node::Type::NotEqual: return p(0) != p(1); + case Node::Type::LessThanEqual: return p(0) <= p(1); + case Node::Type::GreaterThanEqual: return p(0) >= p(1); + case Node::Type::LessThan: return p(0) < p(1); + case Node::Type::GreaterThan: return p(0) > p(1); + case Node::Type::LogicalAnd: return p(0) && p(1); + case Node::Type::LogicalOr: return p(0) || p(1); + case Node::Type::Condition: return p(0) ? p(1) : p(2); + case Node::Type::Assign: return p(1); + case Node::Type::AssignMultiply: return p(0) * p(1); + case Node::Type::AssignDivide: return p(0) / p(1); + case Node::Type::AssignModulo: return p(0) % p(1); + case Node::Type::AssignAdd: return p(0) + p(1); + case Node::Type::AssignSubtract: return p(0) - p(1); + case Node::Type::AssignShiftLeft: return p(0) << p(1); + case Node::Type::AssignShiftRight: return p(0) >> p(1); + case Node::Type::AssignBitwiseAnd: return p(0) & p(1); + case Node::Type::AssignBitwiseOr: return p(0) | p(1); + case Node::Type::AssignBitwiseXor: return p(0) ^ p(1); + } + #undef p + + throw "invalid operator"; +} + +inline optional integer(const string& expression) { + try { + auto tree = new Node; + const char* p = expression; + parse(tree, p, 0); + auto result = evaluateInteger(tree); + delete tree; + return {true, result}; + } catch(const char*) { + return false; + } +} + +inline long double evaluateReal(Node* node) { + if(node->type == Node::Type::Literal) return nall::real(node->literal); + + #define p(n) evaluateReal(node->link[n]) + switch(node->type) { + case Node::Type::LogicalNot: return !p(0); + case Node::Type::Positive: return +p(0); + case Node::Type::Negative: return -p(0); + case Node::Type::Multiply: return p(0) * p(1); + case Node::Type::Divide: return p(0) / p(1); + case Node::Type::Add: return p(0) + p(1); + case Node::Type::Subtract: return p(0) - p(1); + case Node::Type::Equal: return p(0) == p(1); + case Node::Type::NotEqual: return p(0) != p(1); + case Node::Type::LessThanEqual: return p(0) <= p(1); + case Node::Type::GreaterThanEqual: return p(0) >= p(1); + case Node::Type::LessThan: return p(0) < p(1); + case Node::Type::GreaterThan: return p(0) > p(1); + case Node::Type::LogicalAnd: return p(0) && p(1); + case Node::Type::LogicalOr: return p(0) || p(1); + case Node::Type::Condition: return p(0) ? p(1) : p(2); + case Node::Type::Assign: return p(1); + case Node::Type::AssignMultiply: return p(0) * p(1); + case Node::Type::AssignDivide: return p(0) / p(1); + case Node::Type::AssignAdd: return p(0) + p(1); + case Node::Type::AssignSubtract: return p(0) - p(1); + } + #undef p + + throw "invalid operator"; +} + +inline optional real(const string& expression) { + try { + auto tree = new Node; + const char* p = expression; + parse(tree, p, 0); + auto result = evaluateReal(tree); + delete tree; + return {true, result}; + } catch(const char*) { + return false; + } +} + +} +} + +#endif diff --git a/higan/nall/string/eval/literal.hpp b/higan/nall/string/eval/literal.hpp new file mode 100644 index 00000000..35d1a069 --- /dev/null +++ b/higan/nall/string/eval/literal.hpp @@ -0,0 +1,97 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Eval { + +inline bool isLiteral(const char*& s) { + char n = s[0]; + return (n >= 'A' && n <= 'Z') + || (n >= 'a' && n <= 'z') + || (n >= '0' && n <= '9') + || n == '_' || n == '\"'; +} + +inline string literalNumber(const char*& s) { + const char* p = s; + + //binary + if(p[0] == '0' && p[1] == 'b') { + p += 2; + while(p[0] == '0' || p[0] == '1') p++; + if(p - s < 3) throw "invalid binary literal"; + string result = substr(s, 0, p - s); + s = p; + return result; + } + + //octal + if(p[0] == '0' && p[1] == 'o') { + p += 2; + while(p[0] >= '0' && p[0] <= '7') p++; + if(p - s < 3) throw "invalid octal literal"; + string result = substr(s, 0, p - s); + s = p; + return result; + } + + //hex + if(p[0] == '0' && p[1] == 'x') { + p += 2; + while((p[0] >= '0' && p[0] <= '9') || (p[0] >= 'A' && p[0] <= 'F') || (p[0] >= 'a' && p[0] <= 'f')) p++; + if(p - s < 3) throw "invalid hex literal"; + string result = substr(s, 0, p - s); + s = p; + return result; + } + + //decimal + while(p[0] >= '0' && p[0] <= '9') p++; + if(p[0] != '.') { + string result = substr(s, 0, p - s); + s = p; + return result; + } + + //floating-point + p++; + while(p[0] >= '0' && p[0] <= '9') p++; + string result = substr(s, 0, p - s); + s = p; + return result; +} + +inline string literalString(const char*& s) { + const char* p = s + 1; + + while(p[0] && p[0] != '\"') p++; + if(*p++ != '\"') throw "unclosed string literal"; + + string result = substr(s, 0, p - s); + s = p; + return result; +} + +inline string literalVariable(const char*& s) { + const char* p = s; + + while(p[0] == '_' || (p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z') || (p[0] >= '0' && p[0] <= '9')) p++; + + string result = substr(s, 0, p - s); + s = p; + return result; +} + +inline string literal(const char*& s) { + const char* p = s; + + if(p[0] >= '0' && p[0] <= '9') return literalNumber(s); + if(p[0] == '\"') return literalString(s); + if(p[0] == '_' || (p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z')) return literalVariable(s); + + throw "invalid literal"; +} + +} +} + +#endif diff --git a/higan/nall/string/eval/node.hpp b/higan/nall/string/eval/node.hpp new file mode 100644 index 00000000..b60b3aa7 --- /dev/null +++ b/higan/nall/string/eval/node.hpp @@ -0,0 +1,41 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Eval { + +struct Node { + enum class Type : unsigned { + Null, + Literal, + Function, Subscript, Member, SuffixIncrement, SuffixDecrement, + Reference, Dereference, LogicalNot, BitwiseNot, Positive, Negative, PrefixIncrement, PrefixDecrement, + Multiply, Divide, Modulo, + Add, Subtract, + RotateLeft, RotateRight, ShiftLeft, ShiftRight, + BitwiseAnd, BitwiseOr, BitwiseXor, + Concatenate, + Equal, NotEqual, LessThanEqual, GreaterThanEqual, LessThan, GreaterThan, + LogicalAnd, LogicalOr, + Coalesce, Condition, + Assign, Create, //all assignment operators have the same precedence + AssignMultiply, AssignDivide, AssignModulo, + AssignAdd, AssignSubtract, + AssignRotateLeft, AssignRotateRight, AssignShiftLeft, AssignShiftRight, + AssignBitwiseAnd, AssignBitwiseOr, AssignBitwiseXor, + AssignConcatenate, + Separator, + }; + + Type type; + string literal; + vector link; + + Node() : type(Type::Null) {} + Node(Type type) : type(type) {} + ~Node() { for(auto& node : link) delete node; } +}; + +} +} + +#endif diff --git a/higan/nall/string/eval/parser.hpp b/higan/nall/string/eval/parser.hpp new file mode 100644 index 00000000..2aee1474 --- /dev/null +++ b/higan/nall/string/eval/parser.hpp @@ -0,0 +1,168 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +namespace nall { +namespace Eval { + +inline bool whitespace(char n) { + return n == ' ' || n == '\t' || n == '\r' || n == '\n'; +} + +inline void parse(Node*& node, const char*& s, unsigned depth) { + auto unaryPrefix = [&](Node::Type type, unsigned seek, unsigned depth) { + auto parent = new Node(type); + parse(parent->link(0) = new Node, s += seek, depth); + node = parent; + }; + + auto unarySuffix = [&](Node::Type type, unsigned seek, unsigned depth) { + auto parent = new Node(type); + parent->link(0) = node; + parse(parent, s += seek, depth); + node = parent; + }; + + auto binary = [&](Node::Type type, unsigned seek, unsigned depth) { + auto parent = new Node(type); + parent->link(0) = node; + parse(parent->link(1) = new Node, s += seek, depth); + node = parent; + }; + + auto ternary = [&](Node::Type type, unsigned seek, unsigned depth) { + auto parent = new Node(type); + parent->link(0) = node; + parse(parent->link(1) = new Node, s += seek, depth); + if(s[0] != ':') throw "mismatched ternary"; + parse(parent->link(2) = new Node, s += seek, depth); + node = parent; + }; + + auto separator = [&](Node::Type type, unsigned seek, unsigned depth) { + if(node->type != Node::Type::Separator) return binary(type, seek, depth); + unsigned n = node->link.size(); + parse(node->link(n) = new Node, s += seek, depth); + }; + + while(whitespace(s[0])) s++; + if(!s[0]) return; + + if(s[0] == '(' && node->link.empty()) { + parse(node, s += 1, 1); + if(*s++ != ')') throw "mismatched group"; + } + + if(isLiteral(s)) { + node->type = Node::Type::Literal; + node->literal = literal(s); + } + + #define p() (node->literal.empty() && node->link.empty()) + while(true) { + while(whitespace(s[0])) s++; + if(!s[0]) return; + + if(depth >= 13) break; + if(s[0] == '(' && !p()) { + binary(Node::Type::Function, 1, 1); + if(*s++ != ')') throw "mismatched function"; + continue; + } + if(s[0] == '[') { + binary(Node::Type::Subscript, 1, 1); + if(*s++ != ']') throw "mismatched subscript"; + continue; + } + if(s[0] == '.') { binary(Node::Type::Member, 1, 13); continue; } + if(s[0] == '+' && s[1] == '+' && !p()) { unarySuffix(Node::Type::SuffixIncrement, 2, 13); continue; } + if(s[0] == '-' && s[1] == '-' && !p()) { unarySuffix(Node::Type::SuffixDecrement, 2, 13); continue; } + + if(s[0] == '&' && p()) { unaryPrefix(Node::Type::Reference, 1, 12); continue; } + if(s[0] == '*' && p()) { unaryPrefix(Node::Type::Dereference, 1, 12); continue; } + if(s[0] == '!' && p()) { unaryPrefix(Node::Type::LogicalNot, 1, 12); continue; } + if(s[0] == '~' && p()) { unaryPrefix(Node::Type::BitwiseNot, 1, 12); continue; } + if(s[0] == '+' && s[1] != '+' && p()) { unaryPrefix(Node::Type::Positive, 1, 12); continue; } + if(s[0] == '-' && s[1] != '-' && p()) { unaryPrefix(Node::Type::Negative, 1, 12); continue; } + if(s[0] == '+' && s[1] == '+' && p()) { unaryPrefix(Node::Type::PrefixIncrement, 2, 12); continue; } + if(s[0] == '-' && s[1] == '-' && p()) { unaryPrefix(Node::Type::PrefixDecrement, 2, 12); continue; } + if(depth >= 12) break; + + if(depth >= 11) break; + if(s[0] == '*' && s[1] != '=') { binary(Node::Type::Multiply, 1, 11); continue; } + if(s[0] == '/' && s[1] != '=') { binary(Node::Type::Divide, 1, 11); continue; } + if(s[0] == '%' && s[1] != '=') { binary(Node::Type::Modulo, 1, 11); continue; } + + if(depth >= 10) break; + if(s[0] == '+' && s[1] != '=') { binary(Node::Type::Add, 1, 10); continue; } + if(s[0] == '-' && s[1] != '=') { binary(Node::Type::Subtract, 1, 10); continue; } + + if(depth >= 9) break; + if(s[0] == '<' && s[1] == '<' && s[2] == '<' && s[3] != '=') { binary(Node::Type::RotateLeft, 3, 9); continue; } + if(s[0] == '>' && s[1] == '>' && s[2] == '>' && s[3] != '=') { binary(Node::Type::RotateRight, 3, 9); continue; } + if(s[0] == '<' && s[1] == '<' && s[2] != '=') { binary(Node::Type::ShiftLeft, 2, 9); continue; } + if(s[0] == '>' && s[1] == '>' && s[2] != '=') { binary(Node::Type::ShiftRight, 2, 9); continue; } + + if(depth >= 8) break; + if(s[0] == '&' && s[1] != '&' && s[1] != '=') { binary(Node::Type::BitwiseAnd, 1, 8); continue; } + if(s[0] == '|' && s[1] != '|' && s[1] != '=') { binary(Node::Type::BitwiseOr, 1, 8); continue; } + if(s[0] == '^' && s[1] != '^' && s[1] != '=') { binary(Node::Type::BitwiseXor, 1, 8); continue; } + + if(depth >= 7) break; + if(s[0] == '~' && s[1] != '=') { binary(Node::Type::Concatenate, 1, 7); continue; } + + if(depth >= 6) break; + if(s[0] == '=' && s[1] == '=') { binary(Node::Type::Equal, 2, 6); continue; } + if(s[0] == '!' && s[1] == '=') { binary(Node::Type::NotEqual, 2, 6); continue; } + if(s[0] == '<' && s[1] == '=') { binary(Node::Type::LessThanEqual, 2, 6); continue; } + if(s[0] == '>' && s[1] == '=') { binary(Node::Type::GreaterThanEqual, 2, 6); continue; } + if(s[0] == '<') { binary(Node::Type::LessThan, 1, 6); continue; } + if(s[0] == '>') { binary(Node::Type::GreaterThan, 1, 6); continue; } + + if(depth >= 5) break; + if(s[0] == '&' && s[1] == '&') { binary(Node::Type::LogicalAnd, 2, 5); continue; } + if(s[0] == '|' && s[1] == '|') { binary(Node::Type::LogicalOr, 2, 5); continue; } + + if(s[0] == '?' && s[1] == '?') { binary(Node::Type::Coalesce, 2, 4); continue; } + if(s[0] == '?' && s[1] != '?') { ternary(Node::Type::Condition, 1, 4); continue; } + if(depth >= 4) break; + + if(s[0] == '=') { binary(Node::Type::Assign, 1, 3); continue; } + if(s[0] == ':' && s[1] == '=') { binary(Node::Type::Create, 2, 3); continue; } + if(s[0] == '*' && s[1] == '=') { binary(Node::Type::AssignMultiply, 2, 3); continue; } + if(s[0] == '/' && s[1] == '=') { binary(Node::Type::AssignDivide, 2, 3); continue; } + if(s[0] == '%' && s[1] == '=') { binary(Node::Type::AssignModulo, 2, 3); continue; } + if(s[0] == '+' && s[1] == '=') { binary(Node::Type::AssignAdd, 2, 3); continue; } + if(s[0] == '-' && s[1] == '=') { binary(Node::Type::AssignSubtract, 2, 3); continue; } + if(s[0] == '<' && s[1] == '<' && s[2] == '<' && s[3] == '=') { binary(Node::Type::AssignRotateLeft, 4, 3); continue; } + if(s[0] == '>' && s[1] == '>' && s[2] == '>' && s[3] == '=') { binary(Node::Type::AssignRotateRight, 4, 3); continue; } + if(s[0] == '<' && s[1] == '<' && s[2] == '=') { binary(Node::Type::AssignShiftLeft, 3, 3); continue; } + if(s[0] == '>' && s[1] == '>' && s[2] == '=') { binary(Node::Type::AssignShiftRight, 3, 3); continue; } + if(s[0] == '&' && s[1] == '=') { binary(Node::Type::AssignBitwiseAnd, 2, 3); continue; } + if(s[0] == '|' && s[1] == '=') { binary(Node::Type::AssignBitwiseOr, 2, 3); continue; } + if(s[0] == '^' && s[1] == '=') { binary(Node::Type::AssignBitwiseXor, 2, 3); continue; } + if(s[0] == '~' && s[1] == '=') { binary(Node::Type::AssignConcatenate, 2, 3); continue; } + if(depth >= 3) break; + + if(depth >= 2) break; + if(s[0] == ',') { separator(Node::Type::Separator, 1, 2); continue; } + + if(depth >= 1 && (s[0] == ')' || s[0] == ']')) break; + + while(whitespace(s[0])) s++; + if(!s[0]) break; + + throw "unrecognized terminal"; + } + #undef p +} + +inline Node* parse(const string& expression) { + auto result = new Node; + const char* p = expression; + parse(result, p, 0); + return result; +} + +} +} + +#endif diff --git a/higan/nall/string/file.hpp b/higan/nall/string/file.hpp index a287ac2a..1b809c2f 100644 --- a/higan/nall/string/file.hpp +++ b/higan/nall/string/file.hpp @@ -2,34 +2,28 @@ namespace nall { -bool string::readfile(rstring filename) { - reset(); +string string::read(const string& filename) { + string result; #if !defined(_WIN32) FILE* fp = fopen(filename, "rb"); #else FILE* fp = _wfopen(utf16_t(filename), L"rb"); #endif - if(!fp) return false; + if(!fp) return result; fseek(fp, 0, SEEK_END); unsigned fsize = ftell(fp); rewind(fp); - char *fdata = new char[fsize + 1]; + char* fdata = new char[fsize + 1]; unsigned unused = fread(fdata, 1, fsize, fp); fclose(fp); fdata[fsize] = 0; - resize(fsize); - memcpy(data(), fdata, fsize); + result.resize(fsize); + memcpy(result.data(), fdata, fsize); delete[] fdata; - return true; -} - -string string::read(rstring filename) { - string data; - data.readfile(filename); - return data; + return result; } } diff --git a/higan/nall/string/format.hpp b/higan/nall/string/format.hpp index b9ed3ff2..98c0c5b7 100644 --- a/higan/nall/string/format.hpp +++ b/higan/nall/string/format.hpp @@ -31,7 +31,7 @@ template string hex(uintmax_t value) { buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10; value >>= 4; } while(value); - buffer[size] = 0; + buffer.resize(size); buffer.reverse(); return format(buffer); @@ -46,7 +46,7 @@ template string octal(uintmax_t value) { buffer[size++] = '0' + (value & 7); value >>= 3; } while(value); - buffer[size] = 0; + buffer.resize(size); buffer.reverse(); return format(buffer); @@ -61,7 +61,7 @@ template string binary(uintmax_t value) { buffer[size++] = '0' + (value & 1); value >>= 1; } while(value); - buffer[size] = 0; + buffer.resize(size); buffer.reverse(); return format(buffer); diff --git a/higan/nall/string/markup/node.hpp b/higan/nall/string/markup/node.hpp index a78cce14..5198a723 100644 --- a/higan/nall/string/markup/node.hpp +++ b/higan/nall/string/markup/node.hpp @@ -39,12 +39,12 @@ struct Node { for(auto& rule : rules) { enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE }; auto comparator = Comparator::ID; - if(rule.wildcard("*!=*")) comparator = Comparator::NE; - else if(rule.wildcard("*<=*")) comparator = Comparator::LE; - else if(rule.wildcard("*>=*")) comparator = Comparator::GE; - else if(rule.wildcard ("*=*")) comparator = Comparator::EQ; - else if(rule.wildcard ("*<*")) comparator = Comparator::LT; - else if(rule.wildcard ("*>*")) comparator = Comparator::GT; + if(rule.match("*!=*")) comparator = Comparator::NE; + else if(rule.match("*<=*")) comparator = Comparator::LE; + else if(rule.match("*>=*")) comparator = Comparator::GE; + else if(rule.match ("*=*")) comparator = Comparator::EQ; + else if(rule.match ("*<*")) comparator = Comparator::LT; + else if(rule.match ("*>*")) comparator = Comparator::GT; if(comparator == Comparator::ID) { if(find(rule).size()) continue; @@ -69,8 +69,8 @@ struct Node { } switch(comparator) { - case Comparator::EQ: if(data.wildcard(side(1)) == true) continue; break; - case Comparator::NE: if(data.wildcard(side(1)) == false) continue; break; + case Comparator::EQ: if(data.match(side(1)) == true) continue; break; + case Comparator::NE: if(data.match(side(1)) == false) continue; break; case Comparator::LT: if(numeral(data) < numeral(side(1))) continue; break; case Comparator::LE: if(numeral(data) <= numeral(side(1))) continue; break; case Comparator::GT: if(numeral(data) > numeral(side(1))) continue; break; @@ -90,7 +90,7 @@ struct Node { string name = path.take(0), rule; unsigned lo = 0u, hi = ~0u; - if(name.wildcard("*[*]")) { + if(name.match("*[*]")) { lstring side = name.split<1>("["); name = side(0); side = side(1).rtrim<1>("]").split<1>("-"); @@ -98,7 +98,7 @@ struct Node { hi = side(1).empty() ? ~0u : numeral(side(1)); } - if(name.wildcard("*(*)")) { + if(name.match("*(*)")) { lstring side = name.split<1>("("); name = side(0); rule = side(1).rtrim<1>(")"); @@ -106,7 +106,7 @@ struct Node { unsigned position = 0; for(auto& node : children) { - if(node.name.wildcard(name) == false) continue; + if(node.name.match(name) == false) continue; if(node.evaluate(rule) == false) continue; bool inrange = position >= lo && position <= hi; diff --git a/higan/nall/string/ref.hpp b/higan/nall/string/ref.hpp index e973bd21..19200891 100644 --- a/higan/nall/string/ref.hpp +++ b/higan/nall/string/ref.hpp @@ -13,8 +13,8 @@ struct stringref { unsigned size() const { if(!_initialized) { + _initialized = true; _size = strlen(_data); - _initialized = 1; } return _size; } @@ -23,45 +23,21 @@ struct stringref { stringref(const stringref& source) = delete; stringref(stringref&& source) = delete; - template - stringref(T&& source, Args&&... args) { - if(sizeof...(Args) == 0) { construct(std::forward(source)); _string = nullptr; return; } - _string = new string(std::forward(source), std::forward(args)...); - _data = _string->data(); - _size = _string->size(); - _initialized = 1; + stringref(const char* source) { + _data = source; + _initialized = false; } - ~stringref() { - if(_string) delete _string; + stringref(const string& source) { + _data = source.data(); + _size = source.size(); + _initialized = true; } protected: const char* _data; - string* _string; mutable unsigned _size; mutable bool _initialized; - - template - void construct(const char (&source)[size]) { - _data = source; - _size = size - 1; - _initialized = 1; - } - - template::value>::type> - void construct(const T* source) { - _data = source; - _size = 0; - _initialized = 0; - } - - template::value>::type> - void construct(const T& source) { - _data = source.data(); - _size = source.size(); - _initialized = 1; - } }; } diff --git a/higan/nall/string/utility.hpp b/higan/nall/string/utility.hpp index 9880c72d..429636cd 100755 --- a/higan/nall/string/utility.hpp +++ b/higan/nall/string/utility.hpp @@ -79,7 +79,7 @@ char* decimal(char* result, uintmax_t value) { //using sprintf is certainly not the most ideal method to convert //a double to a string ... but attempting to parse a double by //hand, digit-by-digit, results in subtle rounding errors. -unsigned fp(char* str, long double value) { +unsigned real(char* str, long double value) { char buffer[256]; #ifdef _WIN32 //Windows C-runtime does not support long double via sprintf() @@ -105,10 +105,10 @@ unsigned fp(char* str, long double value) { return length + 1; } -string fp(long double value) { +string real(long double value) { string temp; - temp.reserve(fp(nullptr, value)); - fp(temp.data(), value); + temp.resize(real(nullptr, value)); + real(temp.data(), value); return temp; } diff --git a/higan/nall/string/wrapper.hpp b/higan/nall/string/wrapper.hpp index d5f2deb9..6551b38d 100755 --- a/higan/nall/string/wrapper.hpp +++ b/higan/nall/string/wrapper.hpp @@ -7,17 +7,25 @@ template lstring string::isplit(rstring key) const { lstring res template lstring string::qsplit(rstring key) const { lstring result; result.qsplit(key, data()); return result; } template lstring string::iqsplit(rstring key) const { lstring result; result.iqsplit(key, data()); return result; } -bool string::wildcard(rstring source) const { return nall::wildcard(data(), source); } -bool string::iwildcard(rstring source) const { return nall::iwildcard(data(), source); } +bool string::match(rstring source) const { return nall::strmatch(data(), source); } +bool string::imatch(rstring source) const { return nall::istrmatch(data(), source); } + +signed string::compare(rstring source) const { + return strcmp(data(), source.data()); +} + +signed string::icompare(rstring source) const { + return istrcmp(data(), source.data()); +} bool string::equals(rstring source) const { if(size() != source.size()) return false; - return memcmp(data(), source.data(), source.size()) == 0; + return compare(source) == 0; } bool string::iequals(rstring source) const { if(size() != source.size()) return false; - return imemcmp(data(), source.data(), source.size()) == 0; + return icompare(source) == 0; } bool string::beginswith(rstring source) const { @@ -40,6 +48,12 @@ bool string::iendswith(rstring source) const { return imemcmp(data() + size() - source.size(), source.data(), source.size()) == 0; } +string string::slice(unsigned offset, unsigned length) const { + if(offset >= size()) return ""; + if(length == ~0u) length = size() - offset; + return substr(data(), offset, length); +} + string& string::lower() { nall::strlower(data()); return *this; } string& string::upper() { nall::strupper(data()); return *this; } string& string::qlower() { nall::qstrlower(data()); return *this; } @@ -82,9 +96,16 @@ template string& string::rtrim(rstring key) { return *this; } -template string& string::trim(rstring key, rstring rkey) { - rtrim(rkey.size() ? rkey : key); - return ltrim(key); +template string& string::trim(rstring key) { + rtrim(key); + ltrim(key); + return *this; +} + +template string& string::trim(rstring lkey, rstring rkey) { + rtrim(rkey); + ltrim(lkey); + return *this; } string& string::strip() { @@ -93,10 +114,10 @@ string& string::strip() { return *this; } -optional string::position(rstring key) const { return strpos(data(), key); } -optional string::iposition(rstring key) const { return istrpos(data(), key); } -optional string::qposition(rstring key) const { return qstrpos(data(), key); } -optional string::iqposition(rstring key) const { return iqstrpos(data(), key); } +optional string::find(rstring key) const { return strpos(data(), key); } +optional string::ifind(rstring key) const { return istrpos(data(), key); } +optional string::qfind(rstring key) const { return qstrpos(data(), key); } +optional string::iqfind(rstring key) const { return iqstrpos(data(), key); } } diff --git a/higan/nall/utility.hpp b/higan/nall/utility.hpp index c487a9a8..0447b94a 100755 --- a/higan/nall/utility.hpp +++ b/higan/nall/utility.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace nall { @@ -11,6 +12,22 @@ template struct base_from_member { base_from_member(T value) : value(value) {} }; +template struct ref { + T& operator*() { + if(type == Type::Reference) return *any_cast(value); + return any_cast(value); + } + + operator T&() { return operator*(); } + + ref(T& value) : type(Type::Reference), value(&value) {} + ref(T&& value) : type(Type::Temporary), value(value) {} + +protected: + enum class Type : unsigned { Reference, Temporary } type; + any value; +}; + template struct optional { typedef typename std::remove_reference::type T; static const bool isConst = std::is_const::value; @@ -30,12 +47,23 @@ template struct optional { } } + template::type> + T& operator*() { + if(!valid) throw optional_value_not_valid{}; + return *value; + } + template::type> T& operator()() { if(!valid) throw optional_value_not_valid{}; return *value; } + const T& operator*() const { + if(!valid) throw optional_value_not_valid{}; + return *value; + } + const T& operator()() const { if(!valid) throw optional_value_not_valid{}; return *value; diff --git a/higan/nall/varint.hpp b/higan/nall/varint.hpp index 58b403d6..6bc2184e 100755 --- a/higan/nall/varint.hpp +++ b/higan/nall/varint.hpp @@ -2,6 +2,7 @@ #define NALL_VARINT_HPP #include +#include #include namespace nall { @@ -34,6 +35,8 @@ public: template inline type_t operator=(const uint_t &i) { return data = uclip((type_t)i); } template inline uint_t(const uint_t &i) : data(uclip(i)) {} + + void serialize(serializer& s) { s(data); } }; template struct int_t { @@ -64,6 +67,8 @@ public: template inline type_t operator=(const int_t &i) { return data = sclip((type_t)i); } template inline int_t(const int_t &i) : data(sclip(i)) {} + + void serialize(serializer& s) { s(data); } }; template struct varuint_t { @@ -92,6 +97,8 @@ public: inline void bits(type_t bits) { mask = (1ull << (bits - 1)) + ((1ull << (bits - 1)) - 1); data &= mask; } inline varuint_t() : data(0ull), mask((type_t)~0ull) {} inline varuint_t(const type_t i) : data(i), mask((type_t)~0ull) {} + + void serialize(serializer& s) { s(data); s(mask); } }; } diff --git a/higan/nall/vector.hpp b/higan/nall/vector.hpp index 01e114c1..2ffd419f 100755 --- a/higan/nall/vector.hpp +++ b/higan/nall/vector.hpp @@ -77,7 +77,7 @@ public: prepend(data); } - void prepend(const T& data) { + T& prepend(const T& data) { reserve(objectsize + 1); if(poolbase == 0) { unsigned available = poolsize - objectsize; @@ -88,6 +88,7 @@ public: } new(pool + --poolbase) T(data); objectsize++; + return first(); } template void append(const T& data, Args&&... args) { @@ -95,9 +96,10 @@ public: append(std::forward(args)...); } - void append(const T& data) { + T& append(const T& data) { reserve(poolbase + objectsize + 1); new(pool + poolbase + objectsize++) T(data); + return last(); } bool appendonce(const T& data) { diff --git a/higan/phoenix/cocoa/action/check-item.cpp b/higan/phoenix/cocoa/action/check-item.cpp index 46e8b317..9ea49464 100644 --- a/higan/phoenix/cocoa/action/check-item.cpp +++ b/higan/phoenix/cocoa/action/check-item.cpp @@ -33,7 +33,7 @@ void pCheckItem::setChecked(bool checked) { } } -void pCheckItem::setText(const string& text) { +void pCheckItem::setText(string text) { @autoreleasepool { [cocoaAction setTitle:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/action/check-item.hpp b/higan/phoenix/cocoa/action/check-item.hpp index d2517def..beda4ad8 100644 --- a/higan/phoenix/cocoa/action/check-item.hpp +++ b/higan/phoenix/cocoa/action/check-item.hpp @@ -14,7 +14,7 @@ struct pCheckItem : public pAction { bool checked(); void setChecked(bool checked); - void setText(const string& text); + void setText(string text); pCheckItem(CheckItem& checkItem) : pAction(checkItem), checkItem(checkItem) {} void constructor(); diff --git a/higan/phoenix/cocoa/action/item.cpp b/higan/phoenix/cocoa/action/item.cpp index 285c7e44..2af3c76d 100644 --- a/higan/phoenix/cocoa/action/item.cpp +++ b/higan/phoenix/cocoa/action/item.cpp @@ -24,7 +24,7 @@ void pItem::setImage(const image& image) { } } -void pItem::setText(const string& text) { +void pItem::setText(string text) { @autoreleasepool { [cocoaAction setTitle:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/action/item.hpp b/higan/phoenix/cocoa/action/item.hpp index 33977a73..23fc908f 100644 --- a/higan/phoenix/cocoa/action/item.hpp +++ b/higan/phoenix/cocoa/action/item.hpp @@ -13,7 +13,7 @@ struct pItem : public pAction { CocoaItem* cocoaItem = nullptr; void setImage(const image& image); - void setText(const string& text); + void setText(string text); pItem(Item& item) : pAction(item), item(item) {} void constructor(); diff --git a/higan/phoenix/cocoa/action/menu.cpp b/higan/phoenix/cocoa/action/menu.cpp index 97e4ac09..8553432c 100644 --- a/higan/phoenix/cocoa/action/menu.cpp +++ b/higan/phoenix/cocoa/action/menu.cpp @@ -37,7 +37,7 @@ void pMenu::setImage(const image& image) { } } -void pMenu::setText(const string& text) { +void pMenu::setText(string text) { @autoreleasepool { [[cocoaAction cocoaMenu] setTitle:[NSString stringWithUTF8String:text]]; [cocoaAction setTitle:[NSString stringWithUTF8String:text]]; diff --git a/higan/phoenix/cocoa/action/menu.hpp b/higan/phoenix/cocoa/action/menu.hpp index fc453f89..322b8782 100644 --- a/higan/phoenix/cocoa/action/menu.hpp +++ b/higan/phoenix/cocoa/action/menu.hpp @@ -16,7 +16,7 @@ struct pMenu : public pAction { void append(Action& action); void remove(Action& action); void setImage(const image& image); - void setText(const string& text); + void setText(string text); pMenu(Menu& menu) : pAction(menu), menu(menu) {} void constructor(); diff --git a/higan/phoenix/cocoa/action/radio-item.cpp b/higan/phoenix/cocoa/action/radio-item.cpp index 35fa4d0a..3c18eff3 100644 --- a/higan/phoenix/cocoa/action/radio-item.cpp +++ b/higan/phoenix/cocoa/action/radio-item.cpp @@ -37,7 +37,7 @@ void pRadioItem::setChecked() { void pRadioItem::setGroup(const group& group) { } -void pRadioItem::setText(const string& text) { +void pRadioItem::setText(string text) { @autoreleasepool { [cocoaAction setTitle:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/action/radio-item.hpp b/higan/phoenix/cocoa/action/radio-item.hpp index d13c3265..f98ae942 100644 --- a/higan/phoenix/cocoa/action/radio-item.hpp +++ b/higan/phoenix/cocoa/action/radio-item.hpp @@ -15,7 +15,7 @@ struct pRadioItem : public pAction { bool checked(); void setChecked(); void setGroup(const group& group); - void setText(const string& text); + void setText(string text); pRadioItem(RadioItem& radioItem) : pAction(radioItem), radioItem(radioItem) {} void constructor(); diff --git a/higan/phoenix/cocoa/browser-window.cpp b/higan/phoenix/cocoa/browser-window.cpp index 31a1f47b..014cb6dd 100644 --- a/higan/phoenix/cocoa/browser-window.cpp +++ b/higan/phoenix/cocoa/browser-window.cpp @@ -50,7 +50,7 @@ string pBrowserWindow::save(BrowserWindow::State& state) { NSMutableArray* filters = [[NSMutableArray alloc] init]; for(auto& rule : state.filters) { string pattern = rule.split<1>("(")(1).rtrim<1>(")"); - if(!pattern.empty()) [filters addObjects:[NSString stringWithUTF8String:pattern]]; + if(!pattern.empty()) [filters addObject:[NSString stringWithUTF8String:pattern]]; } NSSavePanel* panel = [NSSavePanel savePanel]; if(state.title) [panel setTitle:[NSString stringWithUTF8String:state.title]]; diff --git a/higan/phoenix/cocoa/font.cpp b/higan/phoenix/cocoa/font.cpp index cdfeec73..6c9e57cd 100644 --- a/higan/phoenix/cocoa/font.cpp +++ b/higan/phoenix/cocoa/font.cpp @@ -18,7 +18,7 @@ string pFont::monospace(unsigned size, string style) { return {"Menlo, ", size, ", ", style}; } -Size pFont::size(const string& font, const string& text) { +Size pFont::size(string font, string text) { @autoreleasepool { if(NSFont* nsFont = cocoaFont(font)) { return size(nsFont, text); @@ -27,27 +27,26 @@ Size pFont::size(const string& font, const string& text) { return {0, 0}; } -NSFont* pFont::cocoaFont(const string& description) { - lstring part = description.split<2>(","); - for(auto& item : part) item.strip(); +NSFont* pFont::cocoaFont(string description) { + lstring part = description.split<2>(",").strip(); NSString* family = @"Lucida Grande"; NSFontTraitMask traits = 0; CGFloat size = 12; if(!part(0).empty()) family = [NSString stringWithUTF8String:part(0)]; - if(!part(1).empty()) size = fp(part(1)); - if(part(2).iposition("bold")) traits |= NSBoldFontMask; - if(part(2).iposition("italic")) traits |= NSItalicFontMask; - if(part(2).iposition("narrow")) traits |= NSNarrowFontMask; - if(part(2).iposition("expanded")) traits |= NSExpandedFontMask; - if(part(2).iposition("condensed")) traits |= NSCondensedFontMask; - if(part(2).iposition("smallcaps")) traits |= NSSmallCapsFontMask; + if(!part(1).empty()) size = real(part(1)); + if(part(2).ifind("bold")) traits |= NSBoldFontMask; + if(part(2).ifind("italic")) traits |= NSItalicFontMask; + if(part(2).ifind("narrow")) traits |= NSNarrowFontMask; + if(part(2).ifind("expanded")) traits |= NSExpandedFontMask; + if(part(2).ifind("condensed")) traits |= NSCondensedFontMask; + if(part(2).ifind("smallcaps")) traits |= NSSmallCapsFontMask; return [[NSFontManager sharedFontManager] fontWithFamily:family traits:traits weight:5 size:size]; } -Size pFont::size(NSFont* font, const string& text) { +Size pFont::size(NSFont* font, string text) { @autoreleasepool { NSString* cocoaText = [NSString stringWithUTF8String:text]; NSDictionary* fontAttributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]; diff --git a/higan/phoenix/cocoa/font.hpp b/higan/phoenix/cocoa/font.hpp index 8717da00..1eca0ad9 100644 --- a/higan/phoenix/cocoa/font.hpp +++ b/higan/phoenix/cocoa/font.hpp @@ -4,10 +4,10 @@ struct pFont { static string serif(unsigned size, string style); static string sans(unsigned size, string style); static string monospace(unsigned size, string style); - static Size size(const string& font, const string& text); + static Size size(string font, string text); - static NSFont* cocoaFont(const string& description); - static Size size(NSFont* font, const string& text); + static NSFont* cocoaFont(string description); + static Size size(NSFont* font, string text); }; } diff --git a/higan/phoenix/cocoa/utility.cpp b/higan/phoenix/cocoa/utility.cpp index a3c5a787..22f8b26e 100644 --- a/higan/phoenix/cocoa/utility.cpp +++ b/higan/phoenix/cocoa/utility.cpp @@ -15,3 +15,27 @@ NSImage* NSMakeImage(nall::image image, unsigned width = 0, unsigned height = 0) [cocoaImage addRepresentation:bitmap]; return cocoaImage; } + +NSDragOperation DropPathsOperation(id sender) { + NSPasteboard* pboard = [sender draggingPasteboard]; + if([[pboard types] containsObject:NSFilenamesPboardType]) { + if([sender draggingSourceOperationMask] & NSDragOperationGeneric) { + return NSDragOperationGeneric; + } + } + return NSDragOperationNone; +} + +lstring DropPaths(id sender) { + lstring paths; + NSPasteboard* pboard = [sender draggingPasteboard]; + if([[pboard types] containsObject:NSFilenamesPboardType]) { + NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; + for(unsigned n = 0; n < [files count]; n++) { + string path = [[files objectAtIndex:n] UTF8String]; + if(directory::exists(path) && !path.endswith("/")) path.append("/"); + paths.append(path); + } + } + return paths; +} diff --git a/higan/phoenix/cocoa/widget/button.cpp b/higan/phoenix/cocoa/widget/button.cpp index fd76bfc8..950bffef 100644 --- a/higan/phoenix/cocoa/widget/button.cpp +++ b/higan/phoenix/cocoa/widget/button.cpp @@ -35,7 +35,7 @@ Size pButton::minimumSize() { return {size.width + 20, size.height + 4}; } -void pButton::setGeometry(const Geometry& geometry) { +void pButton::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x - 2, geometry.y - 2, geometry.width + 4, geometry.height + 4 @@ -56,7 +56,7 @@ void pButton::setImage(const image& image, Orientation orientation) { } } -void pButton::setText(const string& text) { +void pButton::setText(string text) { @autoreleasepool { [cocoaView setTitle:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/widget/button.hpp b/higan/phoenix/cocoa/widget/button.hpp index b1d046e8..98569454 100644 --- a/higan/phoenix/cocoa/widget/button.hpp +++ b/higan/phoenix/cocoa/widget/button.hpp @@ -13,9 +13,9 @@ struct pButton : public pWidget { CocoaButton* cocoaButton = nullptr; Size minimumSize(); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setImage(const image& image, Orientation orientation); - void setText(const string& text); + void setText(string text); pButton(Button& button) : pWidget(button), button(button) {} void constructor(); diff --git a/higan/phoenix/cocoa/widget/canvas.cpp b/higan/phoenix/cocoa/widget/canvas.cpp index dd1eaf4f..b8b12ff1 100644 --- a/higan/phoenix/cocoa/widget/canvas.cpp +++ b/higan/phoenix/cocoa/widget/canvas.cpp @@ -13,6 +13,17 @@ return self; } +-(NSDragOperation) draggingEntered:(id)sender { + return DropPathsOperation(sender); +} + +-(BOOL) performDragOperation:(id)sender { + lstring paths = DropPaths(sender); + if(paths.empty()) return NO; + if(canvas->onDrop) canvas->onDrop(paths); + return YES; +} + -(void) mouseButton:(NSEvent*)event down:(BOOL)isDown { if(auto &callback = isDown ? canvas->onMousePress : canvas->onMouseRelease) { switch([event buttonNumber]) { @@ -73,7 +84,17 @@ namespace phoenix { -void pCanvas::setSize(const Size& size) { +void pCanvas::setDroppable(bool droppable) { + @autoreleasepool { + if(droppable) { + [cocoaCanvas registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; + } else { + [cocoaCanvas unregisterDraggedTypes]; + } + } +} + +void pCanvas::setSize(Size size) { @autoreleasepool { NSImage* image = [[[NSImage alloc] initWithSize:NSMakeSize(size.width, size.height)] autorelease]; NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] diff --git a/higan/phoenix/cocoa/widget/canvas.hpp b/higan/phoenix/cocoa/widget/canvas.hpp index 28ab7e52..0ee3af87 100644 --- a/higan/phoenix/cocoa/widget/canvas.hpp +++ b/higan/phoenix/cocoa/widget/canvas.hpp @@ -3,6 +3,8 @@ phoenix::Canvas* canvas; } -(id) initWith:(phoenix::Canvas&)canvas; +-(NSDragOperation) draggingEntered:(id)sender; +-(BOOL) performDragOperation:(id)sender; -(void) mouseButton:(NSEvent*)event down:(BOOL)isDown; -(void) mouseExited:(NSEvent*)event; -(void) mouseMove:(NSEvent*)event; @@ -23,7 +25,8 @@ struct pCanvas : public pWidget { Canvas& canvas; CocoaCanvas* cocoaCanvas = nullptr; - void setSize(const Size& size); + void setDroppable(bool droppable); + void setSize(Size size); void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} diff --git a/higan/phoenix/cocoa/widget/check-button.cpp b/higan/phoenix/cocoa/widget/check-button.cpp index edadd5c4..a91a674c 100644 --- a/higan/phoenix/cocoa/widget/check-button.cpp +++ b/higan/phoenix/cocoa/widget/check-button.cpp @@ -37,14 +37,14 @@ void pCheckButton::setChecked(bool checked) { } } -void pCheckButton::setGeometry(const Geometry& geometry) { +void pCheckButton::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x - 2, geometry.y, geometry.width + 4, geometry.height }); } -void pCheckButton::setText(const string& text) { +void pCheckButton::setText(string text) { @autoreleasepool { [cocoaView setTitle:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/widget/check-button.hpp b/higan/phoenix/cocoa/widget/check-button.hpp index f18d83f2..13cffc4f 100644 --- a/higan/phoenix/cocoa/widget/check-button.hpp +++ b/higan/phoenix/cocoa/widget/check-button.hpp @@ -15,8 +15,8 @@ struct pCheckButton : public pWidget { bool checked(); Size minimumSize(); void setChecked(bool checked); - void setGeometry(const Geometry& geometry); - void setText(const string& text); + void setGeometry(Geometry geometry); + void setText(string text); pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} void constructor(); diff --git a/higan/phoenix/cocoa/widget/combo-button.cpp b/higan/phoenix/cocoa/widget/combo-button.cpp index 34f61b8c..4287caa8 100644 --- a/higan/phoenix/cocoa/widget/combo-button.cpp +++ b/higan/phoenix/cocoa/widget/combo-button.cpp @@ -18,7 +18,7 @@ namespace phoenix { -void pComboButton::append(const string& text) { +void pComboButton::append(string text) { @autoreleasepool { [cocoaView addItemWithTitle:[NSString stringWithUTF8String:text]]; } @@ -31,7 +31,7 @@ Size pComboButton::minimumSize() { return {maximumWidth + 36, size.height + 6}; } -void pComboButton::modify(unsigned row, const string& text) { +void pComboButton::modify(unsigned row, string text) { @autoreleasepool { [[cocoaView itemAtIndex:row] setTitle:[NSString stringWithUTF8String:text]]; } @@ -55,7 +55,7 @@ unsigned pComboButton::selection() { } } -void pComboButton::setGeometry(const Geometry& geometry) { +void pComboButton::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x - 2, geometry.y, geometry.width + 4, geometry.height diff --git a/higan/phoenix/cocoa/widget/combo-button.hpp b/higan/phoenix/cocoa/widget/combo-button.hpp index 7d8c3d25..27f8732e 100644 --- a/higan/phoenix/cocoa/widget/combo-button.hpp +++ b/higan/phoenix/cocoa/widget/combo-button.hpp @@ -12,13 +12,13 @@ struct pComboButton : public pWidget { ComboButton& comboButton; CocoaComboButton* cocoaComboButton = nullptr; - void append(const string& text); + void append(string text); Size minimumSize(); - void modify(unsigned row, const string& text); + void modify(unsigned row, string text); void remove(unsigned row); void reset(); unsigned selection(); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setSelection(unsigned row); pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {} diff --git a/higan/phoenix/cocoa/widget/horizontal-slider.cpp b/higan/phoenix/cocoa/widget/horizontal-slider.cpp index 212a5c7f..62be0c8d 100644 --- a/higan/phoenix/cocoa/widget/horizontal-slider.cpp +++ b/higan/phoenix/cocoa/widget/horizontal-slider.cpp @@ -30,7 +30,7 @@ unsigned pHorizontalSlider::position() { } } -void pHorizontalSlider::setGeometry(const Geometry& geometry) { +void pHorizontalSlider::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x - 2, geometry.y, geometry.width + 4, geometry.height diff --git a/higan/phoenix/cocoa/widget/horizontal-slider.hpp b/higan/phoenix/cocoa/widget/horizontal-slider.hpp index 610b70b3..dc7a5cd3 100644 --- a/higan/phoenix/cocoa/widget/horizontal-slider.hpp +++ b/higan/phoenix/cocoa/widget/horizontal-slider.hpp @@ -14,7 +14,7 @@ struct pHorizontalSlider : public pWidget { Size minimumSize(); unsigned position(); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/higan/phoenix/cocoa/widget/label.cpp b/higan/phoenix/cocoa/widget/label.cpp index f967864d..a1a7218a 100644 --- a/higan/phoenix/cocoa/widget/label.cpp +++ b/higan/phoenix/cocoa/widget/label.cpp @@ -21,7 +21,7 @@ Size pLabel::minimumSize() { return {size.width, size.height}; } -void pLabel::setGeometry(const Geometry& geometry) { +void pLabel::setGeometry(Geometry geometry) { //NSTextField does not support vertical text centering: //simulate this by adjusting the geometry placement (reduce height, move view down) unsigned height = Font::size(label.font(), " ").height; @@ -40,7 +40,7 @@ void pLabel::setGeometry(const Geometry& geometry) { }); } -void pLabel::setText(const string& text) { +void pLabel::setText(string text) { @autoreleasepool { [cocoaView setStringValue:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/widget/label.hpp b/higan/phoenix/cocoa/widget/label.hpp index 09ec43c4..e1d87d06 100644 --- a/higan/phoenix/cocoa/widget/label.hpp +++ b/higan/phoenix/cocoa/widget/label.hpp @@ -12,8 +12,8 @@ struct pLabel : public pWidget { CocoaLabel* cocoaLabel = nullptr; Size minimumSize(); - void setGeometry(const Geometry& geometry); - void setText(const string& text); + void setGeometry(Geometry geometry); + void setText(string text); pLabel(Label& label) : pWidget(label), label(label) {} void constructor(); diff --git a/higan/phoenix/cocoa/widget/line-edit.cpp b/higan/phoenix/cocoa/widget/line-edit.cpp index d5798d09..296eea69 100644 --- a/higan/phoenix/cocoa/widget/line-edit.cpp +++ b/higan/phoenix/cocoa/widget/line-edit.cpp @@ -37,7 +37,7 @@ void pLineEdit::setEditable(bool editable) { } } -void pLineEdit::setText(const string& text) { +void pLineEdit::setText(string text) { @autoreleasepool { [cocoaView setStringValue:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/widget/line-edit.hpp b/higan/phoenix/cocoa/widget/line-edit.hpp index cc649a6c..ad10427a 100644 --- a/higan/phoenix/cocoa/widget/line-edit.hpp +++ b/higan/phoenix/cocoa/widget/line-edit.hpp @@ -15,7 +15,7 @@ struct pLineEdit : public pWidget { Size minimumSize(); void setEditable(bool editable); - void setText(const string& text); + void setText(string text); string text(); pLineEdit(LineEdit& lineEdit) : pWidget(lineEdit), lineEdit(lineEdit) {} diff --git a/higan/phoenix/cocoa/widget/list-view.cpp b/higan/phoenix/cocoa/widget/list-view.cpp index a5d0c8bf..88202f96 100644 --- a/higan/phoenix/cocoa/widget/list-view.cpp +++ b/higan/phoenix/cocoa/widget/list-view.cpp @@ -285,7 +285,7 @@ void pListView::setChecked(unsigned row, bool checked) { } } -void pListView::setFont(const string& font) { +void pListView::setFont(string font) { @autoreleasepool { [cocoaView setFont:pFont::cocoaFont(font)]; } diff --git a/higan/phoenix/cocoa/widget/list-view.hpp b/higan/phoenix/cocoa/widget/list-view.hpp index 34016489..a94d8e0f 100644 --- a/higan/phoenix/cocoa/widget/list-view.hpp +++ b/higan/phoenix/cocoa/widget/list-view.hpp @@ -50,7 +50,7 @@ struct pListView : public pWidget { unsigned selection(); void setCheckable(bool checkable); void setChecked(unsigned row, bool checked); - void setFont(const string& font); + void setFont(string font); void setHeaderText(const lstring& text); void setHeaderVisible(bool visible); void setImage(unsigned row, unsigned column, const image& image); diff --git a/higan/phoenix/cocoa/widget/radio-button.cpp b/higan/phoenix/cocoa/widget/radio-button.cpp index cbbd30b1..775a0b00 100644 --- a/higan/phoenix/cocoa/widget/radio-button.cpp +++ b/higan/phoenix/cocoa/widget/radio-button.cpp @@ -40,7 +40,7 @@ void pRadioButton::setChecked() { } } -void pRadioButton::setGeometry(const Geometry& geometry) { +void pRadioButton::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x - 1, geometry.y, geometry.width + 2, geometry.height @@ -50,7 +50,7 @@ void pRadioButton::setGeometry(const Geometry& geometry) { void pRadioButton::setGroup(const group& group) { } -void pRadioButton::setText(const string& text) { +void pRadioButton::setText(string text) { @autoreleasepool { [cocoaView setTitle:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/widget/radio-button.hpp b/higan/phoenix/cocoa/widget/radio-button.hpp index cbe6bc74..5c8ab665 100644 --- a/higan/phoenix/cocoa/widget/radio-button.hpp +++ b/higan/phoenix/cocoa/widget/radio-button.hpp @@ -15,9 +15,9 @@ struct pRadioButton : public pWidget { bool checked(); Size minimumSize(); void setChecked(); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setGroup(const group& group); - void setText(const string& text); + void setText(string text); pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {} void constructor(); diff --git a/higan/phoenix/cocoa/widget/text-edit.cpp b/higan/phoenix/cocoa/widget/text-edit.cpp index a4570d48..77dbce07 100644 --- a/higan/phoenix/cocoa/widget/text-edit.cpp +++ b/higan/phoenix/cocoa/widget/text-edit.cpp @@ -57,13 +57,13 @@ void pTextEdit::setEditable(bool editable) { } } -void pTextEdit::setFont(const string& font) { +void pTextEdit::setFont(string font) { @autoreleasepool { [[cocoaView content] setFont:pFont::cocoaFont(font)]; } } -void pTextEdit::setText(const string& text) { +void pTextEdit::setText(string text) { @autoreleasepool { [[cocoaView content] setString:[NSString stringWithUTF8String:text]]; } diff --git a/higan/phoenix/cocoa/widget/text-edit.hpp b/higan/phoenix/cocoa/widget/text-edit.hpp index f264443b..432efac9 100644 --- a/higan/phoenix/cocoa/widget/text-edit.hpp +++ b/higan/phoenix/cocoa/widget/text-edit.hpp @@ -17,8 +17,8 @@ struct pTextEdit : public pWidget { void setCursorPosition(unsigned position); void setEditable(bool editable); - void setFont(const string& font); - void setText(const string& text); + void setFont(string font); + void setText(string text); void setWordWrap(bool wordWrap); string text(); diff --git a/higan/phoenix/cocoa/widget/vertical-slider.cpp b/higan/phoenix/cocoa/widget/vertical-slider.cpp index 9268ffeb..be75778d 100644 --- a/higan/phoenix/cocoa/widget/vertical-slider.cpp +++ b/higan/phoenix/cocoa/widget/vertical-slider.cpp @@ -30,7 +30,7 @@ unsigned pVerticalSlider::position() { } } -void pVerticalSlider::setGeometry(const Geometry& geometry) { +void pVerticalSlider::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x, geometry.y - 2, geometry.width, geometry.height + 4 diff --git a/higan/phoenix/cocoa/widget/vertical-slider.hpp b/higan/phoenix/cocoa/widget/vertical-slider.hpp index 171cc968..977f834c 100644 --- a/higan/phoenix/cocoa/widget/vertical-slider.hpp +++ b/higan/phoenix/cocoa/widget/vertical-slider.hpp @@ -14,7 +14,7 @@ struct pVerticalSlider : public pWidget { Size minimumSize(); unsigned position(); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/higan/phoenix/cocoa/widget/viewport.cpp b/higan/phoenix/cocoa/widget/viewport.cpp index 5e2a5db4..4f5172cb 100644 --- a/higan/phoenix/cocoa/widget/viewport.cpp +++ b/higan/phoenix/cocoa/widget/viewport.cpp @@ -16,6 +16,17 @@ return YES; } +-(NSDragOperation) draggingEntered:(id)sender { + return DropPathsOperation(sender); +} + +-(BOOL) performDragOperation:(id)sender { + lstring paths = DropPaths(sender); + if(paths.empty()) return NO; + if(viewport->onDrop) viewport->onDrop(paths); + return YES; +} + -(void) keyDown:(NSEvent*)event { } @@ -30,6 +41,16 @@ uintptr_t pViewport::handle() { return (uintptr_t)cocoaViewport; } +void pViewport::setDroppable(bool droppable) { + @autoreleasepool { + if(droppable) { + [cocoaViewport registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; + } else { + [cocoaViewport unregisterDraggedTypes]; + } + } +} + void pViewport::constructor() { @autoreleasepool { cocoaView = cocoaViewport = [[CocoaViewport alloc] initWith:viewport]; diff --git a/higan/phoenix/cocoa/widget/viewport.hpp b/higan/phoenix/cocoa/widget/viewport.hpp index e1ebed64..67cff565 100644 --- a/higan/phoenix/cocoa/widget/viewport.hpp +++ b/higan/phoenix/cocoa/widget/viewport.hpp @@ -5,6 +5,8 @@ -(id) initWith:(phoenix::Viewport&)viewport; -(void) drawRect:(NSRect)rect; -(BOOL) acceptsFirstResponder; +-(NSDragOperation) draggingEntered:(id)sender; +-(BOOL) performDragOperation:(id)sender; -(void) keyDown:(NSEvent*)event; -(void) keyUp:(NSEvent*)event; @end @@ -16,6 +18,7 @@ struct pViewport : public pWidget { CocoaViewport* cocoaViewport = nullptr; uintptr_t handle(); + void setDroppable(bool droppable); pViewport(Viewport& viewport) : pWidget(viewport), viewport(viewport) {} void constructor(); diff --git a/higan/phoenix/cocoa/widget/widget.cpp b/higan/phoenix/cocoa/widget/widget.cpp index 423567a7..e527871e 100644 --- a/higan/phoenix/cocoa/widget/widget.cpp +++ b/higan/phoenix/cocoa/widget/widget.cpp @@ -33,7 +33,7 @@ void pWidget::setFocused() { } } -void pWidget::setFont(const string& font) { +void pWidget::setFont(string font) { @autoreleasepool { if([cocoaView respondsToSelector:@selector(setFont:)]) { [cocoaView setFont:pFont::cocoaFont(font)]; @@ -41,7 +41,7 @@ void pWidget::setFont(const string& font) { } } -void pWidget::setGeometry(const Geometry& geometry) { +void pWidget::setGeometry(Geometry geometry) { @autoreleasepool { CGFloat windowHeight = [[cocoaView superview] frame].size.height; [cocoaView setFrame:NSMakeRect(geometry.x, windowHeight - geometry.y - geometry.height, geometry.width, geometry.height)]; diff --git a/higan/phoenix/cocoa/widget/widget.hpp b/higan/phoenix/cocoa/widget/widget.hpp index 73798183..caf4e4d1 100644 --- a/higan/phoenix/cocoa/widget/widget.hpp +++ b/higan/phoenix/cocoa/widget/widget.hpp @@ -9,8 +9,8 @@ struct pWidget : public pSizable { virtual Size minimumSize(); void setEnabled(bool enabled); void setFocused(); - virtual void setFont(const string& font); - virtual void setGeometry(const Geometry& geometry); + virtual void setFont(string font); + virtual void setGeometry(Geometry geometry); void setVisible(bool visible); pWidget(Widget& widget) : pSizable(widget), widget(widget) {} diff --git a/higan/phoenix/cocoa/window.cpp b/higan/phoenix/cocoa/window.cpp index 0aede532..0f796301 100644 --- a/higan/phoenix/cocoa/window.cpp +++ b/higan/phoenix/cocoa/window.cpp @@ -105,6 +105,17 @@ return NO; } +-(NSDragOperation) draggingEntered:(id)sender { + return DropPathsOperation(sender); +} + +-(BOOL) performDragOperation:(id)sender { + lstring paths = DropPaths(sender); + if(paths.empty()) return NO; + if(window->onDrop) window->onDrop(paths); + return YES; +} + -(NSMenu*) menuBar { return menuBar; } @@ -217,7 +228,7 @@ void pWindow::remove(Widget& widget) { } } -void pWindow::setBackgroundColor(const Color& color) { +void pWindow::setBackgroundColor(Color color) { @autoreleasepool { [cocoaWindow setBackgroundColor:[NSColor @@ -230,6 +241,16 @@ void pWindow::setBackgroundColor(const Color& color) { } } +void pWindow::setDroppable(bool droppable) { + @autoreleasepool { + if(droppable) { + [cocoaWindow registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; + } else { + [cocoaWindow unregisterDraggedTypes]; + } + } +} + void pWindow::setFocused() { @autoreleasepool { [cocoaWindow makeKeyAndOrderFront:nil]; @@ -250,7 +271,7 @@ void pWindow::setFullScreen(bool fullScreen) { } } -void pWindow::setGeometry(const Geometry& geometry) { +void pWindow::setGeometry(Geometry geometry) { locked = true; @autoreleasepool { @@ -276,7 +297,7 @@ void pWindow::setGeometry(const Geometry& geometry) { locked = false; } -void pWindow::setMenuFont(const string& font) { +void pWindow::setMenuFont(string font) { } void pWindow::setMenuVisible(bool visible) { @@ -302,14 +323,14 @@ void pWindow::setResizable(bool resizable) { } } -void pWindow::setStatusFont(const string& font) { +void pWindow::setStatusFont(string font) { @autoreleasepool { [[cocoaWindow statusBar] setFont:pFont::cocoaFont(font)]; } statusBarReposition(); } -void pWindow::setStatusText(const string& text) { +void pWindow::setStatusText(string text) { @autoreleasepool { [[cocoaWindow statusBar] setStringValue:[NSString stringWithUTF8String:text]]; } @@ -322,7 +343,7 @@ void pWindow::setStatusVisible(bool visible) { } } -void pWindow::setTitle(const string& text) { +void pWindow::setTitle(string text) { @autoreleasepool { [cocoaWindow setTitle:[NSString stringWithUTF8String:text]]; } @@ -335,7 +356,7 @@ void pWindow::setVisible(bool visible) { } } -void pWindow::setWidgetFont(const string& font) { +void pWindow::setWidgetFont(string font) { } void pWindow::constructor() { diff --git a/higan/phoenix/cocoa/window.hpp b/higan/phoenix/cocoa/window.hpp index 0dcb5569..3c0bafe4 100644 --- a/higan/phoenix/cocoa/window.hpp +++ b/higan/phoenix/cocoa/window.hpp @@ -12,6 +12,8 @@ -(void) windowDidMove:(NSNotification*)notification; -(void) windowDidResize:(NSNotification*)notification; -(BOOL) windowShouldClose:(id)sender; +-(NSDragOperation) draggingEntered:(id)sender; +-(BOOL) performDragOperation:(id)sender; -(NSMenu*) menuBar; -(void) menuAbout; -(void) menuPreferences; @@ -37,20 +39,21 @@ struct pWindow : public pObject { void remove(Layout& layout); void remove(Menu& menu); void remove(Widget& widget); - void setBackgroundColor(const Color& color); + void setBackgroundColor(Color color); + void setDroppable(bool droppable); void setFocused(); void setFullScreen(bool fullScreen); - void setGeometry(const Geometry& geometry); - void setMenuFont(const string& font); + void setGeometry(Geometry geometry); + void setMenuFont(string font); void setMenuVisible(bool visible); void setModal(bool modal); void setResizable(bool resizable); - void setStatusFont(const string& font); - void setStatusText(const string& text); + void setStatusFont(string font); + void setStatusText(string text); void setStatusVisible(bool visible); - void setTitle(const string& text); + void setTitle(string text); void setVisible(bool visible); - void setWidgetFont(const string& font); + void setWidgetFont(string font); pWindow(Window& window) : pObject(window), window(window) {} void constructor(); diff --git a/higan/phoenix/core/core.cpp b/higan/phoenix/core/core.cpp index 0a324c65..b745d680 100755 --- a/higan/phoenix/core/core.cpp +++ b/higan/phoenix/core/core.cpp @@ -39,6 +39,9 @@ namespace phoenix { function Application::main; +function Application::Windows::onModalBegin; +function Application::Windows::onModalEnd; + function Application::Cocoa::onAbout; function Application::Cocoa::onActivate; function Application::Cocoa::onPreferences; @@ -377,13 +380,18 @@ void Window::remove_(Widget& widget) { } } -void Window::setBackgroundColor(const Color& color) { +void Window::setBackgroundColor(Color color) { state.backgroundColorOverride = true; state.backgroundColor = color; return p.setBackgroundColor(color); } -void Window::setFrameGeometry(const Geometry& geometry) { +void Window::setDroppable(bool droppable) { + state.droppable = droppable; + return p.setDroppable(droppable); +} + +void Window::setFrameGeometry(Geometry geometry) { Geometry margin = p.frameMargin(); return setGeometry({ geometry.x + margin.x, geometry.y + margin.y, @@ -400,7 +408,7 @@ void Window::setFullScreen(bool fullScreen) { return p.setFullScreen(fullScreen); } -void Window::setGeometry(const Geometry& geometry) { +void Window::setGeometry(Geometry geometry) { state.geometry = geometry; return p.setGeometry(geometry); } @@ -425,7 +433,7 @@ void Window::setResizable(bool resizable) { return p.setResizable(resizable); } -void Window::setSmartGeometry(const Geometry& geometry) { +void Window::setSmartGeometry(Geometry geometry) { Geometry margin = p.frameMargin(); return setGeometry({ geometry.x + margin.x, geometry.y + margin.y, @@ -790,7 +798,7 @@ void Widget::setFont(string font) { return p.setFont(font); } -void Widget::setGeometry(const Geometry& geometry) { +void Widget::setGeometry(Geometry geometry) { state.geometry = geometry; return p.setGeometry(geometry); } @@ -860,6 +868,11 @@ uint32_t* Canvas::data() { return state.data; } +void Canvas::setDroppable(bool droppable) { + state.droppable = droppable; + return p.setDroppable(droppable); +} + bool Canvas::setImage(const nall::image& image) { if(image.data == nullptr || image.width == 0 || image.height == 0) return false; state.width = image.width; @@ -869,7 +882,7 @@ bool Canvas::setImage(const nall::image& image) { return true; } -void Canvas::setSize(const Size& size) { +void Canvas::setSize(Size size) { state.width = size.width; state.height = size.height; delete[] state.data; @@ -1416,7 +1429,13 @@ uintptr_t Viewport::handle() { return p.handle(); } +void Viewport::setDroppable(bool droppable) { + state.droppable = droppable; + return p.setDroppable(droppable); +} + Viewport::Viewport(): +state(*new State), base_from_member(*new pViewport(*this)), Widget(base_from_member::value), p(base_from_member::value) { @@ -1425,6 +1444,7 @@ p(base_from_member::value) { Viewport::~Viewport() { p.destructor(); + delete &state; } } diff --git a/higan/phoenix/core/core.hpp b/higan/phoenix/core/core.hpp index 4325b8a8..a66ae76d 100755 --- a/higan/phoenix/core/core.hpp +++ b/higan/phoenix/core/core.hpp @@ -64,6 +64,11 @@ struct Application { struct State; static void initialize(); + struct Windows { + static nall::function onModalBegin; + static nall::function onModalEnd; + }; + struct Cocoa { static nall::function onAbout; static nall::function onActivate; @@ -72,8 +77,6 @@ struct Application { }; }; -typedef Application App; - enum : unsigned { MaximumSize = ~0u, MinimumSize = 0u, @@ -106,7 +109,7 @@ struct Geometry { Size size() const; nall::string text() const; inline Geometry() : x(0), y(0), width(0), height(0) {} - inline Geometry(const Position& position, const Size& size) : x(position.x), y(position.y), width(size.width), height(size.height) {} + inline Geometry(Position position, Size size) : x(position.x), y(position.y), width(size.width), height(size.height) {} template inline Geometry(X x, Y y, W width, H height) : x(x), y(y), width(width), height(height) {} Geometry(nall::string text); }; @@ -212,6 +215,7 @@ struct Timer : private nall::base_from_member, Object { struct Window : private nall::base_from_member, Object { nall::function onClose; + nall::function onDrop; nall::function onKeyPress; nall::function onKeyRelease; nall::function onMove; @@ -236,16 +240,17 @@ struct Window : private nall::base_from_member, Object { void remove_(Layout& layout); void remove_(Menu& menu); void remove_(Widget& widget); - void setBackgroundColor(const Color& color); - void setFrameGeometry(const Geometry& geometry); + void setBackgroundColor(Color color); + void setDroppable(bool droppable = true); + void setFrameGeometry(Geometry geometry); void setFocused(); void setFullScreen(bool fullScreen = true); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setMenuFont(nall::string font); void setMenuVisible(bool visible = true); void setModal(bool modal = true); void setResizable(bool resizable = true); - void setSmartGeometry(const Geometry& geometry); + void setSmartGeometry(Geometry geometry); void setStatusFont(nall::string font); void setStatusText(nall::string text); void setStatusVisible(bool visible = true); @@ -348,7 +353,7 @@ struct Sizable : Object { Layout* layout(); virtual Size minimumSize() = 0; virtual void setEnabled(bool enabled = true) = 0; - virtual void setGeometry(const Geometry& geometry) = 0; + virtual void setGeometry(Geometry geometry) = 0; virtual void setVisible(bool visible = true) = 0; virtual bool visible() = 0; Window* window(); @@ -383,7 +388,7 @@ struct Widget : private nall::base_from_member, Sizable { void setEnabled(bool enabled = true); void setFocused(); void setFont(nall::string font); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setVisible(bool visible = true); bool visible(); @@ -409,14 +414,16 @@ struct Button : private nall::base_from_member, Widget { }; struct Canvas : private nall::base_from_member, Widget { + nall::function onDrop; nall::function onMouseLeave; nall::function onMouseMove; nall::function onMousePress; nall::function onMouseRelease; uint32_t* data(); + void setDroppable(bool droppable = true); bool setImage(const nall::image& image); - void setSize(const Size& size); + void setSize(Size size); Size size(); void update(); @@ -641,15 +648,19 @@ struct VerticalSlider : private nall::base_from_member, Widget }; struct Viewport : private nall::base_from_member, Widget { + nall::function onDrop; nall::function onMouseLeave; nall::function onMouseMove; nall::function onMousePress; nall::function onMouseRelease; uintptr_t handle(); + void setDroppable(bool droppable = true); Viewport(); ~Viewport(); + struct State; + State& state; pViewport& p; }; diff --git a/higan/phoenix/core/layout/fixed-layout.cpp b/higan/phoenix/core/layout/fixed-layout.cpp index cd61df65..5eeb8c84 100755 --- a/higan/phoenix/core/layout/fixed-layout.cpp +++ b/higan/phoenix/core/layout/fixed-layout.cpp @@ -1,4 +1,4 @@ -void FixedLayout::append(Sizable& sizable, const Geometry& geometry) { +void FixedLayout::append(Sizable& sizable, Geometry geometry) { children.append({&sizable, geometry}); synchronizeLayout(); if(window()) window()->synchronizeLayout(); @@ -48,7 +48,7 @@ void FixedLayout::setEnabled(bool enabled) { } } -void FixedLayout::setGeometry(const Geometry& geometry) { +void FixedLayout::setGeometry(Geometry geometry) { } void FixedLayout::setVisible(bool visible) { diff --git a/higan/phoenix/core/layout/fixed-layout.hpp b/higan/phoenix/core/layout/fixed-layout.hpp index 3b672e0d..2b4cb630 100755 --- a/higan/phoenix/core/layout/fixed-layout.hpp +++ b/higan/phoenix/core/layout/fixed-layout.hpp @@ -1,12 +1,12 @@ struct FixedLayout : Layout { - void append(Sizable& sizable, const Geometry& geometry); + void append(Sizable& sizable, Geometry geometry); void append(Sizable& sizable); bool enabled(); Size minimumSize(); void remove(Sizable& sizable); void reset(); void setEnabled(bool enabled = true); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setVisible(bool visible = true); void synchronizeLayout(); bool visible(); diff --git a/higan/phoenix/core/layout/horizontal-layout.cpp b/higan/phoenix/core/layout/horizontal-layout.cpp index 2e984b9d..e378c804 100755 --- a/higan/phoenix/core/layout/horizontal-layout.cpp +++ b/higan/phoenix/core/layout/horizontal-layout.cpp @@ -1,4 +1,4 @@ -void HorizontalLayout::append(Sizable& sizable, const Size& size, unsigned spacing) { +void HorizontalLayout::append(Sizable& sizable, Size size, unsigned spacing) { for(auto& child : children) if(child.sizable == &sizable) return; children.append({&sizable, size.width, size.height, spacing}); synchronizeLayout(); @@ -72,7 +72,7 @@ void HorizontalLayout::setEnabled(bool enabled) { } } -void HorizontalLayout::setGeometry(const Geometry& containerGeometry) { +void HorizontalLayout::setGeometry(Geometry containerGeometry) { auto children = this->children; for(auto& child : children) { if(child.width == MinimumSize) child.width = child.sizable->minimumSize().width; diff --git a/higan/phoenix/core/layout/horizontal-layout.hpp b/higan/phoenix/core/layout/horizontal-layout.hpp index d3203a57..ca29b691 100755 --- a/higan/phoenix/core/layout/horizontal-layout.hpp +++ b/higan/phoenix/core/layout/horizontal-layout.hpp @@ -1,5 +1,5 @@ struct HorizontalLayout : public Layout { - void append(Sizable& sizable, const Size& size, unsigned spacing = 0); + void append(Sizable& sizable, Size size, unsigned spacing = 0); void append(Sizable& sizable); bool enabled(); Size minimumSize(); @@ -7,7 +7,7 @@ struct HorizontalLayout : public Layout { void reset(); void setAlignment(double alignment); void setEnabled(bool enabled = true); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setMargin(unsigned margin); void setVisible(bool visible = true); void synchronizeLayout(); diff --git a/higan/phoenix/core/layout/vertical-layout.cpp b/higan/phoenix/core/layout/vertical-layout.cpp index f6062369..0e2451bd 100755 --- a/higan/phoenix/core/layout/vertical-layout.cpp +++ b/higan/phoenix/core/layout/vertical-layout.cpp @@ -1,4 +1,4 @@ -void VerticalLayout::append(Sizable& sizable, const Size& size, unsigned spacing) { +void VerticalLayout::append(Sizable& sizable, Size size, unsigned spacing) { for(auto& child : children) if(child.sizable == &sizable) return; children.append({&sizable, size.width, size.height, spacing}); synchronizeLayout(); @@ -72,7 +72,7 @@ void VerticalLayout::setEnabled(bool enabled) { } } -void VerticalLayout::setGeometry(const Geometry& containerGeometry) { +void VerticalLayout::setGeometry(Geometry containerGeometry) { auto children = this->children; for(auto& child : children) { if(child.width == MinimumSize) child.width = child.sizable->minimumSize().width; diff --git a/higan/phoenix/core/layout/vertical-layout.hpp b/higan/phoenix/core/layout/vertical-layout.hpp index 0fc0c8bf..7e8c7809 100755 --- a/higan/phoenix/core/layout/vertical-layout.hpp +++ b/higan/phoenix/core/layout/vertical-layout.hpp @@ -1,5 +1,5 @@ struct VerticalLayout : public Layout { - void append(Sizable& sizable, const Size& size, unsigned spacing = 0); + void append(Sizable& sizable, Size size, unsigned spacing = 0); void append(Sizable& sizable); bool enabled(); Size minimumSize(); @@ -7,7 +7,7 @@ struct VerticalLayout : public Layout { void reset(); void setAlignment(double alignment); void setEnabled(bool enabled = true); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setMargin(unsigned margin); void setVisible(bool visible = true); void synchronizeLayout(); diff --git a/higan/phoenix/core/state.hpp b/higan/phoenix/core/state.hpp index ad7d0af7..4d8d5171 100755 --- a/higan/phoenix/core/state.hpp +++ b/higan/phoenix/core/state.hpp @@ -25,6 +25,7 @@ struct MessageWindow::State { struct Window::State { bool backgroundColorOverride = false; Color backgroundColor = {0, 0, 0, 255}; + bool droppable = false; bool fullScreen = false; Geometry geometry = {128, 128, 256, 256}; group layout; @@ -95,6 +96,7 @@ struct Button::State { struct Canvas::State { uint32_t* data = nullptr; + bool droppable = false; unsigned width = 256; unsigned height = 256; }; @@ -172,3 +174,7 @@ struct VerticalSlider::State { unsigned length = 101; unsigned position = 0; }; + +struct Viewport::State { + bool droppable = false; +}; diff --git a/higan/phoenix/gtk/action/action.cpp b/higan/phoenix/gtk/action/action.cpp index ad5bf1cb..978b9747 100755 --- a/higan/phoenix/gtk/action/action.cpp +++ b/higan/phoenix/gtk/action/action.cpp @@ -24,7 +24,7 @@ string pAction::mnemonic(string text) { return text; } -void pAction::setFont(const string& font) { +void pAction::setFont(string font) { pFont::setFont(widget, font); } diff --git a/higan/phoenix/gtk/font.cpp b/higan/phoenix/gtk/font.cpp index e8917cad..750d47d4 100755 --- a/higan/phoenix/gtk/font.cpp +++ b/higan/phoenix/gtk/font.cpp @@ -36,8 +36,8 @@ PangoFontDescription* pFont::create(string description) { if(part[0] != "") family = part[0]; if(part.size() >= 2) size = decimal(part[1]); - if(part.size() >= 3) bold = part[2].position("Bold"); - if(part.size() >= 3) italic = part[2].position("Italic"); + if(part.size() >= 3) bold = part[2].find("Bold"); + if(part.size() >= 3) italic = part[2].find("Italic"); PangoFontDescription* font = pango_font_description_new(); pango_font_description_set_family(font, family); diff --git a/higan/phoenix/gtk/platform.hpp b/higan/phoenix/gtk/platform.hpp index 143e30df..bd3cb321 100755 --- a/higan/phoenix/gtk/platform.hpp +++ b/higan/phoenix/gtk/platform.hpp @@ -124,10 +124,11 @@ struct pWindow : public pObject { void remove(Layout& layout); void remove(Menu& menu); void remove(Widget& widget); - void setBackgroundColor(const Color& color); + void setBackgroundColor(Color color); + void setDroppable(bool droppable); void setFocused(); void setFullScreen(bool fullScreen); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setMenuFont(string font); void setMenuVisible(bool visible); void setModal(bool modal); @@ -156,7 +157,7 @@ struct pAction : public pObject { void constructor(); virtual void orphan(); string mnemonic(string text); - virtual void setFont(const string &font); + virtual void setFont(string font); }; struct pMenu : public pAction { @@ -245,7 +246,7 @@ struct pWidget : public pSizable { void setEnabled(bool enabled); virtual void setFocused(); virtual void setFont(string font); - virtual void setGeometry(const Geometry& geometry); + virtual void setGeometry(Geometry geometry); void setVisible(bool visible); pWidget(Widget& widget) : pSizable(widget), widget(widget) {} @@ -271,7 +272,8 @@ struct pCanvas : public pWidget { Canvas& canvas; cairo_surface_t* surface; - void setSize(const Size& size); + void setDroppable(bool droppable); + void setSize(Size size); void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} @@ -510,6 +512,7 @@ struct pViewport : public pWidget { Viewport& viewport; uintptr_t handle(); + void setDroppable(bool droppable); pViewport(Viewport& viewport) : pWidget(viewport), viewport(viewport) {} void constructor(); diff --git a/higan/phoenix/gtk/utility.cpp b/higan/phoenix/gtk/utility.cpp index 95bc5f52..dc7285db 100755 --- a/higan/phoenix/gtk/utility.cpp +++ b/higan/phoenix/gtk/utility.cpp @@ -18,6 +18,25 @@ static GtkImage* CreateImage(const nall::image& image, bool scale = false) { return gtkImage; } +static lstring DropPaths(GtkSelectionData* data) { + gchar** uris = gtk_selection_data_get_uris(data); + if(uris == nullptr) return {}; + + lstring paths; + for(unsigned n = 0; uris[n] != nullptr; n++) { + gchar* pathname = g_filename_from_uri(uris[n], nullptr, nullptr); + if(pathname == nullptr) continue; + + string path = pathname; + g_free(pathname); + if(directory::exists(path) && !path.endswith("/")) path.append("/"); + paths.append(path); + } + + g_strfreev(uris); + return paths; +} + static Keyboard::Keycode Keysym(unsigned keysym) { switch(keysym) { case GDK_Escape: return Keyboard::Keycode::Escape; diff --git a/higan/phoenix/gtk/widget/canvas.cpp b/higan/phoenix/gtk/widget/canvas.cpp index 048adc69..bd7bbd97 100755 --- a/higan/phoenix/gtk/widget/canvas.cpp +++ b/higan/phoenix/gtk/widget/canvas.cpp @@ -8,6 +8,14 @@ static gboolean Canvas_expose(GtkWidget* widget, GdkEvent* event, pCanvas* self) return true; } +static void Canvas_dropEvent(GtkWidget* widget, GdkDragContext* context, gint x, gint y, +GtkSelectionData* data, guint type, guint timestamp, Canvas* canvas) { + if(canvas->state.droppable == false) return; + lstring paths = DropPaths(data); + if(paths.empty()) return; + if(canvas->onDrop) canvas->onDrop(paths); +} + static gboolean Canvas_mouseLeave(GtkWidget* widget, GdkEventButton* event, pCanvas* self) { if(self->canvas.onMouseLeave) self->canvas.onMouseLeave(); return true; @@ -36,7 +44,12 @@ static gboolean Canvas_mouseRelease(GtkWidget* widget, GdkEventButton* event, pC return true; } -void pCanvas::setSize(const Size& size) { +void pCanvas::setDroppable(bool droppable) { + gtk_drag_dest_set(gtkWidget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY); + if(droppable) gtk_drag_dest_add_uri_targets(gtkWidget); +} + +void pCanvas::setSize(Size size) { cairo_surface_destroy(surface); surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, canvas.state.width, canvas.state.height); } @@ -54,6 +67,7 @@ void pCanvas::constructor() { gtk_widget_set_double_buffered(gtkWidget, false); gtk_widget_add_events(gtkWidget, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK); + g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Canvas_dropEvent), (gpointer)&canvas); g_signal_connect(G_OBJECT(gtkWidget), "button_press_event", G_CALLBACK(Canvas_mousePress), (gpointer)this); g_signal_connect(G_OBJECT(gtkWidget), "button_release_event", G_CALLBACK(Canvas_mouseRelease), (gpointer)this); g_signal_connect(G_OBJECT(gtkWidget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this); diff --git a/higan/phoenix/gtk/widget/viewport.cpp b/higan/phoenix/gtk/widget/viewport.cpp index 8a734536..d04cea9f 100755 --- a/higan/phoenix/gtk/widget/viewport.cpp +++ b/higan/phoenix/gtk/widget/viewport.cpp @@ -1,5 +1,13 @@ namespace phoenix { +static void Viewport_dropEvent(GtkWidget* widget, GdkDragContext* context, gint x, gint y, +GtkSelectionData* data, guint type, guint timestamp, Viewport* viewport) { + if(viewport->state.droppable == false) return; + lstring paths = DropPaths(data); + if(paths.empty()) return; + if(viewport->onDrop) viewport->onDrop(paths); +} + static gboolean Viewport_mouseLeave(GtkWidget* widget, GdkEventButton* event, pViewport* self) { if(self->viewport.onMouseLeave) self->viewport.onMouseLeave(); return true; @@ -32,11 +40,17 @@ uintptr_t pViewport::handle() { return GDK_WINDOW_XID(gtk_widget_get_window(gtkWidget)); } +void pViewport::setDroppable(bool droppable) { + gtk_drag_dest_set(gtkWidget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY); + if(droppable) gtk_drag_dest_add_uri_targets(gtkWidget); +} + void pViewport::constructor() { gtkWidget = gtk_drawing_area_new(); //gtk_widget_set_double_buffered(gtkWidget, false); gtk_widget_add_events(gtkWidget, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK); + g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Viewport_dropEvent), (gpointer)&viewport); g_signal_connect(G_OBJECT(gtkWidget), "button_press_event", G_CALLBACK(Viewport_mousePress), (gpointer)this); g_signal_connect(G_OBJECT(gtkWidget), "button_release_event", G_CALLBACK(Viewport_mouseRelease), (gpointer)this); g_signal_connect(G_OBJECT(gtkWidget), "leave_notify_event", G_CALLBACK(Viewport_mouseLeave), (gpointer)this); diff --git a/higan/phoenix/gtk/widget/widget.cpp b/higan/phoenix/gtk/widget/widget.cpp index cafba39e..ecbfe899 100755 --- a/higan/phoenix/gtk/widget/widget.cpp +++ b/higan/phoenix/gtk/widget/widget.cpp @@ -26,7 +26,7 @@ void pWidget::setFont(string font) { pFont::setFont(gtkWidget, font); } -void pWidget::setGeometry(const Geometry& geometry) { +void pWidget::setGeometry(Geometry geometry) { if(sizable.window() && sizable.window()->visible()) gtk_fixed_move(GTK_FIXED(sizable.window()->p.formContainer), gtkWidget, geometry.x, geometry.y); unsigned width = (signed)geometry.width <= 0 ? 1U : geometry.width; unsigned height = (signed)geometry.height <= 0 ? 1U : geometry.height; diff --git a/higan/phoenix/gtk/window.cpp b/higan/phoenix/gtk/window.cpp index 6cdb02ac..4e99aab5 100755 --- a/higan/phoenix/gtk/window.cpp +++ b/higan/phoenix/gtk/window.cpp @@ -80,6 +80,14 @@ static gboolean Window_configure(GtkWidget* widget, GdkEvent* event, Window* win return false; } +static void Window_dropEvent(GtkWidget* widget, GdkDragContext* context, gint x, gint y, +GtkSelectionData* data, guint type, guint timestamp, Window* window) { + if(window->state.droppable == false) return; + lstring paths = DropPaths(data); + if(paths.empty()) return; + if(window->onDrop) window->onDrop(paths); +} + static gboolean Window_keyPressEvent(GtkWidget* widget, GdkEventKey* event, Window* window) { Keyboard::Keycode key = Keysym(event->keyval); if(key != Keyboard::Keycode::None && window->onKeyPress) window->onKeyPress(key); @@ -203,7 +211,7 @@ void pWindow::remove(Widget& widget) { widget.p.orphan(); } -void pWindow::setBackgroundColor(const Color& color) { +void pWindow::setBackgroundColor(Color color) { GdkColor gdkColor; gdkColor.pixel = (color.red << 16) | (color.green << 8) | (color.blue << 0); gdkColor.red = (color.red << 8) | (color.red << 0); @@ -212,6 +220,11 @@ void pWindow::setBackgroundColor(const Color& color) { gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gdkColor); } +void pWindow::setDroppable(bool droppable) { + gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY); + if(droppable) gtk_drag_dest_add_uri_targets(widget); +} + void pWindow::setFocused() { gtk_window_present(GTK_WINDOW(widget)); } @@ -224,7 +237,7 @@ void pWindow::setFullScreen(bool fullScreen) { } } -void pWindow::setGeometry(const Geometry& geometry) { +void pWindow::setGeometry(Geometry geometry) { Geometry margin = frameMargin(); gtk_window_move(GTK_WINDOW(widget), geometry.x - margin.x, geometry.y - margin.y); @@ -362,6 +375,7 @@ void pWindow::constructor() { g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window); g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(Window_expose), (gpointer)&window); g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window); + g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_dropEvent), (gpointer)&window); g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPressEvent), (gpointer)&window); g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyPressEvent), (gpointer)&window); diff --git a/higan/phoenix/qt/action/action.cpp b/higan/phoenix/qt/action/action.cpp index 643d0b5c..e2358ab4 100755 --- a/higan/phoenix/qt/action/action.cpp +++ b/higan/phoenix/qt/action/action.cpp @@ -14,7 +14,7 @@ void pAction::setEnabled(bool enabled) { } } -void pAction::setFont(const string& font) { +void pAction::setFont(string font) { QFont qtFont = pFont::create(font); if(dynamic_cast(&action)) { diff --git a/higan/phoenix/qt/action/check-item.cpp b/higan/phoenix/qt/action/check-item.cpp index ec2491a7..3b011c1e 100755 --- a/higan/phoenix/qt/action/check-item.cpp +++ b/higan/phoenix/qt/action/check-item.cpp @@ -8,7 +8,7 @@ void pCheckItem::setChecked(bool checked) { qtAction->setChecked(checked); } -void pCheckItem::setText(const string& text) { +void pCheckItem::setText(string text) { qtAction->setText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/action/item.cpp b/higan/phoenix/qt/action/item.cpp index d45f105b..2eeba6ea 100755 --- a/higan/phoenix/qt/action/item.cpp +++ b/higan/phoenix/qt/action/item.cpp @@ -4,7 +4,7 @@ void pItem::setImage(const image& image) { qtAction->setIcon(CreateIcon(image)); } -void pItem::setText(const string& text) { +void pItem::setText(string text) { qtAction->setText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/action/menu.cpp b/higan/phoenix/qt/action/menu.cpp index 6e0e8d39..efe5188a 100755 --- a/higan/phoenix/qt/action/menu.cpp +++ b/higan/phoenix/qt/action/menu.cpp @@ -30,7 +30,7 @@ void pMenu::remove(Action& action) { } } -void pMenu::setFont(const string& font) { +void pMenu::setFont(string font) { qtMenu->setFont(pFont::create(font)); for(auto &item : menu.state.action) item.p.setFont(font); } @@ -39,7 +39,7 @@ void pMenu::setImage(const image& image) { qtMenu->setIcon(CreateIcon(image)); } -void pMenu::setText(const string& text) { +void pMenu::setText(string text) { qtMenu->setTitle(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/action/radio-item.cpp b/higan/phoenix/qt/action/radio-item.cpp index 4ab4d2b9..e43034f7 100755 --- a/higan/phoenix/qt/action/radio-item.cpp +++ b/higan/phoenix/qt/action/radio-item.cpp @@ -17,7 +17,7 @@ void pRadioItem::setChecked() { void pRadioItem::setGroup(const group& group) { } -void pRadioItem::setText(const string& text) { +void pRadioItem::setText(string text) { qtAction->setText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/font.cpp b/higan/phoenix/qt/font.cpp index 05896ad4..ce1a6832 100755 --- a/higan/phoenix/qt/font.cpp +++ b/higan/phoenix/qt/font.cpp @@ -18,11 +18,11 @@ string pFont::monospace(unsigned size, string style) { return {"Liberation Mono, ", size, ", ", style}; } -Size pFont::size(const string& font, const string& text) { +Size pFont::size(string font, string text) { return pFont::size(pFont::create(font), text); } -QFont pFont::create(const string& description) { +QFont pFont::create(string description) { lstring part; part.split<2>(",", description); for(auto& item : part) item.trim(" "); @@ -34,8 +34,8 @@ QFont pFont::create(const string& description) { if(part[0] != "") family = part[0]; if(part.size() >= 2) size = decimal(part[1]); - if(part.size() >= 3) bold = part[2].position("Bold"); - if(part.size() >= 3) italic = part[2].position("Italic"); + if(part.size() >= 3) bold = part[2].find("Bold"); + if(part.size() >= 3) italic = part[2].find("Italic"); QFont qtFont; qtFont.setFamily(family); @@ -45,7 +45,7 @@ QFont pFont::create(const string& description) { return qtFont; } -Size pFont::size(const QFont& qtFont, const string& text) { +Size pFont::size(const QFont& qtFont, string text) { QFontMetrics metrics(qtFont); lstring lines; diff --git a/higan/phoenix/qt/platform.moc b/higan/phoenix/qt/platform.moc index fba4186d..b229d658 100755 --- a/higan/phoenix/qt/platform.moc +++ b/higan/phoenix/qt/platform.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'platform.moc.hpp' ** -** Created: Mon Apr 29 08:08:05 2013 +** Created: Wed Jul 17 05:52:48 2013 ** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3) ** ** WARNING! All changes made in this file will be lost! diff --git a/higan/phoenix/qt/platform.moc.hpp b/higan/phoenix/qt/platform.moc.hpp index ff699eef..33db7828 100755 --- a/higan/phoenix/qt/platform.moc.hpp +++ b/higan/phoenix/qt/platform.moc.hpp @@ -40,10 +40,10 @@ struct pFont { static string serif(unsigned size, string style); static string sans(unsigned size, string style); static string monospace(unsigned size, string style); - static Size size(const string& font, const string& text); + static Size size(string font, string text); - static QFont create(const string& description); - static Size size(const QFont& qtFont, const string& text); + static QFont create(string description); + static Size size(const QFont& qtFont, string text); }; struct pDesktop { @@ -112,6 +112,8 @@ public: struct QtWindow : public QWidget { pWindow& self; void closeEvent(QCloseEvent*); + void dragEnterEvent(QDragEnterEvent*); + void dropEvent(QDropEvent*); void keyPressEvent(QKeyEvent*); void keyReleaseEvent(QKeyEvent*); void moveEvent(QMoveEvent*); @@ -137,20 +139,21 @@ public: void remove(Layout& layout); void remove(Menu& menu); void remove(Widget& widget); - void setBackgroundColor(const Color& color); + void setBackgroundColor(Color color); + void setDroppable(bool droppable); void setFocused(); void setFullScreen(bool fullScreen); - void setGeometry(const Geometry& geometry); - void setMenuFont(const string& font); + void setGeometry(Geometry geometry); + void setMenuFont(string font); void setMenuVisible(bool visible); void setModal(bool modal); void setResizable(bool resizable); - void setStatusFont(const string& font); - void setStatusText(const string& text); + void setStatusFont(string font); + void setStatusText(string text); void setStatusVisible(bool visible); - void setTitle(const string& text); + void setTitle(string text); void setVisible(bool visible); - void setWidgetFont(const string& font); + void setWidgetFont(string font); pWindow(Window& window) : pObject(window), window(window) {} void constructor(); @@ -162,7 +165,7 @@ struct pAction : public pObject { Action& action; void setEnabled(bool enabled); - void setFont(const string& font); + void setFont(string font); void setVisible(bool visible); pAction(Action& action) : pObject(action), action(action) {} @@ -176,9 +179,9 @@ struct pMenu : public pAction { void append(Action& action); void remove(Action& action); - void setFont(const string& font); + void setFont(string font); void setImage(const image& image); - void setText(const string& text); + void setText(string text); pMenu(Menu& menu) : pAction(menu), menu(menu) {} void constructor(); @@ -202,7 +205,7 @@ public: QAction* qtAction; void setImage(const image& image); - void setText(const string& text); + void setText(string text); pItem(Item& item) : pAction(item), item(item) {} void constructor(); @@ -221,7 +224,7 @@ public: bool checked(); void setChecked(bool checked); - void setText(const string& text); + void setText(string text); pCheckItem(CheckItem& checkItem) : pAction(checkItem), checkItem(checkItem) {} void constructor(); @@ -242,7 +245,7 @@ public: bool checked(); void setChecked(); void setGroup(const group& group); - void setText(const string& text); + void setText(string text); pRadioItem(RadioItem& radioItem) : pAction(radioItem), radioItem(radioItem) {} void constructor(); @@ -278,8 +281,8 @@ struct pWidget : public pSizable { virtual Size minimumSize(); void setEnabled(bool enabled); void setFocused(); - void setFont(const string& font); - virtual void setGeometry(const Geometry& geometry); + void setFont(string font); + void setGeometry(Geometry geometry); void setVisible(bool visible); pWidget(Widget& widget) : pSizable(widget), widget(widget) {} @@ -298,7 +301,7 @@ public: Size minimumSize(); void setImage(const image& image, Orientation orientation); - void setText(const string& text); + void setText(string text); pButton(Button& button) : pWidget(button), button(button) {} void constructor(); @@ -317,6 +320,8 @@ public: QImage* qtImage; struct QtCanvas : public QWidget { pCanvas& self; + void dragEnterEvent(QDragEnterEvent*); + void dropEvent(QDropEvent*); void leaveEvent(QEvent*); void mouseMoveEvent(QMouseEvent*); void mousePressEvent(QMouseEvent*); @@ -326,7 +331,8 @@ public: }; QtCanvas* qtCanvas; - void setSize(const Size& size); + void setDroppable(bool droppable); + void setSize(Size size); void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} @@ -347,7 +353,7 @@ public: bool checked(); Size minimumSize(); void setChecked(bool checked); - void setText(const string& text); + void setText(string text); pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} void constructor(); @@ -365,8 +371,8 @@ public: ComboButton& comboButton; QComboBox* qtComboButton; - void append(const string& text); - void modify(unsigned row, const string& text); + void append(string text); + void modify(unsigned row, string text); void remove(unsigned row); Size minimumSize(); void reset(); @@ -460,7 +466,7 @@ struct pLabel : public pWidget { QLabel* qtLabel; Size minimumSize(); - void setText(const string& text); + void setText(string text); pLabel(Label& label) : pWidget(label), label(label) {} void constructor(); @@ -477,7 +483,7 @@ public: Size minimumSize(); void setEditable(bool editable); - void setText(const string& text); + void setText(string text); string text(); pLineEdit(LineEdit& lineEdit) : pWidget(lineEdit), lineEdit(lineEdit) {} @@ -548,7 +554,7 @@ public: Size minimumSize(); void setChecked(); void setGroup(const group& group); - void setText(const string& text); + void setText(string text); pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {} pRadioButton& parent(); @@ -569,7 +575,7 @@ public: void setCursorPosition(unsigned position); void setEditable(bool editable); - void setText(const string& text); + void setText(string text); void setWordWrap(bool wordWrap); string text(); @@ -628,6 +634,8 @@ struct pViewport : public pWidget { Viewport& viewport; struct QtViewport : public QWidget { pViewport& self; + void dragEnterEvent(QDragEnterEvent*); + void dropEvent(QDropEvent*); void leaveEvent(QEvent*); void mouseMoveEvent(QMouseEvent*); void mousePressEvent(QMouseEvent*); @@ -637,6 +645,7 @@ struct pViewport : public pWidget { QtViewport* qtViewport; uintptr_t handle(); + void setDroppable(bool droppable); pViewport(Viewport& viewport) : pWidget(viewport), viewport(viewport) {} void constructor(); diff --git a/higan/phoenix/qt/utility.cpp b/higan/phoenix/qt/utility.cpp index 56dc562d..91e16191 100755 --- a/higan/phoenix/qt/utility.cpp +++ b/higan/phoenix/qt/utility.cpp @@ -8,6 +8,21 @@ static QIcon CreateIcon(const nall::image& image, bool scale = false) { return QIcon(QPixmap::fromImage(qtImage)); } +static lstring DropPaths(QDropEvent* event) { + QList urls = event->mimeData()->urls(); + if(urls.size() == 0) return {}; + + lstring paths; + for(unsigned n = 0; n < urls.size(); n++) { + string path = urls[n].path().toUtf8().constData(); + if(path.empty()) continue; + if(directory::exists(path) && !path.endswith("/")) path.append("/"); + paths.append(path); + } + + return paths; +} + static Keyboard::Keycode Keysym(int keysym) { switch(keysym) { case XK_Escape: return Keyboard::Keycode::Escape; diff --git a/higan/phoenix/qt/widget/button.cpp b/higan/phoenix/qt/widget/button.cpp index 74e05d31..32ad8463 100755 --- a/higan/phoenix/qt/widget/button.cpp +++ b/higan/phoenix/qt/widget/button.cpp @@ -26,7 +26,7 @@ void pButton::setImage(const image& image, Orientation orientation) { } } -void pButton::setText(const string& text) { +void pButton::setText(string text) { qtButton->setText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/widget/canvas.cpp b/higan/phoenix/qt/widget/canvas.cpp index 6f1fa374..a63ff0e3 100755 --- a/higan/phoenix/qt/widget/canvas.cpp +++ b/higan/phoenix/qt/widget/canvas.cpp @@ -1,6 +1,10 @@ namespace phoenix { -void pCanvas::setSize(const Size& size) { +void pCanvas::setDroppable(bool droppable) { + qtCanvas->setAcceptDrops(droppable); +} + +void pCanvas::setSize(Size size) { delete qtImage; qtImage = new QImage(size.width, size.height, QImage::Format_ARGB32); } @@ -33,6 +37,18 @@ void pCanvas::orphan() { constructor(); } +void pCanvas::QtCanvas::dragEnterEvent(QDragEnterEvent* event) { + if(event->mimeData()->hasUrls()) { + event->acceptProposedAction(); + } +} + +void pCanvas::QtCanvas::dropEvent(QDropEvent* event) { + lstring paths = DropPaths(event); + if(paths.empty()) return; + if(self.canvas.onDrop) self.canvas.onDrop(paths); +} + void pCanvas::QtCanvas::leaveEvent(QEvent* event) { if(self.canvas.onMouseLeave) self.canvas.onMouseLeave(); } diff --git a/higan/phoenix/qt/widget/check-button.cpp b/higan/phoenix/qt/widget/check-button.cpp index 0e7cad1f..93b4e1c9 100644 --- a/higan/phoenix/qt/widget/check-button.cpp +++ b/higan/phoenix/qt/widget/check-button.cpp @@ -15,7 +15,7 @@ void pCheckButton::setChecked(bool checked) { locked = false; } -void pCheckButton::setText(const string& text) { +void pCheckButton::setText(string text) { qtCheckButton->setText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/widget/combo-button.cpp b/higan/phoenix/qt/widget/combo-button.cpp index 08cdbca9..73433450 100644 --- a/higan/phoenix/qt/widget/combo-button.cpp +++ b/higan/phoenix/qt/widget/combo-button.cpp @@ -1,6 +1,6 @@ namespace phoenix { -void pComboButton::append(const string& text) { +void pComboButton::append(string text) { locked = true; qtComboButton->addItem(QString::fromUtf8(text)); locked = false; @@ -13,7 +13,7 @@ Size pComboButton::minimumSize() { return {maximumWidth + 32, size.height + 12}; } -void pComboButton::modify(unsigned row, const string& text) { +void pComboButton::modify(unsigned row, string text) { qtComboButton->setItemText(row, text); } diff --git a/higan/phoenix/qt/widget/label.cpp b/higan/phoenix/qt/widget/label.cpp index fbeaffec..e41a968d 100755 --- a/higan/phoenix/qt/widget/label.cpp +++ b/higan/phoenix/qt/widget/label.cpp @@ -5,7 +5,7 @@ Size pLabel::minimumSize() { return {size.width, size.height}; } -void pLabel::setText(const string& text) { +void pLabel::setText(string text) { qtLabel->setText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/widget/line-edit.cpp b/higan/phoenix/qt/widget/line-edit.cpp index 261039b7..f5d610b4 100755 --- a/higan/phoenix/qt/widget/line-edit.cpp +++ b/higan/phoenix/qt/widget/line-edit.cpp @@ -9,7 +9,7 @@ void pLineEdit::setEditable(bool editable) { qtLineEdit->setReadOnly(!editable); } -void pLineEdit::setText(const string& text) { +void pLineEdit::setText(string text) { qtLineEdit->setText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/widget/radio-button.cpp b/higan/phoenix/qt/widget/radio-button.cpp index 034621a0..83c9cca2 100644 --- a/higan/phoenix/qt/widget/radio-button.cpp +++ b/higan/phoenix/qt/widget/radio-button.cpp @@ -26,7 +26,7 @@ void pRadioButton::setGroup(const group& group) { parent().locked = false; } -void pRadioButton::setText(const string& text) { +void pRadioButton::setText(string text) { qtRadioButton->setText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/widget/text-edit.cpp b/higan/phoenix/qt/widget/text-edit.cpp index 1e4c2c29..eb7a4e6e 100755 --- a/higan/phoenix/qt/widget/text-edit.cpp +++ b/higan/phoenix/qt/widget/text-edit.cpp @@ -11,7 +11,7 @@ void pTextEdit::setEditable(bool editable) { qtTextEdit->setReadOnly(!editable); } -void pTextEdit::setText(const string& text) { +void pTextEdit::setText(string text) { qtTextEdit->setPlainText(QString::fromUtf8(text)); } diff --git a/higan/phoenix/qt/widget/viewport.cpp b/higan/phoenix/qt/widget/viewport.cpp index e6fb1a18..e812e6fb 100755 --- a/higan/phoenix/qt/widget/viewport.cpp +++ b/higan/phoenix/qt/widget/viewport.cpp @@ -4,6 +4,10 @@ uintptr_t pViewport::handle() { return (uintptr_t)qtViewport->winId(); } +void pViewport::setDroppable(bool droppable) { + qtViewport->setAcceptDrops(droppable); +} + void pViewport::constructor() { qtWidget = qtViewport = new QtViewport(*this); qtViewport->setMouseTracking(true); @@ -23,6 +27,18 @@ void pViewport::orphan() { constructor(); } +void pViewport::QtViewport::dragEnterEvent(QDragEnterEvent* event) { + if(event->mimeData()->hasUrls()) { + event->acceptProposedAction(); + } +} + +void pViewport::QtViewport::dropEvent(QDropEvent* event) { + lstring paths = DropPaths(event); + if(paths.empty()) return; + if(self.viewport.onDrop) self.viewport.onDrop(paths); +} + void pViewport::QtViewport::leaveEvent(QEvent* event) { if(self.viewport.onMouseLeave) self.viewport.onMouseLeave(); } diff --git a/higan/phoenix/qt/widget/widget.cpp b/higan/phoenix/qt/widget/widget.cpp index 075b876d..a0c991aa 100755 --- a/higan/phoenix/qt/widget/widget.cpp +++ b/higan/phoenix/qt/widget/widget.cpp @@ -18,11 +18,11 @@ void pWidget::setFocused() { qtWidget->setFocus(Qt::OtherFocusReason); } -void pWidget::setFont(const string& font) { +void pWidget::setFont(string font) { qtWidget->setFont(pFont::create(font)); } -void pWidget::setGeometry(const Geometry& geometry) { +void pWidget::setGeometry(Geometry geometry) { qtWidget->setGeometry(geometry.x, geometry.y, geometry.width, geometry.height); } diff --git a/higan/phoenix/qt/window.cpp b/higan/phoenix/qt/window.cpp index 55db722d..7971f9ad 100755 --- a/higan/phoenix/qt/window.cpp +++ b/higan/phoenix/qt/window.cpp @@ -73,7 +73,7 @@ void pWindow::remove(Widget& widget) { if(qtApplication) widget.p.orphan(); } -void pWindow::setBackgroundColor(const Color& color) { +void pWindow::setBackgroundColor(Color color) { QPalette palette; palette.setColor(QPalette::Window, QColor(color.red, color.green, color.blue, color.alpha)); qtContainer->setPalette(palette); @@ -81,6 +81,10 @@ void pWindow::setBackgroundColor(const Color& color) { qtWindow->setAttribute(Qt::WA_TranslucentBackground, color.alpha != 255); } +void pWindow::setDroppable(bool droppable) { + qtWindow->setAcceptDrops(droppable); +} + void pWindow::setFocused() { qtWindow->raise(); qtWindow->activateWindow(); @@ -98,11 +102,11 @@ void pWindow::setFullScreen(bool fullScreen) { } } -void pWindow::setGeometry(const Geometry& geometry_) { +void pWindow::setGeometry(Geometry geometry) { locked = true; Application::processEvents(); QApplication::syncX(); - Geometry geometry = geometry_, margin = frameMargin(); + Geometry margin = frameMargin(); setResizable(window.state.resizable); qtWindow->move(geometry.x - frameMargin().x, geometry.y - frameMargin().y); @@ -115,14 +119,13 @@ void pWindow::setGeometry(const Geometry& geometry_) { } for(auto& layout : window.state.layout) { - geometry = geometry_; geometry.x = geometry.y = 0; layout.setGeometry(geometry); } locked = false; } -void pWindow::setMenuFont(const string& font) { +void pWindow::setMenuFont(string font) { qtMenu->setFont(pFont::create(font)); for(auto& item : window.state.menu) item.p.setFont(font); } @@ -157,11 +160,11 @@ void pWindow::setResizable(bool resizable) { qtStatus->setSizeGripEnabled(resizable); } -void pWindow::setStatusFont(const string& font) { +void pWindow::setStatusFont(string font) { qtStatus->setFont(pFont::create(font)); } -void pWindow::setStatusText(const string& text) { +void pWindow::setStatusText(string text) { qtStatus->showMessage(QString::fromUtf8(text), 0); } @@ -170,7 +173,7 @@ void pWindow::setStatusVisible(bool visible) { setGeometry(window.state.geometry); } -void pWindow::setTitle(const string& text) { +void pWindow::setTitle(string text) { qtWindow->setWindowTitle(QString::fromUtf8(text)); } @@ -184,7 +187,7 @@ void pWindow::setVisible(bool visible) { locked = false; } -void pWindow::setWidgetFont(const string& font) { +void pWindow::setWidgetFont(string font) { } void pWindow::constructor() { @@ -273,6 +276,18 @@ void pWindow::QtWindow::moveEvent(QMoveEvent* event) { } } +void pWindow::QtWindow::dragEnterEvent(QDragEnterEvent* event) { + if(event->mimeData()->hasUrls()) { + event->acceptProposedAction(); + } +} + +void pWindow::QtWindow::dropEvent(QDropEvent* event) { + lstring paths = DropPaths(event); + if(paths.empty()) return; + if(self.window.onDrop) self.window.onDrop(paths); +} + void pWindow::QtWindow::keyPressEvent(QKeyEvent* event) { Keyboard::Keycode sym = Keysym(event->nativeVirtualKey()); if(sym != Keyboard::Keycode::None && self.window.onKeyPress) self.window.onKeyPress(sym); diff --git a/higan/phoenix/reference/widget/canvas.cpp b/higan/phoenix/reference/widget/canvas.cpp index 2ad3accb..1f2a3988 100755 --- a/higan/phoenix/reference/widget/canvas.cpp +++ b/higan/phoenix/reference/widget/canvas.cpp @@ -1,6 +1,9 @@ namespace phoenix { -void pCanvas::setSize(const Size& size) { +void pCanvas::setDroppable(bool droppable) { +} + +void pCanvas::setSize(Size size) { } void pCanvas::update() { diff --git a/higan/phoenix/reference/widget/canvas.hpp b/higan/phoenix/reference/widget/canvas.hpp index bccbb042..98e5d520 100644 --- a/higan/phoenix/reference/widget/canvas.hpp +++ b/higan/phoenix/reference/widget/canvas.hpp @@ -3,7 +3,8 @@ namespace phoenix { struct pCanvas : public pWidget { Canvas& canvas; - void setSize(const Size& size); + void setDroppable(bool droppable); + void setSize(Size size); void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} diff --git a/higan/phoenix/reference/widget/viewport.cpp b/higan/phoenix/reference/widget/viewport.cpp index f4ad01ee..7ed229a2 100755 --- a/higan/phoenix/reference/widget/viewport.cpp +++ b/higan/phoenix/reference/widget/viewport.cpp @@ -4,6 +4,9 @@ uintptr_t pViewport::handle() { return 0; } +void pViewport::setDroppable(bool droppable) { +} + void pViewport::constructor() { } diff --git a/higan/phoenix/reference/widget/viewport.hpp b/higan/phoenix/reference/widget/viewport.hpp index 21aff082..75ef63ad 100644 --- a/higan/phoenix/reference/widget/viewport.hpp +++ b/higan/phoenix/reference/widget/viewport.hpp @@ -4,6 +4,7 @@ struct pViewport : public pWidget { Viewport& viewport; uintptr_t handle(); + void setDroppable(bool droppable); pViewport(Viewport& viewport) : pWidget(viewport), viewport(viewport) {} void constructor(); diff --git a/higan/phoenix/reference/widget/widget.cpp b/higan/phoenix/reference/widget/widget.cpp index 7fc18aa7..8589f5db 100755 --- a/higan/phoenix/reference/widget/widget.cpp +++ b/higan/phoenix/reference/widget/widget.cpp @@ -21,7 +21,7 @@ void pWidget::setFocused() { void pWidget::setFont(string font) { } -void pWidget::setGeometry(const Geometry& geometry) { +void pWidget::setGeometry(Geometry geometry) { } void pWidget::setVisible(bool visible) { diff --git a/higan/phoenix/reference/widget/widget.hpp b/higan/phoenix/reference/widget/widget.hpp index 3947064d..6a624a0f 100644 --- a/higan/phoenix/reference/widget/widget.hpp +++ b/higan/phoenix/reference/widget/widget.hpp @@ -9,7 +9,7 @@ struct pWidget : public pSizable { void setEnabled(bool enabled); void setFocused(); void setFont(string font); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setVisible(bool visible); pWidget(Widget& widget) : pSizable(widget), widget(widget) {} diff --git a/higan/phoenix/reference/window.cpp b/higan/phoenix/reference/window.cpp index dc4d3f4a..f431e807 100755 --- a/higan/phoenix/reference/window.cpp +++ b/higan/phoenix/reference/window.cpp @@ -1,7 +1,7 @@ namespace phoenix { Window& pWindow::none() { - static Window *window = nullptr; + static Window* window = nullptr; if(window == nullptr) window = new Window; return *window; } @@ -40,7 +40,10 @@ void pWindow::remove(Menu& menu) { void pWindow::remove(Widget& widget) { } -void pWindow::setBackgroundColor(const Color& color) { +void pWindow::setBackgroundColor(Color color) { +} + +void pWindow::setDroppable(bool droppable) { } void pWindow::setFocused() { @@ -49,7 +52,7 @@ void pWindow::setFocused() { void pWindow::setFullScreen(bool fullScreen) { } -void pWindow::setGeometry(const Geometry& geometry) { +void pWindow::setGeometry(Geometry geometry) { } void pWindow::setMenuFont(string font) { diff --git a/higan/phoenix/reference/window.hpp b/higan/phoenix/reference/window.hpp index 6546ea55..7d3b6e3f 100644 --- a/higan/phoenix/reference/window.hpp +++ b/higan/phoenix/reference/window.hpp @@ -15,10 +15,11 @@ struct pWindow : public pObject { void remove(Layout& layout); void remove(Menu& menu); void remove(Widget& widget); - void setBackgroundColor(const Color& color); + void setBackgroundColor(Color color); + void setDroppable(bool droppable); void setFocused(); void setFullScreen(bool fullScreen); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void setMenuFont(string font); void setMenuVisible(bool visible); void setModal(bool modal); diff --git a/higan/phoenix/windows/action/check-item.cpp b/higan/phoenix/windows/action/check-item.cpp index 8ccce049..825ad702 100755 --- a/higan/phoenix/windows/action/check-item.cpp +++ b/higan/phoenix/windows/action/check-item.cpp @@ -8,7 +8,7 @@ void pCheckItem::setChecked(bool checked) { if(parentMenu) CheckMenuItem(parentMenu->p.hmenu, id, checked ? MF_CHECKED : MF_UNCHECKED); } -void pCheckItem::setText(const string& text) { +void pCheckItem::setText(string text) { if(parentWindow) parentWindow->p.updateMenu(); } diff --git a/higan/phoenix/windows/action/item.cpp b/higan/phoenix/windows/action/item.cpp index 97f82355..a7a146e4 100755 --- a/higan/phoenix/windows/action/item.cpp +++ b/higan/phoenix/windows/action/item.cpp @@ -5,7 +5,7 @@ void pItem::setImage(const image& image) { if(parentWindow) parentWindow->p.updateMenu(); } -void pItem::setText(const string& text) { +void pItem::setText(string text) { if(parentWindow) parentWindow->p.updateMenu(); } diff --git a/higan/phoenix/windows/action/menu.cpp b/higan/phoenix/windows/action/menu.cpp index 6e5a8665..a44457b7 100755 --- a/higan/phoenix/windows/action/menu.cpp +++ b/higan/phoenix/windows/action/menu.cpp @@ -15,7 +15,7 @@ void pMenu::setImage(const image& image) { if(parentWindow) parentWindow->p.updateMenu(); } -void pMenu::setText(const string& text) { +void pMenu::setText(string text) { if(parentWindow) parentWindow->p.updateMenu(); } diff --git a/higan/phoenix/windows/action/radio-item.cpp b/higan/phoenix/windows/action/radio-item.cpp index 10540aa5..ce0b445b 100755 --- a/higan/phoenix/windows/action/radio-item.cpp +++ b/higan/phoenix/windows/action/radio-item.cpp @@ -16,7 +16,7 @@ void pRadioItem::setChecked() { void pRadioItem::setGroup(const group& group) { } -void pRadioItem::setText(const string& text) { +void pRadioItem::setText(string text) { if(parentWindow) parentWindow->p.updateMenu(); } diff --git a/higan/phoenix/windows/application.cpp b/higan/phoenix/windows/application.cpp index a50ec1ca..1d6ff354 100644 --- a/higan/phoenix/windows/application.cpp +++ b/higan/phoenix/windows/application.cpp @@ -239,6 +239,18 @@ static LRESULT CALLBACK Application_windowProc(HWND hwnd, UINT msg, WPARAM wpara break; } + case WM_ENTERMENULOOP: + case WM_ENTERSIZEMOVE: { + if(Application::Windows::onModalBegin) Application::Windows::onModalBegin(); + return 0; + } + + case WM_EXITMENULOOP: + case WM_EXITSIZEMOVE: { + if(Application::Windows::onModalEnd) Application::Windows::onModalEnd(); + return 0; + } + case WM_ERASEBKGND: { if(window.p.brush == 0) break; RECT rc; @@ -261,6 +273,14 @@ static LRESULT CALLBACK Application_windowProc(HWND hwnd, UINT msg, WPARAM wpara break; } + case WM_DROPFILES: { + lstring paths = DropPaths(wparam); + if(paths.empty() == false) { + if(window.onDrop) window.onDrop(paths); + } + return FALSE; + } + case WM_COMMAND: { unsigned id = LOWORD(wparam); HWND control = GetDlgItem(window.p.hwnd, id); diff --git a/higan/phoenix/windows/font.cpp b/higan/phoenix/windows/font.cpp index f65673c5..19781143 100755 --- a/higan/phoenix/windows/font.cpp +++ b/higan/phoenix/windows/font.cpp @@ -18,14 +18,14 @@ string pFont::monospace(unsigned size, string style) { return {"Lucida Console, ", size, ", ", style}; } -Size pFont::size(const string& font, const string& text) { +Size pFont::size(string font, string text) { HFONT hfont = pFont::create(font); Size size = pFont::size(hfont, text); pFont::free(hfont); return size; } -HFONT pFont::create(const string& description) { +HFONT pFont::create(string description) { lstring part; part.split(",", description); for(auto& item : part) item.trim(" "); @@ -37,8 +37,8 @@ HFONT pFont::create(const string& description) { if(part[0] != "") family = part[0]; if(part.size() >= 2) size = decimal(part[1]); - if(part.size() >= 3) bold = part[2].position("Bold"); - if(part.size() >= 3) italic = part[2].position("Italic"); + if(part.size() >= 3) bold = part[2].find("Bold"); + if(part.size() >= 3) italic = part[2].find("Italic"); return CreateFont( -(size * 96.0 / 72.0 + 0.5), @@ -51,9 +51,9 @@ void pFont::free(HFONT hfont) { DeleteObject(hfont); } -Size pFont::size(HFONT hfont, const string& text_) { +Size pFont::size(HFONT hfont, string text) { //temporary fix: empty text string returns height of zero; bad for eg Button height - string text = (text_ == "" ? " " : text_); + if(text.empty()) text = " "; HDC hdc = GetDC(0); SelectObject(hdc, hfont); diff --git a/higan/phoenix/windows/platform.hpp b/higan/phoenix/windows/platform.hpp index a92615a6..9b464c58 100755 --- a/higan/phoenix/windows/platform.hpp +++ b/higan/phoenix/windows/platform.hpp @@ -24,11 +24,11 @@ struct pFont { static string serif(unsigned size, string style); static string sans(unsigned size, string style); static string monospace(unsigned size, string style); - static Size size(const string& font, const string& text); + static Size size(string font, string text); - static HFONT create(const string& description); + static HFONT create(string description); static void free(HFONT hfont); - static Size size(HFONT hfont, const string& text); + static Size size(HFONT hfont, string text); }; struct pDesktop { @@ -111,20 +111,21 @@ struct pWindow : public pObject { void remove(Layout& layout); void remove(Menu& menu); void remove(Widget& widget); - void setBackgroundColor(const Color& color); + void setBackgroundColor(Color color); + void setDroppable(bool droppable); void setFocused(); void setFullScreen(bool fullScreen); - void setGeometry(const Geometry& geometry); - void setMenuFont(const string& font); + void setGeometry(Geometry geometry); + void setMenuFont(string font); void setMenuVisible(bool visible); void setModal(bool modal); void setResizable(bool resizable); - void setStatusFont(const string& font); - void setStatusText(const string& text); + void setStatusFont(string font); + void setStatusText(string text); void setStatusVisible(bool visible); - void setTitle(const string& text); + void setTitle(string text); void setVisible(bool visible); - void setWidgetFont(const string& font); + void setWidgetFont(string font); pWindow(Window& window) : pObject(window), window(window) {} void constructor(); @@ -152,7 +153,7 @@ struct pMenu : public pAction { void append(Action& action); void remove(Action& action); void setImage(const image& image); - void setText(const string& text); + void setText(string text); pMenu(Menu& menu) : pAction(menu), menu(menu), hbitmap(0) {} void constructor(); @@ -174,7 +175,7 @@ struct pItem : public pAction { HBITMAP hbitmap; void setImage(const image& image); - void setText(const string& text); + void setText(string text); pItem(Item& item) : pAction(item), item(item), hbitmap(0) {} void constructor(); @@ -187,7 +188,7 @@ struct pCheckItem : public pAction { bool checked(); void setChecked(bool checked); - void setText(const string& text); + void setText(string text); pCheckItem(CheckItem& checkItem) : pAction(checkItem), checkItem(checkItem) {} void constructor(); @@ -200,7 +201,7 @@ struct pRadioItem : public pAction { bool checked(); void setChecked(); void setGroup(const group& group); - void setText(const string& text); + void setText(string text); pRadioItem(RadioItem& radioItem) : pAction(radioItem), radioItem(radioItem) {} void constructor(); @@ -230,8 +231,8 @@ struct pWidget : public pSizable { virtual Size minimumSize(); void setEnabled(bool enabled); void setFocused(); - void setFont(const string &font); - virtual void setGeometry(const Geometry& geometry); + void setFont(string font); + virtual void setGeometry(Geometry geometry); void setVisible(bool visible); pWidget(Widget& widget) : pSizable(widget), widget(widget) { parentWindow = &Window::none(); } @@ -249,7 +250,7 @@ struct pButton : public pWidget { Size minimumSize(); void setImage(const image& image, Orientation orientation); - void setText(const string& text); + void setText(string text); pButton(Button& button) : pWidget(button), button(button), hbitmap(0), himagelist(0) {} void constructor(); @@ -261,7 +262,8 @@ struct pCanvas : public pWidget { Canvas& canvas; uint32_t* data; - void setSize(const Size& size); + void setDroppable(bool droppable); + void setSize(Size size); void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} @@ -277,7 +279,7 @@ struct pCheckButton : public pWidget { bool checked(); Size minimumSize(); void setChecked(bool checked); - void setText(const string& text); + void setText(string text); pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} void constructor(); @@ -288,8 +290,8 @@ struct pCheckButton : public pWidget { struct pComboButton : public pWidget { ComboButton& comboButton; - void append(const string& text); - void modify(unsigned row, const string& text); + void append(string text); + void modify(unsigned row, string text); void remove(unsigned row); Size minimumSize(); void reset(); @@ -300,7 +302,7 @@ struct pComboButton : public pWidget { void constructor(); void destructor(); void orphan(); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); }; struct pHexEdit : public pWidget { @@ -352,7 +354,7 @@ struct pLabel : public pWidget { Label& label; Size minimumSize(); - void setText(const string& text); + void setText(string text); pLabel(Label& label) : pWidget(label), label(label) {} void constructor(); @@ -365,7 +367,7 @@ struct pLineEdit : public pWidget { Size minimumSize(); void setEditable(bool editable); - void setText(const string& text); + void setText(string text); string text(); pLineEdit(LineEdit& lineEdit) : pWidget(lineEdit), lineEdit(lineEdit) {} @@ -401,7 +403,7 @@ struct pListView : public pWidget { void constructor(); void destructor(); void orphan(); - void setGeometry(const Geometry& geometry); + void setGeometry(Geometry geometry); void buildImageList(); }; @@ -424,7 +426,7 @@ struct pRadioButton : public pWidget { Size minimumSize(); void setChecked(); void setGroup(const group& group); - void setText(const string& text); + void setText(string text); pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {} void constructor(); @@ -437,7 +439,7 @@ struct pTextEdit : public pWidget { void setCursorPosition(unsigned position); void setEditable(bool editable); - void setText(const string& text); + void setText(string text); void setWordWrap(bool wordWrap); string text(); @@ -479,6 +481,7 @@ struct pViewport : public pWidget { Viewport& viewport; uintptr_t handle(); + void setDroppable(bool droppable); pViewport(Viewport& viewport) : pWidget(viewport), viewport(viewport) {} void constructor(); diff --git a/higan/phoenix/windows/utility.cpp b/higan/phoenix/windows/utility.cpp index f0dd90c8..a1375539 100755 --- a/higan/phoenix/windows/utility.cpp +++ b/higan/phoenix/windows/utility.cpp @@ -36,6 +36,28 @@ static unsigned GetWindowZOrder(HWND hwnd) { return z; } +static lstring DropPaths(WPARAM wparam) { + auto dropList = HDROP(wparam); + auto fileCount = DragQueryFile(dropList, ~0u, nullptr, 0); + + lstring paths; + for(unsigned n = 0; n < fileCount; n++) { + auto length = DragQueryFile(dropList, n, nullptr, 0); + auto buffer = new wchar_t[length + 1]; + + if(DragQueryFile(dropList, n, buffer, length + 1)) { + string path = (const char*)utf8_t(buffer); + path.transform("\\", "/"); + if(directory::exists(path) && !path.endswith("/")) path.append("/"); + paths.append(path); + } + + delete[] buffer; + } + + return paths; +} + static Keyboard::Keycode Keysym(unsigned keysym, unsigned keyflags) { #define pressed(keysym) (GetAsyncKeyState(keysym) & 0x8000) #define enabled(keysym) (GetKeyState(keysym)) diff --git a/higan/phoenix/windows/widget/button.cpp b/higan/phoenix/windows/widget/button.cpp index 9ff498e6..dd861176 100755 --- a/higan/phoenix/windows/widget/button.cpp +++ b/higan/phoenix/windows/widget/button.cpp @@ -69,7 +69,7 @@ void pButton::setImage(const image& image, Orientation orientation) { setText(button.state.text); //update text to display nicely with image (or lack thereof) } -void pButton::setText(const string& text) { +void pButton::setText(string text) { if(text.empty()) { //bitmaps will not show up if text is empty SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP); diff --git a/higan/phoenix/windows/widget/canvas.cpp b/higan/phoenix/windows/widget/canvas.cpp index 568e97cb..887650c5 100755 --- a/higan/phoenix/windows/widget/canvas.cpp +++ b/higan/phoenix/windows/widget/canvas.cpp @@ -6,6 +6,14 @@ static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LP if(!dynamic_cast(object)) return DefWindowProc(hwnd, msg, wparam, lparam); Canvas& canvas = (Canvas&)*object; + if(msg == WM_DROPFILES) { + lstring paths = DropPaths(wparam); + if(paths.empty() == false) { + if(canvas.onDrop) canvas.onDrop(paths); + } + return FALSE; + } + if(msg == WM_GETDLGCODE) { return DLGC_STATIC | DLGC_WANTCHARS; } @@ -44,7 +52,11 @@ static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LP return DefWindowProc(hwnd, msg, wparam, lparam); } -void pCanvas::setSize(const Size& size) { +void pCanvas::setDroppable(bool droppable) { + DragAcceptFiles(hwnd, droppable); +} + +void pCanvas::setSize(Size size) { delete[] data; data = new uint32_t[size.width * size.height]; } @@ -59,6 +71,7 @@ void pCanvas::constructor() { memcpy(data, canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t)); hwnd = CreateWindow(L"phoenix_canvas", L"", WS_CHILD, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&canvas); + setDroppable(canvas.state.droppable); synchronize(); } diff --git a/higan/phoenix/windows/widget/check-button.cpp b/higan/phoenix/windows/widget/check-button.cpp index 393aa048..b8b40de0 100644 --- a/higan/phoenix/windows/widget/check-button.cpp +++ b/higan/phoenix/windows/widget/check-button.cpp @@ -13,7 +13,7 @@ void pCheckButton::setChecked(bool checked) { SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0); } -void pCheckButton::setText(const string& text) { +void pCheckButton::setText(string text) { SetWindowText(hwnd, utf16_t(text)); } diff --git a/higan/phoenix/windows/widget/combo-button.cpp b/higan/phoenix/windows/widget/combo-button.cpp index 8aa507cd..6f0d0235 100644 --- a/higan/phoenix/windows/widget/combo-button.cpp +++ b/higan/phoenix/windows/widget/combo-button.cpp @@ -1,6 +1,6 @@ namespace phoenix { -void pComboButton::append(const string& text) { +void pComboButton::append(string text) { SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)(wchar_t*)utf16_t(text)); if(SendMessage(hwnd, CB_GETCOUNT, 0, 0) == 1) setSelection(0); } @@ -11,7 +11,7 @@ Size pComboButton::minimumSize() { return {maximumWidth + 24, pFont::size(hfont, " ").height + 10}; } -void pComboButton::modify(unsigned row, const string& text) { +void pComboButton::modify(unsigned row, string text) { locked = true; unsigned position = selection(); SendMessage(hwnd, CB_DELETESTRING, row, 0); @@ -63,7 +63,7 @@ void pComboButton::orphan() { constructor(); } -void pComboButton::setGeometry(const Geometry& geometry) { +void pComboButton::setGeometry(Geometry geometry) { SetWindowPos(hwnd, NULL, geometry.x, geometry.y, geometry.width, 1, SWP_NOZORDER); RECT rc; GetWindowRect(hwnd, &rc); diff --git a/higan/phoenix/windows/widget/label.cpp b/higan/phoenix/windows/widget/label.cpp index 97c7d72f..26f6754b 100755 --- a/higan/phoenix/windows/widget/label.cpp +++ b/higan/phoenix/windows/widget/label.cpp @@ -5,7 +5,7 @@ Size pLabel::minimumSize() { return {size.width, size.height}; } -void pLabel::setText(const string& text) { +void pLabel::setText(string text) { SetWindowText(hwnd, utf16_t(text)); InvalidateRect(hwnd, 0, false); } diff --git a/higan/phoenix/windows/widget/line-edit.cpp b/higan/phoenix/windows/widget/line-edit.cpp index e147553d..7f60b173 100755 --- a/higan/phoenix/windows/widget/line-edit.cpp +++ b/higan/phoenix/windows/widget/line-edit.cpp @@ -9,7 +9,7 @@ void pLineEdit::setEditable(bool editable) { SendMessage(hwnd, EM_SETREADONLY, editable == false, 0); } -void pLineEdit::setText(const string& text) { +void pLineEdit::setText(string text) { locked = true; SetWindowText(hwnd, utf16_t(text)); locked = false; diff --git a/higan/phoenix/windows/widget/list-view.cpp b/higan/phoenix/windows/widget/list-view.cpp index cc269696..31dcaf32 100755 --- a/higan/phoenix/windows/widget/list-view.cpp +++ b/higan/phoenix/windows/widget/list-view.cpp @@ -195,7 +195,7 @@ void pListView::orphan() { constructor(); } -void pListView::setGeometry(const Geometry& geometry) { +void pListView::setGeometry(Geometry geometry) { pWidget::setGeometry(geometry); autoSizeColumns(); } diff --git a/higan/phoenix/windows/widget/radio-button.cpp b/higan/phoenix/windows/widget/radio-button.cpp index 0b1cc483..7f1d5648 100644 --- a/higan/phoenix/windows/widget/radio-button.cpp +++ b/higan/phoenix/windows/widget/radio-button.cpp @@ -18,7 +18,7 @@ void pRadioButton::setChecked() { void pRadioButton::setGroup(const group& group) { } -void pRadioButton::setText(const string& text) { +void pRadioButton::setText(string text) { SetWindowText(hwnd, utf16_t(text)); } diff --git a/higan/phoenix/windows/widget/text-edit.cpp b/higan/phoenix/windows/widget/text-edit.cpp index 7259c52b..2433320a 100755 --- a/higan/phoenix/windows/widget/text-edit.cpp +++ b/higan/phoenix/windows/widget/text-edit.cpp @@ -10,7 +10,7 @@ void pTextEdit::setEditable(bool editable) { SendMessage(hwnd, EM_SETREADONLY, editable == false, (LPARAM)0); } -void pTextEdit::setText(const string& text) { +void pTextEdit::setText(string text) { locked = true; string output = text; output.replace("\r", ""); diff --git a/higan/phoenix/windows/widget/viewport.cpp b/higan/phoenix/windows/widget/viewport.cpp index bffbf417..3bda4529 100755 --- a/higan/phoenix/windows/widget/viewport.cpp +++ b/higan/phoenix/windows/widget/viewport.cpp @@ -6,6 +6,14 @@ static LRESULT CALLBACK Viewport_windowProc(HWND hwnd, UINT msg, WPARAM wparam, if(!dynamic_cast(object)) return DefWindowProc(hwnd, msg, wparam, lparam); Viewport& viewport = (Viewport&)*object; + if(msg == WM_DROPFILES) { + lstring paths = DropPaths(wparam); + if(paths.empty() == false) { + if(viewport.onDrop) viewport.onDrop(paths); + } + return FALSE; + } + if(msg == WM_GETDLGCODE) { return DLGC_STATIC | DLGC_WANTCHARS; } @@ -43,9 +51,14 @@ uintptr_t pViewport::handle() { return (uintptr_t)hwnd; } +void pViewport::setDroppable(bool droppable) { + DragAcceptFiles(hwnd, droppable); +} + void pViewport::constructor() { hwnd = CreateWindow(L"phoenix_viewport", L"", WS_CHILD | WS_DISABLED, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&viewport); + setDroppable(viewport.state.droppable); synchronize(); } diff --git a/higan/phoenix/windows/widget/widget.cpp b/higan/phoenix/windows/widget/widget.cpp index 5545d82c..fbec9a32 100755 --- a/higan/phoenix/windows/widget/widget.cpp +++ b/higan/phoenix/windows/widget/widget.cpp @@ -22,13 +22,13 @@ void pWidget::setFocused() { SetFocus(hwnd); } -void pWidget::setFont(const string& font) { +void pWidget::setFont(string font) { if(hfont) DeleteObject(hfont); hfont = pFont::create(font); SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0); } -void pWidget::setGeometry(const Geometry& geometry) { +void pWidget::setGeometry(Geometry geometry) { SetWindowPos(hwnd, NULL, geometry.x, geometry.y, geometry.width, geometry.height, SWP_NOZORDER); } diff --git a/higan/phoenix/windows/window.cpp b/higan/phoenix/windows/window.cpp index 6dce405d..16223da4 100755 --- a/higan/phoenix/windows/window.cpp +++ b/higan/phoenix/windows/window.cpp @@ -122,12 +122,16 @@ void pWindow::remove(Widget& widget) { widget.p.orphan(); } -void pWindow::setBackgroundColor(const Color& color) { +void pWindow::setBackgroundColor(Color color) { if(brush) DeleteObject(brush); brushColor = RGB(color.red, color.green, color.blue); brush = CreateSolidBrush(brushColor); } +void pWindow::setDroppable(bool droppable) { + DragAcceptFiles(hwnd, droppable); +} + void pWindow::setFocused() { if(window.state.visible == false) setVisible(true); SetFocus(hwnd); @@ -146,7 +150,7 @@ void pWindow::setFullScreen(bool fullScreen) { locked = false; } -void pWindow::setGeometry(const Geometry& geometry) { +void pWindow::setGeometry(Geometry geometry) { locked = true; Geometry margin = frameMargin(); SetWindowPos( @@ -164,7 +168,7 @@ void pWindow::setGeometry(const Geometry& geometry) { locked = false; } -void pWindow::setMenuFont(const string& font) { +void pWindow::setMenuFont(string font) { } void pWindow::setMenuVisible(bool visible) { @@ -192,13 +196,13 @@ void pWindow::setResizable(bool resizable) { setGeometry(window.state.geometry); } -void pWindow::setStatusFont(const string& font) { +void pWindow::setStatusFont(string font) { if(hstatusfont) DeleteObject(hstatusfont); hstatusfont = pFont::create(font); SendMessage(hstatus, WM_SETFONT, (WPARAM)hstatusfont, 0); } -void pWindow::setStatusText(const string& text) { +void pWindow::setStatusText(string text) { SendMessage(hstatus, SB_SETTEXT, 0, (LPARAM)(wchar_t*)utf16_t(text)); } @@ -209,7 +213,7 @@ void pWindow::setStatusVisible(bool visible) { locked = false; } -void pWindow::setTitle(const string& text) { +void pWindow::setTitle(string text) { SetWindowText(hwnd, utf16_t(text)); } @@ -218,7 +222,7 @@ void pWindow::setVisible(bool visible) { if(visible == false) setModal(false); } -void pWindow::setWidgetFont(const string &font) { +void pWindow::setWidgetFont(string font) { } void pWindow::constructor() { @@ -234,6 +238,7 @@ void pWindow::constructor() { SetWindowLongPtr(hstatus, GWL_STYLE, GetWindowLong(hstatus, GWL_STYLE) | WS_DISABLED); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&window); + setDroppable(window.state.droppable); setGeometry({128, 128, 256, 256}); } diff --git a/higan/ruby/implementation.cpp b/higan/ruby/implementation.cpp index 7eebb795..7b13504d 100644 --- a/higan/ruby/implementation.cpp +++ b/higan/ruby/implementation.cpp @@ -1,17 +1,5 @@ /* Global Headers */ -#if defined(VIDEO_QTOPENGL) || defined(VIDEO_QTRASTER) - #include - #include -#endif - -#if defined(VIDEO_QTOPENGL) - #include - #if defined(PLATFORM_WINDOWS) - #include - #endif -#endif - #if defined(PLATFORM_X) #include #include @@ -72,14 +60,6 @@ using namespace nall; #include #endif -#ifdef VIDEO_QTOPENGL - #include -#endif - -#ifdef VIDEO_QTRASTER - #include -#endif - #ifdef VIDEO_SDL #include #endif diff --git a/higan/ruby/ruby.cpp b/higan/ruby/ruby.cpp index e4ff9357..68d89165 100755 --- a/higan/ruby/ruby.cpp +++ b/higan/ruby/ruby.cpp @@ -24,7 +24,7 @@ const unsigned Video::FilterLinear = 1; void VideoInterface::driver(const char* driver) { if(p) term(); - if(!driver || !*driver) driver = default_driver(); + if(!driver || !*driver) driver = optimalDriver(); if(0); @@ -75,8 +75,34 @@ void VideoInterface::driver(const char* driver) { else p = new Video(); } -//select the *safest* available driver, not the fastest -const char* VideoInterface::default_driver() { +const char* VideoInterface::optimalDriver() { + #if defined(VIDEO_WGL) + return "OpenGL"; + #elif defined(VIDEO_DIRECT3D) + return "Direct3D"; + #elif defined(VIDEO_DIRECTDRAW) + return "DirectDraw"; + #elif defined(VIDEO_GDI) + return "GDI"; + + #elif defined(VIDEO_CGL) + return "OpenGL"; + + #elif defined(VIDEO_GLX) + return "OpenGL"; + #elif defined(VIDEO_XV) + return "X-Video"; + #elif defined(VIDEO_XSHM) + return "XShm"; + #elif defined(VIDEO_SDL) + return "SDL"; + + #else + return "None"; + #endif +} + +const char* VideoInterface::safestDriver() { #if defined(VIDEO_DIRECT3D) return "Direct3D"; #elif defined(VIDEO_WGL) @@ -85,39 +111,37 @@ const char* VideoInterface::default_driver() { return "DirectDraw"; #elif defined(VIDEO_GDI) return "GDI"; + #elif defined(VIDEO_CGL) return "OpenGL"; + #elif defined(VIDEO_XSHM) return "XShm"; - #elif defined(VIDEO_QTOPENGL) - return "Qt-OpenGL"; - #elif defined(VIDEO_QTRASTER) - return "Qt-Raster"; #elif defined(VIDEO_SDL) return "SDL"; #elif defined(VIDEO_XV) return "X-Video"; #elif defined(VIDEO_GLX) return "OpenGL"; + #else return "None"; #endif } -//returns list of available drivers, sorted from most to least optimal -const char* VideoInterface::driver_list() { +const char* VideoInterface::availableDrivers() { return //Windows - #if defined(VIDEO_DIRECT3D) - "Direct3D;" - #endif - #if defined(VIDEO_WGL) "OpenGL;" #endif + #if defined(VIDEO_DIRECT3D) + "Direct3D;" + #endif + #if defined(VIDEO_DIRECTDRAW) "DirectDraw;" #endif @@ -138,10 +162,6 @@ const char* VideoInterface::driver_list() { "OpenGL;" #endif - #if defined(VIDEO_QTOPENGL) - "Qt-OpenGL;" - #endif - #if defined(VIDEO_XV) "X-Video;" #endif @@ -150,10 +170,6 @@ const char* VideoInterface::driver_list() { "XShm;" #endif - #if defined(VIDEO_QTRASTER) - "Qt-Raster;" - #endif - #if defined(VIDEO_SDL) "SDL;" #endif @@ -193,7 +209,7 @@ const char* Audio::Latency = "Latency"; void AudioInterface::driver(const char* driver) { if(p) term(); - if(!driver || !*driver) driver = default_driver(); + if(!driver || !*driver) driver = optimalDriver(); if(0); @@ -232,12 +248,36 @@ void AudioInterface::driver(const char* driver) { else p = new Audio(); } -//select the *safest* available driver, not the fastest -const char* AudioInterface::default_driver() { +const char* AudioInterface::optimalDriver() { + #if defined(AUDIO_XAUDIO2) + return "XAudio2"; + #elif defined(AUDIO_DIRECTSOUND) + return "DirectSound"; + + #elif defined(AUDIO_ALSA) + return "ALSA"; + #elif defined(AUDIO_OPENAL) + return "OpenAL"; + #elif defined(AUDIO_OSS) + return "OSS"; + #elif defined(AUDIO_PULSEAUDIO) + return "PulseAudio"; + #elif defined(AUDIO_PULSEAUDIOSIMPLE) + return "PulseAudioSimple"; + #elif defined(AUDIO_AO) + return "libao"; + + #else + return "None"; + #endif +} + +const char* AudioInterface::safestDriver() { #if defined(AUDIO_DIRECTSOUND) return "DirectSound"; #elif defined(AUDIO_XAUDIO2) return "XAudio2"; + #elif defined(AUDIO_ALSA) return "ALSA"; #elif defined(AUDIO_OPENAL) @@ -250,25 +290,25 @@ const char* AudioInterface::default_driver() { return "libao"; #elif defined(AUDIO_OSS) return "OSS"; + #else return "None"; #endif } -//returns list of available drivers, sorted from most to least optimal -const char* AudioInterface::driver_list() { +const char* AudioInterface::availableDrivers() { return //Windows - #if defined(AUDIO_DIRECTSOUND) - "DirectSound;" - #endif - #if defined(AUDIO_XAUDIO2) "XAudio2;" #endif + #if defined(AUDIO_DIRECTSOUND) + "DirectSound;" + #endif + //Linux #if defined(AUDIO_ALSA) @@ -328,7 +368,7 @@ const char* Input::JoypadSupport = "JoypadSupport"; void InputInterface::driver(const char* driver) { if(p) term(); - if(!driver || !*driver) driver = default_driver(); + if(!driver || !*driver) driver = optimalDriver(); if(0); @@ -355,24 +395,45 @@ void InputInterface::driver(const char* driver) { else p = new Input(); } -//select the *safest* available driver, not the fastest -const char* InputInterface::default_driver() { +const char* InputInterface::optimalDriver() { #if defined(INPUT_RAWINPUT) return "RawInput"; #elif defined(INPUT_DIRECTINPUT) return "DirectInput"; + #elif defined(INPUT_CARBON) return "Carbon"; + #elif defined(INPUT_SDL) return "SDL"; #elif defined(INPUT_X) return "X-Windows"; + + #else + return "None"; + #endif +} + +const char* InputInterface::safestDriver() { + #if defined(INPUT_RAWINPUT) + return "RawInput"; + #elif defined(INPUT_DIRECTINPUT) + return "DirectInput"; + + #elif defined(INPUT_CARBON) + return "Carbon"; + + #elif defined(INPUT_SDL) + return "SDL"; + #elif defined(INPUT_X) + return "X-Windows"; + #else return "none"; #endif } -const char* InputInterface::driver_list() { +const char* InputInterface::availableDrivers() { return //Windows diff --git a/higan/ruby/ruby.hpp b/higan/ruby/ruby.hpp index e5357bd6..d50adf94 100755 --- a/higan/ruby/ruby.hpp +++ b/higan/ruby/ruby.hpp @@ -1,6 +1,6 @@ /* ruby - version: 0.09 (2013-04-06) + version: 0.10 (2013-07-27) license: public domain */ @@ -18,8 +18,9 @@ namespace ruby { struct VideoInterface { void driver(const char* driver = ""); - const char* default_driver(); - const char* driver_list(); + const char* optimalDriver(); + const char* safestDriver(); + const char* availableDrivers(); bool init(); void term(); @@ -40,8 +41,9 @@ private: struct AudioInterface { void driver(const char* driver = ""); - const char* default_driver(); - const char* driver_list(); + const char* optimalDriver(); + const char* safestDriver(); + const char* availableDrivers(); bool init(); void term(); @@ -60,8 +62,9 @@ private: struct InputInterface { void driver(const char* driver = ""); - const char* default_driver(); - const char* driver_list(); + const char* optimalDriver(); + const char* safestDriver(); + const char* availableDrivers(); bool init(); void term(); diff --git a/higan/ruby/video/glx.cpp b/higan/ruby/video/glx.cpp index d7abe2e2..1c6a6329 100755 --- a/higan/ruby/video/glx.cpp +++ b/higan/ruby/video/glx.cpp @@ -1,9 +1,13 @@ #include "opengl/opengl.hpp" +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 + namespace ruby { struct pVideoGLX : OpenGL { - int (*glSwapInterval)(int); + GLXContext (*glXCreateContextAttribs)(Display*, GLXFBConfig, GLXContext, int, const int*) = nullptr; + int (*glXSwapInterval)(int) = nullptr; Display* display; int screen; @@ -52,7 +56,7 @@ struct pVideoGLX : OpenGL { if(name == Video::Synchronize) { if(settings.synchronize != any_cast(value)) { settings.synchronize = any_cast(value); - if(glSwapInterval) glSwapInterval(settings.synchronize); + if(glXSwapInterval) glXSwapInterval(settings.synchronize); return true; } } @@ -136,7 +140,7 @@ struct pVideoGLX : OpenGL { GLX_RED_SIZE, (signed)(settings.depth / 3), GLX_GREEN_SIZE, (signed)(settings.depth / 3) + (signed)(settings.depth % 3), GLX_BLUE_SIZE, (signed)(settings.depth / 3), - None, + None }; int fbCount; @@ -170,6 +174,28 @@ struct pVideoGLX : OpenGL { glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE); glXMakeCurrent(display, glxwindow = xwindow, glxcontext); + glXCreateContextAttribs = (GLXContext (*)(Display*, GLXFBConfig, GLXContext, int, const int*))glGetProcAddress("glXCreateContextAttribsARB"); + glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI"); + if(!glXSwapInterval) glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA"); + + if(glXCreateContextAttribs) { + int attributes[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 2, + None + }; + GLXContext context = glXCreateContextAttribs(display, fbConfig[0], nullptr, true, attributes); + if(context) { + glXMakeCurrent(display, 0, nullptr); + glXDestroyContext(display, glxcontext); + glXMakeCurrent(display, glxwindow, glxcontext = context); + } + } + + if(glXSwapInterval) { + glXSwapInterval(settings.synchronize); + } + //read attributes of frame buffer for later use, as requested attributes from above are not always granted int value = 0; glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value); @@ -177,12 +203,6 @@ struct pVideoGLX : OpenGL { glx.is_direct = glXIsDirect(display, glxcontext); OpenGL::init(); - - //vertical synchronization - if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI"); - if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA"); - if( glSwapInterval) glSwapInterval(settings.synchronize); - return true; } @@ -191,7 +211,7 @@ struct pVideoGLX : OpenGL { if(glxcontext) { glXDestroyContext(display, glxcontext); - glxcontext = 0; + glxcontext = nullptr; } if(xwindow) { @@ -205,7 +225,7 @@ struct pVideoGLX : OpenGL { } } - pVideoGLX() : glSwapInterval(0) { + pVideoGLX() { display = XOpenDisplay(0); screen = DefaultScreen(display); @@ -216,7 +236,7 @@ struct pVideoGLX : OpenGL { xwindow = 0; colormap = 0; - glxcontext = 0; + glxcontext = nullptr; glxwindow = 0; } diff --git a/higan/ruby/video/opengl/program.hpp b/higan/ruby/video/opengl/program.hpp index e36ac3fe..cfbb9026 100644 --- a/higan/ruby/video/opengl/program.hpp +++ b/higan/ruby/video/opengl/program.hpp @@ -4,9 +4,9 @@ void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin modulo = glrModulo(node["modulo"].integer()); string w = node["width"].text(), h = node["height"].text(); - if(w.endswith("%")) relativeWidth = fp(w.rtrim<1>("%")) / 100.0; + if(w.endswith("%")) relativeWidth = real(w.rtrim<1>("%")) / 100.0; else absoluteWidth = decimal(w); - if(h.endswith("%")) relativeHeight = fp(h.rtrim<1>("%")) / 100.0; + if(h.endswith("%")) relativeHeight = real(h.rtrim<1>("%")) / 100.0; else absoluteHeight = decimal(h); if(node.name != "program") return; diff --git a/higan/ruby/video/opengl/shaders.hpp b/higan/ruby/video/opengl/shaders.hpp index 558283c3..a7c46908 100644 --- a/higan/ruby/video/opengl/shaders.hpp +++ b/higan/ruby/video/opengl/shaders.hpp @@ -29,6 +29,7 @@ static string OpenGLOutputVertexShader = R"( //align image to even pixel boundary to prevent aliasing vec2 align = fract((outputSize.xy + targetSize.xy) / 2.0) * 2.0; gl_Position.xy -= align / outputSize.xy; + gl_Position.zw = vec2(0.0, 1.0); vertexOut.texCoord = texCoord; } diff --git a/higan/ruby/video/wgl.cpp b/higan/ruby/video/wgl.cpp index c24cf085..a5801f96 100755 --- a/higan/ruby/video/wgl.cpp +++ b/higan/ruby/video/wgl.cpp @@ -1,9 +1,13 @@ #include "opengl/opengl.hpp" +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 + namespace ruby { struct pVideoWGL : OpenGL { - BOOL (APIENTRY* glSwapInterval)(int); + HGLRC (APIENTRY* wglCreateContextAttribs)(HDC, HGLRC, const int*) = nullptr; + BOOL (APIENTRY* wglSwapInterval)(int) = nullptr; HDC display; HGLRC wglcontext; @@ -104,12 +108,28 @@ struct pVideoWGL : OpenGL { wglcontext = wglCreateContext(display); wglMakeCurrent(display, wglcontext); + wglCreateContextAttribs = (HGLRC (APIENTRY*)(HDC, HGLRC, const int*))glGetProcAddress("wglCreateContextAttribsARB"); + wglSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT"); + + if(wglCreateContextAttribs) { + int attributes[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 2, + 0 + }; + HGLRC context = wglCreateContextAttribs(display, 0, attributes); + if(context) { + wglMakeCurrent(NULL, NULL); + wglDeleteContext(wglcontext); + wglMakeCurrent(display, wglcontext = context); + } + } + + if(wglSwapInterval) { + wglSwapInterval(settings.synchronize); + } + OpenGL::init(); - - //vertical synchronization - if(!glSwapInterval) glSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT"); - if( glSwapInterval) glSwapInterval(settings.synchronize); - return true; } @@ -122,7 +142,7 @@ struct pVideoWGL : OpenGL { } } - pVideoWGL() : glSwapInterval(0) { + pVideoWGL() { settings.handle = 0; settings.synchronize = false; settings.filter = 0; diff --git a/higan/sfc/cartridge/markup.cpp b/higan/sfc/cartridge/markup.cpp index 6b3431be..ee9b9174 100755 --- a/higan/sfc/cartridge/markup.cpp +++ b/higan/sfc/cartridge/markup.cpp @@ -19,7 +19,7 @@ void Cartridge::parse_markup(const char* markup) { parse_markup_sa1(cartridge["sa1"]); parse_markup_superfx(cartridge["superfx"]); parse_markup_armdsp(cartridge["armdsp"]); - parse_markup_hitachidsp(cartridge["hitachidsp"], cartridge["board/type"].data.wildcard("2DC*") ? 2 : 1); + parse_markup_hitachidsp(cartridge["hitachidsp"], cartridge["board/type"].data.match("2DC*") ? 2 : 1); parse_markup_necdsp(cartridge["necdsp"]); parse_markup_epsonrtc(cartridge["epsonrtc"]); parse_markup_sharprtc(cartridge["sharprtc"]); diff --git a/higan/sfc/cheat/cheat.cpp b/higan/sfc/cheat/cheat.cpp index d8a7f6f1..9f1f2020 100755 --- a/higan/sfc/cheat/cheat.cpp +++ b/higan/sfc/cheat/cheat.cpp @@ -71,7 +71,7 @@ bool Cheat::decode(string code, unsigned& addr, unsigned& data) { #define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f')) - if(t.wildcard("??????:??")) { + if(t.match("??????:??")) { //Direct t = {substr(t, 0, 6), substr(t, 7, 2)}; for(unsigned n = 0; n < 8; n++) if(!ischr(t[n])) return false; //validate input @@ -82,7 +82,7 @@ bool Cheat::decode(string code, unsigned& addr, unsigned& data) { return true; } - if(t.wildcard("????" "-" "????")) { + if(t.match(R"(????-????)")) { //Game Genie t = {substr(t, 0, 4), substr(t, 5, 4)}; for(unsigned n = 0; n < 8; n++) if(!ischr(t[n])) return false; //validate input diff --git a/higan/target-ethos/configuration/configuration.cpp b/higan/target-ethos/configuration/configuration.cpp index 4f2da7f3..420a206d 100755 --- a/higan/target-ethos/configuration/configuration.cpp +++ b/higan/target-ethos/configuration/configuration.cpp @@ -2,7 +2,7 @@ ConfigurationSettings* config = nullptr; ConfigurationSettings::ConfigurationSettings() { - video.append(video.driver = ruby::video.default_driver(), "Driver"); + video.append(video.driver = ruby::video.optimalDriver(), "Driver"); video.append(video.synchronize = false, "Synchronize"); video.append(video.shader = "Blur", "Shader"); video.append(video.scaleMode = 0, "ScaleMode"); @@ -17,7 +17,7 @@ ConfigurationSettings::ConfigurationSettings() { video.append(video.startFullScreen = false, "StartFullScreen"); append(video, "Video"); - audio.append(audio.driver = ruby::audio.default_driver(), "Driver"); + audio.append(audio.driver = ruby::audio.optimalDriver(), "Driver"); audio.append(audio.synchronize = true, "Synchronize"); audio.append(audio.frequency = 48000, "Frequency"); audio.append(audio.latency = 60, "Latency"); @@ -26,7 +26,7 @@ ConfigurationSettings::ConfigurationSettings() { audio.append(audio.mute = false, "Mute"); append(audio, "Audio"); - input.append(input.driver = ruby::input.default_driver(), "Driver"); + input.append(input.driver = ruby::input.optimalDriver(), "Driver"); input.focus.append(input.focus.pause = false, "Pause"); input.focus.append(input.focus.allow = false, "AllowInput"); input.append(input.focus, "Focus"); diff --git a/higan/target-ethos/ethos.cpp b/higan/target-ethos/ethos.cpp index f26d0d21..0ca2a067 100755 --- a/higan/target-ethos/ethos.cpp +++ b/higan/target-ethos/ethos.cpp @@ -134,6 +134,10 @@ int main(int argc, char** argv) { Application::setName("higan"); + Application::Windows::onModalBegin = [&] { + audio.clear(); + }; + Application::Cocoa::onActivate = [&] { presentation->setVisible(); }; diff --git a/higan/target-ethos/general/dip-switches.cpp b/higan/target-ethos/general/dip-switches.cpp index 233e8251..900f624d 100755 --- a/higan/target-ethos/general/dip-switches.cpp +++ b/higan/target-ethos/general/dip-switches.cpp @@ -41,8 +41,10 @@ unsigned DipSwitches::run(const Markup::Node& node) { dip[index].value.setEnabled(); for(auto& option : setting) { if(option.name != "option") continue; - dip[index].value.append(option["name"].data); - dip[index].values.append(fixedpoint::parse(option["value"].data)); + if(auto result = Eval::integer(option["value"].data)) { + dip[index].value.append(option["name"].data); + dip[index].values.append(result()); + } } if(++index >= Slots) break; diff --git a/higan/target-ethos/general/presentation.cpp b/higan/target-ethos/general/presentation.cpp index 997d5339..91450e20 100755 --- a/higan/target-ethos/general/presentation.cpp +++ b/higan/target-ethos/general/presentation.cpp @@ -57,6 +57,9 @@ Presentation::Presentation() { setMenuVisible(); setStatusVisible(); + setDroppable(); + viewport.setDroppable(); + loadMenu.setText("Library"); loadImport.setText("Import Game ..."); settingsMenu.setText("Settings"); @@ -112,6 +115,13 @@ Presentation::Presentation() { append(layout); layout.append(viewport, {0, 0, 1, 1}); + onDrop = viewport.onDrop = [&](lstring paths) { + if(paths.size() && directory::exists(paths[0])) { + utility->loadMedia(paths[0]); + setFocused(); + } + }; + onSize = [&] { utility->resize(); }; diff --git a/higan/target-ethos/input/input.cpp b/higan/target-ethos/input/input.cpp index f86abc34..39a1ec1a 100755 --- a/higan/target-ethos/input/input.cpp +++ b/higan/target-ethos/input/input.cpp @@ -14,13 +14,13 @@ void AbstractInput::bind() { else if(mapping.endswith(".Right")) type = Input::Type::HatRight; else if(mapping.endswith(".Lo")) type = Input::Type::AxisLo; else if(mapping.endswith(".Hi")) type = Input::Type::AxisHi; - else if(mapping.beginswith("JP") && mapping.position("Axis")) type = Input::Type::Axis; + else if(mapping.beginswith("JP") && mapping.find("Axis")) type = Input::Type::Axis; else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Input::Type::MouseAxis; else if(mapping.beginswith("MS")) type = Input::Type::MouseButton; else type = Input::Type::Button; string decode = mapping; - if(auto position = decode.position(".")) decode[position()] = 0; + if(auto position = decode.find(".")) decode.resize(position()); unsigned scancode = Scancode::decode(decode); inputList.append({type, scancode}); @@ -28,7 +28,7 @@ void AbstractInput::bind() { } bool AbstractInput::append(string encode) { - if(mapping.position(encode)) return true; //mapping already bound + if(mapping.find(encode)) return true; //mapping already bound if(mapping.empty() || mapping == "None") mapping = encode; //remove "None" else mapping.append(",", encode); //add to existing mapping list bind(); diff --git a/higan/target-ethos/settings/advanced.cpp b/higan/target-ethos/settings/advanced.cpp index cb4fb3d9..826604fe 100644 --- a/higan/target-ethos/settings/advanced.cpp +++ b/higan/target-ethos/settings/advanced.cpp @@ -26,19 +26,19 @@ AdvancedSettings::AdvancedSettings() { lstring list; - list.split(";", video.driver_list()); + list.split(";", video.availableDrivers()); for(unsigned n = 0; n < list.size(); n++) { videoDriver.append(list[n]); if(list[n] == config->video.driver) videoDriver.setSelection(n); } - list.split(";", audio.driver_list()); + list.split(";", audio.availableDrivers()); for(unsigned n = 0; n < list.size(); n++) { audioDriver.append(list[n]); if(list[n] == config->audio.driver) audioDriver.setSelection(n); } - list.split(";", input.driver_list()); + list.split(";", input.availableDrivers()); for(unsigned n = 0; n < list.size(); n++) { inputDriver.append(list[n]); if(list[n] == config->input.driver) inputDriver.setSelection(n);